Passed
Push — master ( 1f7ba1...fdc25c )
by Michael
09:41
created

MyTextSanitizer::getInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
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-2025 XOOPS Project (https://xoops.org)
13
 * @license             GNU GPL 2 (https://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 <[email protected]>
19
 */
20
21
/**
22
 * Abstract class for extensions
23
 *
24
 * @author              Taiwen Jiang <[email protected]>
25
 * @copyright       (c) 2000-2025 XOOPS Project (https://xoops.org)
26
 */
27
class MyTextSanitizerExtension
28
{
29
    public $instance;
30
    public $myts;
31
    public $config;
32
    public $image_path;
33
34
    /**
35
     * Constructor
36
     *
37
     * @param MyTextSanitizer $myts
38
     */
39
    public function __construct(MyTextSanitizer $myts)
40
    {
41
        $this->myts         = $myts;
42
        $this->image_path = XOOPS_URL . '/images/form';
43
    }
44
45
    /**
46
     * loadConfig
47
     *
48
     * @param  string $path
49
     * @return string|array
50
     */
51
    public static function loadConfig($path = null)
52
    {
53
        $myts   = \MyTextSanitizer::getInstance();
54
        $extensionName = (null === $path) ? '' : basename($path);
55
        $pathDist = $myts->path_basic;
56
        $pathConfig = $myts->path_config;
57
58
        if ('' !== $extensionName) {
59
            $configFileName = $pathConfig . '/config.' . $extensionName . '.php';
60
            $distFileName = $pathDist . '/' . $extensionName . '/config.' . $extensionName . '.dist.php';
61
        } else {
62
            $configFileName = $pathConfig . '/config.php';
63
            $distFileName = $pathDist . '/config.dist.php';
64
        }
65
        if (!file_exists($configFileName)) {
66
            if (false === copy($distFileName, $configFileName)) {
67
                trigger_error('Could not create textsanitizer config file ' . basename($configFileName));
68
                return $a = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $a is dead and can be removed.
Loading history...
69
            }
70
        }
71
        $configs = include $configFileName;
72
        return $configs;
73
    }
74
75
    /**
76
     * Merge Config
77
     *
78
     * @param  array $config_default
79
     * @param  array $config_custom
80
     * @return array
81
     */
82
    public static function mergeConfig($config_default, $config_custom)
83
    {
84
        if (is_array($config_custom)) {
0 ignored issues
show
introduced by
The condition is_array($config_custom) is always true.
Loading history...
85
            foreach ($config_custom as $key => $val) {
86
                if (is_array($config_default[$key])) {
87
                    $config_default[$key] = self::mergeConfig($config_default[$key], $config_custom[$key]);
88
                } else {
89
                    $config_default[$key] = $val;
90
                }
91
            }
92
        }
93
94
        return $config_default;
95
    }
96
97
    /**
98
     * encode
99
     *
100
     * @param string $textarea_id id attribute of text area
101
     *
102
     * @return array
103
     */
104
    public function encode($textarea_id)
0 ignored issues
show
Unused Code introduced by
The parameter $textarea_id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

104
    public function encode(/** @scrutinizer ignore-unused */ $textarea_id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
105
    {
106
        return [[], []];
107
    }
108
109
    /**
110
     * decode
111
     *
112
     * @param string $url
113
     * @param string|integer $width
114
     * @param string|integer $height
115
     *
116
     * @return Null
117
     */
118
    public static function decode($url, $width, $height)
0 ignored issues
show
Unused Code introduced by
The parameter $height is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

118
    public static function decode($url, $width, /** @scrutinizer ignore-unused */ $height)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $width is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

118
    public static function decode($url, /** @scrutinizer ignore-unused */ $width, $height)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
119
    {
120
        return null;
121
    }
122
}
123
124
/**
125
 * Class to "clean up" text for various uses
126
 *
127
 * <strong>Singleton</strong>
128
 *
129
 * @package       kernel
130
 * @subpackage    core
131
 * @author        Kazumi Ono <[email protected]>
132
 * @author        Taiwen Jiang <[email protected]>
133
 * @author        Goghs Cheng
134
 * @copyright (c) 2000-2025 XOOPS Project (https://xoops.org)
135
 */
136
class MyTextSanitizer
137
{
138
    /**
139
     *
140
     * @var array
141
     */
142
    public $smileys = [];
143
144
    /**
145
     */
146
    public $censorConf;
147
148
    /**
149
     *
150
     * @var string holding reference to text
151
     */
152
    public $text         = '';
153
    public $patterns     = [];
154
    public $replacements = [];
155
    public $callbackPatterns = [];
156
    public $callbacks        = [];
157
158
    public $path_basic;
159
    public $path_config;
160
    public $path_plugin;
161
162
    public $config;
163
164
    /**
165
     * Constructor of this class
166
     *
167
     * Gets allowed html tags from admin config settings
168
     * <br> should not be allowed since nl2br will be used
169
     * when storing data.
170
     *
171
     * @access private
172
     */
173
174
    public function __construct()
175
    {
176
        $this->path_basic  = XOOPS_ROOT_PATH . '/class/textsanitizer';
177
        $this->path_config = XOOPS_VAR_PATH . '/configs/textsanitizer';
178
        $this->path_plugin = XOOPS_ROOT_PATH . '/Frameworks/textsanitizer';
179
        $this->config      = $this->loadConfig();
180
    }
181
182
    /**
183
     * Enter description here...
184
     *
185
     * @param  string $name
186
     * @return array|string
187
     */
188
    public function loadConfig($name = null)
189
    {
190
        // NB: sending a null name results in an infinite loop
191
        if (!empty($name)) {
192
            return MyTextSanitizerExtension::loadConfig($name);
193
        }
194
195
        $configFileName = $this->path_config . '/config.php';
196
        $distFileName = $this->path_basic . '/config.dist.php';
197
198
        if (!file_exists($configFileName)) {
199
            if (false === copy($distFileName, $configFileName)) {
200
                trigger_error('Could not create textsanitizer config file ' . basename($configFileName));
201
                return [];
202
            }
203
        }
204
        return include $configFileName;
205
    }
206
207
    /**
208
     * Enter description here...
209
     *
210
     * @param  array $config_default
211
     * @param  array $config_custom
212
     * @return mixed
213
     */
214
    public function mergeConfig($config_default, $config_custom)
215
    {
216
        if (is_array($config_custom)) {
0 ignored issues
show
introduced by
The condition is_array($config_custom) is always true.
Loading history...
217
            foreach ($config_custom as $key => $val) {
218
                if (isset($config_default[$key]) && \is_array($config_default[$key])) {
219
                    $config_default[$key] = $this->mergeConfig($config_default[$key], $config_custom[$key]);
220
                } else {
221
                    $config_default[$key] = $val;
222
                }
223
            }
224
        }
225
226
        return $config_default;
227
    }
228
229
    /**
230
     * Access the only instance of this class
231
     *
232
     * @return MyTextSanitizer
233
     */
234
    public static function getInstance()
235
    {
236
        static $instance;
237
        if (!isset($instance)) {
238
            $instance = new MyTextSanitizer();
239
        }
240
241
        return $instance;
242
    }
243
244
    /**
245
     * Get the smileys
246
     *
247
     * @param bool $isAll TRUE for all smileys, FALSE for smileys with display = 1
248
     *
249
     * @return array
250
     */
251
    public function getSmileys($isAll = true)
252
    {
253
        if (count($this->smileys) == 0) {
254
            /** @var XoopsMySQLDatabase $xoopsDB */
255
            $xoopsDB = XoopsDatabaseFactory::getDatabaseConnection();
256
            $sql     = 'SELECT * FROM ' . $xoopsDB->prefix('smiles');
257
            $result  = $xoopsDB->query($sql);
258
            if ($xoopsDB->isResultSet($result)) {
259
                while (false !== ($smiles = $xoopsDB->fetchArray($result))) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type boolean; however, parameter $result of XoopsMySQLDatabase::fetchArray() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
                while (false !== ($smiles = $xoopsDB->fetchArray(/** @scrutinizer ignore-type */ $result))) {
Loading history...
260
                    $this->smileys[] = $smiles;
261
                }
262
            }
263
        }
264
        if ($isAll) {
265
            return $this->smileys;
266
        }
267
268
        $smileys = [];
269
        foreach ($this->smileys as $smile) {
270
            if (empty($smile['display'])) {
271
                continue;
272
            }
273
            $smileys[] = $smile;
274
        }
275
276
        return $smileys;
277
    }
278
279
    /**
280
     * Replace emoticons in the message with smiley images
281
     *
282
     * @param  string $message
283
     * @return string
284
     */
285
    public function smiley($message)
286
    {
287
        $smileys = $this->getSmileys();
288
        foreach ($smileys as $smile) {
289
            $message = str_replace($smile['code'], '<img class="imgsmile" src="' . XOOPS_UPLOAD_URL . '/' . htmlspecialchars($smile['smile_url'], ENT_QUOTES | ENT_HTML5) . '" alt="" />', $message);
290
        }
291
292
        return $message;
293
    }
294
295
    /**
296
     * Callback to process email address match
297
     *
298
     * @param array $match array of matched elements
299
     *
300
     * @return string
301
     */
302
    protected function makeClickableCallbackEmailAddress0($match)
303
    {
304
        $email = $match[2];  // Extract the email address
305
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
306
    }
307
308
    /**
309
     * Make links in the text clickable
310
     * Presently handles email addresses and http, https, ftp, and sftp urls
311
     * (Note: at this time, major browsers no longer directly handle ftp/sftp urls.)
312
     *
313
     * @param string $text
314
     * @return string
315
     */
316
    public function makeClickable0($text)
317
    {
318
        // Decode HTML entities to ensure URLs are properly formatted
319
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
320
321
        // Convert email addresses into clickable mailto links
322
        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
323
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
324
325
        // Convert URLs into clickable links
326
        $pattern = "/(?:\s|^|[\(\[\{>])((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
327
        $text = preg_replace_callback(
328
            $pattern,
329
            function ($matches) {
330
                $url = $matches[1];
331
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
332
                if (strpos($url, 'www.') === 0) {
333
                    $url = "http://" . $url;
334
                }
335
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
336
                return $prefix . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
337
            },
338
            $text
339
        );
340
341
        // Convert URLs within angular brackets into clickable links
342
        $pattern = "/(<)(https?:\/\/[^\s>]+)(>)/i";
343
        $text = preg_replace_callback(
344
            $pattern,
345
            function ($matches) {
346
                $url = htmlspecialchars($matches[2], ENT_QUOTES, 'UTF-8');
347
                return $matches[1] . '<a href="' . $url . '" target="_blank" rel="external noopener nofollow">' . $url . '</a>' . $matches[3];
348
            },
349
            $text
350
        );
351
352
        // Ensure consistent handling of newlines by converting them to <br /> tags
353
        $text = nl2br($text);
354
355
        // Clean up extra newlines
356
        $text = preg_replace('/(<br \/>|<br>)[\n\s]*/', '$1', $text);
357
358
        return $text;
359
    }
360
361
362
    protected function makeClickableCallbackEmailAddress1($match)
363
    {
364
        $email = $match[2];  // Extract the email address
365
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
366
    }
367
368
    public function makeClickable1($text)
369
    {
370
        // Decode HTML entities to ensure URLs are properly formatted
371
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
372
373
        // Convert email addresses into clickable mailto links
374
        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
375
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
376
377
        // Convert URLs into clickable links
378
        $pattern = "/(?:\s|^|[\(\[\{>])((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
379
        $text = preg_replace_callback(
380
            $pattern,
381
            function ($matches) {
382
                $url = $matches[1];
383
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
384
                if (strpos($url, 'www.') === 0) {
385
                    $url = "http://" . $url;
386
                }
387
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
388
                return $prefix . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
389
            },
390
            $text
391
        );
392
393
        // Convert URLs within angular brackets into clickable links
394
        $pattern = "/(<)(https?:\/\/[^\s>]+)(>)/i";
395
        $text = preg_replace_callback(
396
            $pattern,
397
            function ($matches) {
398
                $url = htmlspecialchars($matches[2], ENT_QUOTES, 'UTF-8');
399
                return $matches[1] . '<a href="' . $url . '" target="_blank" rel="external noopener nofollow">' . $url . '</a>' . $matches[3];
400
            },
401
            $text
402
        );
403
404
        $text = preg_replace('/[\n\s]+/', ' ', $text);
405
406
        return $text;
407
    }
408
409
    protected function makeClickableCallbackEmailAddress2($match)
410
    {
411
        $email = $match[2];  // Extract the email address
412
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
413
    }
414
415
    public function makeClickable2($text)
416
    {
417
        // Decode HTML entities to ensure URLs are properly formatted
418
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
419
420
        // Convert email addresses into clickable mailto links
421
/*        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";*/
422
//        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
423
424
425
        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
426
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
427
428
        // Convert URLs into clickable links
429
        $pattern = "/(?:\s|^|[\(\[\{>])((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
430
        $text = preg_replace_callback(
431
            $pattern,
432
            function ($matches) {
433
                $url = $matches[1];
434
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
435
                if (strpos($url, 'www.') === 0) {
436
                    $url = "http://" . $url;
437
                }
438
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
439
                return $prefix . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
440
            },
441
            $text
442
        );
443
444
        // Convert URLs within angular brackets into clickable links
445
        $pattern = "/(<)(https?:\/\/[^\s>]+)(>)/i";
446
        $text = preg_replace_callback(
447
            $pattern,
448
            function ($matches) {
449
                $url = htmlspecialchars($matches[2], ENT_QUOTES, 'UTF-8');
450
                return $matches[1] . '<a href="' . $url . '" target="_blank" rel="external noopener nofollow">' . $url . '</a>' . $matches[3];
451
            },
452
            $text
453
        );
454
455
        return $text;
456
    }
457
458
459
    protected function makeClickableCallbackEmailAddress3($match)
460
    {
461
        $email = $match[2];  // Extract the email address
462
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
463
    }
464
465
    /**
466
     * Make links in the text clickable
467
     * Presently handles email addresses and http, https, ftp, and sftp urls
468
     * (Note: at this time, major browsers no longer directly handle ftp/sftp urls.)
469
     *
470
     * @param string $text
471
     * @return string
472
     */
473
    public function makeClickable3($text)
474
    {
475
        // Decode HTML entities to ensure URLs are properly formatted
476
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
477
478
        // Convert email addresses into clickable mailto links
479
        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
480
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
481
482
        // Convert URLs into clickable links
483
        $pattern = "/(?:\s|^|[\(\[\{>])((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
484
        $text = preg_replace_callback(
485
            $pattern,
486
            function ($matches) {
487
                $url = $matches[1];
488
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
489
                if (strpos($url, 'www.') === 0) {
490
                    $url = "http://" . $url;
491
                }
492
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
493
                return $prefix . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
494
            },
495
            $text
496
        );
497
498
        // Ensure consistent handling of newlines by converting them to <br /> tags
499
//        $text = nl2br($text);
500
501
        $text = preg_replace('/[\n\s]+/', ' ', $text);
502
503
        return $text;
504
    }
505
506
507
508
    protected function makeClickableCallbackEmailAddress4($match)
509
    {
510
        $email = $match[2];  // Extract the email address
511
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
512
    }
513
514
    /**
515
     * Make links in the text clickable
516
     * Presently handles email addresses and http, https, ftp, and sftp urls
517
     * (Note: at this time, major browsers no longer directly handle ftp/sftp urls.)
518
     *
519
     * @param string $text
520
     * @return string
521
     */
522
    public function makeClickable4($text)
523
    {
524
        // Decode HTML entities to ensure URLs are properly formatted
525
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
526
527
        // Convert email addresses into clickable mailto links
528
        $pattern = "/(^|[\s\n]|<br\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
529
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
530
531
        // Convert URLs into clickable links
532
        $pattern = "/(?:\s|^|[\(\[\{>])((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
533
        $text = preg_replace_callback(
534
            $pattern,
535
            function ($matches) {
536
                $url = $matches[1];
537
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
538
                if (strpos($url, 'www.') === 0) {
539
                    $url = "http://" . $url;
540
                }
541
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
542
                return $prefix . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
543
            },
544
            $text
545
        );
546
547
        // Convert line breaks to <br> tags
548
//        $text = str_replace("\n", "<br>", $text);
549
        $text = preg_replace('/[\n\s]+/', ' ', $text);
550
551
        return $text;
552
    }
553
554
    /**
555
     * Callback to process email address match
556
     *
557
     * @param array $match array of matched elements
558
     *
559
     * @return string
560
     */
561
    protected function makeClickableCallbackEmailAddress($match)
562
    {
563
        $email = $match[2];  // Extract the email address
564
        return $match[1] . '<a href="mailto:' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . '</a>';
565
    }
566
567
    /**
568
     * Make links in the text clickable
569
     * Presently handles email addresses and http, https, ftp, and sftp urls
570
     * (Note: at this time, major browsers no longer directly handle ftp/sftp urls.)
571
     *
572
     * @param string $text
573
     * @return string
574
     */
575
    public function makeClickable6($text)
576
    {
577
        // Decode HTML entities to ensure URLs are properly formatted
578
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
579
580
        // Convert line breaks and multiple spaces to a single space
581
        $text = preg_replace('/[\n\s]+/', ' ', $text);
582
583
        // Convert email addresses into clickable mailto links
584
        $pattern = "/(^|[\s\n]|<br\s*\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
585
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
586
587
        // Convert URLs into clickable links, allowing for angle brackets
588
        $pattern = "/(?:\s|^|[\(\[\{>])(<)?((https?:\/\/|s?ftp:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
589
        $text = preg_replace_callback(
590
            $pattern,
591
            function ($matches) {
592
                $url = $matches[2];
593
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
594
                $openingBracket = $matches[1] ?? ''; // Check for the opening angle bracket
595
596
                if (strpos($url, 'www.') === 0) {
597
                    $url = "http://" . $url;
598
                }
599
                $relAttr = strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 ? 'external' : 'external noopener nofollow';
600
601
                // Add the opening bracket back if it was present
602
                return $prefix . $openingBracket . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
603
            },
604
            $text
605
        );
606
607
        return $text;
608
    }
609
610
    public function makeClickable7($text)
611
    {
612
        // Decode HTML entities to ensure URLs are properly formatted
613
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
614
615
        // Convert line breaks and multiple spaces to a single space
616
        $text = preg_replace('/[\n\s]+/', ' ', $text);
617
618
        // Convert email addresses into clickable mailto links
619
        $pattern = "/(^|[\s\n]|<br\s*\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
620
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
621
622
        // Convert URLs into clickable links, allowing for angle brackets, file paths, and custom protocols
623
        $pattern = "/(?:\s|^|[\(\[\{>])(<)?((https?:\/\/|s?ftp:\/\/|file:\/\/|custom:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
624
        $text = preg_replace_callback(
625
            $pattern,
626
            function ($matches) {
627
                $url = $matches[2];
628
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
629
                $openingBracket = $matches[1] ?? ''; // Check for the opening angle bracket
630
631
                if (strpos($url, 'www.') === 0) {
632
                    $url = "http://" . $url;
633
                }
634
                $relAttr = (strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 || strpos($url, 'file://') === 0 || strpos($url, 'custom://') === 0) ? 'external' : 'external noopener nofollow';
635
636
                // Add the opening bracket back if it was present
637
                return $prefix . $openingBracket . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
638
            },
639
            $text
640
        );
641
642
        return $text;
643
    }
644
645
    public function makeClickable($text) {
646
        // Decode HTML entities
647
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
648
649
        // Convert line breaks and multiple spaces to a single space
650
        $text = preg_replace('/[\n\s]+/', ' ', $text);
651
652
        // Convert email addresses into clickable mailto links
653
        $pattern = "/(^|[\s\n]|<br\s*\/?>)([-_a-z0-9\'+*$^&%=~!?{}]+(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*@[-a-z0-9.]+\.[a-z]{2,6})/i";
654
        $text = preg_replace_callback($pattern, [$this, 'makeClickableCallbackEmailAddress'], $text);
655
656
        // Convert URLs into clickable links, allowing for angle brackets, file paths, and custom protocols
657
        $pattern = "/(?:\s|^|[\(\[\{>])(<)?((https?:\/\/|s?ftp:\/\/|file:\/\/|custom:\/\/|www\.)[^\s<>\(\)\[\]]+[^\s<>\(\)\[\]\.,!\"'\(\)\[\]{}<>])(?<![\.,!\"'\(\)\[\]{}])/";
658
        $text = preg_replace_callback(
659
            $pattern,
660
            function ($matches) {
661
                $url = $matches[2];
662
                $prefix = $matches[0][0] ?? ''; // Get the prefix character (space, bracket, etc.)
663
                $openingBracket = $matches[1] ?? ''; // Check for the opening angle bracket
664
665
                // Ensure the URL is not a javascript: URL
666
                if (stripos($url, 'javascript:') === 0) {
667
                    return $matches[0];
668
                }
669
670
                // Add http prefix if missing
671
                if (strpos($url, 'www.') === 0) {
672
                    $url = "http://" . $url;
673
                }
674
675
                // Allow only specific protocols
676
                $allowedProtocols = ['http://', 'https://', 'ftp://', 'sftp://', 'file://', 'custom://'];
677
                $protocolAllowed = false;
678
                foreach ($allowedProtocols as $protocol) {
679
                    if (strpos($url, $protocol) === 0) {
680
                        $protocolAllowed = true;
681
                        break;
682
                    }
683
                }
684
                if (!$protocolAllowed) {
685
                    return $matches[0];
686
                }
687
688
                // Check if the URL is already inside an anchor tag, specifically looking for href attribute
689
                if (!preg_match('#<a\s[^>]*href\s*=\s*(["\'])' . preg_quote($url, '/') . '\\1[^>]*>#i', $url)) { // <-- Change here!
690
                    $relAttr = (strpos($url, 'ftp://') === 0 || strpos($url, 'sftp://') === 0 || strpos($url, 'file://') === 0 || strpos($url, 'custom://') === 0) ? 'external' : 'external noopener nofollow';
691
                    return $prefix . $openingBracket . '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="' . $relAttr . '">' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '</a>';
692
                }
693
694
                return $matches[0]; // Return the original match if it's already an anchor tag
695
            },
696
            $text
697
        );
698
699
        return $text;
700
    }
701
702
    /**
703
     * MyTextSanitizer::truncate()
704
     *
705
     * @param  mixed $text
706
     * @return mixed|string
707
     */
708
    public function truncate($text)
709
    {
710
        $instance = \MyTextSanitizer::getInstance();
711
        if (empty($text) || empty($instance->config['truncate_length']) || strlen($text) < $instance->config['truncate_length']) {
712
            return $text;
713
        }
714
        $len = floor($instance->config['truncate_length'] / 2);
715
        $ret = substr($text, 0, $len) . ' ... ' . substr($text, 5 - $len);
0 ignored issues
show
Bug introduced by
$len of type double is incompatible with the type integer|null expected by parameter $length of substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

715
        $ret = substr($text, 0, /** @scrutinizer ignore-type */ $len) . ' ... ' . substr($text, 5 - $len);
Loading history...
Bug introduced by
5 - $len of type double is incompatible with the type integer expected by parameter $offset of substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

715
        $ret = substr($text, 0, $len) . ' ... ' . substr($text, /** @scrutinizer ignore-type */ 5 - $len);
Loading history...
716
717
        return $ret;
718
    }
719
720
    /**
721
     * Replace XoopsCodes with their equivalent HTML formatting
722
     *
723
     * @param  string   $text
724
     * @param  bool|int $allowimage Allow images in the text?
725
     *                              On FALSE, uses links to the images.
726
     * @return string
727
     */
728
    public function &xoopsCodeDecode(&$text, $allowimage = 1)
729
    {
730
        $patterns       = [];
731
        $replacements   = [];
732
        $patterns[]     = "/\[siteurl=(['\"]?)([^\"'<>]*)\\1](.*)\[\/siteurl\]/sU";
733
        $replacements[] = '<a href="' . XOOPS_URL . '/\\2" title="">\\3</a>';
734
        $patterns[]     = "/\[url=(['\"]?)(http[s]?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
735
        $replacements[] = '<a href="\\2" rel="noopener external" title="">\\3</a>';
736
        $patterns[]     = "/\[url=(['\"]?)(ftp?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
737
        $replacements[] = '<a href="\\2" rel="external" title="">\\3</a>';
738
        $patterns[]     = "/\[url=(['\"]?)([^'\"<>]*)\\1](.*)\[\/url\]/sU";
739
        $replacements[] = '<a href="http://\\2" rel="noopener external" title="">\\3</a>';
740
        $patterns[]     = "/\[color=(['\"]?)([a-zA-Z0-9#]+)\\1?](.*)\[\/color\]/sU";
741
        $replacements[] = '<span style="color: #\\2;">\\3</span>';
742
        $patterns[]     = "/\[size=(['\"]?)([a-zA-Z0-9-]+)\\1?](.*)\[\/size\]/sU";
743
        $replacements[] = '<span style="font-size: \\2;">\\3</span>';
744
        $patterns[]     = "/\[font=(['\"]?)([^;<>\*\(\)\"']*)\\1](.*)\[\/font\]/sU";
745
        $replacements[] = '<span style="font-family: \\2;">\\3</span>';
746
        $patterns[]     = "/\[email]([^;<>\*\(\)\"']*)\[\/email\]/sU";
747
        $replacements[] = '<a href="mailto:\\1" title="">\\1</a>';
748
749
        $patterns[]     = '/\[b](.*)\[\/b\]/sU';
750
        $replacements[] = '<strong>\\1</strong>';
751
        $patterns[]     = '/\[i](.*)\[\/i\]/sU';
752
        $replacements[] = '<em>\\1</em>';
753
        $patterns[]     = '/\[u](.*)\[\/u\]/sU';
754
        $replacements[] = '<span style="text-decoration: underline;">\\1</span>';
755
        $patterns[]     = '/\[d](.*)\[\/d\]/sU';
756
        $replacements[] = '<del>\\1</del>';
757
        $patterns[]     = '/\[center](.*)\[\/center\]/sU';
758
        $replacements[] = '<div style="text-align: center;">\\1</div>';
759
        $patterns[]     = '/\[left](.*)\[\/left\]/sU';
760
        $replacements[] = '<div style="text-align: left;">\\1</div>';
761
        $patterns[]     = '/\[right](.*)\[\/right\]/sU';
762
        $replacements[] = '<div style="text-align: right;">\\1</div>';
763
764
        $this->text         = $text;
765
        $this->patterns     = $patterns;
766
        $this->replacements = $replacements;
767
768
        $this->config['allowimage'] = $allowimage;
769
        $this->executeExtensions();
770
771
        $text = preg_replace($this->patterns, $this->replacements, $this->text);
772
        //-------------------------------------------------------------------------------
773
        $count = count($this->callbackPatterns);
774
775
        for ($i = 0; $i < $count; ++$i) {
776
            $text = preg_replace_callback($this->callbackPatterns[$i], $this->callbacks[$i], $text);
777
        }
778
        //------------------------------------------------------------------------------
779
        $text = $this->quoteConv($text);
780
781
        return $text;
782
    }
783
784
    /**
785
     * Convert quote tags
786
     *
787
     * @param  string $text
788
     * @return string
789
     */
790
    public function quoteConv($text)
791
    {
792
        //look for both open and closing tags in the correct order
793
        $pattern     = '/\[quote](.*)\[\/quote\]/sU';
794
        $replacement = _QUOTEC . '<div class="xoopsQuote"><blockquote>\\1</blockquote></div>';
795
796
        $text = preg_replace($pattern, $replacement, $text, -1, $count);
797
        //no more matches, return now
798
        if (!$count) {
799
            return $text;
800
        }
801
802
        //new matches could have been created, keep doing it until we have no matches
803
        return $this->quoteConv($text);
804
    }
805
806
    /**
807
     * A quick solution for filtering XSS scripts
808
     *
809
     * @TODO : To be improved
810
     * @param $text
811
     * @return mixed
812
     */
813
    public function filterXss($text)
814
    {
815
        $patterns       = [];
816
        $replacements   = [];
817
        $text           = str_replace("\x00", '', $text);
818
        $c              = "[\x01-\x1f]*";
819
        $patterns[]     = "/\bj{$c}a{$c}v{$c}a{$c}s{$c}c{$c}r{$c}i{$c}p{$c}t{$c}[\s]*:/si";
820
        $replacements[] = 'javascript;';
821
        $patterns[]     = "/\ba{$c}b{$c}o{$c}u{$c}t{$c}[\s]*:/si";
822
        $replacements[] = 'about;';
823
        $patterns[]     = "/\bx{$c}s{$c}s{$c}[\s]*:/si";
824
        $replacements[] = 'xss;';
825
        $text           = preg_replace($patterns, $replacements, $text);
826
827
        return $text;
828
    }
829
830
    /**
831
     * Convert linebreaks to <br> tags
832
     *
833
     * @param  string $text
834
     * @return string
835
     */
836
    public function nl2Br($text)
837
    {
838
        return preg_replace('/(\015\012)|(\015)|(\012)/', '<br>', $text);
839
    }
840
841
    /**
842
     * Add slashes to the text if magic_quotes_gpc is turned off.
843
     *
844
     * @param  string $text
845
     * @return string
846
     */
847
    public function addSlashes($text)
848
    {
849
        global $xoopsDB;
850
        $GLOBALS['xoopsLogger']->addDeprecated(
851
            __METHOD__ . ' is deprecated. Use $xoopsDB->escape() or $xoopsDB->quoteString() instead.'
852
        );
853
        return $xoopsDB->escape($text);
854
    }
855
856
    /**
857
     * Convert special characters to HTML entities
858
     *
859
     * @param  string $text    string being converted
860
     * @param  int|null    $quote_style
861
     * @param  string|null $charset character set used in conversion
862
     * @param  bool   $double_encode
863
     * @return string
864
     */
865
    public function htmlSpecialChars(string $text, ?int $quote_style = null, ?string $charset = null, $double_encode = true)
866
    {
867
        if ($quote_style === null) {
868
            $quote_style = ENT_QUOTES;
869
        }
870
        $text = (string) $text;
871
        if (version_compare(phpversion(), '5.2.3', '>=')) {
872
            $text = htmlspecialchars($text, $quote_style, $charset ?: (defined('_CHARSET') ? _CHARSET : 'UTF-8'), $double_encode);
873
        } else {
874
            $text = htmlspecialchars($text, $quote_style);
875
        }
876
877
        return preg_replace(['/&amp;/i', '/&nbsp;/i'], ['&', '&amp;nbsp;'], $text);
878
    }
879
880
    /**
881
     * Reverses {@link htmlSpecialChars()}
882
     *
883
     * @param  string $text
884
     * @return string
885
     */
886
    public function undoHtmlSpecialChars($text)
887
    {
888
        return preg_replace(['/&gt;/i', '/&lt;/i', '/&quot;/i', '/&#039;/i', '/&amp;nbsp;/i'], ['>', '<', '"', '\'', '&nbsp;'], $text);
889
    }
890
891
    /**
892
     * Filters textarea form data in DB for display
893
     *
894
     * @param  string   $text
895
     * @param  bool|int $html   allow html?
896
     * @param  bool|int $smiley allow smileys?
897
     * @param  bool|int $xcode  allow xoopscode?
898
     * @param  bool|int $image  allow inline images?
899
     * @param  bool|int $br     convert linebreaks?
900
     * @return string
901
     */
902
    public function &displayTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
903
    {
904
        $text = (string) $text;
905
        $charset = (defined('_CHARSET') ? _CHARSET : 'UTF-8');
906
        if (function_exists('mb_convert_encoding')) {
907
            $text = mb_convert_encoding($text, $charset, mb_detect_encoding($text, mb_detect_order(), true));
0 ignored issues
show
Bug introduced by
It seems like mb_detect_order() can also be of type true; however, parameter $encodings of mb_detect_encoding() does only seem to accept array|null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

907
            $text = mb_convert_encoding($text, $charset, mb_detect_encoding($text, /** @scrutinizer ignore-type */ mb_detect_order(), true));
Loading history...
908
        }
909
        if ($html && $br) {
910
            $testText = strip_tags($text);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $string of strip_tags() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

910
            $testText = strip_tags(/** @scrutinizer ignore-type */ $text);
Loading history...
911
            if (mb_strlen($text) != mb_strlen($testText)) {
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $string of mb_strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

911
            if (mb_strlen(/** @scrutinizer ignore-type */ $text) != mb_strlen($testText)) {
Loading history...
912
                $br = 0;
913
            }
914
            unset($testText);
915
        }
916
        if ($html != 1) {
917
            // html not allowed
918
            $text = $this->htmlSpecialChars($text, ENT_COMPAT, $charset);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $text of MyTextSanitizer::htmlSpecialChars() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

918
            $text = $this->htmlSpecialChars(/** @scrutinizer ignore-type */ $text, ENT_COMPAT, $charset);
Loading history...
919
        }
920
        $text = $this->codePreConv($text, $xcode); // Ryuji_edit(2003-11-18)
921
        if ($smiley != 0) {
922
            // process smiley
923
            $text = $this->smiley($text);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $message of MyTextSanitizer::smiley() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

923
            $text = $this->smiley(/** @scrutinizer ignore-type */ $text);
Loading history...
924
        }
925
        if ($xcode != 0) {
926
            // decode xcode
927
            if ($image != 0) {
928
                // image allowed
929
                $text = & $this->xoopsCodeDecode($text);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $text of MyTextSanitizer::xoopsCodeDecode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

929
                $text = & $this->xoopsCodeDecode(/** @scrutinizer ignore-type */ $text);
Loading history...
930
            } else {
931
                // image not allowed
932
                $text = & $this->xoopsCodeDecode($text, 0);
933
            }
934
        }
935
        if ($br != 0) {
936
            $text = $this->nl2Br($text);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type array; however, parameter $text of MyTextSanitizer::nl2Br() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

936
            $text = $this->nl2Br(/** @scrutinizer ignore-type */ $text);
Loading history...
937
        }
938
        $text = $this->codeConv($text, $xcode);
939
        $text = $this->makeClickable($text);
940
        if (!empty($this->config['filterxss_on_display'])) {
941
            $text = $this->filterXss($text);
942
        }
943
944
        return $text;
945
    }
946
947
    /**
948
     * Filters textarea form data submitted for preview
949
     *
950
     * @param  string   $text
951
     * @param  bool|int $html   allow html?
952
     * @param  bool|int $smiley allow smileys?
953
     * @param  bool|int $xcode  allow xoopscode?
954
     * @param  bool|int $image  allow inline images?
955
     * @param  bool|int $br     convert linebreaks?
956
     * @return string
957
     */
958
    public function &previewTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
959
    {
960
        $text = & $this->displayTarea($text, $html, $smiley, $xcode, $image, $br);
961
962
        return $text;
963
    }
964
965
    /**
966
     * Replaces banned words in a string with their replacements
967
     *
968
     * @param  string $text
969
     * @return string
970
     */
971
    public function &censorString(&$text)
972
    {
973
        $ret = $this->executeExtension('censor', $text);
974
        if ($ret === false) {
975
            return $text;
976
        }
977
978
        return $ret;
979
    }
980
981
    /**
982
     * MyTextSanitizer::codePreConv()
983
     *
984
     * @param  mixed $text
985
     * @param  mixed $xcode
986
     * @return mixed
987
     */
988
    public function codePreConv($text, $xcode = 1)
989
    {
990
        if ($xcode != 0) {
991
            //            $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/esU";
992
            //            $replacements = "'[code\\1]'.base64_encode('\\2').'[/code]'";
993
994
            $patterns = '/\[code([^\]]*?)\](.*)\[\/code\]/sU';
995
            $text = preg_replace_callback(
996
                $patterns,
997
                fn($matches) => '[code'. $matches[1] . ']' . base64_encode($matches[2]) . '[/code]',
998
                $text,
999
            );
1000
        }
1001
1002
        return $text;
1003
    }
1004
1005
    /**
1006
     * @param $match
1007
     *
1008
     * @return string
1009
     */
1010
    public function codeConvCallback($match)
1011
    {
1012
        return '<div class="xoopsCode">' . $this->executeExtension('syntaxhighlight', str_replace('\\\"', '\"', base64_decode($match[2])), $match[1]) . '</div>';
1013
    }
1014
1015
    /**
1016
     * MyTextSanitizer::codeConv()
1017
     *
1018
     * @param  mixed $text
1019
     * @param  mixed $xcode
1020
     * @return mixed
1021
     */
1022
    public function codeConv($text, $xcode = 1)
1023
    {
1024
        if (empty($xcode)) {
1025
            return $text;
1026
        }
1027
        $patterns = '/\[code([^\]]*?)\](.*)\[\/code\]/sU';
1028
        $text1    = preg_replace_callback($patterns, [$this, 'codeConvCallback'], $text);
1029
1030
        return $text1;
1031
    }
1032
1033
    /**
1034
     * MyTextSanitizer::executeExtensions()
1035
     *
1036
     * @return bool
1037
     */
1038
    public function executeExtensions()
1039
    {
1040
        $extensions = array_filter($this->config['extensions']);
1041
        if (empty($extensions)) {
1042
            return true;
1043
        }
1044
        foreach (array_keys($extensions) as $extension) {
1045
            $this->executeExtension($extension);
1046
        }
1047
        return null;
1048
    }
1049
1050
    /**
1051
     * MyTextSanitizer::loadExtension()
1052
     *
1053
     * @param  mixed $name
1054
     * @return MyTextSanitizerExtension|false
1055
     */
1056
    public function loadExtension($name)
1057
    {
1058
        if (file_exists($file = $this->path_basic . '/' . $name . '/' . $name . '.php')) {
1059
            include_once $file;
1060
        } elseif (file_exists($file = $this->path_plugin . '/' . $name . '/' . $name . '.php')) {
1061
            include_once $file;
1062
        } else {
1063
            return false;
1064
        }
1065
        $class = 'Myts' . ucfirst($name);
1066
        if (!class_exists($class)) {
1067
            trigger_error("Extension '{$name}' does not exist", E_USER_WARNING);
1068
1069
            return false;
1070
        }
1071
        return new $class($this);
1072
    }
1073
1074
    /**
1075
     * MyTextSanitizer::executeExtension()
1076
     *
1077
     * @param  mixed $name
1078
     * @return mixed
1079
     */
1080
    public function executeExtension($name)
1081
    {
1082
        $extension = $this->loadExtension($name);
1083
        $args      = array_slice(func_get_args(), 1);
1084
        array_unshift($args, $this);
1085
1086
        return call_user_func_array([$extension, 'load'], $args);
1087
    }
1088
1089
    /**
1090
     * Filter out possible malicious text
1091
     * kses project at SF could be a good solution to check
1092
     *
1093
     * @param  string $text  text to filter
1094
     * @param  bool   $force force filtering
1095
     * @return string filtered text
1096
     */
1097
    public function textFilter($text, $force = false)
1098
    {
1099
        $ret = $this->executeExtension('textfilter', $text, $force);
1100
        if ($ret === false) {
1101
            return $text;
1102
        }
1103
1104
        return $ret;
1105
    }
1106
1107
    // #################### Deprecated Methods ######################
1108
1109
    /**
1110
     * if magic_quotes_gpc is on, strip back slashes
1111
     *
1112
     * @param  string $text
1113
     * @return string
1114
     * @deprecated as of XOOPS 2.5.11 and will be removed in next XOOPS version
1115
     *
1116
     * This remains here until we officially drop support for PHP 5.3 in next release
1117
     */
1118
    public function stripSlashesGPC($text)
1119
    {
1120
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1121
1122
        return $text;
1123
    }
1124
1125
    /**
1126
     * MyTextSanitizer::codeSanitizer()
1127
     *
1128
     * @param  mixed $str
1129
     * @param  mixed $image
1130
     * @return mixed|string
1131
     * @deprecated will be removed in next XOOPS version
1132
     */
1133
    public function codeSanitizer($str, $image = 1)
1134
    {
1135
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1136
        $str = $this->htmlSpecialChars(str_replace('\"', '"', base64_decode($str)));
1137
        $str = & $this->xoopsCodeDecode($str, $image);
1138
1139
        return $str;
1140
    }
1141
1142
    /**
1143
     * MyTextSanitizer::sanitizeForDisplay()
1144
     *
1145
     * @param  mixed   $text
1146
     * @param  integer $allowhtml
1147
     * @param  integer $smiley
1148
     * @param  mixed   $bbcode
1149
     * @return mixed|string
1150
     * @deprecated will be removed in next XOOPS version
1151
     */
1152
    public function sanitizeForDisplay($text, $allowhtml = 0, $smiley = 1, $bbcode = 1)
1153
    {
1154
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1155
        if ($allowhtml == 0) {
1156
            $text = $this->htmlSpecialChars($text);
1157
        } else {
1158
            // $config =& $GLOBALS['xoopsConfig'];
1159
            // $allowed = $config['allowed_html'];
1160
            // $text = strip_tags($text, $allowed);
1161
            $text = $this->makeClickable($text);
1162
        }
1163
        if ($smiley == 1) {
1164
            $text = $this->smiley($text);
1165
        }
1166
        if ($bbcode == 1) {
1167
            $text = & $this->xoopsCodeDecode($text);
1168
        }
1169
        $text = $this->nl2Br($text);
1170
1171
        return $text;
1172
    }
1173
1174
    /**
1175
     * MyTextSanitizer::sanitizeForPreview()
1176
     *
1177
     * @param  mixed   $text
1178
     * @param  integer $allowhtml
1179
     * @param  integer $smiley
1180
     * @param  mixed   $bbcode
1181
     * @return mixed|string
1182
     * @deprecated will be removed in next XOOPS version
1183
     */
1184
    public function sanitizeForPreview($text, $allowhtml = 0, $smiley = 1, $bbcode = 1)
1185
    {
1186
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1187
        if ($allowhtml == 0) {
1188
            $text = $this->htmlSpecialChars($text);
1189
        } else {
1190
            // $config =& $GLOBALS['xoopsConfig'];
1191
            // $allowed = $config['allowed_html'];
1192
            // $text = strip_tags($text, $allowed);
1193
            $text = $this->makeClickable($text);
1194
        }
1195
        if ($smiley == 1) {
1196
            $text = $this->smiley($text);
1197
        }
1198
        if ($bbcode == 1) {
1199
            $text = & $this->xoopsCodeDecode($text);
1200
        }
1201
        $text = $this->nl2Br($text);
1202
1203
        return $text;
1204
    }
1205
1206
    /**
1207
     * MyTextSanitizer::makeTboxData4Save()
1208
     *
1209
     * @param  mixed $text
1210
     * @return string
1211
     * @deprecated will be removed in next XOOPS version
1212
     */
1213
    public function makeTboxData4Save($text)
1214
    {
1215
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1216
1217
        // $text = $this->undoHtmlSpecialChars($text);
1218
        return $this->addSlashes($text);
1219
    }
1220
1221
    /**
1222
     * MyTextSanitizer::makeTboxData4Show()
1223
     *
1224
     * @param  mixed $text
1225
     * @param  mixed $smiley
1226
     * @return mixed|string
1227
     * @deprecated will be removed in next XOOPS version
1228
     */
1229
    public function makeTboxData4Show($text, $smiley = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $smiley is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1229
    public function makeTboxData4Show($text, /** @scrutinizer ignore-unused */ $smiley = 0)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1230
    {
1231
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1232
        $text = $this->htmlSpecialChars($text);
1233
1234
        return $text;
1235
    }
1236
1237
    /**
1238
     * MyTextSanitizer::makeTboxData4Edit()
1239
     *
1240
     * @param  mixed $text
1241
     * @return string
1242
     * @deprecated will be removed in next XOOPS version
1243
     */
1244
    public function makeTboxData4Edit($text)
1245
    {
1246
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1247
1248
        return $this->htmlSpecialChars($text);
1249
    }
1250
1251
    /**
1252
     * MyTextSanitizer::makeTboxData4Preview()
1253
     *
1254
     * @param  mixed $text
1255
     * @param  mixed $smiley
1256
     * @return mixed|string
1257
     * @deprecated will be removed in next XOOPS version
1258
     */
1259
    public function makeTboxData4Preview($text, $smiley = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $smiley is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1259
    public function makeTboxData4Preview($text, /** @scrutinizer ignore-unused */ $smiley = 0)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1260
    {
1261
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1262
        $text = $this->htmlSpecialChars($text);
1263
1264
        return $text;
1265
    }
1266
1267
    /**
1268
     * MyTextSanitizer::makeTboxData4PreviewInForm()
1269
     *
1270
     * @param  mixed $text
1271
     * @return string
1272
     * @deprecated will be removed in next XOOPS version
1273
     */
1274
    public function makeTboxData4PreviewInForm($text)
1275
    {
1276
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1277
1278
        return $this->htmlSpecialChars($text);
1279
    }
1280
1281
    /**
1282
     * MyTextSanitizer::makeTareaData4Save()
1283
     *
1284
     * @param  mixed $text
1285
     * @return string
1286
     * @deprecated will be removed in next XOOPS version
1287
     */
1288
    public function makeTareaData4Save($text)
1289
    {
1290
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1291
1292
        return $this->addSlashes($text);
1293
    }
1294
1295
    /**
1296
     * MyTextSanitizer::makeTareaData4Show()
1297
     *
1298
     * @param  mixed   $text
1299
     * @param  integer $html
1300
     * @param  integer $smiley
1301
     * @param  mixed   $xcode
1302
     * @return mixed|string
1303
     * @deprecated will be removed in next XOOPS version
1304
     */
1305
    public function &makeTareaData4Show($text, $html = 1, $smiley = 1, $xcode = 1)
1306
    {
1307
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1308
        $text = & $this->displayTarea($text, $html, $smiley, $xcode);
1309
1310
        return $text;
1311
    }
1312
1313
    /**
1314
     * MyTextSanitizer::makeTareaData4Edit()
1315
     *
1316
     * @param  mixed $text
1317
     * @return string
1318
     * @deprecated will be removed in next XOOPS version
1319
     */
1320
    public function makeTareaData4Edit($text)
1321
    {
1322
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1323
1324
        return $this->htmlSpecialChars($text);
1325
    }
1326
1327
    /**
1328
     * MyTextSanitizer::makeTareaData4Preview()
1329
     *
1330
     * @param  mixed   $text
1331
     * @param  integer $html
1332
     * @param  integer $smiley
1333
     * @param  mixed   $xcode
1334
     * @return mixed|string
1335
     * @deprecated will be removed in next XOOPS version
1336
     */
1337
    public function &makeTareaData4Preview($text, $html = 1, $smiley = 1, $xcode = 1)
1338
    {
1339
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1340
        $text = & $this->previewTarea($text, $html, $smiley, $xcode);
1341
1342
        return $text;
1343
    }
1344
1345
    /**
1346
     * MyTextSanitizer::makeTareaData4PreviewInForm()
1347
     *
1348
     * @param  mixed $text
1349
     * @return string
1350
     * @deprecated will be removed in next XOOPS version
1351
     */
1352
    public function makeTareaData4PreviewInForm($text)
1353
    {
1354
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1355
1356
        return $this->htmlSpecialChars($text);
1357
    }
1358
1359
    /**
1360
     * MyTextSanitizer::makeTareaData4InsideQuotes()
1361
     *
1362
     * @param  mixed $text
1363
     * @return string
1364
     * @deprecated will be removed in next XOOPS version
1365
     */
1366
    public function makeTareaData4InsideQuotes($text)
1367
    {
1368
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1369
1370
        return $this->htmlSpecialChars($text);
1371
    }
1372
1373
    /**
1374
     * MyTextSanitizer::oopsStripSlashesGPC()
1375
     *
1376
     * @param  mixed $text
1377
     * @return string
1378
     * @deprecated will be removed in next XOOPS version
1379
     */
1380
    public function oopsStripSlashesGPC($text)
1381
    {
1382
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1383
1384
        return $text;
1385
    }
1386
1387
    /**
1388
     * MyTextSanitizer::oopsStripSlashesRT()
1389
     *
1390
     * @param  mixed $text
1391
     * @return mixed|string
1392
     * @deprecated will be removed in next XOOPS version
1393
     */
1394
    public function oopsStripSlashesRT($text)
1395
    {
1396
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1397
1398
        return $text;
1399
    }
1400
1401
    /**
1402
     * MyTextSanitizer::oopsAddSlashes()
1403
     *
1404
     * @param  mixed $text
1405
     * @return string
1406
     * @deprecated will be removed in next XOOPS version
1407
     */
1408
    public function oopsAddSlashes($text)
1409
    {
1410
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1411
1412
        return $this->addSlashes($text);
1413
    }
1414
1415
    /**
1416
     * MyTextSanitizer::oopsHtmlSpecialChars()
1417
     *
1418
     * @param  mixed $text
1419
     * @return string
1420
     * @deprecated will be removed in next XOOPS version
1421
     */
1422
    public function oopsHtmlSpecialChars($text)
1423
    {
1424
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1425
1426
        return $this->htmlSpecialChars($text);
1427
    }
1428
1429
    /**
1430
     * MyTextSanitizer::oopsNl2Br()
1431
     *
1432
     * @param  mixed $text
1433
     * @return string
1434
     * @deprecated will be removed in next XOOPS version
1435
     */
1436
    public function oopsNl2Br($text)
1437
    {
1438
        $GLOBALS['xoopsLogger']->addDeprecated(__METHOD__ . ' is deprecated');
1439
1440
        return $this->nl2Br($text);
1441
    }
1442
}
1443