1<?php
2/**
3 * XOOPS TextSanitizer extension
4 *
5 * You may not change or alter any portion of this comment or credits
6 * of supporting developers from this source code or any supporting source code
7 * which is considered copyrighted (c) material of the original comment or credit authors.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
13 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14 * @package             class
15 * @since               2.0.0
16 * @author              Kazumi Ono (http://www.myweb.ne.jp/, http://jp.xoops.org/)
17 * @author              Goghs Cheng (http://www.eqiao.com, http://www.devbeez.com/)
18 * @author              Taiwen Jiang <phppp@users.sourceforge.net>
19 */
20
21/**
22 * Abstract class for extensions
23 *
24 * @author              Taiwen Jiang <phppp@users.sourceforge.net>
25 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
26 */
27class MyTextSanitizerExtension
28{
29    public $instance;
30    public $ts;
31    public $config;
32    public $image_path;
33
34    /**
35     * Constructor
36     *
37     * @param MyTextSanitizer $ts
38     */
39    public function __construct(MyTextSanitizer $ts)
40    {
41        $this->ts         = $ts;
42        $this->image_path = XOOPS_URL . '/images/form';
43    }
44
45    /**
46     * loadConfig
47     *
48     * @param  string $path
49     * @return string
50     */
51    public static function loadConfig($path = null)
52    {
53        $ts   = MyTextSanitizer::getInstance();
54        $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
55        if (false === strpos($path, '/')) {
56            if (is_dir($ts->path_basic . '/' . $path)) {
57                $path = $ts->path_basic . '/' . $path;
58            } else {
59                if (is_dir($ts->path_plugin . '/' . $path)) {
60                    $path = $ts->path_plugin . '/' . $path;
61                }
62            }
63        }
64        $config_default = array();
65        $config_custom  = array();
66        if (file_exists($path . '/config.php')) {
67            $config_default = include $path . '/config.php';
68        }
69        if (file_exists($path . '/config.custom.php')) {
70            $config_custom = include $path . '/config.custom.php';
71        }
72
73        return self::mergeConfig($config_default, $config_custom);
74    }
75
76    /**
77     * Merge Config
78     *
79     * @param  array $config_default
80     * @param  array $config_custom
81     * @return array
82     */
83    public static function mergeConfig($config_default, $config_custom)
84    {
85        if (is_array($config_custom)) {
86            foreach ($config_custom as $key => $val) {
87                if (is_array($config_default[$key])) {
88                    $config_default[$key] = self::mergeConfig($config_default[$key], $config_custom[$key]);
89                } else {
90                    $config_default[$key] = $val;
91                }
92            }
93        }
94
95        return $config_default;
96    }
97
98    /**
99     * encode
100     *
101     * @param string $textarea_id id attribute of text area
102     *
103     * @return array
104     */
105    public function encode($textarea_id)
106    {
107        return array();
108    }
109
110    /**
111     * decode
112     *
113     * @return Null
114     */
115    public static function decode($url, $width, $height)
116    {
117        return null;
118    }
119}
120
121/**
122 * Class to "clean up" text for various uses
123 *
124 * <strong>Singleton</strong>
125 *
126 * @package       kernel
127 * @subpackage    core
128 * @author        Kazumi Ono <onokazu@xoops.org>
129 * @author        Taiwen Jiang <phppp@users.sourceforge.net>
130 * @author        Goghs Cheng
131 * @copyright (c) 2000-2016 XOOPS Project - www.xoops.org
132 */
133class MyTextSanitizer
134{
135    /**
136     *
137     * @var array
138     */
139    public $smileys = array();
140
141    /**
142     */
143    public $censorConf;
144
145    /**
146     *
147     * @var holding reference to text
148     */
149    public $text         = '';
150    public $patterns     = array();
151    public $replacements = array();
152
153    //mb------------------------------
154    public $callbackPatterns = array();
155    public $callbacks        = array();
156    //mb------------------------------
157
158    public $path_basic;
159    public $path_plugin;
160
161    public $config;
162
163    /**
164     * Constructor of this class
165     *
166     * Gets allowed html tags from admin config settings
167     * <br> should not be allowed since nl2br will be used
168     * when storing data.
169     *
170     * @access private
171     */
172
173    public function __construct()
174    {
175        $this->path_basic  = XOOPS_ROOT_PATH . '/class/textsanitizer';
176        $this->path_plugin = XOOPS_ROOT_PATH . '/Frameworks/textsanitizer';
177        $this->config      = $this->loadConfig();
178    }
179
180    /**
181     * Enter description here...
182     *
183     * @param  string $name
184     * @return array
185     */
186    public function loadConfig($name = null)
187    {
188        if (!empty($name)) {
189            return MyTextSanitizerExtension::loadConfig($name);
190        }
191        $config_default = include $this->path_basic . '/config.php';
192        $config_custom  = array();
193        if (file_exists($file = $this->path_basic . '/config.custom.php')) {
194            $config_custom = include $file;
195        }
196
197        return $this->mergeConfig($config_default, $config_custom);
198    }
199
200    /**
201     * Enter description here...
202     *
203     * @param  array $config_default
204     * @param  array $config_custom
205     * @return unknown
206     */
207    public function mergeConfig($config_default, $config_custom)
208    {
209        if (is_array($config_custom)) {
210            foreach ($config_custom as $key => $val) {
211                if (isset($config_default[$key]) && is_array($config_default[$key])) {
212                    $config_default[$key] = $this->mergeConfig($config_default[$key], $config_custom[$key]);
213                } else {
214                    $config_default[$key] = $val;
215                }
216            }
217        }
218
219        return $config_default;
220    }
221
222    /**
223     * Access the only instance of this class
224     *
225     * @return object
226     * @static
227     * @staticvar object
228     */
229    public static function getInstance()
230    {
231        static $instance;
232        if (!isset($instance)) {
233            $instance = new MyTextSanitizer();
234        }
235
236        return $instance;
237    }
238
239    /**
240     * Get the smileys
241     *
242     * @param bool $isAll TRUE for all smileys, FALSE for smileys with display = 1
243     *
244     * @return array
245     */
246    public function getSmileys($isAll = true)
247    {
248        if (count($this->smileys) == 0) {
249            /* @var XoopsMySQLDatabase $xoopsDB */
250            $xoopsDB = XoopsDatabaseFactory::getDatabaseConnection();
251            if ($getsmiles = $xoopsDB->query('SELECT * FROM ' . $xoopsDB->prefix('smiles'))) {
252                while (false !== ($smiles = $xoopsDB->fetchArray($getsmiles))) {
253                    $this->smileys[] = $smiles;
254                }
255            }
256        }
257        if ($isAll) {
258            return $this->smileys;
259        }
260
261        $smileys = array();
262        foreach ($this->smileys as $smile) {
263            if (empty($smile['display'])) {
264                continue;
265            }
266            $smileys[] = $smile;
267        }
268
269        return $smileys;
270    }
271
272    /**
273     * Replace emoticons in the message with smiley images
274     *
275     * @param  string $message
276     * @return string
277     */
278    public function smiley($message)
279    {
280        $smileys = $this->getSmileys();
281        foreach ($smileys as $smile) {
282            $message = str_replace($smile['code'], '<img class="imgsmile" src="' . XOOPS_UPLOAD_URL . '/' . htmlspecialchars($smile['smile_url']) . '" alt="" />', $message);
283        }
284
285        return $message;
286    }
287
288    /**
289     * @param $match
290     *
291     * @return string
292     */
293    public function makeClickableCallback01($match)
294    {
295        return $match[1] . "<a href=\"$match[2]://$match[3]\" title=\"$match[2]://$match[3]\" rel=\"external\">$match[2]://" . $this->truncate($match[3]) . '</a>';
296    }
297
298    /**
299     * @param $match
300     *
301     * @return string
302     */
303    public function makeClickableCallback02($match)
304    {
305        return $match[1] . "<a href=\"http://www.$match[2]$match[6]\" title=\"www.$match[2]$match[6]\" rel=\"external\">" . $this->truncate('www.' . $match[2] . $match[6]) . '</a>';
306    }
307
308    /**
309     * @param $match
310     *
311     * @return string
312     */
313    public function makeClickableCallback03($match)
314    {
315        return $match[1] . "<a href=\"ftp://ftp.$match[2].$match[3]\" title=\"ftp.$match[2].$match[3]\" rel=\"external\">" . $this->truncate('ftp.' . $match[2] . $match[3]) . '</a>';
316    }
317
318    /**
319     * @param $match
320     *
321     * @return string
322     */
323    public function makeClickableCallback04($match)
324    {
325        return $match[1] . "<a href=\"mailto:$match[2]@$match[3]\" title=\"$match[2]@$match[3]\">" . $this->truncate($match[2] . '@' . $match[3]) . '</a>';
326    }
327
328    /**
329     * Make links in the text clickable
330     *
331     * @param  string $text
332     * @return string
333     */
334    public function makeClickable(&$text)
335    {
336        $text1 = $text;
337
338        $valid_chars = "a-z0-9\/\-_+=.~!%@?#&;:$\|";
339        $end_chars   = "a-z0-9\/\-_+=~!%@?#&;:$\|";
340
341        //        $patterns   = array();
342        //        $replacements   = array();
343        //
344        //        $patterns[]     = "/(^|[^]_a-z0-9-=\"'\/])([a-z]+?):\/\/([{$valid_chars}]+[{$end_chars}])/ei";
345        //        $replacements[] = "'\\1<a href=\"\\2://\\3\" title=\"\\2://\\3\" rel=\"external\">\\2://'.MyTextSanitizer::truncate( '\\3' ).'</a>'";
346        //
347        //
348        //        $patterns[]     = "/(^|[^]_a-z0-9-=\"'\/:\.])www\.((([a-zA-Z0-9\-]*\.){1,}){1}([a-zA-Z]{2,6}){1})((\/([a-zA-Z0-9\-\._\?\,\'\/\\+&%\$#\=~])*)*)/ei";
349        //        $replacements[] = "'\\1<a href=\"http://www.\\2\\6\" title=\"www.\\2\\6\" rel=\"external\">'.MyTextSanitizer::truncate( 'www.\\2\\6' ).'</a>'";
350        //
351        //        $patterns[]     = "/(^|[^]_a-z0-9-=\"'\/])ftp\.([a-z0-9\-]+)\.([{$valid_chars}]+[{$end_chars}])/ei";
352        //        $replacements[] = "'\\1<a href=\"ftp://ftp.\\2.\\3\" title=\"ftp.\\2.\\3\" rel=\"external\">'.MyTextSanitizer::truncate( 'ftp.\\2.\\3' ).'</a>'";
353        //
354        //        $patterns[]     = "/(^|[^]_a-z0-9-=\"'\/:\.])([-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+)@((?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?)/ei";
355        //        $replacements[] = "'\\1<a href=\"mailto:\\2@\\3\" title=\"\\2@\\3\">'.MyTextSanitizer::truncate( '\\2@\\3' ).'</a>'";
356        //
357        //        $text = preg_replace($patterns, $replacements, $text);
358        //
359        //----------------------------------------------------------------------------------
360
361        $pattern = "/(^|[^]_a-z0-9-=\"'\/])([a-z]+?):\/\/([{$valid_chars}]+[{$end_chars}])/i";
362        $text1   = preg_replace_callback($pattern, 'self::makeClickableCallback01', $text1);
363
364        $pattern = "/(^|[^]_a-z0-9-=\"'\/:\.])www\.((([a-zA-Z0-9\-]*\.){1,}){1}([a-zA-Z]{2,6}){1})((\/([a-zA-Z0-9\-\._\?\,\'\/\\+&%\$#\=~])*)*)/i";
365        $text1   = preg_replace_callback($pattern, 'self::makeClickableCallback02', $text1);
366
367        $pattern = "/(^|[^]_a-z0-9-=\"'\/])ftp\.([a-z0-9\-]+)\.([{$valid_chars}]+[{$end_chars}])/i";
368        $text1   = preg_replace_callback($pattern, 'self::makeClickableCallback03', $text1);
369
370        $pattern = "/(^|[^]_a-z0-9-=\"'\/:\.])([-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+)@((?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?)/i";
371        $text1   = preg_replace_callback($pattern, 'self::makeClickableCallback04', $text1);
372
373        return $text1;
374    }
375
376    /**
377     * MyTextSanitizer::truncate()
378     *
379     * @param  mixed $text
380     * @return mixed|string
381     */
382    public function truncate($text)
383    {
384        $instance = MyTextSanitizer::getInstance();
385        if (empty($text) || empty($instance->config['truncate_length']) || strlen($text) < $instance->config['truncate_length']) {
386            return $text;
387        }
388        $len = floor($instance->config['truncate_length'] / 2);
389        $ret = substr($text, 0, $len) . ' ... ' . substr($text, 5 - $len);
390
391        return $ret;
392    }
393
394    /**
395     * Replace XoopsCodes with their equivalent HTML formatting
396     *
397     * @param  string   $text
398     * @param  bool|int $allowimage Allow images in the text?
399     *                              On FALSE, uses links to images.
400     * @return string
401     */
402    public function &xoopsCodeDecode(&$text, $allowimage = 1)
403    {
404        $patterns       = array();
405        $replacements   = array();
406        $patterns[]     = "/\[siteurl=(['\"]?)([^\"'<>]*)\\1](.*)\[\/siteurl\]/sU";
407        $replacements[] = '<a href="' . XOOPS_URL . '/\\2" title="">\\3</a>';
408        $patterns[]     = "/\[url=(['\"]?)(http[s]?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
409        $replacements[] = '<a href="\\2" rel="external" title="">\\3</a>';
410        $patterns[]     = "/\[url=(['\"]?)(ftp?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
411        $replacements[] = '<a href="\\2" rel="external" title="">\\3</a>';
412        $patterns[]     = "/\[url=(['\"]?)([^'\"<>]*)\\1](.*)\[\/url\]/sU";
413        $replacements[] = '<a href="http://\\2" rel="external" title="">\\3</a>';
414        $patterns[]     = "/\[color=(['\"]?)([a-zA-Z0-9]*)\\1](.*)\[\/color\]/sU";
415        $replacements[] = '<span style="color: #\\2;">\\3</span>';
416        $patterns[]     = "/\[size=(['\"]?)([a-z0-9-]*)\\1](.*)\[\/size\]/sU";
417        $replacements[] = '<span style="font-size: \\2;">\\3</span>';
418        $patterns[]     = "/\[font=(['\"]?)([^;<>\*\(\)\"']*)\\1](.*)\[\/font\]/sU";
419        $replacements[] = '<span style="font-family: \\2;">\\3</span>';
420        $patterns[]     = "/\[email]([^;<>\*\(\)\"']*)\[\/email\]/sU";
421        $replacements[] = '<a href="mailto:\\1" title="">\\1</a>';
422
423        $patterns[]     = "/\[b](.*)\[\/b\]/sU";
424        $replacements[] = '<strong>\\1</strong>';
425        $patterns[]     = "/\[i](.*)\[\/i\]/sU";
426        $replacements[] = '<em>\\1</em>';
427        $patterns[]     = "/\[u](.*)\[\/u\]/sU";
428        $replacements[] = '<span style="text-decoration: underline;">\\1</span>';
429        $patterns[]     = "/\[d](.*)\[\/d\]/sU";
430        $replacements[] = '<del>\\1</del>';
431        $patterns[]     = "/\[center](.*)\[\/center\]/sU";
432        $replacements[] = '<div style="text-align: center;">\\1</div>';
433        $patterns[]     = "/\[left](.*)\[\/left\]/sU";
434        $replacements[] = '<div style="text-align: left;">\\1</div>';
435        $patterns[]     = "/\[right](.*)\[\/right\]/sU";
436        $replacements[] = '<div style="text-align: right;">\\1</div>';
437
438        $this->text         = $text;
439        $this->patterns     = $patterns;
440        $this->replacements = $replacements;
441
442        $this->config['allowimage'] = $allowimage;
443        $this->executeExtensions();
444
445        $text = preg_replace($this->patterns, $this->replacements, $this->text);
446        //-------------------------------------------------------------------------------
447        $count = count($this->callbackPatterns);
448
449        for ($i = 0; $i < $count; ++$i) {
450            $text = preg_replace_callback($this->callbackPatterns[$i], $this->callbacks[$i], $text);
451        }
452        //------------------------------------------------------------------------------
453        $text = $this->quoteConv($text);
454
455        return $text;
456    }
457
458    /**
459     * Convert quote tags
460     *
461     * @param  string $text
462     * @return string
463     */
464    public function quoteConv($text)
465    {
466        //look for both open and closing tags in the correct order
467        $pattern     = "/\[quote](.*)\[\/quote\]/sU";
468        $replacement = _QUOTEC . '<div class="xoopsQuote"><blockquote>\\1</blockquote></div>';
469
470        $text = preg_replace($pattern, $replacement, $text, -1, $count);
471        //no more matches, return now
472        if (!$count) {
473            return $text;
474        }
475
476        //new matches could have been created, keep doing it until we have no matches
477        return $this->quoteConv($text);
478    }
479
480    /**
481     * A quick solution for filtering XSS scripts
482     *
483     * @TODO : To be improved
484     * @param $text
485     * @return mixed
486     */
487    public function filterXss($text)
488    {
489        $patterns       = array();
490        $replacements   = array();
491        $text           = str_replace("\x00", '', $text);
492        $c              = "[\x01-\x1f]*";
493        $patterns[]     = "/\bj{$c}a{$c}v{$c}a{$c}s{$c}c{$c}r{$c}i{$c}p{$c}t{$c}[\s]*:/si";
494        $replacements[] = 'javascript;';
495        $patterns[]     = "/\ba{$c}b{$c}o{$c}u{$c}t{$c}[\s]*:/si";
496        $replacements[] = 'about;';
497        $patterns[]     = "/\bx{$c}s{$c}s{$c}[\s]*:/si";
498        $replacements[] = 'xss;';
499        $text           = preg_replace($patterns, $replacements, $text);
500
501        return $text;
502    }
503
504    /**
505     * Convert linebreaks to <br> tags
506     *
507     * @param  string $text
508     * @return string
509     */
510    public function nl2Br($text)
511    {
512        return preg_replace('/(\015\012)|(\015)|(\012)/', '<br>', $text);
513    }
514
515    /**
516     * Add slashes to the text if magic_quotes_gpc is turned off.
517     *
518     * @param  string $text
519     * @return string
520     */
521    public function addSlashes($text)
522    {
523        if (!get_magic_quotes_gpc()) {
524            $text = addslashes($text);
525        }
526
527        return $text;
528    }
529
530    /**
531     * if magic_quotes_gpc is on, stirip back slashes
532     *
533     * @param  string $text
534     * @return string
535     */
536    public function stripSlashesGPC($text)
537    {
538        if (get_magic_quotes_gpc()) {
539            $text = stripslashes($text);
540        }
541
542        return $text;
543    }
544
545    /**
546     * Convert special characters to HTML entities
547     *
548     * @param  string $text    string being converted
549     * @param  int|null    $quote_style
550     * @param  string $charset character set used in conversion
551     * @param  bool   $double_encode
552     * @return string
553     */
554    public function htmlSpecialChars($text, $quote_style = NULL, $charset = null, $double_encode = true)
555    {
556        if ($quote_style === NULL) {
557            $quote_style = ENT_QUOTES;
558        }
559
560        if (version_compare(phpversion(), '5.2.3', '>=')) {
561            $text = htmlspecialchars($text, $quote_style, $charset ?: (defined('_CHARSET') ? _CHARSET : 'UTF-8'), $double_encode);
562        } else {
563            $text = htmlspecialchars($text, $quote_style);
564        }
565
566        return preg_replace(array('/&amp;/i', '/&nbsp;/i'), array('&', '&amp;nbsp;'), $text);
567    }
568
569    /**
570     * Reverses {@link htmlSpecialChars()}
571     *
572     * @param  string $text
573     * @return string
574     */
575    public function undoHtmlSpecialChars($text)
576    {
577        return preg_replace(array('/&gt;/i', '/&lt;/i', '/&quot;/i', '/&#039;/i', '/&amp;nbsp;/i'), array('>', '<', '"', '\'', '&nbsp;'), $text);
578    }
579
580    /**
581     * Filters textarea form data in DB for display
582     *
583     * @param  string   $text
584     * @param  bool|int $html   allow html?
585     * @param  bool|int $smiley allow smileys?
586     * @param  bool|int $xcode  allow xoopscode?
587     * @param  bool|int $image  allow inline images?
588     * @param  bool|int $br     convert linebreaks?
589     * @return string
590     */
591    public function &displayTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
592    {
593        $charset = (defined('_CHARSET') ? _CHARSET : 'UTF-8');
594        if (function_exists('mb_convert_encoding')) {
595            $text = mb_convert_encoding($text, $charset, mb_detect_encoding($text, mb_detect_order(), true));
596        }
597        if ($html != 1) {
598            // html not allowed
599            $text = $this->htmlSpecialChars($text, ENT_COMPAT, $charset);
600        }
601        $text = $this->codePreConv($text, $xcode); // Ryuji_edit(2003-11-18)
602        if ($smiley != 0) {
603            // process smiley
604            $text = $this->smiley($text);
605        }
606        if ($xcode != 0) {
607            // decode xcode
608            if ($image != 0) {
609                // image allowed
610                $text =& $this->xoopsCodeDecode($text);
611            } else {
612                // image not allowed
613                $text =& $this->xoopsCodeDecode($text, 0);
614            }
615        }
616        if ($br != 0) {
617            $text = $this->nl2Br($text);
618        }
619        $text = $this->codeConv($text, $xcode);
620        $text = $this->makeClickable($text);
621        if (!empty($this->config['filterxss_on_display'])) {
622            $text = $this->filterXss($text);
623        }
624
625        return $text;
626    }
627
628    /**
629     * Filters textarea form data submitted for preview
630     *
631     * @param  string   $text
632     * @param  bool|int $html   allow html?
633     * @param  bool|int $smiley allow smileys?
634     * @param  bool|int $xcode  allow xoopscode?
635     * @param  bool|int $image  allow inline images?
636     * @param  bool|int $br     convert linebreaks?
637     * @return string
638     */
639    public function &previewTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
640    {
641        $text = $this->stripSlashesGPC($text);
642        $text =& $this->displayTarea($text, $html, $smiley, $xcode, $image, $br);
643
644        return $text;
645    }
646
647    /**
648     * Replaces banned words in a string with their replacements
649     *
650     * @param  string $text
651     * @return string
652     * @deprecated
653     */
654    public function &censorString(&$text)
655    {
656        $ret = $this->executeExtension('censor', $text);
657        if ($ret === false) {
658            return $text;
659        }
660
661        return $ret;
662    }
663
664    /**
665     * MyTextSanitizer::codePreConv()
666     *
667     * @param  mixed $text
668     * @param  mixed $xcode
669     * @return mixed
670     */
671    public function codePreConv($text, $xcode = 1)
672    {
673        if ($xcode != 0) {
674            //            $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/esU";
675            //            $replacements = "'[code\\1]'.base64_encode('\\2').'[/code]'";
676
677            $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/sU";
678            $text = preg_replace_callback(
679                $patterns,
680                function ($matches) {
681                    return '[code'. $matches[1] . ']' . base64_encode($matches[2]) . '[/code]';
682                },
683                $text
684            );
685        }
686
687        return $text;
688    }
689
690    /**
691     * @param $match
692     *
693     * @return string
694     */
695    public function codeConvCallback($match)
696    {
697        return '<div class="xoopsCode">' . $this->executeExtension('syntaxhighlight', str_replace('\\\"', '\"', base64_decode($match[2])), $match[1]) . '</div>';
698    }
699
700    /**
701     * MyTextSanitizer::codeConv()
702     *
703     * @param  mixed $text
704     * @param  mixed $xcode
705     * @return mixed
706     */
707    public function codeConv($text, $xcode = 1)
708    {
709        if (empty($xcode)) {
710            return $text;
711        }
712        $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/sU";
713        $text1    = preg_replace_callback($patterns, array($this, 'codeConvCallback'), $text);
714
715        return $text1;
716    }
717
718    /**
719     * MyTextSanitizer::executeExtensions()
720     *
721     * @return bool
722     */
723    public function executeExtensions()
724    {
725        $extensions = array_filter($this->config['extensions']);
726        if (empty($extensions)) {
727            return true;
728        }
729        foreach (array_keys($extensions) as $extension) {
730            $this->executeExtension($extension);
731        }
732        return null;
733    }
734
735    /**
736     * MyTextSanitizer::loadExtension()
737     *
738     * @param  mixed $name
739     * @return bool|null
740     */
741    public function loadExtension($name)
742    {
743        if (file_exists($file = $this->path_basic . '/' . $name . '/' . $name . '.php')) {
744            include_once $file;
745        } elseif (file_exists($file = $this->path_plugin . '/' . $name . '/' . $name . '.php')) {
746            include_once $file;
747        } else {
748            return false;
749        }
750        $class = 'Myts' . ucfirst($name);
751        if (!class_exists($class)) {
752            trigger_error("Extension '{$name}' does not exist", E_USER_WARNING);
753
754            return false;
755        }
756        $extension = null;
757        $extension = new $class($this);
758
759        return $extension;
760    }
761
762    /**
763     * MyTextSanitizer::executeExtension()
764     *
765     * @param  mixed $name
766     * @return mixed
767     */
768    public function executeExtension($name)
769    {
770        $extension = $this->loadExtension($name);
771        $args      = array_slice(func_get_args(), 1);
772        array_unshift($args, $this);
773
774        return call_user_func_array(array($extension, 'load'), $args);
775    }
776
777    /**
778     * Filter out possible malicious text
779     * kses project at SF could be a good solution to check
780     *
781     * @param  string $text  text to filter
782     * @param  bool   $force force filtering
783     * @return string filtered text
784     */
785    public function textFilter($text, $force = false)
786    {
787        $ret = $this->executeExtension('textfilter', $text, $force);
788        if ($ret === false) {
789            return $text;
790        }
791
792        return $ret;
793    }
794
795    // #################### Deprecated Methods ######################
796    /**
797     * *#@+
798     *
799     * @deprecated
800     */
801
802    /**
803     * MyTextSanitizer::codeSanitizer()
804     *
805     * @param  mixed $str
806     * @param  mixed $image
807     * @return mixed|string
808     */
809    public function codeSanitizer($str, $image = 1)
810    {
811        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
812        $str = $this->htmlSpecialChars(str_replace('\"', '"', base64_decode($str)));
813        $str =& $this->xoopsCodeDecode($str, $image);
814
815        return $str;
816    }
817
818    /**
819     * MyTextSanitizer::sanitizeForDisplay()
820     *
821     * @param  mixed   $text
822     * @param  integer $allowhtml
823     * @param  integer $smiley
824     * @param  mixed   $bbcode
825     * @return mixed|string
826     */
827    public function sanitizeForDisplay($text, $allowhtml = 0, $smiley = 1, $bbcode = 1)
828    {
829        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
830        if ($allowhtml == 0) {
831            $text = $this->htmlSpecialChars($text);
832        } else {
833            // $config =& $GLOBALS['xoopsConfig'];
834            // $allowed = $config['allowed_html'];
835            // $text = strip_tags($text, $allowed);
836            $text = $this->makeClickable($text);
837        }
838        if ($smiley == 1) {
839            $text = $this->smiley($text);
840        }
841        if ($bbcode == 1) {
842            $text =& $this->xoopsCodeDecode($text);
843        }
844        $text = $this->nl2Br($text);
845
846        return $text;
847    }
848
849    /**
850     * MyTextSanitizer::sanitizeForPreview()
851     *
852     * @param  mixed   $text
853     * @param  integer $allowhtml
854     * @param  integer $smiley
855     * @param  mixed   $bbcode
856     * @return mixed|string
857     */
858    public function sanitizeForPreview($text, $allowhtml = 0, $smiley = 1, $bbcode = 1)
859    {
860        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
861        $text = $this->oopsStripSlashesGPC($text);
862        if ($allowhtml == 0) {
863            $text = $this->htmlSpecialChars($text);
864        } else {
865            // $config =& $GLOBALS['xoopsConfig'];
866            // $allowed = $config['allowed_html'];
867            // $text = strip_tags($text, $allowed);
868            $text = $this->makeClickable($text);
869        }
870        if ($smiley == 1) {
871            $text = $this->smiley($text);
872        }
873        if ($bbcode == 1) {
874            $text =& $this->xoopsCodeDecode($text);
875        }
876        $text = $this->nl2Br($text);
877
878        return $text;
879    }
880
881    /**
882     * MyTextSanitizer::makeTboxData4Save()
883     *
884     * @param  mixed $text
885     * @return string
886     */
887    public function makeTboxData4Save($text)
888    {
889        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
890
891        // $text = $this->undoHtmlSpecialChars($text);
892        return $this->addSlashes($text);
893    }
894
895    /**
896     * MyTextSanitizer::makeTboxData4Show()
897     *
898     * @param  mixed $text
899     * @param  mixed $smiley
900     * @return mixed|string
901     */
902    public function makeTboxData4Show($text, $smiley = 0)
903    {
904        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
905        $text = $this->htmlSpecialChars($text);
906
907        return $text;
908    }
909
910    /**
911     * MyTextSanitizer::makeTboxData4Edit()
912     *
913     * @param  mixed $text
914     * @return string
915     */
916    public function makeTboxData4Edit($text)
917    {
918        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
919
920        return $this->htmlSpecialChars($text);
921    }
922
923    /**
924     * MyTextSanitizer::makeTboxData4Preview()
925     *
926     * @param  mixed $text
927     * @param  mixed $smiley
928     * @return mixed|string
929     */
930    public function makeTboxData4Preview($text, $smiley = 0)
931    {
932        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
933        $text = $this->stripSlashesGPC($text);
934        $text = $this->htmlSpecialChars($text);
935
936        return $text;
937    }
938
939    /**
940     * MyTextSanitizer::makeTboxData4PreviewInForm()
941     *
942     * @param  mixed $text
943     * @return string
944     */
945    public function makeTboxData4PreviewInForm($text)
946    {
947        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
948        $text = $this->stripSlashesGPC($text);
949
950        return $this->htmlSpecialChars($text);
951    }
952
953    /**
954     * MyTextSanitizer::makeTareaData4Save()
955     *
956     * @param  mixed $text
957     * @return string
958     */
959    public function makeTareaData4Save($text)
960    {
961        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
962
963        return $this->addSlashes($text);
964    }
965
966    /**
967     * MyTextSanitizer::makeTareaData4Show()
968     *
969     * @param  mixed   $text
970     * @param  integer $html
971     * @param  integer $smiley
972     * @param  mixed   $xcode
973     * @return mixed|string
974     */
975    public function &makeTareaData4Show(&$text, $html = 1, $smiley = 1, $xcode = 1)
976    {
977        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
978        $text =& $this->displayTarea($text, $html, $smiley, $xcode);
979
980        return $text;
981    }
982
983    /**
984     * MyTextSanitizer::makeTareaData4Edit()
985     *
986     * @param  mixed $text
987     * @return string
988     */
989    public function makeTareaData4Edit($text)
990    {
991        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
992
993        return $this->htmlSpecialChars($text);
994    }
995
996    /**
997     * MyTextSanitizer::makeTareaData4Preview()
998     *
999     * @param  mixed   $text
1000     * @param  integer $html
1001     * @param  integer $smiley
1002     * @param  mixed   $xcode
1003     * @return mixed|string
1004     */
1005    public function &makeTareaData4Preview(&$text, $html = 1, $smiley = 1, $xcode = 1)
1006    {
1007        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1008        $text =& $this->previewTarea($text, $html, $smiley, $xcode);
1009
1010        return $text;
1011    }
1012
1013    /**
1014     * MyTextSanitizer::makeTareaData4PreviewInForm()
1015     *
1016     * @param  mixed $text
1017     * @return string
1018     */
1019    public function makeTareaData4PreviewInForm($text)
1020    {
1021        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1022        // if magic_quotes_gpc is on, do stipslashes
1023        $text = $this->stripSlashesGPC($text);
1024
1025        return $this->htmlSpecialChars($text);
1026    }
1027
1028    /**
1029     * MyTextSanitizer::makeTareaData4InsideQuotes()
1030     *
1031     * @param  mixed $text
1032     * @return string
1033     */
1034    public function makeTareaData4InsideQuotes($text)
1035    {
1036        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1037
1038        return $this->htmlSpecialChars($text);
1039    }
1040
1041    /**
1042     * MyTextSanitizer::oopsStripSlashesGPC()
1043     *
1044     * @param  mixed $text
1045     * @return string
1046     */
1047    public function oopsStripSlashesGPC($text)
1048    {
1049        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1050
1051        return $this->stripSlashesGPC($text);
1052    }
1053
1054    /**
1055     * MyTextSanitizer::oopsStripSlashesRT()
1056     *
1057     * @param  mixed $text
1058     * @return mixed|string
1059     */
1060    public function oopsStripSlashesRT($text)
1061    {
1062        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1063        if (get_magic_quotes_runtime()) {
1064            $text = stripslashes($text);
1065        }
1066
1067        return $text;
1068    }
1069
1070    /**
1071     * MyTextSanitizer::oopsAddSlashes()
1072     *
1073     * @param  mixed $text
1074     * @return string
1075     */
1076    public function oopsAddSlashes($text)
1077    {
1078        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1079
1080        return $this->addSlashes($text);
1081    }
1082
1083    /**
1084     * MyTextSanitizer::oopsHtmlSpecialChars()
1085     *
1086     * @param  mixed $text
1087     * @return string
1088     */
1089    public function oopsHtmlSpecialChars($text)
1090    {
1091        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1092
1093        return $this->htmlSpecialChars($text);
1094    }
1095
1096    /**
1097     * MyTextSanitizer::oopsNl2Br()
1098     *
1099     * @param  mixed $text
1100     * @return string
1101     */
1102    public function oopsNl2Br($text)
1103    {
1104        $GLOBALS['xoopsLogger']->addDeprecated(__CLASS__ . '::' . __FUNCTION__ . ' is deprecated');
1105
1106        return $this->nl2Br($text);
1107    }
1108}
1109