source: trunk/forum/includes/message_parser.php

Last change on this file was 702, checked in by george, 15 years ago
  • Upraveno: Aktualizace fóra.
File size: 48.0 KB
Line 
1<?php
2/**
3*
4* @package phpBB3
5* @version $Id$
6* @copyright (c) 2005 phpBB Group
7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8*
9*/
10
11/**
12* @ignore
13*/
14if (!defined('IN_PHPBB'))
15{
16 exit;
17}
18
19if (!class_exists('bbcode'))
20{
21 include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
22}
23
24/**
25* BBCODE FIRSTPASS
26* BBCODE first pass class (functions for parsing messages for db storage)
27* @package phpBB3
28*/
29class bbcode_firstpass extends bbcode
30{
31 var $message = '';
32 var $warn_msg = array();
33 var $parsed_items = array();
34
35 /**
36 * Parse BBCode
37 */
38 function parse_bbcode()
39 {
40 if (!$this->bbcodes)
41 {
42 $this->bbcode_init();
43 }
44
45 global $user;
46
47 $this->bbcode_bitfield = '';
48 $bitfield = new bitfield();
49
50 foreach ($this->bbcodes as $bbcode_name => $bbcode_data)
51 {
52 if (isset($bbcode_data['disabled']) && $bbcode_data['disabled'])
53 {
54 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
55 {
56 if (preg_match($regexp, $this->message))
57 {
58 $this->warn_msg[] = sprintf($user->lang['UNAUTHORISED_BBCODE'] , '[' . $bbcode_name . ']');
59 continue;
60 }
61 }
62 }
63 else
64 {
65 foreach ($bbcode_data['regexp'] as $regexp => $replacement)
66 {
67 // The pattern gets compiled and cached by the PCRE extension,
68 // it should not demand recompilation
69 if (preg_match($regexp, $this->message))
70 {
71 $this->message = preg_replace($regexp, $replacement, $this->message);
72 $bitfield->set($bbcode_data['bbcode_id']);
73 }
74 }
75 }
76 }
77
78 $this->bbcode_bitfield = $bitfield->get_base64();
79 }
80
81 /**
82 * Prepare some bbcodes for better parsing
83 */
84 function prepare_bbcodes()
85 {
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.
88
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)
91 {
92 $this->message = str_replace("\r\n", "\n", $this->message);
93
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);
96 }
97 */
98
99 // Add other checks which needs to be placed before actually parsing anything (be it bbcodes, smilies, urls...)
100 }
101
102 /**
103 * Init bbcode data for later parsing
104 */
105 function bbcode_init()
106 {
107 static $rowset;
108
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]{3}|#[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')"))
126 );
127
128 // Zero the parsed items array
129 $this->parsed_items = array();
130
131 foreach ($this->bbcodes as $tag => $bbcode_data)
132 {
133 $this->parsed_items[$tag] = 0;
134 }
135
136 if (!is_array($rowset))
137 {
138 global $db;
139 $rowset = array();
140
141 $sql = 'SELECT *
142 FROM ' . BBCODES_TABLE;
143 $result = $db->sql_query($sql);
144
145 while ($row = $db->sql_fetchrow($result))
146 {
147 $rowset[] = $row;
148 }
149 $db->sql_freeresult($result);
150 }
151
152 foreach ($rowset as $row)
153 {
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']))
157 );
158 }
159 }
160
161 /**
162 * Making some pre-checks for bbcodes as well as increasing the number of parsed items
163 */
164 function check_bbcode($bbcode, &$in)
165 {
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));
169
170 // Trimming here to make sure no empty bbcodes are parsed accidently
171 if (trim($in) == '')
172 {
173 return false;
174 }
175
176 $this->parsed_items[$bbcode]++;
177
178 return true;
179 }
180
181 /**
182 * Transform some characters in valid bbcodes
183 */
184 function bbcode_specialchars($text)
185 {
186 $str_from = array('<', '>', '[', ']', '.', ':');
187 $str_to = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;');
188
189 return str_replace($str_from, $str_to, $text);
190 }
191
192 /**
193 * Parse size tag
194 */
195 function bbcode_size($stx, $in)
196 {
197 global $user, $config;
198
199 if (!$this->check_bbcode('size', $in))
200 {
201 return $in;
202 }
203
204 if ($config['max_' . $this->mode . '_font_size'] && $config['max_' . $this->mode . '_font_size'] < $stx)
205 {
206 $this->warn_msg[] = sprintf($user->lang['MAX_FONT_SIZE_EXCEEDED'], $config['max_' . $this->mode . '_font_size']);
207
208 return '[size=' . $stx . ']' . $in . '[/size]';
209 }
210
211 // Do not allow size=0
212 if ($stx <= 0)
213 {
214 return '[size=' . $stx . ']' . $in . '[/size]';
215 }
216
217 return '[size=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/size:' . $this->bbcode_uid . ']';
218 }
219
220 /**
221 * Parse color tag
222 */
223 function bbcode_color($stx, $in)
224 {
225 if (!$this->check_bbcode('color', $in))
226 {
227 return $in;
228 }
229
230 return '[color=' . $stx . ':' . $this->bbcode_uid . ']' . $in . '[/color:' . $this->bbcode_uid . ']';
231 }
232
233 /**
234 * Parse u tag
235 */
236 function bbcode_underline($in)
237 {
238 if (!$this->check_bbcode('u', $in))
239 {
240 return $in;
241 }
242
243 return '[u:' . $this->bbcode_uid . ']' . $in . '[/u:' . $this->bbcode_uid . ']';
244 }
245
246 /**
247 * Parse b tag
248 */
249 function bbcode_strong($in)
250 {
251 if (!$this->check_bbcode('b', $in))
252 {
253 return $in;
254 }
255
256 return '[b:' . $this->bbcode_uid . ']' . $in . '[/b:' . $this->bbcode_uid . ']';
257 }
258
259 /**
260 * Parse i tag
261 */
262 function bbcode_italic($in)
263 {
264 if (!$this->check_bbcode('i', $in))
265 {
266 return $in;
267 }
268
269 return '[i:' . $this->bbcode_uid . ']' . $in . '[/i:' . $this->bbcode_uid . ']';
270 }
271
272 /**
273 * Parse img tag
274 */
275 function bbcode_img($in)
276 {
277 global $user, $config;
278
279 if (!$this->check_bbcode('img', $in))
280 {
281 return $in;
282 }
283
284 $in = trim($in);
285 $error = false;
286
287 $in = str_replace(' ', '%20', $in);
288
289 // Checking urls
290 if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#i', $in))
291 {
292 return '[img]' . $in . '[/img]';
293 }
294
295 // Try to cope with a common user error... not specifying a protocol but only a subdomain
296 if (!preg_match('#^[a-z0-9]+://#i', $in))
297 {
298 $in = 'http://' . $in;
299 }
300
301 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
302 {
303 $stats = @getimagesize($in);
304
305 if ($stats === false)
306 {
307 $error = true;
308 $this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
309 }
310 else
311 {
312 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $stats[1])
313 {
314 $error = true;
315 $this->warn_msg[] = sprintf($user->lang['MAX_IMG_HEIGHT_EXCEEDED'], $config['max_' . $this->mode . '_img_height']);
316 }
317
318 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $stats[0])
319 {
320 $error = true;
321 $this->warn_msg[] = sprintf($user->lang['MAX_IMG_WIDTH_EXCEEDED'], $config['max_' . $this->mode . '_img_width']);
322 }
323 }
324 }
325
326 if ($error || $this->path_in_domain($in))
327 {
328 return '[img]' . $in . '[/img]';
329 }
330
331 return '[img:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/img:' . $this->bbcode_uid . ']';
332 }
333
334 /**
335 * Parse flash tag
336 */
337 function bbcode_flash($width, $height, $in)
338 {
339 global $user, $config;
340
341 if (!$this->check_bbcode('flash', $in))
342 {
343 return $in;
344 }
345
346 $in = trim($in);
347 $error = false;
348
349 // Do not allow 0-sizes generally being entered
350 if ($width <= 0 || $height <= 0)
351 {
352 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
353 }
354
355 // Apply the same size checks on flash files as on images
356 if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])
357 {
358 if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $height)
359 {
360 $error = true;
361 $this->warn_msg[] = sprintf($user->lang['MAX_FLASH_HEIGHT_EXCEEDED'], $config['max_' . $this->mode . '_img_height']);
362 }
363
364 if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $width)
365 {
366 $error = true;
367 $this->warn_msg[] = sprintf($user->lang['MAX_FLASH_WIDTH_EXCEEDED'], $config['max_' . $this->mode . '_img_width']);
368 }
369 }
370
371 if ($error || $this->path_in_domain($in))
372 {
373 return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';
374 }
375
376 return '[flash=' . $width . ',' . $height . ':' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($in) . '[/flash:' . $this->bbcode_uid . ']';
377 }
378
379 /**
380 * Parse inline attachments [ia]
381 */
382 function bbcode_attachment($stx, $in)
383 {
384 if (!$this->check_bbcode('attachment', $in))
385 {
386 return $in;
387 }
388
389 return '[attachment=' . $stx . ':' . $this->bbcode_uid . ']<!-- ia' . $stx . ' -->' . trim($in) . '<!-- ia' . $stx . ' -->[/attachment:' . $this->bbcode_uid . ']';
390 }
391
392 /**
393 * Parse code text from code tag
394 * @access private
395 */
396 function bbcode_parse_code($stx, &$code)
397 {
398 switch (strtolower($stx))
399 {
400 case 'php':
401
402 $remove_tags = false;
403
404 $str_from = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
405 $str_to = array('<', '>', '[', ']', '.', ':', ':');
406 $code = str_replace($str_from, $str_to, $code);
407
408 if (!preg_match('/\<\?.*?\?\>/is', $code))
409 {
410 $remove_tags = true;
411 $code = "<?php $code ?>";
412 }
413
414 $conf = array('highlight.bg', 'highlight.comment', 'highlight.default', 'highlight.html', 'highlight.keyword', 'highlight.string');
415 foreach ($conf as $ini_var)
416 {
417 @ini_set($ini_var, str_replace('highlight.', 'syntax', $ini_var));
418 }
419
420 // Because highlight_string is specialcharing the text (but we already did this before), we have to reverse this in order to get correct results
421 $code = htmlspecialchars_decode($code);
422 $code = highlight_string($code, true);
423
424 $str_from = array('<span style="color: ', '<font color="syntax', '</font>', '<code>', '</code>','[', ']', '.', ':');
425 $str_to = array('<span class="', '<span class="syntax', '</span>', '', '', '&#91;', '&#93;', '&#46;', '&#58;');
426
427 if ($remove_tags)
428 {
429 $str_from[] = '<span class="syntaxdefault">&lt;?php </span>';
430 $str_to[] = '';
431 $str_from[] = '<span class="syntaxdefault">&lt;?php&nbsp;';
432 $str_to[] = '<span class="syntaxdefault">';
433 }
434
435 $code = str_replace($str_from, $str_to, $code);
436 $code = preg_replace('#^(<span class="[a-z_]+">)\n?(.*?)\n?(</span>)$#is', '$1$2$3', $code);
437
438 if ($remove_tags)
439 {
440 $code = preg_replace('#(<span class="[a-z]+">)?\?&gt;(</span>)#', '$1&nbsp;$2', $code);
441 }
442
443 $code = preg_replace('#^<span class="[a-z]+"><span class="([a-z]+)">(.*)</span></span>#s', '<span class="$1">$2</span>', $code);
444 $code = preg_replace('#(?:\s++|&nbsp;)*+</span>$#u', '</span>', $code);
445
446 // remove newline at the end
447 if (!empty($code) && substr($code, -1) == "\n")
448 {
449 $code = substr($code, 0, -1);
450 }
451
452 return "[code=$stx:" . $this->bbcode_uid . ']' . $code . '[/code:' . $this->bbcode_uid . ']';
453 break;
454
455 default:
456 return '[code:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($code) . '[/code:' . $this->bbcode_uid . ']';
457 break;
458 }
459 }
460
461 /**
462 * Parse code tag
463 * Expects the argument to start right after the opening [code] tag and to end with [/code]
464 */
465 function bbcode_code($stx, $in)
466 {
467 if (!$this->check_bbcode('code', $in))
468 {
469 return $in;
470 }
471
472 // We remove the hardcoded elements from the code block here because it is not used in code blocks
473 // Having it here saves us one preg_replace per message containing [code] blocks
474 // Additionally, magic url parsing should go after parsing bbcodes, but for safety those are stripped out too...
475 $htm_match = get_preg_expression('bbcode_htm');
476 unset($htm_match[4], $htm_match[5]);
477 $htm_replace = array('\1', '\1', '\2', '\1');
478
479 $out = $code_block = '';
480 $open = 1;
481
482 while ($in)
483 {
484 // Determine position and tag length of next code block
485 preg_match('#(.*?)(\[code(?:=([a-z]+))?\])(.+)#is', $in, $buffer);
486 $pos = (isset($buffer[1])) ? strlen($buffer[1]) : false;
487 $tag_length = (isset($buffer[2])) ? strlen($buffer[2]) : false;
488
489 // Determine position of ending code tag
490 $pos2 = stripos($in, '[/code]');
491
492 // Which is the next block, ending code or code block
493 if ($pos !== false && $pos < $pos2)
494 {
495 // Open new block
496 if (!$open)
497 {
498 $out .= substr($in, 0, $pos);
499 $in = substr($in, $pos);
500 $stx = (isset($buffer[3])) ? $buffer[3] : '';
501 $code_block = '';
502 }
503 else
504 {
505 // Already opened block, just append to the current block
506 $code_block .= substr($in, 0, $pos) . ((isset($buffer[2])) ? $buffer[2] : '');
507 $in = substr($in, $pos);
508 }
509
510 $in = substr($in, $tag_length);
511 $open++;
512 }
513 else
514 {
515 // Close the block
516 if ($open == 1)
517 {
518 $code_block .= substr($in, 0, $pos2);
519 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
520
521 // Parse this code block
522 $out .= $this->bbcode_parse_code($stx, $code_block);
523 $code_block = '';
524 $open--;
525 }
526 else if ($open)
527 {
528 // Close one open tag... add to the current code block
529 $code_block .= substr($in, 0, $pos2 + 7);
530 $open--;
531 }
532 else
533 {
534 // end code without opening code... will be always outside code block
535 $out .= substr($in, 0, $pos2 + 7);
536 }
537
538 $in = substr($in, $pos2 + 7);
539 }
540 }
541
542 // if now $code_block has contents we need to parse the remaining code while removing the last closing tag to match up.
543 if ($code_block)
544 {
545 $code_block = substr($code_block, 0, -7);
546 $code_block = preg_replace($htm_match, $htm_replace, $code_block);
547
548 $out .= $this->bbcode_parse_code($stx, $code_block);
549 }
550
551 return $out;
552 }
553
554 /**
555 * Parse list bbcode
556 * Expects the argument to start with a tag
557 */
558 function bbcode_parse_list($in)
559 {
560 if (!$this->check_bbcode('list', $in))
561 {
562 return $in;
563 }
564
565 // $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
566 $tok = ']';
567 $out = '[';
568
569 // First character is [
570 $in = substr($in, 1);
571 $list_end_tags = $item_end_tags = array();
572
573 do
574 {
575 $pos = strlen($in);
576
577 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
578 {
579 $tmp_pos = strpos($in, $tok[$i]);
580
581 if ($tmp_pos !== false && $tmp_pos < $pos)
582 {
583 $pos = $tmp_pos;
584 }
585 }
586
587 $buffer = substr($in, 0, $pos);
588 $tok = $in[$pos];
589
590 $in = substr($in, $pos + 1);
591
592 if ($tok == ']')
593 {
594 // if $tok is ']' the buffer holds a tag
595 if (strtolower($buffer) == '/list' && sizeof($list_end_tags))
596 {
597 // valid [/list] tag, check nesting so that we don't hit false positives
598 if (sizeof($item_end_tags) && sizeof($item_end_tags) >= sizeof($list_end_tags))
599 {
600 // current li tag has not been closed
601 $out = preg_replace('/\n?\[$/', '[', $out) . array_pop($item_end_tags) . '][';
602 }
603
604 $out .= array_pop($list_end_tags) . ']';
605 $tok = '[';
606 }
607 else if (preg_match('#^list(=[0-9a-z]+)?$#i', $buffer, $m))
608 {
609 // sub-list, add a closing tag
610 if (empty($m[1]) || preg_match('/^=(?:disc|square|circle)$/i', $m[1]))
611 {
612 array_push($list_end_tags, '/list:u:' . $this->bbcode_uid);
613 }
614 else
615 {
616 array_push($list_end_tags, '/list:o:' . $this->bbcode_uid);
617 }
618 $out .= 'list' . substr($buffer, 4) . ':' . $this->bbcode_uid . ']';
619 $tok = '[';
620 }
621 else
622 {
623 if (($buffer == '*' || substr($buffer, -2) == '[*') && sizeof($list_end_tags))
624 {
625 // the buffer holds a bullet tag and we have a [list] tag open
626 if (sizeof($item_end_tags) >= sizeof($list_end_tags))
627 {
628 if (substr($buffer, -2) == '[*')
629 {
630 $out .= substr($buffer, 0, -2) . '[';
631 }
632 // current li tag has not been closed
633 if (preg_match('/\n\[$/', $out, $m))
634 {
635 $out = preg_replace('/\n\[$/', '[', $out);
636 $buffer = array_pop($item_end_tags) . "]\n[*:" . $this->bbcode_uid;
637 }
638 else
639 {
640 $buffer = array_pop($item_end_tags) . '][*:' . $this->bbcode_uid;
641 }
642 }
643 else
644 {
645 $buffer = '*:' . $this->bbcode_uid;
646 }
647
648 $item_end_tags[] = '/*:m:' . $this->bbcode_uid;
649 }
650 else if ($buffer == '/*')
651 {
652 array_pop($item_end_tags);
653 $buffer = '/*:' . $this->bbcode_uid;
654 }
655
656 $out .= $buffer . $tok;
657 $tok = '[]';
658 }
659 }
660 else
661 {
662 // Not within a tag, just add buffer to the return string
663 $out .= $buffer . $tok;
664 $tok = ($tok == '[') ? ']' : '[]';
665 }
666 }
667 while ($in);
668
669 // do we have some tags open? close them now
670 if (sizeof($item_end_tags))
671 {
672 $out .= '[' . implode('][', $item_end_tags) . ']';
673 }
674 if (sizeof($list_end_tags))
675 {
676 $out .= '[' . implode('][', $list_end_tags) . ']';
677 }
678
679 return $out;
680 }
681
682 /**
683 * Parse quote bbcode
684 * Expects the argument to start with a tag
685 */
686 function bbcode_quote($in)
687 {
688 global $config, $user;
689
690 /**
691 * If you change this code, make sure the cases described within the following reports are still working:
692 * #3572 - [quote="[test]test"]test [ test[/quote] - (correct: parsed)
693 * #14667 - [quote]test[/quote] test ] and [ test [quote]test[/quote] (correct: parsed)
694 * #14770 - [quote="["]test[/quote] (correct: parsed)
695 * [quote="[i]test[/i]"]test[/quote] (correct: parsed)
696 * [quote="[quote]test[/quote]"]test[/quote] (correct: parsed - Username displayed as [quote]test[/quote])
697 * #20735 - [quote]test[/[/b]quote] test [/quote][/quote] test - (correct: quoted: "test[/[/b]quote] test" / non-quoted: "[/quote] test" - also failed if layout distorted)
698 * #40565 - [quote="a"]a[/quote][quote="a]a[/quote] (correct: first quote tag parsed, second quote tag unparsed)
699 */
700
701 $in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));
702
703 if (!$in)
704 {
705 return '';
706 }
707
708 // To let the parser not catch tokens within quote_username quotes we encode them before we start this...
709 $in = preg_replace('#quote=&quot;(.*?)&quot;\]#ie', "'quote=&quot;' . str_replace(array('[', ']', '\\\"'), array('&#91;', '&#93;', '\"'), '\$1') . '&quot;]'", $in);
710
711 $tok = ']';
712 $out = '[';
713
714 $in = substr($in, 1);
715 $close_tags = $error_ary = array();
716 $buffer = '';
717
718 do
719 {
720 $pos = strlen($in);
721 for ($i = 0, $tok_len = strlen($tok); $i < $tok_len; ++$i)
722 {
723 $tmp_pos = strpos($in, $tok[$i]);
724 if ($tmp_pos !== false && $tmp_pos < $pos)
725 {
726 $pos = $tmp_pos;
727 }
728 }
729
730 $buffer .= substr($in, 0, $pos);
731 $tok = $in[$pos];
732 $in = substr($in, $pos + 1);
733
734 if ($tok == ']')
735 {
736 if (strtolower($buffer) == '/quote' && sizeof($close_tags) && substr($out, -1, 1) == '[')
737 {
738 // we have found a closing tag
739 $out .= array_pop($close_tags) . ']';
740 $tok = '[';
741 $buffer = '';
742
743 /* Add space at the end of the closing tag if not happened before to allow following urls/smilies to be parsed correctly
744 * 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.
745 * Also, we won't have any spaces within $in anyway, only adding up spaces -> #10982
746 if (!$in || $in[0] !== ' ')
747 {
748 $out .= ' ';
749 }*/
750 }
751 else if (preg_match('#^quote(?:=&quot;(.*?)&quot;)?$#is', $buffer, $m) && substr($out, -1, 1) == '[')
752 {
753 $this->parsed_items['quote']++;
754
755 // the buffer holds a valid opening tag
756 if ($config['max_quote_depth'] && sizeof($close_tags) >= $config['max_quote_depth'])
757 {
758 // there are too many nested quotes
759 $error_ary['quote_depth'] = sprintf($user->lang['QUOTE_DEPTH_EXCEEDED'], $config['max_quote_depth']);
760
761 $out .= $buffer . $tok;
762 $tok = '[]';
763 $buffer = '';
764
765 continue;
766 }
767
768 array_push($close_tags, '/quote:' . $this->bbcode_uid);
769
770 if (isset($m[1]) && $m[1])
771 {
772 $username = str_replace(array('&#91;', '&#93;'), array('[', ']'), $m[1]);
773 $username = preg_replace('#\[(?!b|i|u|color|url|email|/b|/i|/u|/color|/url|/email)#iU', '&#91;$1', $username);
774
775 $end_tags = array();
776 $error = false;
777
778 preg_match_all('#\[((?:/)?(?:[a-z]+))#i', $username, $tags);
779 foreach ($tags[1] as $tag)
780 {
781 if ($tag[0] != '/')
782 {
783 $end_tags[] = '/' . $tag;
784 }
785 else
786 {
787 $end_tag = array_pop($end_tags);
788 $error = ($end_tag != $tag) ? true : false;
789 }
790 }
791
792 if ($error)
793 {
794 $username = $m[1];
795 }
796
797 $out .= 'quote=&quot;' . $username . '&quot;:' . $this->bbcode_uid . ']';
798 }
799 else
800 {
801 $out .= 'quote:' . $this->bbcode_uid . ']';
802 }
803
804 $tok = '[';
805 $buffer = '';
806 }
807 else if (preg_match('#^quote=&quot;(.*?)#is', $buffer, $m))
808 {
809 // the buffer holds an invalid opening tag
810 $buffer .= ']';
811 }
812 else
813 {
814 $out .= $buffer . $tok;
815 $tok = '[]';
816 $buffer = '';
817 }
818 }
819 else
820 {
821/**
822* Old quote code working fine, but having errors listed in bug #3572
823*
824* $out .= $buffer . $tok;
825* $tok = ($tok == '[') ? ']' : '[]';
826* $buffer = '';
827*/
828
829 $out .= $buffer . $tok;
830
831 if ($tok == '[')
832 {
833 // Search the text for the next tok... if an ending quote comes first, then change tok to []
834 $pos1 = stripos($in, '[/quote');
835 // If the token ] comes first, we change it to ]
836 $pos2 = strpos($in, ']');
837 // If the token [ comes first, we change it to [
838 $pos3 = strpos($in, '[');
839
840 if ($pos1 !== false && ($pos2 === false || $pos1 < $pos2) && ($pos3 === false || $pos1 < $pos3))
841 {
842 $tok = '[]';
843 }
844 else if ($pos3 !== false && ($pos2 === false || $pos3 < $pos2))
845 {
846 $tok = '[';
847 }
848 else
849 {
850 $tok = ']';
851 }
852 }
853 else
854 {
855 $tok = '[]';
856 }
857 $buffer = '';
858 }
859 }
860 while ($in);
861
862 $out .= $buffer;
863
864 if (sizeof($close_tags))
865 {
866 $out .= '[' . implode('][', $close_tags) . ']';
867 }
868
869 foreach ($error_ary as $error_msg)
870 {
871 $this->warn_msg[] = $error_msg;
872 }
873
874 return $out;
875 }
876
877 /**
878 * Validate email
879 */
880 function validate_email($var1, $var2)
881 {
882 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
883 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
884
885 $txt = $var2;
886 $email = ($var1) ? $var1 : $var2;
887
888 $validated = true;
889
890 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
891 {
892 $validated = false;
893 }
894
895 if (!$validated)
896 {
897 return '[email' . (($var1) ? "=$var1" : '') . ']' . $var2 . '[/email]';
898 }
899
900 $this->parsed_items['email']++;
901
902 if ($var1)
903 {
904 $retval = '[email=' . $this->bbcode_specialchars($email) . ':' . $this->bbcode_uid . ']' . $txt . '[/email:' . $this->bbcode_uid . ']';
905 }
906 else
907 {
908 $retval = '[email:' . $this->bbcode_uid . ']' . $this->bbcode_specialchars($email) . '[/email:' . $this->bbcode_uid . ']';
909 }
910
911 return $retval;
912 }
913
914 /**
915 * Validate url
916 *
917 * @param string $var1 optional url parameter for url bbcode: [url(=$var1)]$var2[/url]
918 * @param string $var2 url bbcode content: [url(=$var1)]$var2[/url]
919 */
920 function validate_url($var1, $var2)
921 {
922 global $config;
923
924 $var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));
925 $var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2)));
926
927 $url = ($var1) ? $var1 : $var2;
928
929 if ($var1 && !$var2)
930 {
931 $var2 = $var1;
932 }
933
934 if (!$url)
935 {
936 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
937 }
938
939 $valid = false;
940
941 $url = str_replace(' ', '%20', $url);
942
943 // Checking urls
944 if (preg_match('#^' . get_preg_expression('url') . '$#i', $url) ||
945 preg_match('#^' . get_preg_expression('www_url') . '$#i', $url) ||
946 preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#i', $url))
947 {
948 $valid = true;
949 }
950
951 if ($valid)
952 {
953 $this->parsed_items['url']++;
954
955 // if there is no scheme, then add http schema
956 if (!preg_match('#^[a-z][a-z\d+\-.]*:/{2}#i', $url))
957 {
958 $url = 'http://' . $url;
959 }
960
961 // Is this a link to somewhere inside this board? If so then remove the session id from the url
962 if (strpos($url, generate_board_url()) !== false && strpos($url, 'sid=') !== false)
963 {
964 $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}&amp;/', '\1', $url);
965 $url = preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}$/', '', $url);
966 $url = append_sid($url);
967 }
968
969 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 . ']';
970 }
971
972 return '[url' . (($var1) ? '=' . $var1 : '') . ']' . $var2 . '[/url]';
973 }
974
975 /**
976 * Check if url is pointing to this domain/script_path/php-file
977 *
978 * @param string $url the url to check
979 * @return true if the url is pointing to this domain/script_path/php-file, false if not
980 *
981 * @access private
982 */
983 function path_in_domain($url)
984 {
985 global $config, $phpEx, $user;
986
987 if ($config['force_server_vars'])
988 {
989 $check_path = $config['script_path'];
990 }
991 else
992 {
993 $check_path = ($user->page['root_script_path'] != '/') ? substr($user->page['root_script_path'], 0, -1) : '/';
994 }
995
996 // Is the user trying to link to a php file in this domain and script path?
997 if (strpos($url, ".{$phpEx}") !== false && strpos($url, $check_path) !== false)
998 {
999 $server_name = $user->host;
1000
1001 // Forcing server vars is the only way to specify/override the protocol
1002 if ($config['force_server_vars'] || !$server_name)
1003 {
1004 $server_name = $config['server_name'];
1005 }
1006
1007 // Check again in correct order...
1008 $pos_ext = strpos($url, ".{$phpEx}");
1009 $pos_path = strpos($url, $check_path);
1010 $pos_domain = strpos($url, $server_name);
1011
1012 if ($pos_domain !== false && $pos_path >= $pos_domain && $pos_ext >= $pos_path)
1013 {
1014 // Ok, actually we allow linking to some files (this may be able to be extended in some way later...)
1015 if (strpos($url, '/' . $check_path . '/download/file.' . $phpEx) !== 0)
1016 {
1017 return false;
1018 }
1019
1020 return true;
1021 }
1022 }
1023
1024 return false;
1025 }
1026}
1027
1028/**
1029* Main message parser for posting, pm, etc. takes raw message
1030* and parses it for attachments, bbcode and smilies
1031* @package phpBB3
1032*/
1033class parse_message extends bbcode_firstpass
1034{
1035 var $attachment_data = array();
1036 var $filename_data = array();
1037
1038 // Helps ironing out user error
1039 var $message_status = '';
1040
1041 var $allow_img_bbcode = true;
1042 var $allow_flash_bbcode = true;
1043 var $allow_quote_bbcode = true;
1044 var $allow_url_bbcode = true;
1045
1046 var $mode;
1047
1048 /**
1049 * Init - give message here or manually
1050 */
1051 function parse_message($message = '')
1052 {
1053 // Init BBCode UID
1054 $this->bbcode_uid = substr(base_convert(unique_id(), 16, 36), 0, BBCODE_UID_LEN);
1055 $this->message = $message;
1056 }
1057
1058 /**
1059 * Parse Message
1060 */
1061 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')
1062 {
1063 global $config, $db, $user;
1064
1065 $this->mode = $mode;
1066
1067 foreach (array('chars', 'smilies', 'urls', 'font_size', 'img_height', 'img_width') as $key)
1068 {
1069 if (!isset($config['max_' . $mode . '_' . $key]))
1070 {
1071 $config['max_' . $mode . '_' . $key] = 0;
1072 }
1073 }
1074
1075 $this->allow_img_bbcode = $allow_img_bbcode;
1076 $this->allow_flash_bbcode = $allow_flash_bbcode;
1077 $this->allow_quote_bbcode = $allow_quote_bbcode;
1078 $this->allow_url_bbcode = $allow_url_bbcode;
1079
1080 // If false, then $this->message won't be altered, the text will be returned instead.
1081 if (!$update_this_message)
1082 {
1083 $tmp_message = $this->message;
1084 $return_message = &$this->message;
1085 }
1086
1087 if ($this->message_status == 'display')
1088 {
1089 $this->decode_message();
1090 }
1091
1092 // Do some general 'cleanup' first before processing message,
1093 // e.g. remove excessive newlines(?), smilies(?)
1094 $match = array('#(script|about|applet|activex|chrome):#i');
1095 $replace = array("\\1&#058;");
1096 $this->message = preg_replace($match, $replace, trim($this->message));
1097
1098 // Store message length...
1099 $message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message));
1100
1101 // Maximum message length check. 0 disables this check completely.
1102 if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars'])
1103 {
1104 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_CHARS_' . strtoupper($mode)], $message_length, (int) $config['max_' . $mode . '_chars']);
1105 return (!$update_this_message) ? $return_message : $this->warn_msg;
1106 }
1107
1108 // Minimum message length check for post only
1109 if ($mode === 'post')
1110 {
1111 if (!$message_length || $message_length < (int) $config['min_post_chars'])
1112 {
1113 $this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : sprintf($user->lang['TOO_FEW_CHARS_LIMIT'], $message_length, (int) $config['min_post_chars']);
1114 return (!$update_this_message) ? $return_message : $this->warn_msg;
1115 }
1116 }
1117
1118 // Prepare BBcode (just prepares some tags for better parsing)
1119 if ($allow_bbcode && strpos($this->message, '[') !== false)
1120 {
1121 $this->bbcode_init();
1122 $disallow = array('img', 'flash', 'quote', 'url');
1123 foreach ($disallow as $bool)
1124 {
1125 if (!${'allow_' . $bool . '_bbcode'})
1126 {
1127 $this->bbcodes[$bool]['disabled'] = true;
1128 }
1129 }
1130
1131 $this->prepare_bbcodes();
1132 }
1133
1134 // Parse smilies
1135 if ($allow_smilies)
1136 {
1137 $this->smilies($config['max_' . $mode . '_smilies']);
1138 }
1139
1140 $num_urls = 0;
1141
1142 // Parse BBCode
1143 if ($allow_bbcode && strpos($this->message, '[') !== false)
1144 {
1145 $this->parse_bbcode();
1146 $num_urls += $this->parsed_items['url'];
1147 }
1148
1149 // Parse URL's
1150 if ($allow_magic_url)
1151 {
1152 $this->magic_url(generate_board_url());
1153
1154 if ($config['max_' . $mode . '_urls'])
1155 {
1156 $num_urls += preg_match_all('#\<!-- ([lmwe]) --\>.*?\<!-- \1 --\>#', $this->message, $matches);
1157 }
1158 }
1159
1160 // Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length.
1161 // The maximum length check happened before any parsings.
1162 if ($mode === 'post' && utf8_clean_string($this->message) === '')
1163 {
1164 $this->warn_msg[] = $user->lang['TOO_FEW_CHARS'];
1165 return (!$update_this_message) ? $return_message : $this->warn_msg;
1166 }
1167
1168 // Check number of links
1169 if ($config['max_' . $mode . '_urls'] && $num_urls > $config['max_' . $mode . '_urls'])
1170 {
1171 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_URLS'], $config['max_' . $mode . '_urls']);
1172 return (!$update_this_message) ? $return_message : $this->warn_msg;
1173 }
1174
1175 if (!$update_this_message)
1176 {
1177 unset($this->message);
1178 $this->message = $tmp_message;
1179 return $return_message;
1180 }
1181
1182 $this->message_status = 'parsed';
1183 return false;
1184 }
1185
1186 /**
1187 * Formatting text for display
1188 */
1189 function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true)
1190 {
1191 // If false, then the parsed message get returned but internal message not processed.
1192 if (!$update_this_message)
1193 {
1194 $tmp_message = $this->message;
1195 $return_message = &$this->message;
1196 }
1197
1198 if ($this->message_status == 'plain')
1199 {
1200 // Force updating message - of course.
1201 $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);
1202 }
1203
1204 // Replace naughty words such as farty pants
1205 $this->message = censor_text($this->message);
1206
1207 // Parse BBcode
1208 if ($allow_bbcode)
1209 {
1210 $this->bbcode_cache_init();
1211
1212 // We are giving those parameters to be able to use the bbcode class on its own
1213 $this->bbcode_second_pass($this->message, $this->bbcode_uid);
1214 }
1215
1216 $this->message = bbcode_nl2br($this->message);
1217 $this->message = smiley_text($this->message, !$allow_smilies);
1218
1219 if (!$update_this_message)
1220 {
1221 unset($this->message);
1222 $this->message = $tmp_message;
1223 return $return_message;
1224 }
1225
1226 $this->message_status = 'display';
1227 return false;
1228 }
1229
1230 /**
1231 * Decode message to be placed back into form box
1232 */
1233 function decode_message($custom_bbcode_uid = '', $update_this_message = true)
1234 {
1235 // If false, then the parsed message get returned but internal message not processed.
1236 if (!$update_this_message)
1237 {
1238 $tmp_message = $this->message;
1239 $return_message = &$this->message;
1240 }
1241
1242 ($custom_bbcode_uid) ? decode_message($this->message, $custom_bbcode_uid) : decode_message($this->message, $this->bbcode_uid);
1243
1244 if (!$update_this_message)
1245 {
1246 unset($this->message);
1247 $this->message = $tmp_message;
1248 return $return_message;
1249 }
1250
1251 $this->message_status = 'plain';
1252 return false;
1253 }
1254
1255 /**
1256 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
1257 * Cuts down displayed size of link if over 50 chars, turns absolute links
1258 * into relative versions when the server/script path matches the link
1259 */
1260 function magic_url($server_url)
1261 {
1262 // We use the global make_clickable function
1263 $this->message = make_clickable($this->message, $server_url);
1264 }
1265
1266 /**
1267 * Parse Smilies
1268 */
1269 function smilies($max_smilies = 0)
1270 {
1271 global $db, $user;
1272 static $match;
1273 static $replace;
1274
1275 // See if the static arrays have already been filled on an earlier invocation
1276 if (!is_array($match))
1277 {
1278 $match = $replace = array();
1279
1280 // NOTE: obtain_* function? chaching the table contents?
1281
1282 // For now setting the ttl to 10 minutes
1283 switch ($db->sql_layer)
1284 {
1285 case 'mssql':
1286 case 'mssql_odbc':
1287 $sql = 'SELECT *
1288 FROM ' . SMILIES_TABLE . '
1289 ORDER BY LEN(code) DESC';
1290 break;
1291
1292 case 'firebird':
1293 $sql = 'SELECT *
1294 FROM ' . SMILIES_TABLE . '
1295 ORDER BY CHAR_LENGTH(code) DESC';
1296 break;
1297
1298 // LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure...
1299 default:
1300 $sql = 'SELECT *
1301 FROM ' . SMILIES_TABLE . '
1302 ORDER BY LENGTH(code) DESC';
1303 break;
1304 }
1305 $result = $db->sql_query($sql, 600);
1306
1307 while ($row = $db->sql_fetchrow($result))
1308 {
1309 if (empty($row['code']))
1310 {
1311 continue;
1312 }
1313
1314 // (assertion)
1315 $match[] = preg_quote($row['code'], '#');
1316 $replace[] = '<!-- s' . $row['code'] . ' --><img src="{SMILIES_PATH}/' . $row['smiley_url'] . '" alt="' . $row['code'] . '" title="' . $row['emotion'] . '" /><!-- s' . $row['code'] . ' -->';
1317 }
1318 $db->sql_freeresult($result);
1319 }
1320
1321 if (sizeof($match))
1322 {
1323 if ($max_smilies)
1324 {
1325 $num_matches = preg_match_all('#(?<=^|[\n .])(?:' . implode('|', $match) . ')(?![^<>]*>)#', $this->message, $matches);
1326 unset($matches);
1327
1328 if ($num_matches !== false && $num_matches > $max_smilies)
1329 {
1330 $this->warn_msg[] = sprintf($user->lang['TOO_MANY_SMILIES'], $max_smilies);
1331 return;
1332 }
1333 }
1334
1335 // Make sure the delimiter # is added in front and at the end of every element within $match
1336 $this->message = trim(preg_replace(explode(chr(0), '#(?<=^|[\n .])' . implode('(?![^<>]*>)#' . chr(0) . '#(?<=^|[\n .])', $match) . '(?![^<>]*>)#'), $replace, $this->message));
1337 }
1338 }
1339
1340 /**
1341 * Parse Attachments
1342 */
1343 function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false)
1344 {
1345 global $config, $auth, $user, $phpbb_root_path, $phpEx, $db;
1346
1347 $error = array();
1348
1349 $num_attachments = sizeof($this->attachment_data);
1350 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true));
1351 $upload_file = (isset($_FILES[$form_name]) && $_FILES[$form_name]['name'] != 'none' && trim($_FILES[$form_name]['name'])) ? true : false;
1352
1353 $add_file = (isset($_POST['add_file'])) ? true : false;
1354 $delete_file = (isset($_POST['delete_file'])) ? true : false;
1355
1356 // First of all adjust comments if changed
1357 $actual_comment_list = utf8_normalize_nfc(request_var('comment_list', array(''), true));
1358
1359 foreach ($actual_comment_list as $comment_key => $comment)
1360 {
1361 if (!isset($this->attachment_data[$comment_key]))
1362 {
1363 continue;
1364 }
1365
1366 if ($this->attachment_data[$comment_key]['attach_comment'] != $actual_comment_list[$comment_key])
1367 {
1368 $this->attachment_data[$comment_key]['attach_comment'] = $actual_comment_list[$comment_key];
1369 }
1370 }
1371
1372 $cfg = array();
1373 $cfg['max_attachments'] = ($is_message) ? $config['max_attachments_pm'] : $config['max_attachments'];
1374 $forum_id = ($is_message) ? 0 : $forum_id;
1375
1376 if ($submit && in_array($mode, array('post', 'reply', 'quote', 'edit')) && $upload_file)
1377 {
1378 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id))
1379 {
1380 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message);
1381 $error = $filedata['error'];
1382
1383 if ($filedata['post_attach'] && !sizeof($error))
1384 {
1385 $sql_ary = array(
1386 'physical_filename' => $filedata['physical_filename'],
1387 'attach_comment' => $this->filename_data['filecomment'],
1388 'real_filename' => $filedata['real_filename'],
1389 'extension' => $filedata['extension'],
1390 'mimetype' => $filedata['mimetype'],
1391 'filesize' => $filedata['filesize'],
1392 'filetime' => $filedata['filetime'],
1393 'thumbnail' => $filedata['thumbnail'],
1394 'is_orphan' => 1,
1395 'in_message' => ($is_message) ? 1 : 0,
1396 'poster_id' => $user->data['user_id'],
1397 );
1398
1399 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1400
1401 $new_entry = array(
1402 'attach_id' => $db->sql_nextid(),
1403 'is_orphan' => 1,
1404 'real_filename' => $filedata['real_filename'],
1405 'attach_comment'=> $this->filename_data['filecomment'],
1406 );
1407
1408 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1409 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message);
1410
1411 $this->filename_data['filecomment'] = '';
1412
1413 // This Variable is set to false here, because Attachments are entered into the
1414 // Database in two modes, one if the id_list is 0 and the second one if post_attach is true
1415 // Since post_attach is automatically switched to true if an Attachment got added to the filesystem,
1416 // but we are assigning an id of 0 here, we have to reset the post_attach variable to false.
1417 //
1418 // This is very relevant, because it could happen that the post got not submitted, but we do not
1419 // know this circumstance here. We could be at the posting page or we could be redirected to the entered
1420 // post. :)
1421 $filedata['post_attach'] = false;
1422 }
1423 }
1424 else
1425 {
1426 $error[] = sprintf($user->lang['TOO_MANY_ATTACHMENTS'], $cfg['max_attachments']);
1427 }
1428 }
1429
1430 if ($preview || $refresh || sizeof($error))
1431 {
1432 // Perform actions on temporary attachments
1433 if ($delete_file)
1434 {
1435 include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1436
1437 $index = array_keys(request_var('delete_file', array(0 => 0)));
1438 $index = (!empty($index)) ? $index[0] : false;
1439
1440 if ($index !== false && !empty($this->attachment_data[$index]))
1441 {
1442 // delete selected attachment
1443 if ($this->attachment_data[$index]['is_orphan'])
1444 {
1445 $sql = 'SELECT attach_id, physical_filename, thumbnail
1446 FROM ' . ATTACHMENTS_TABLE . '
1447 WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id'] . '
1448 AND is_orphan = 1
1449 AND poster_id = ' . $user->data['user_id'];
1450 $result = $db->sql_query($sql);
1451 $row = $db->sql_fetchrow($result);
1452 $db->sql_freeresult($result);
1453
1454 if ($row)
1455 {
1456 phpbb_unlink($row['physical_filename'], 'file');
1457
1458 if ($row['thumbnail'])
1459 {
1460 phpbb_unlink($row['physical_filename'], 'thumbnail');
1461 }
1462
1463 $db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']);
1464 }
1465 }
1466 else
1467 {
1468 delete_attachments('attach', array(intval($this->attachment_data[$index]['attach_id'])));
1469 }
1470
1471 unset($this->attachment_data[$index]);
1472 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "(\\1 == \$index) ? '' : ((\\1 > \$index) ? '[attachment=' . (\\1 - 1) . ']\\2[/attachment]' : '\\0')", $this->message);
1473
1474 // Reindex Array
1475 $this->attachment_data = array_values($this->attachment_data);
1476 }
1477 }
1478 else if (($add_file || $preview) && $upload_file)
1479 {
1480 if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id))
1481 {
1482 $filedata = upload_attachment($form_name, $forum_id, false, '', $is_message);
1483 $error = array_merge($error, $filedata['error']);
1484
1485 if (!sizeof($error))
1486 {
1487 $sql_ary = array(
1488 'physical_filename' => $filedata['physical_filename'],
1489 'attach_comment' => $this->filename_data['filecomment'],
1490 'real_filename' => $filedata['real_filename'],
1491 'extension' => $filedata['extension'],
1492 'mimetype' => $filedata['mimetype'],
1493 'filesize' => $filedata['filesize'],
1494 'filetime' => $filedata['filetime'],
1495 'thumbnail' => $filedata['thumbnail'],
1496 'is_orphan' => 1,
1497 'in_message' => ($is_message) ? 1 : 0,
1498 'poster_id' => $user->data['user_id'],
1499 );
1500
1501 $db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1502
1503 $new_entry = array(
1504 'attach_id' => $db->sql_nextid(),
1505 'is_orphan' => 1,
1506 'real_filename' => $filedata['real_filename'],
1507 'attach_comment'=> $this->filename_data['filecomment'],
1508 );
1509
1510 $this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
1511 $this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message);
1512 $this->filename_data['filecomment'] = '';
1513 }
1514 }
1515 else
1516 {
1517 $error[] = sprintf($user->lang['TOO_MANY_ATTACHMENTS'], $cfg['max_attachments']);
1518 }
1519 }
1520 }
1521
1522 foreach ($error as $error_msg)
1523 {
1524 $this->warn_msg[] = $error_msg;
1525 }
1526 }
1527
1528 /**
1529 * Get Attachment Data
1530 */
1531 function get_submitted_attachment_data($check_user_id = false)
1532 {
1533 global $user, $db, $phpbb_root_path, $phpEx, $config;
1534
1535 $this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true));
1536 $attachment_data = (isset($_POST['attachment_data'])) ? $_POST['attachment_data'] : array();
1537 $this->attachment_data = array();
1538
1539 $check_user_id = ($check_user_id === false) ? $user->data['user_id'] : $check_user_id;
1540
1541 if (!sizeof($attachment_data))
1542 {
1543 return;
1544 }
1545
1546 $not_orphan = $orphan = array();
1547
1548 foreach ($attachment_data as $pos => $var_ary)
1549 {
1550 if ($var_ary['is_orphan'])
1551 {
1552 $orphan[(int) $var_ary['attach_id']] = $pos;
1553 }
1554 else
1555 {
1556 $not_orphan[(int) $var_ary['attach_id']] = $pos;
1557 }
1558 }
1559
1560 // Regenerate already posted attachments
1561 if (sizeof($not_orphan))
1562 {
1563 // Get the attachment data, based on the poster id...
1564 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
1565 FROM ' . ATTACHMENTS_TABLE . '
1566 WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . '
1567 AND poster_id = ' . $check_user_id;
1568 $result = $db->sql_query($sql);
1569
1570 while ($row = $db->sql_fetchrow($result))
1571 {
1572 $pos = $not_orphan[$row['attach_id']];
1573 $this->attachment_data[$pos] = $row;
1574 set_var($this->attachment_data[$pos]['attach_comment'], $_POST['attachment_data'][$pos]['attach_comment'], 'string', true);
1575
1576 unset($not_orphan[$row['attach_id']]);
1577 }
1578 $db->sql_freeresult($result);
1579 }
1580
1581 if (sizeof($not_orphan))
1582 {
1583 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1584 }
1585
1586 // Regenerate newly uploaded attachments
1587 if (sizeof($orphan))
1588 {
1589 $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
1590 FROM ' . ATTACHMENTS_TABLE . '
1591 WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . '
1592 AND poster_id = ' . $user->data['user_id'] . '
1593 AND is_orphan = 1';
1594 $result = $db->sql_query($sql);
1595
1596 while ($row = $db->sql_fetchrow($result))
1597 {
1598 $pos = $orphan[$row['attach_id']];
1599 $this->attachment_data[$pos] = $row;
1600 set_var($this->attachment_data[$pos]['attach_comment'], $_POST['attachment_data'][$pos]['attach_comment'], 'string', true);
1601
1602 unset($orphan[$row['attach_id']]);
1603 }
1604 $db->sql_freeresult($result);
1605 }
1606
1607 if (sizeof($orphan))
1608 {
1609 trigger_error('NO_ACCESS_ATTACHMENT', E_USER_ERROR);
1610 }
1611
1612 ksort($this->attachment_data);
1613 }
1614
1615 /**
1616 * Parse Poll
1617 */
1618 function parse_poll(&$poll)
1619 {
1620 global $auth, $user, $config;
1621
1622 $poll_max_options = $poll['poll_max_options'];
1623
1624 // Parse Poll Option text ;)
1625 $tmp_message = $this->message;
1626 $this->message = $poll['poll_option_text'];
1627 $bbcode_bitfield = $this->bbcode_bitfield;
1628
1629 $poll['poll_option_text'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll');
1630
1631 $bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield));
1632 $this->message = $tmp_message;
1633
1634 // Parse Poll Title
1635 $tmp_message = $this->message;
1636 $this->message = $poll['poll_title'];
1637 $this->bbcode_bitfield = $bbcode_bitfield;
1638
1639 $poll['poll_options'] = explode("\n", trim($poll['poll_option_text']));
1640 $poll['poll_options_size'] = sizeof($poll['poll_options']);
1641
1642 if (!$poll['poll_title'] && $poll['poll_options_size'])
1643 {
1644 $this->warn_msg[] = $user->lang['NO_POLL_TITLE'];
1645 }
1646 else
1647 {
1648 if (utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message)) > 100)
1649 {
1650 $this->warn_msg[] = $user->lang['POLL_TITLE_TOO_LONG'];
1651 }
1652 $poll['poll_title'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll');
1653 if (strlen($poll['poll_title']) > 255)
1654 {
1655 $this->warn_msg[] = $user->lang['POLL_TITLE_COMP_TOO_LONG'];
1656 }
1657 }
1658
1659 $this->bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield));
1660 $this->message = $tmp_message;
1661 unset($tmp_message);
1662
1663 if (sizeof($poll['poll_options']) == 1)
1664 {
1665 $this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS'];
1666 }
1667 else if ($poll['poll_options_size'] > (int) $config['max_poll_options'])
1668 {
1669 $this->warn_msg[] = $user->lang['TOO_MANY_POLL_OPTIONS'];
1670 }
1671 else if ($poll_max_options > $poll['poll_options_size'])
1672 {
1673 $this->warn_msg[] = $user->lang['TOO_MANY_USER_OPTIONS'];
1674 }
1675
1676 $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']);
1677 }
1678}
1679
1680?>
Note: See TracBrowser for help on using the repository browser.