further updates
[phpbb.git] / phpBB / includes / message_parser.php
blobacc4547039ff8da818fd13714d71b0d0c0680f88
1 <?php
2 /**
4 * @package phpBB3
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
12 * @ignore
14 if (!defined('IN_PHPBB'))
16 exit;
19 if (!class_exists('bbcode'))
21 include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
24 /**
25 * BBCODE FIRSTPASS
26 * BBCODE first pass class (functions for parsing messages for db storage)
27 * @package phpBB3
29 class bbcode_firstpass extends bbcode
31 var $message = '';
32 var $warn_msg = array();
33 var $parsed_items = array();
35 /**
36 * Parse BBCode
38 function parse_bbcode()
40 if (!$this->bbcodes)
42 $this->bbcode_init();
45 global $user;
47 $this->bbcode_bitfield = '';
48 $bitfield = new bitfield();
50 foreach ($this->bbcodes as $bbcode_name => $bbcode_data)
52 if (isset($bbcode_data['disabled']) && $bbcode_data['disabled'])
54 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
56 if (preg_match($regexp, $this->message))
58 $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']');
59 continue;
63 else
65 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
67 // The pattern gets compiled and cached by the PCRE extension,
68 // it should not demand recompilation
69 if (preg_match($regexp, $this->message))
71 $this->message = preg_replace($regexp, $replacement, $this->message);
72 $bitfield->set($bbcode_data['bbcode_id']);
78 $this->bbcode_bitfield = $bitfield->get_base64();
81 /**
82 * Prepare some bbcodes for better parsing
84 function prepare_bbcodes()
86 // 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".
87 // Fine by me ;) Will ease our live... but do not come back and cry at us, we won't hear you.
89 /* Add newline at the end and in front of each quote block to prevent parsing errors (urls, smilies, etc.)
90 if (strpos($this->message, '[quote') !== false && strpos($this->message, '[/quote]') !== false)
92 $this->message = str_replace("\r\n", "\n", $this->message);
94 // We strip newlines and spaces after and before quotes in quotes (trimming) and then add exactly one newline
95 $this->message = preg_replace('#\[quote(=&quot;.*?&quot;)?\]\s*(.*?)\s*\[/quote\]#siu', '[quote\1]' . "\n" . '\2' ."\n[/quote]", $this->message);
99 // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...)
103 * Init bbcode data for later parsing
105 function bbcode_init()
107 static $rowset;
109 // This array holds all bbcode data. BBCodes will be processed in this
110 // order, so it is important to keep [code] in first position and
111 // [quote] in second position.
112 $this->bbcodes = array(
113 'code' => array('bbcode_id' => 8, 'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#ise' => "\$this->bbcode_code('\$1', '\$2')")),
114 'quote' => array('bbcode_id' => 0, 'regexp' => array('#\[quote(?:=&quot;(.*?)&quot;)?\](.+)\[/quote\]#ise' => "\$this->bbcode_quote('\$0')")),
115 'attachment' => array('bbcode_id' => 12, 'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#ise' => "\$this->bbcode_attachment('\$1', '\$2')")),
116 'b' => array('bbcode_id' => 1, 'regexp' => array('#\[b\](.*?)\[/b\]#ise' => "\$this->bbcode_strong('\$1')")),
117 'i' => array('bbcode_id' => 2, 'regexp' => array('#\[i\](.*?)\[/i\]#ise' => "\$this->bbcode_italic('\$1')")),
118 'url' => array('bbcode_id' => 3, 'regexp' => array('#\[url(=(.*))?\](.*)\[/url\]#iUe' => "\$this->validate_url('\$2', '\$3')")),
119 'img' => array('bbcode_id' => 4, 'regexp' => array('#\[img\](.*)\[/img\]#iUe' => "\$this->bbcode_img('\$1')")),
120 'size' => array('bbcode_id' => 5, 'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#ise' => "\$this->bbcode_size('\$1', '\$2')")),
121 'color' => array('bbcode_id' => 6, 'regexp' => array('!\[color=(#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!ise' => "\$this->bbcode_color('\$1', '\$2')")),
122 'u' => array('bbcode_id' => 7, 'regexp' => array('#\[u\](.*?)\[/u\]#ise' => "\$this->bbcode_underline('\$1')")),
123 'list' => array('bbcode_id' => 9, 'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#ise' => "\$this->bbcode_parse_list('\$0')")),
124 'email' => array('bbcode_id' => 10, 'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#ise' => "\$this->validate_email('\$1', '\$2')")),
125 'flash' => array('bbcode_id' => 11, 'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#ie' => "\$this->bbcode_flash('\$1', '\$2', '\$3')"))
128 // Zero the parsed items array
129 $this->parsed_items = array();
131 foreach ($this->bbcodes as $tag => $bbcode_data)
133 $this->parsed_items[$tag] = 0;
136 if (!is_array($rowset))
138 global $db;
139 $rowset = array();
141 $sql = 'SELECT *
142 FROM ' . BBCODES_TABLE;
143 $result = $db->sql_query($sql);
145 while ($row = $db->sql_fetchrow($result))
147 $rowset[] = $row;
149 $db->sql_freeresult($result);
152 foreach ($rowset as $row)
154 $this->bbcodes[$row['bbcode_tag']] = array(
155 'bbcode_id' => (int) $row['bbcode_id'],
156 'regexp' => array($row['first_pass_match'] => str_replace('$uid', $this->bbcode_uid, $row['first_pass_replace']))
162 * Making some pre-checks for bbcodes as well as increasing the number of parsed items
164 function check_bbcode($bbcode, &$in)
166 // when using the /e modifier, preg_replace slashes double-quotes but does not
167 // seem to slash anything else
168 $in = str_replace("\r\n", "\n", str_replace('\"', '"', $in));
170 // Trimming here to make sure no empty bbcodes are parsed accidently
171 if (trim($in) == '')
173 return false;
176 $this->parsed_items[$bbcode]++;
178 return true;
182 * Transform some characters in valid bbcodes
184 function bbcode_specialchars($text)
186 $str_from = array('<', '>', '[', ']', '.', ':');
187 $str_to = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;');
189 return str_replace($str_from, $str_to, $text);
193 * Parse size tag
195 function bbcode_size($stx, $in)
197 global $user, $config;
199 if (!$this->check_bbcode('size', $in))
201 return '';
204 if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx)
206 $this->warn_msg[] = sprintf($user->lang['MAX_FONT_SIZE_EXCEEDED'], $config['max_' . $this->mode . '_font_size']);
208 return '[size=' . $stx . ']' . $in . '[/size]';
211 return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']';
215 * Parse color tag
217 function bbcode_color($stx, $in)
219 if (!$this->check_bbcode('color', $in))
221 return '';
224 return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']';
228 * Parse u tag
230 function bbcode_underline($in)
232 if (!$this->check_bbcode('u', $in))
234 return '';
237 return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']';
241 * Parse b tag
243 function bbcode_strong($in)
245 if (!$this->check_bbcode('b', $in))
247 return '';
250 return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']';
254 * Parse i tag
256 function bbcode_italic($in)
258 if (!$this->check_bbcode('i', $in))
260 return '';
263 return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']';
267 * Parse img tag
269 function bbcode_img($in)
271 global $user, $config;
273 if (!$this->check_bbcode('img', $in))
275 return '';
278 $in = trim($in);
279 $error = false;
281 $in = str_replace(' ', '%20', $in);
283 // Checking urls
284 if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#i', $in))
286 return '[img]' . $in . '[/img]';
289 // Try to cope with a common user error... not specifying a protocol but only a subdomain
290 if (!preg_match('#^[a-z0-9]+://#i', $in))
292 $in = 'http://' . $in;
295 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
297 $stats = @getimagesize($in);
299 if ($stats === false)
301 $error = true;
302 $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
304 else
306 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $stats[1])
308 $error = true;
309 $this->warn_msg[] = sprintf($user->lang['MAX_IMG_HEIGHT_EXCEEDED'], $config['max_' . $this->mode . '_img_height']);
312 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $stats[0])
314 $error = true;
315 $this->warn_msg[] = sprintf($user->lang['MAX_IMG_WIDTH_EXCEEDED'], $config['max_' . $this->mode . '_img_width']);
320 if ($error || $this->path_in_domain($in))
322 return '[img]' . $in . '[/img]';
325 return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']';
329 * Parse flash tag
331 function bbcode_flash($width, $height, $in)
333 global $user, $config;
335 if (!$this->check_bbcode('flash', $in))
337 return '';
340 $in = trim($in);
341 $error = false;
343 // Apply the same size checks on flash files as on images
344 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
346 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height)
348 $error = true;
349 $this->warn_msg[] = sprintf($user->lang['MAX_FLASH_HEIGHT_EXCEEDED'], $config['max_' . $this->mode . '_img_height']);
352 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width)
354 $error = true;
355 $this->warn_msg[] = sprintf($user->lang['MAX_FLASH_WIDTH_EXCEEDED'], $config['max_' . $this->mode . '_img_width']);
359 if ($error || $this->path_in_domain($in))
361 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
364 return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']';
368 * Parse inline attachments [ia]
370 function bbcode_attachment($stx, $in)
372 if (!$this->check_bbcode('attachment', $in))
374 return '';
377 return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']';
381 * Parse code text from code tag
382 * @private
384 function bbcode_parse_code($stx, &$code)
386 switch (strtolower($stx))
388 case 'php':
390 $remove_tags = false;
391 $code = str_replace(array('&lt;', '&gt;'), array('<', '>'), $code);
393 if (!preg_match('/\<\?.*?\?\>/is', $code))
395 $remove_tags = true;
396 $code = "<?php $code ?>";
399 $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string');
400 foreach ($conf as $ini_var)
402 @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var));
405 // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results
406 $code = htmlspecialchars_decode($code);
407 $code = highlight_string($code, true);
409 $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':');
410 $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '&#91;', '&#93;', '&#46;', '&#58;');
412 if ($remove_tags)
414 $str_from[] = '<span class="syntaxdefault">&lt;?php </span>';
415 $str_to[] = '';
416 $str_from[] = '<span class="syntaxdefault">&lt;?php&nbsp;';
417 $str_to[] = '<span class="syntaxdefault">';
420 $code = str_replace($str_from, $str_to, $code);
421 $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code);
423 if ($remove_tags)
425 $code = preg_replace('#(<span class="[a-z]+">)?\?&gt;(</span>)#', '$1&nbsp;$2', $code);
428 $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code);
429 $code = preg_replace('#(?:\s++|&nbsp;)*+</span>$#u', '</span>', $code);
431 // remove newline at the end
432 if (!empty($code) && substr($code, -1) == "\n")
434 $code = substr($code, 0, -1);
437 return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']';
438 break;
440 default:
441 return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']';
442 break;
447 * Parse code tag
448 * Expects the argument to start right after the opening [code] tag and to end with [/code]
450 function bbcode_code($stx, $in)
452 if (!$this->check_bbcode('code', $in))
454 return '';
457 // We remove the hardcoded elements from the code block here because it is not used in code blocks
458 // Having it here saves us one preg_replace per message containing [code] blocks
459 // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too...
460 $htm_match = get_preg_expression('bbcode_htm');
461 unset($htm_match[4], $htm_match[5]);
462 $htm_replace = array('\1', '\1', '\2', '\1');
464 $out = $code_block = '';
465 $open = 1;
467 while ($in)
469 // Determine position and tag length of next code block
470 preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer);
471 $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false;
472 $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false;
474 // Determine position of ending code tag
475 $pos2 = stripos($in, '[/code]');
477 // Which is the next block, ending code or code block
478 if ($pos !== false && $pos < $pos2)
480 // Open new block
481 if (!$open)
483 $out .= substr($in, 0, $pos);
484 $in = substr($in, $pos);
485 $stx = (isset($buffer[3])) ? $buffer[3] : '';
486 $code_block = '';
488 else
490 // Already opened block, just append to the current block
491 $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : '');
492 $in = substr($in, $pos);
495 $in = substr($in, $tag_length);
496 $open++;
498 else
500 // Close the block
501 if ($open == 1)
503 $code_block .= substr($in, 0, $pos2);
504 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
506 // Parse this code block
507 $out .= $this->bbcode_parse_code($stx, $code_block);
508 $code_block = '';
509 $open--;
511 else if ($open)
513 // Close one open tag... add to the current code block
514 $code_block .= substr($in, 0, $pos2 + 7);
515 $open--;
517 else
519 // end code without opening code... will be always outside code block
520 $out .= substr($in, 0, $pos2 + 7);
523 $in = substr($in, $pos2 + 7);
527 // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up.
528 if ($code_block)
530 $code_block = substr($code_block, 0, -7);
531 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
533 $out .= $this->bbcode_parse_code($stx, $code_block);
536 return $out;
540 * Parse list bbcode
541 * Expects the argument to start with a tag
543 function bbcode_parse_list($in)
545 if (!$this->check_bbcode('list', $in))
547 return '';
550 // $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
551 $tok = ']';
552 $out = '[';
554 // First character is [
555 $in = substr($in, 1);
556 $list_end_tags = $item_end_tags = array();
560 $pos = strlen($in);
562 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
564 $tmp_pos = strpos($in, $tok[$i]);
566 if ($tmp_pos !== false && $tmp_pos < $pos)
568 $pos = $tmp_pos;
572 $buffer = substr($in, 0, $pos);
573 $tok = $in[$pos];
575 $in = substr($in, $pos + 1);
577 if ($tok == ']')
579 // if $tok is ']' the buffer holds a tag
580 if (strtolower($buffer) == '/list' && sizeof($list_end_tags))
582 // valid [/list] tag, check nesting so that we don't hit false positives
583 if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags))
585 // current li tag has not been closed
586 $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . '][';
589 $out .= array_pop($list_end_tags) . ']';
590 $tok = '[';
592 else if (preg_match('#^list(=[0-9a-z])?$#i', $buffer, $m))
594 // sub-list, add a closing tag
595 if (empty($m[1]) || preg_match('/^(?:disc|square|circle)$/i', $m[1]))
597 array_push($list_end_tags, '/list:u:' . $this->bbcode_uid);
599 else
601 array_push($list_end_tags, '/list:o:' . $this->bbcode_uid);
603 $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']';
604 $tok = '[';
606 else
608 if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags))
610 // the buffer holds a bullet tag and we have a [list] tag open
611 if (sizeof($item_end_tags) >= sizeof($list_end_tags))
613 if (substr($buffer, -2) == '[*')
615 $out .= substr($buffer, 0, -2) . '[';
617 // current li tag has not been closed
618 if (preg_match('/\n\[$/', $out, $m))
620 $out = preg_replace('/\n\[$/', '[', $out);
621 $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid;
623 else
625 $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid;
628 else
630 $buffer = '*:' . $this->bbcode_uid;
633 $item_end_tags[] = '/*:m:' . $this->bbcode_uid;
635 else if ($buffer == '/*')
637 array_pop($item_end_tags);
638 $buffer = '/*:' . $this->bbcode_uid;
641 $out .= $buffer . $tok;
642 $tok = '[]';
645 else
647 // Not within a tag, just add buffer to the return string
648 $out .= $buffer . $tok;
649 $tok = ($tok == '[') ? ']' : '[]';
652 while ($in);
654 // do we have some tags open? close them now
655 if (sizeof($item_end_tags))
657 $out .= '[' . implode('][', $item_end_tags) . ']';
659 if (sizeof($list_end_tags))
661 $out .= '[' . implode('][', $list_end_tags) . ']';
664 return $out;
668 * Parse quote bbcode
669 * Expects the argument to start with a tag
671 function bbcode_quote($in)
673 global $config, $user;
676 * If you change this code, make sure the cases described within the following reports are still working:
677 * #3572 - [quote="[test]test"]test [ test[/quote] - (correct: parsed)
678 * #14667 - [quote]test[/quote] test ] and [ test [quote]test[/quote] (correct: parsed)
679 * #14770 - [quote="["]test[/quote] (correct: parsed)
680 * [quote="[i]test[/i]"]test[/quote] (correct: parsed)
681 * [quote="[quote]test[/quote]"]test[/quote] (correct: NOT parsed)
684 $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));
686 if (!$in)
688 return '';
691 // To let the parser not catch tokens within quote_username quotes we encode them before we start this...
692 $in = preg_replace('#quote=&quot;(.*?)&quot;\]#ie', "'quote=&quot;' . str_replace(array('[', ']'), array('&#91;', '&#93;'), '\$1') . '&quot;]'", $in);
694 $tok = ']';
695 $out = '[';
697 $in = substr($in, 1);
698 $close_tags = $error_ary = array();
699 $buffer = '';
703 $pos = strlen($in);
704 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
706 $tmp_pos = strpos($in, $tok[$i]);
707 if ($tmp_pos !== false && $tmp_pos < $pos)
709 $pos = $tmp_pos;
713 $buffer .= substr($in, 0, $pos);
714 $tok = $in[$pos];
715 $in = substr($in, $pos + 1);
717 if ($tok == ']')
719 if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[')
721 // we have found a closing tag
722 $out .= array_pop($close_tags) . ']';
723 $tok = '[';
724 $buffer = '';
726 /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly
727 * 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.
728 * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982
729 if (!$in || $in[0] !== ' ')
731 $out .= ' ';
734 else if (preg_match('#^quote(?:=&quot;(.*?)&quot;)?$#is', $buffer, $m))
736 $this->parsed_items['quote']++;
738 // the buffer holds a valid opening tag
739 if ($config['max_quote_depth'] && sizeof($close_tags) >= $config['max_quote_depth'])
741 // there are too many nested quotes
742 $error_ary['quote_depth'] = sprintf($user->lang['QUOTE_DEPTH_EXCEEDED'], $config['max_quote_depth']);
744 $out .= $buffer . $tok;
745 $tok = '[]';
746 $buffer = '';
748 continue;
751 array_push($close_tags, '/quote:' . $this->bbcode_uid);
753 if (isset($m[1]) && $m[1])
755 $username = str_replace(array('&#91;', '&#93;'), array('[', ']'), $m[1]);
756 $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '&#91;$1', $username);
758 $end_tags = array();
759 $error = false;
761 preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags);
762 foreach ($tags[1] as $tag)
764 if ($tag[0] != '/')
766 $end_tags[] = '/' . $tag;
768 else
770 $end_tag = array_pop($end_tags);
771 $error = ($end_tag != $tag) ? true : false;
775 if ($error)
777 $username = $m[1];
780 $out .= 'quote=&quot;' . $username . '&quot;:' . $this->bbcode_uid . ']';
782 else
784 $out .= 'quote:' . $this->bbcode_uid . ']';
787 $tok = '[';
788 $buffer = '';
790 else if (preg_match('#^quote=&quot;(.*?)#is', $buffer, $m))
792 // the buffer holds an invalid opening tag
793 $buffer .= ']';
795 else
797 $out .= $buffer . $tok;
798 $tok = '[]';
799 $buffer = '';
802 else
805 * Old quote code working fine, but having errors listed in bug #3572
807 * $out .= $buffer . $tok;
808 * $tok = ($tok == '[') ? ']' : '[]';
809 * $buffer = '';
812 $out .= $buffer . $tok;
814 if ($tok == '[')
816 // Search the text for the next tok... if an ending quote comes first, then change tok to []
817 $pos1 = stripos($in, '[/quote');
818 // If the token ] comes first, we change it to ]
819 $pos2 = strpos($in, ']');
820 // If the token [ comes first, we change it to [
821 $pos3 = strpos($in, '[');
823 if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3))
825 $tok = '[]';
827 else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2))
829 $tok = '[';
831 else
833 $tok = ']';
836 else
838 $tok = '[]';
840 $buffer = '';
843 while ($in);
845 if (sizeof($close_tags))
847 $out .= '[' . implode('][', $close_tags) . ']';
850 foreach ($error_ary as $error_msg)
852 $this->warn_msg[] = $error_msg;
855 return $out;
859 * Validate email
861 function validate_email($var1, $var2)
863 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
864 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
866 $txt = $var2;
867 $email = ($var1) ? $var1 : $var2;
869 $validated = true;
871 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
873 $validated = false;
876 if (!$validated)
878 return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]';
881 $this->parsed_items['email']++;
883 if ($var1)
885 $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']';
887 else
889 $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']';
892 return $retval;
896 * Validate url
898 * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url]
899 * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url]
901 function validate_url($var1, $var2)
903 global $config;
905 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
906 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
908 $url = ($var1) ? $var1 : $var2;
910 if (!$url || ($var1 && !$var2))
912 return '';
915 $valid = false;
917 $url = str_replace(' ', '%20', $url);
919 // Checking urls
920 if (preg_match('#^' . get_preg_expression('url') . '$#i', $url) ||
921 preg_match('#^' . get_preg_expression('www_url') . '$#i', $url) ||
922 preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#i', $url))
924 $valid = true;
927 if ($valid)
929 $this->parsed_items['url']++;
931 // if there is no scheme, then add http schema
932 if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url))
934 $url = 'http://' . $url;
937 // Is this a link to somewhere inside this board? If so then remove the session id from the url
938 if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false)
940 $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}&amp;/', '\1', $url);
941 $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}$/', '', $url);
942 $url = append_sid($url);
945 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 . ']';
948 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
952 * Check if url is pointing to this domain/script_path/php-file
954 * @param string $url the url to check
955 * @return true if the url is pointing to this domain/script_path/php-file, false if not
957 * @access private
959 function path_in_domain($url)
961 global $config, $phpEx, $user;
963 if ($config['force_server_vars'])
965 $check_path = $config['script_path'];
967 else
969 $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/';
972 // Is the user trying to link to a php file in this domain and script path?
973 if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false)
975 $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
977 // Forcing server vars is the only way to specify/override the protocol
978 if ($config['force_server_vars'] || !$server_name)
980 $server_name = $config['server_name'];
983 // Check again in correct order...
984 $pos_ext = strpos($url, ".{$phpEx}");
985 $pos_path = strpos($url, $check_path);
986 $pos_domain = strpos($url, $server_name);
988 if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path)
990 // Ok, actually we allow linking to some files (this may be able to be extended in some way later...)
991 if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0)
993 return false;
996 return true;
1000 return false;
1005 * Main message parser for posting, pm, etc. takes raw message
1006 * and parses it for attachments, bbcode and smilies
1007 * @package phpBB3
1009 class parse_message extends bbcode_firstpass
1011 var $attachment_data = array();
1012 var $filename_data = array();
1014 // Helps ironing out user error
1015 var $message_status = '';
1017 var $allow_img_bbcode = true;
1018 var $allow_flash_bbcode = true;
1019 var $allow_quote_bbcode = true;
1020 var $allow_url_bbcode = true;
1022 var $mode;
1025 * Init - give message here or manually
1027 function parse_message($message = '')
1029 // Init BBCode UID
1030 $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN);
1032 if ($message)
1034 $this->message = $message;
1039 * Parse Message
1041 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')
1043 global $config, $db, $user;
1045 $mode = ($mode != 'post') ? 'sig' : 'post';
1047 $this->mode = $mode;
1049 $this->allow_img_bbcode = $allow_img_bbcode;
1050 $this->allow_flash_bbcode = $allow_flash_bbcode;
1051 $this->allow_quote_bbcode = $allow_quote_bbcode;
1052 $this->allow_url_bbcode = $allow_url_bbcode;
1054 // If false, then $this->message won't be altered, the text will be returned instead.
1055 if (!$update_this_message)
1057 $tmp_message = $this->message;
1058 $return_message = &$this->message;
1061 if ($this->message_status == 'display')
1063 $this->decode_message();
1066 // Do some general 'cleanup' first before processing message,
1067 // e.g. remove excessive newlines(?), smilies(?)
1068 $match = array('#(script|about|applet|activex|chrome):#i');
1069 $replace = array("\\1&#058;");
1070 $this->message = preg_replace($match, $replace, trim($this->message));
1072 // Message length check. 0 disables this check completely.
1073 if ($config['max_' . $mode . '_chars'] > 0)
1075 $msg_len = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message));
1077 if ((!$msg_len && $mode !== 'sig') || $config['max_' . $mode . '_chars'] && $msg_len > $config['max_' . $mode . '_chars'])
1079 $this->warn_msg[] = (!$msg_len) ? $user->lang['TOO_FEW_CHARS'] : sprintf($user->lang['TOO_MANY_CHARS_' . strtoupper($mode)], $msg_len, $config['max_' . $mode . '_chars']);
1080 return $this->warn_msg;
1084 // Check for "empty" message
1085 if ($mode !== 'sig' && !utf8_clean_string($this->message))
1087 $this->warn_msg[] = $user->lang['TOO_FEW_CHARS'];
1088 return $this->warn_msg;
1091 // Prepare BBcode (just prepares some tags for better parsing)
1092 if ($allow_bbcode && strpos($this->message, '[') !== false)
1094 $this->bbcode_init();
1095 $disallow = array('img', 'flash', 'quote', 'url');
1096 foreach ($disallow as $bool)
1098 if (!${'allow_' . $bool . '_bbcode'})
1100 $this->bbcodes[$bool]['disabled'] = true;
1104 $this->prepare_bbcodes();
1107 // Parse smilies
1108 if ($allow_smilies)
1110 $this->smilies($config['max_' . $mode . '_smilies']);
1113 $num_urls = 0;
1115 // Parse BBCode
1116 if ($allow_bbcode && strpos($this->message, '[') !== false)
1118 $this->parse_bbcode();
1119 $num_urls += $this->parsed_items['url'];
1122 // Parse URL's
1123 if ($allow_magic_url)
1125 $this->magic_url(generate_board_url());
1127 if ($config['max_' . $mode . '_urls'])
1129 $num_urls += preg_match_all('#\<!-- ([lmwe]) --\>.*?\<!-- \1 --\>#', $this->message, $matches);
1133 // Check number of links
1134 if ($config['max_' . $mode . '_urls'] && $num_urls > $config['max_' . $mode . '_urls'])
1136 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_URLS'], $config['max_' . $mode . '_urls']);
1137 return $this->warn_msg;
1140 if (!$update_this_message)
1142 unset($this->message);
1143 $this->message = $tmp_message;
1144 return $return_message;
1147 $this->message_status = 'parsed';
1148 return false;
1152 * Formatting text for display
1154 function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true)
1156 // If false, then the parsed message get returned but internal message not processed.
1157 if (!$update_this_message)
1159 $tmp_message = $this->message;
1160 $return_message = &$this->message;
1163 if ($this->message_status == 'plain')
1165 // Force updating message - of course.
1166 $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);
1169 // Replace naughty words such as farty pants
1170 $this->message = censor_text($this->message);
1172 // Parse BBcode
1173 if ($allow_bbcode)
1175 $this->bbcode_cache_init();
1177 // We are giving those parameters to be able to use the bbcode class on its own
1178 $this->bbcode_second_pass($this->message, $this->bbcode_uid);
1181 $this->message = bbcode_nl2br($this->message);
1182 $this->message = smiley_text($this->message, !$allow_smilies);
1184 if (!$update_this_message)
1186 unset($this->message);
1187 $this->message = $tmp_message;
1188 return $return_message;
1191 $this->message_status = 'display';
1192 return false;
1196 * Decode message to be placed back into form box
1198 function decode_message($custom_bbcode_uid = '', $update_this_message = true)
1200 // If false, then the parsed message get returned but internal message not processed.
1201 if (!$update_this_message)
1203 $tmp_message = $this->message;
1204 $return_message = &$this->message;
1207 ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid);
1209 if (!$update_this_message)
1211 unset($this->message);
1212 $this->message = $tmp_message;
1213 return $return_message;
1216 $this->message_status = 'plain';
1217 return false;
1221 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
1222 * Cuts down displayed size of link if over 50 chars, turns absolute links
1223 * into relative versions when the server/script path matches the link
1225 function magic_url($server_url)
1227 // We use the global make_clickable function
1228 $this->message = make_clickable($this->message, $server_url);
1232 * Parse Smilies
1234 function smilies($max_smilies = 0)
1236 global $db, $user;
1237 static $match;
1238 static $replace;
1240 // See if the static arrays have already been filled on an earlier invocation
1241 if (!is_array($match))
1243 $match = $replace = array();
1245 // NOTE: obtain_* function? chaching the table contents?
1247 // For now setting the ttl to 10 minutes
1248 switch ($db->sql_layer)
1250 case 'mssql':
1251 case 'mssql_odbc':
1252 $sql = 'SELECT *
1253 FROM ' . SMILIES_TABLE . '
1254 ORDER BY LEN(code) DESC';
1255 break;
1257 case 'firebird':
1258 $sql = 'SELECT *
1259 FROM ' . SMILIES_TABLE . '
1260 ORDER BY CHAR_LENGTH(code) DESC';
1261 break;
1263 // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure...
1264 default:
1265 $sql = 'SELECT *
1266 FROM ' . SMILIES_TABLE . '
1267 ORDER BY LENGTH(code) DESC';
1268 break;
1270 $result = $db->sql_query($sql, 600);
1272 while ($row = $db->sql_fetchrow($result))
1274 if (empty($row['code']))
1276 continue;
1279 // (assertion)
1280 $match[] = '(?<=^|[\n .])' . preg_quote($row['code'], '#') . '(?![^<>]*>)';
1281 $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->';
1283 $db->sql_freeresult($result);
1286 if (sizeof($match))
1288 if ($max_smilies)
1290 $num_matches = preg_match_all('#' . implode('|', $match) . '#', $this->message, $matches);
1291 unset($matches);
1293 if ($num_matches !== false && $num_matches > $max_smilies)
1295 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies);
1296 return;
1300 // Make sure the delimiter # is added in front and at the end of every element within $match
1301 $this->message = trim(preg_replace(explode(chr(0), '#' . implode('#' . chr(0) . '#', $match) . '#'), $replace, $this->message));
1306 * Parse Attachments
1308 function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false)
1310 global $config, $auth, $user, $phpbb_root_path, $phpEx, $db;
1312 $error = array();
1314 $num_attachments = sizeof($this->attachment_data);
1315 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true));
1316 $upload_file = (isset($_FILES[$form_name]) && $_FILES[$form_name]['name'] != 'none' && trim($_FILES[$form_name]['name'])) ? true : false;
1318 $add_file = (isset($_POST['add_file'])) ? true : false;
1319 $delete_file = (isset($_POST['delete_file'])) ? true : false;
1321 // First of all adjust comments if changed
1322 $actual_comment_list = utf8_normalize_nfc(request_var('comment_list', array(''), true));
1324 foreach ($actual_comment_list as $comment_key => $comment)
1326 if (!isset($this->attachment_data[$comment_key]))
1328 continue;
1331 if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key])
1333 $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key];
1337 $cfg = array();
1338 $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments'];
1339 $forum_id = ($is_message) ? 0 : $forum_id;
1341 if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file)
1343 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id))
1345 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message);
1346 $error = $filedata['error'];
1348 if ($filedata['post_attach'] && !sizeof($error))
1350 $sql_ary = array(
1351 'physical_filename' => $filedata['physical_filename'],
1352 'attach_comment' => $this->filename_data['filecomment'],
1353 'real_filename' => $filedata['real_filename'],
1354 'extension' => $filedata['extension'],
1355 'mimetype' => $filedata['mimetype'],
1356 'filesize' => $filedata['filesize'],
1357 'filetime' => $filedata['filetime'],
1358 'thumbnail' => $filedata['thumbnail'],
1359 'is_orphan' => 1,
1360 'in_message' => ($is_message) ? 1 : 0,
1361 'poster_id' => $user->data['user_id'],
1364 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1366 $new_entry = array(
1367 'attach_id' => $db->sql_nextid(),
1368 'is_orphan' => 1,
1369 'real_filename' => $filedata['real_filename'],
1370 'attach_comment'=> $this->filename_data['filecomment'],
1373 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1374 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message);
1376 $this->filename_data['filecomment'] = '';
1378 // This Variable is set to false here, because Attachments are entered into the
1379 // Database in two modes, one if the id_list is 0 and the second one if post_attach is true
1380 // Since post_attach is automatically switched to true if an Attachment got added to the filesystem,
1381 // but we are assigning an id of 0 here, we have to reset the post_attach variable to false.
1383 // This is very relevant, because it could happen that the post got not submitted, but we do not
1384 // know this circumstance here. We could be at the posting page or we could be redirected to the entered
1385 // post. :)
1386 $filedata['post_attach'] = false;
1389 else
1391 $error[] = sprintf($user->lang['TOO_MANY_ATTACHMENTS'], $cfg['max_attachments']);
1395 if ($preview || $refresh || sizeof($error))
1397 // Perform actions on temporary attachments
1398 if ($delete_file)
1400 include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1402 $index = array_keys(request_var('delete_file', array(0 => 0)));
1403 $index = (!empty($index)) ? $index[0] : false;
1405 if ($index !== false && !empty($this->attachment_data[$index]))
1407 // delete selected attachment
1408 if ($this->attachment_data[$index]['is_orphan'])
1410 $sql = 'SELECT attach_id, physical_filename, thumbnail
1411 FROM ' . ATTACHMENTS_TABLE . '
1412 WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . '
1413 AND is_orphan = 1
1414 AND poster_id = ' . $user->data['user_id'];
1415 $result = $db->sql_query($sql);
1416 $row = $db->sql_fetchrow($result);
1417 $db->sql_freeresult($result);
1419 if ($row)
1421 phpbb_unlink($row['physical_filename'], 'file');
1423 if ($row['thumbnail'])
1425 phpbb_unlink($row['physical_filename'], 'thumbnail');
1428 $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']);
1431 else
1433 delete_attachments('attach', array(intval($this->attachment_data[$index]['attach_id'])));
1436 unset($this->attachment_data[$index]);
1437 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "(\\1 == \$index) ? '' : ((\\1 > \$index) ? '[attachment=' . (\\1 - 1) . ']\\2[/attachment]' : '\\0')", $this->message);
1439 // Reindex Array
1440 $this->attachment_data = array_values($this->attachment_data);
1443 else if (($add_file || $preview) && $upload_file)
1445 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id))
1447 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message);
1448 $error = array_merge($error, $filedata['error']);
1450 if (!sizeof($error))
1452 $sql_ary = array(
1453 'physical_filename' => $filedata['physical_filename'],
1454 'attach_comment' => $this->filename_data['filecomment'],
1455 'real_filename' => $filedata['real_filename'],
1456 'extension' => $filedata['extension'],
1457 'mimetype' => $filedata['mimetype'],
1458 'filesize' => $filedata['filesize'],
1459 'filetime' => $filedata['filetime'],
1460 'thumbnail' => $filedata['thumbnail'],
1461 'is_orphan' => 1,
1462 'in_message' => ($is_message) ? 1 : 0,
1463 'poster_id' => $user->data['user_id'],
1466 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1468 $new_entry = array(
1469 'attach_id' => $db->sql_nextid(),
1470 'is_orphan' => 1,
1471 'real_filename' => $filedata['real_filename'],
1472 'attach_comment'=> $this->filename_data['filecomment'],
1475 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1476 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message);
1477 $this->filename_data['filecomment'] = '';
1480 else
1482 $error[] = sprintf($user->lang['TOO_MANY_ATTACHMENTS'], $cfg['max_attachments']);
1487 foreach ($error as $error_msg)
1489 $this->warn_msg[] = $error_msg;
1494 * Get Attachment Data
1496 function get_submitted_attachment_data($check_user_id = false)
1498 global $user, $db, $phpbb_root_path, $phpEx, $config;
1500 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true));
1501 $attachment_data = (isset($_POST['attachment_data'])) ? $_POST['attachment_data'] : array();
1502 $this->attachment_data = array();
1504 $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id;
1506 if (!sizeof($attachment_data))
1508 return;
1511 $not_orphan = $orphan = array();
1513 foreach ($attachment_data as $pos => $var_ary)
1515 if ($var_ary['is_orphan'])
1517 $orphan[(int) $var_ary['attach_id']] = $pos;
1519 else
1521 $not_orphan[(int) $var_ary['attach_id']] = $pos;
1525 // Regenerate already posted attachments
1526 if (sizeof($not_orphan))
1528 // Get the attachment data, based on the poster id...
1529 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
1530 FROM ' . ATTACHMENTS_TABLE . '
1531 WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . '
1532 AND poster_id = ' . $check_user_id;
1533 $result = $db->sql_query($sql);
1535 while ($row = $db->sql_fetchrow($result))
1537 $pos = $not_orphan[$row['attach_id']];
1538 $this->attachment_data[$pos] = $row;
1539 set_var($this->attachment_data[$pos]['attach_comment'], $_POST['attachment_data'][$pos]['attach_comment'], 'string', true);
1541 unset($not_orphan[$row['attach_id']]);
1543 $db->sql_freeresult($result);
1546 if (sizeof($not_orphan))
1548 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1551 // Regenerate newly uploaded attachments
1552 if (sizeof($orphan))
1554 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
1555 FROM ' . ATTACHMENTS_TABLE . '
1556 WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . '
1557 AND poster_id = ' . $user->data['user_id'] . '
1558 AND is_orphan = 1';
1559 $result = $db->sql_query($sql);
1561 while ($row = $db->sql_fetchrow($result))
1563 $pos = $orphan[$row['attach_id']];
1564 $this->attachment_data[$pos] = $row;
1565 set_var($this->attachment_data[$pos]['attach_comment'], $_POST['attachment_data'][$pos]['attach_comment'], 'string', true);
1567 unset($orphan[$row['attach_id']]);
1569 $db->sql_freeresult($result);
1572 if (sizeof($orphan))
1574 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1577 ksort($this->attachment_data);
1581 * Parse Poll
1583 function parse_poll(&$poll)
1585 global $auth, $user, $config;
1587 $poll_max_options = $poll['poll_max_options'];
1589 // Parse Poll Option text ;)
1590 $tmp_message = $this->message;
1591 $this->message = $poll['poll_option_text'];
1592 $bbcode_bitfield = $this->bbcode_bitfield;
1595 $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);
1597 $bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield));
1598 $this->message = $tmp_message;
1600 // Parse Poll Title
1601 $tmp_message = $this->message;
1602 $this->message = $poll['poll_title'];
1603 $this->bbcode_bitfield = $bbcode_bitfield;
1605 $poll['poll_options'] = explode("\n", trim($poll['poll_option_text']));
1606 $poll['poll_options_size'] = sizeof($poll['poll_options']);
1608 if (!$poll['poll_title'] && $poll['poll_options_size'])
1610 $this->warn_msg[] = $user->lang['NO_POLL_TITLE'];
1612 else
1614 if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100)
1616 $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG'];
1618 $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);
1619 if (strlen($poll['poll_title']) > 255)
1621 $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG'];
1625 $this->bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield));
1626 $this->message = $tmp_message;
1627 unset($tmp_message);
1629 if (sizeof($poll['poll_options']) == 1)
1631 $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS'];
1633 else if ($poll['poll_options_size'] > (int) $config['max_poll_options'])
1635 $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS'];
1637 else if ($poll_max_options > $poll['poll_options_size'])
1639 $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS'];
1642 $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']);