source: trunk/web/punbb/include/parser.php @ 3

Last change on this file since 3 was 1, checked in by dj3c1t, 13 years ago

import initial

File size: 15.6 KB
Line 
1<?php
2/***********************************************************************
3
4  Copyright (C) 2002-2005  Rickard Andersson (rickard@punbb.org)
5
6  This file is part of PunBB.
7
8  PunBB is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published
10  by the Free Software Foundation; either version 2 of the License,
11  or (at your option) any later version.
12
13  PunBB is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  MA  02111-1307  USA
22
23************************************************************************/
24
25// Make sure no one attempts to run this script "directly"
26if (!defined('PUN'))
27        exit;
28
29
30// Load cache smiley
31@include_once PUN_ROOT.'cache/cache_smilies.php';
32if (!defined('PUN_CACHE_SMILEY'))
33{
34        require_once PUN_ROOT.'include/cache_smilies.php';
35        generate_smiley_cache();
36        require PUN_ROOT.'cache/cache_smilies.php';
37}
38
39
40//
41// Make sure all BBCodes are lower case and do a little cleanup
42//
43function preparse_bbcode($text, &$errors, $is_signature = false)
44{
45        // Change all simple BBCodes to lower case
46        $a = array('[B]', '[I]', '[U]', '[/B]', '[/I]', '[/U]');
47        $b = array('[b]', '[i]', '[u]', '[/b]', '[/i]', '[/u]');
48        $text = str_replace($a, $b, $text);
49
50        // Do the more complex BBCodes (also strip excessive whitespace and useless quotes)
51        $a = array( '#\[url=("|\'|)(.*?)\\1\]\s*#i',
52                                '#\[url\]\s*#i',
53                                '#\s*\[/url\]#i',
54                                '#\[email=("|\'|)(.*?)\\1\]\s*#i',
55                                '#\[email\]\s*#i',
56                                '#\s*\[/email\]#i',
57                                '#\[img\]\s*(.*?)\s*\[/img\]#is',
58                                '#\[colou?r=("|\'|)(.*?)\\1\](.*?)\[/colou?r\]#is');
59
60        $b = array(     '[url=$2]',
61                                '[url]',
62                                '[/url]',
63                                '[email=$2]',
64                                '[email]',
65                                '[/email]',
66                                '[img]$1[/img]',
67                                '[color=$2]$3[/color]');
68
69        if (!$is_signature)
70        {
71                // For non-signatures, we have to do the quote and code tags as well
72                $a[] = '#\[quote=(&quot;|"|\'|)(.*?)\\1\]\s*#i';
73                $a[] = '#\[quote\]\s*#i';
74                $a[] = '#\s*\[/quote\]\s*#i';
75                $a[] = '#\[code\][\r\n]*(.*?)\s*\[/code\]\s*#is';
76
77                $b[] = '[quote=$1$2$1]';
78                $b[] = '[quote]';
79                $b[] = '[/quote]'."\n";
80                $b[] = '[code]$1[/code]'."\n";
81        }
82
83        // Run this baby!
84        $text = preg_replace($a, $b, $text);
85
86        if (!$is_signature)
87        {
88                $overflow = check_tag_order($text, $error);
89
90                if ($error)
91                        // A BBCode error was spotted in check_tag_order()
92                        $errors[] = $error;
93                else if ($overflow)
94                        // The quote depth level was too high, so we strip out the inner most quote(s)
95                        $text = substr($text, 0, $overflow[0]).substr($text, $overflow[1], (strlen($text) - $overflow[0]));
96        }
97        else
98        {
99                global $lang_prof_reg;
100
101                if (preg_match('#\[quote=(&quot;|"|\'|)(.*)\\1\]|\[quote\]|\[/quote\]|\[code\]|\[/code\]#i', $text))
102                        message($lang_prof_reg['Signature quote/code']);
103        }
104
105        return trim($text);
106}
107
108
109//
110// Parse text and make sure that [code] and [quote] syntax is correct
111//
112function check_tag_order($text, &$error)
113{
114        global $lang_common;
115
116        // The maximum allowed quote depth
117        $max_depth = 3;
118
119        $cur_index = 0;
120        $q_depth = 0;
121
122        while (true)
123        {
124                // Look for regular code and quote tags
125                $c_start = strpos($text, '[code]');
126                $c_end = strpos($text, '[/code]');
127                $q_start = strpos($text, '[quote]');
128                $q_end = strpos($text, '[/quote]');
129
130                // Look for [quote=username] style quote tags
131                if (preg_match('#\[quote=(&quot;|"|\'|)(.*)\\1\]#sU', $text, $matches))
132                        $q2_start = strpos($text, $matches[0]);
133                else
134                        $q2_start = 65536;
135
136                // Deal with strpos() returning false when the string is not found
137                // (65536 is one byte longer than the maximum post length)
138                if ($c_start === false) $c_start = 65536;
139                if ($c_end === false) $c_end = 65536;
140                if ($q_start === false) $q_start = 65536;
141                if ($q_end === false) $q_end = 65536;
142
143                // If none of the strings were found
144                if (min($c_start, $c_end, $q_start, $q_end, $q2_start) == 65536)
145                        break;
146
147                // We are interested in the first quote (regardless of the type of quote)
148                $q3_start = ($q_start < $q2_start) ? $q_start : $q2_start;
149
150                // We found a [quote] or a [quote=username]
151                if ($q3_start < min($q_end, $c_start, $c_end))
152                {
153                        $step = ($q_start < $q2_start) ? 7 : strlen($matches[0]);
154
155                        $cur_index += $q3_start + $step;
156
157                        // Did we reach $max_depth?
158                        if ($q_depth == $max_depth)
159                                $overflow_begin = $cur_index - $step;
160
161                        ++$q_depth;
162                        $text = substr($text, $q3_start + $step);
163                }
164
165                // We found a [/quote]
166                else if ($q_end < min($q_start, $c_start, $c_end))
167                {
168                        if ($q_depth == 0)
169                        {
170                                $error = $lang_common['BBCode error'].' '.$lang_common['BBCode error 1'];
171                                return;
172                        }
173
174                        $q_depth--;
175                        $cur_index += $q_end+8;
176
177                        // Did we reach $max_depth?
178                        if ($q_depth == $max_depth)
179                                $overflow_end = $cur_index;
180
181                        $text = substr($text, $q_end+8);
182                }
183
184                // We found a [code]
185                else if ($c_start < min($c_end, $q_start, $q_end))
186                {
187                        // Make sure there's a [/code] and that any new [code] doesn't occur before the end tag
188                        $tmp = strpos($text, '[/code]');
189                        $tmp2 = strpos(substr($text, $c_start+6), '[code]');
190                        if ($tmp2 !== false)
191                                $tmp2 += $c_start+6;
192                               
193                        if ($tmp === false || ($tmp2 !== false && $tmp2 < $tmp))
194                        {
195                                $error = $lang_common['BBCode error'].' '.$lang_common['BBCode error 2'];
196                                return;
197                        }
198                        else
199                                $text = substr($text, $tmp+7);
200
201                        $cur_index += $tmp+7;
202                }
203
204                // We found a [/code] (this shouldn't happen since we handle both start and end tag in the if clause above)
205                else if ($c_end < min($c_start, $q_start, $q_end))
206                {
207                        $error = $lang_common['BBCode error'].' '.$lang_common['BBCode error 3'];
208                        return;
209                }
210        }
211
212        // If $q_depth <> 0 something is wrong with the quote syntax
213        if ($q_depth)
214        {
215                $error = $lang_common['BBCode error'].' '.$lang_common['BBCode error 4'];
216                return;
217        }
218        else if ($q_depth < 0)
219        {
220                $error = $lang_common['BBCode error'].' '.$lang_common['BBCode error 5'];
221                return;
222        }
223
224        // If the quote depth level was higher than $max_depth we return the index for the
225        // beginning and end of the part we should strip out
226        if (isset($overflow_begin))
227                return array($overflow_begin, $overflow_end);
228        else
229                return null;
230}
231
232
233//
234// Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
235//
236function split_text($text, $start, $end)
237{
238        global $pun_config;
239
240        $tokens = explode($start, $text);
241
242        $outside[] = $tokens[0];
243
244        $num_tokens = count($tokens);
245        for ($i = 1; $i < $num_tokens; ++$i)
246        {
247                $temp = explode($end, $tokens[$i]);
248                $inside[] = $temp[0];
249                $outside[] = $temp[1];
250        }
251
252        if ($pun_config['o_indent_num_spaces'] != 8 && $start == '[code]')
253        {
254                $spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
255                $inside = str_replace("\t", $spaces, $inside);
256        }
257
258        return array($inside, $outside);
259}
260
261
262//
263// Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
264//
265function handle_url_tag($url, $link = '')
266{
267        global $pun_user;
268
269        $full_url = str_replace(array(' ', '\'', '`'), array('%20', '', ''), $url);
270        if (strpos($url, 'www.') === 0)                 // If it starts with www, we add http://
271                $full_url = 'http://'.$full_url;
272        else if (strpos($url, 'ftp.') === 0)    // Else if it starts with ftp, we add ftp://
273                $full_url = 'ftp://'.$full_url;
274        else if (!preg_match('#^([a-z0-9]{3,6})://#', $url, $bah))      // Else if it doesn't start with abcdef://, we add http://
275                $full_url = 'http://'.$full_url;
276
277        // Ok, not very pretty :-)
278        $link = ($link == '' || $link == $url) ? ((strlen($url) > 55) ? substr($url, 0 , 39).' &hellip; '.substr($url, -10) : $url) : stripslashes($link);
279
280        return '<a href="'.$full_url.'" target=\"_blank\">'.$link.'</a>';
281}
282
283
284//
285// Turns an URL from the [img] tag into an <img> tag or a <a href...> tag
286//
287function handle_img_tag($url, $is_signature = false)
288{
289        global $lang_common, $pun_config, $pun_user;
290
291        $img_tag = '<a href="'.$url.'">&lt;'.$lang_common['Image link'].'&gt;</a>';
292
293        if ($is_signature && $pun_user['show_img_sig'] != '0')
294                $img_tag = '<img class="sigimage" src="'.$url.'" alt="'.htmlspecialchars($url).'" />';
295        else if (!$is_signature && $pun_user['show_img'] != '0')
296                $img_tag = '<img class="postimg" src="'.$url.'" alt="'.htmlspecialchars($url).'" />';
297
298        return $img_tag;
299}
300
301
302//
303// Convert BBCodes to their HTML equivalent
304//
305function do_bbcode($text)
306{
307        global $lang_common, $pun_user;
308
309        if (strpos($text, 'quote') !== false)
310        {
311                $text = str_replace('[quote]', '</p><blockquote><div class="incqbox"><p>', $text);
312                $text = preg_replace('#\[quote=(&quot;|"|\'|)(.*)\\1\]#seU', '"</p><blockquote><div class=\"incqbox\"><h4>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\')." ".$lang_common[\'wrote\'].":</h4><p>"', $text);
313                $text = preg_replace('#\[\/quote\]\s*#', '</p></div></blockquote><p>', $text);
314        }
315       
316        $pattern = array('#\[b\](.*?)\[/b\]#s',
317                                         '#\[i\](.*?)\[/i\]#s',
318                                         '#\[u\](.*?)\[/u\]#s',
319                                         '#\[url\]([^\[]*?)\[/url\]#e',
320                                         '#\[url=([^\[]*?)\](.*?)\[/url\]#e',
321                                         '#\[email\]([^\[]*?)\[/email\]#',
322                                         '#\[email=([^\[]*?)\](.*?)\[/email\]#',
323                                         '#\[color=([a-zA-Z]*|\#?[0-9a-fA-F]{6})](.*?)\[/color\]#s',
324                                         '#\[font=(.*?)](.*?)\[/font\]#s',
325                                         '#\[align=(.*?)\](.*?)\[/align\]#s',
326                                         '#\[hr /\]#',
327                                         '#\[hr\]#',
328                                         '#\[s\](.*?)\[/s\]#s',
329                                         '#\[h\](.*?)\[/h\]#s',
330                               '#\[size=([0-9]{1,2})](.*?)\[/size\]#s');
331
332        $replace = array('<strong>$1</strong>',
333                                         '<em>$1</em>',
334                                         '<span class="bbu">$1</span>',
335                                         'handle_url_tag(\'$1\')',
336                                         'handle_url_tag(\'$1\', \'$2\')',
337                                         '<a href="mailto:$1">$1</a>',
338                                         '<a href="mailto:$1">$2</a>',
339                                         '<span style="color: $1">$2</span>',
340                                         '<span style="font-family: $1">$2</span>',
341                                         '<div align="$1">$2</div>',
342                                         '<hr />',
343                                         '<hr />',
344                                         '<del>$1</del>',
345                                         '<span style="background-color: #FFFF00; color: #000000">$1</span>',
346                               '<span style="font-size: $1pt;">$2</span>');
347
348        // This thing takes a while! :)
349        $text = preg_replace($pattern, $replace, $text);
350
351        return $text;
352}
353
354
355//
356// Make hyperlinks clickable
357//
358function do_clickable($text)
359{
360        global $pun_user;
361
362        $text = ' '.$text;
363
364        $text = preg_replace('#([\s\(\)])(https?|ftp|news){1}://([\w\-]+\.([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^"\s\(\)<\[]*)?)#ie', '\'$1\'.handle_url_tag(\'$2://$3\')', $text);
365        $text = preg_replace('#([\s\(\)])(www|ftp)\.(([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^"\s\(\)<\[]*)?)#ie', '\'$1\'.handle_url_tag(\'$2.$3\', \'$2.$3\')', $text);
366
367        return substr($text, 1);
368}
369
370
371//
372// Convert a series of smilies to images
373//
374function do_smilies($text)
375{
376        global $smiley_text, $smiley_img;
377
378        $text = ' '.$text.' ';
379
380        $num_smilies = count($smiley_text);
381        for ($i = 0; $i < $num_smilies; ++$i)
382                $text = preg_replace("#(?<=.\W|\W.|^\W)".preg_quote($smiley_text[$i], '#')."(?=.\W|\W.|\W$)#m", '$1<img src="'.PUN_ROOT.'img/smilies/'.$smiley_img[$i].'" alt="'.substr($smiley_img[$i], 0, strrpos($smiley_img[$i], '.')).'" />$2', $text);
383
384        return substr($text, 1, -1);
385}
386
387
388//
389// Parse message text
390//
391function parse_message($text, $hide_smilies)
392{
393        global $pun_config, $lang_common, $pun_user;
394
395        if ($pun_config['o_censoring'] == '1')
396                $text = censor_words($text);
397
398        // Convert applicable characters to HTML entities
399        $text = pun_htmlspecialchars($text);
400
401        // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
402        if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
403        {
404                list($inside, $outside) = split_text($text, '[code]', '[/code]');
405                $outside = array_map('ltrim', $outside);
406                $text = implode('<">', $outside);
407        }
408
409        if ($pun_config['o_make_links'] == '1')
410                $text = do_clickable($text);
411
412        if ($pun_config['o_smilies'] == '1' && $pun_user['show_smilies'] == '1' && $hide_smilies == '0')
413                $text = do_smilies($text);
414
415        if ($pun_config['p_message_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
416        {
417                $text = do_bbcode($text);
418
419                if ($pun_config['p_message_img_tag'] == '1')
420                {
421//                      $text = preg_replace('#\[img\]((ht|f)tps?://)([^\s<"]*?)\.(jpg|jpeg|png|gif)\[/img\]#e', 'handle_img_tag(\'$1$3.$4\')', $text);
422                        $text = preg_replace('#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e', 'handle_img_tag(\'$1$3\')', $text);
423                }
424        }
425
426        // Deal with newlines, tabs and multiple spaces
427        $pattern = array("\n", "\t", '  ', '  ');
428        $replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
429        $text = str_replace($pattern, $replace, $text);
430
431        // If we split up the message before we have to concatenate it together again (code tags)
432        if (isset($inside))
433        {
434                $outside = explode('<">', $text);
435                $text = '';
436
437                $num_tokens = count($outside);
438
439                for ($i = 0; $i < $num_tokens; ++$i)
440                {
441                        $text .= $outside[$i];
442                        if (isset($inside[$i]))
443                        {
444                                $num_lines = ((substr_count($inside[$i], "\n")) + 3) * 1.5;
445                                $height_str = ($num_lines > 35) ? '35em' : $num_lines.'em';
446                                $text .= '</p><div class="codebox"><div class="incqbox"><h4>'.$lang_common['Code'].':</h4><div class="scrollbox" style="height: '.$height_str.'"><pre>'.$inside[$i].'</pre></div></div></div><p>';
447                        }
448                }
449        }
450
451        // Add paragraph tag around post, but make sure there are no empty paragraphs
452        $text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
453
454        // Mod: Flash MP3 Player (8 nouvelles lignes suivent)
455        $player_url     = 'dewplayer-multi.swf';
456        $player_bgcolor = 'ffffff';
457        $player_width   = 240;
458        $player_height  = 20;
459        $player_param   = '?bgcolor='.$player_bgcolor.'&amp;mp3=$1';  // Pour plus d'options: http://www.alsacreations.fr/?dewplayer
460        $player_alternative = '<strong>Flash non détécté</strong>';
461        $player_code = "\n\t\t\t\t\t<object type=\"application/x-shockwave-flash\" data=\"".$player_url.$player_param."\" width=\"".$player_width."\" height=\"".$player_height."\">\n\t\t\t\t\t  <param name=\"movie\" value=\"".$player_url.$player_param."\" />\n\t\t\t\t\t  <param name=\"bgcolor\" value=\"#".$player_bgcolor."\" />\n\t\t\t\t\t  ".$player_alternative."\n\t\t\t\t\t</object>\n\t\t\t\t\t";
462        $text = preg_replace("/\[mp3 url=([^ ]+)\]/", $player_code, $text);
463
464        return $text;
465}
466
467
468//
469// Parse signature text
470//
471function parse_signature($text)
472{
473        global $pun_config, $lang_common, $pun_user;
474
475        if ($pun_config['o_censoring'] == '1')
476                $text = censor_words($text);
477
478        $text = pun_htmlspecialchars($text);
479
480        if ($pun_config['o_make_links'] == '1')
481                $text = do_clickable($text);
482
483        if ($pun_config['o_smilies_sig'] == '1' && $pun_user['show_smilies'] != '0')
484                $text = do_smilies($text);
485
486        if ($pun_config['p_sig_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
487        {
488                $text = do_bbcode($text);
489
490                if ($pun_config['p_sig_img_tag'] == '1')
491                {
492//                      $text = preg_replace('#\[img\]((ht|f)tps?://)([^\s<"]*?)\.(jpg|jpeg|png|gif)\[/img\]#e', 'handle_img_tag(\'$1$3.$4\', true)', $text);
493                        $text = preg_replace('#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e', 'handle_img_tag(\'$1$3\', true)', $text);
494                }
495        }
496
497        // Deal with newlines, tabs and multiple spaces
498        $pattern = array("\n", "\t", '  ', '  ');
499        $replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
500        $text = str_replace($pattern, $replace, $text);
501
502        return $text;
503}
504?>
Note: See TracBrowser for help on using the repository browser.