XOOPS  2.6.0
module.textsanitizer.php
Go to the documentation of this file.
1 <?php
2 /*
3  You may not change or alter any portion of this comment or credits
4  of supporting developers from this source code or any supporting source code
5  which is considered copyrighted (c) material of the original comment or credit authors.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 */
11 
32 {
36  public $instance;
37 
41  public $ts;
42 
46  public $config;
47 
51  public $image_path;
52 
58  public function __construct(MyTextSanitizer &$ts)
59  {
60  $this->ts = $ts;
61  $this->image_path = \XoopsBaseConfig::get('url') . '/images/form';
62  }
63 
70  static function loadConfig($path = null)
71  {
73  $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
74  if (false === strpos($path, '/')) {
75  if (is_dir($ts->path_basic . '/' . $path)) {
76  $path = $ts->path_basic . '/' . $path;
77  } else {
78  if (is_dir($ts->path_plugin . '/' . $path)) {
79  $path = $ts->path_plugin . '/' . $path;
80  }
81 
82  }
83  }
84  $config_default = array();
85  $config_custom = array();
86  if (XoopsLoad::fileExists($path . '/config.php')) {
87  $config_default = include $path . '/config.php';
88  }
89  if (XoopsLoad::fileExists($path . '/config.custom.php')) {
90  $config_custom = include $path . '/config.custom.php';
91  }
92  return self::mergeConfig($config_default, $config_custom);
93  }
94 
102  static function mergeConfig($config_default, $config_custom)
103  {
104  if (is_array($config_custom)) {
105  foreach ($config_custom as $key => $val) {
106  if (array_key_exists($key, $config_default) and is_array($config_default[$key])) {
107  $config_default[$key] = self::mergeConfig($config_default[$key], $config_custom[$key]);
108  } else {
109  $config_default[$key] = $val;
110  }
111  }
112  }
113  return $config_default;
114  }
115 
123  public function encode($value)
124  {
125  return array();
126  }
127 
139  public static function decode($url, $width, $height)
140  {
141  return '';
142  }
143 }
144 
158 {
162  public $smileys = array();
163 
167  public $censorConf;
168 
172  public $text = "";
173 
177  public $patterns = array();
178 
182  public $replacements = array();
183 
184 //mb------------------------------
185  public $callbackPatterns = array();
186  public $callbacks = array();
187 //mb------------------------------
188 
192  public $path_basic;
193 
197  public $path_plugin;
198 
202  public $config = array();
203 
204  public function __construct()
205  {
207  $this->path_basic = $xoops_root_path . '/class/textsanitizer';
208  $this->path_plugin = $xoops_root_path . '/Frameworks/textsanitizer';
209  $this->config = $this->loadConfig();
210  }
211 
216  public function loadConfig($name = null)
217  {
218  if (!empty($name)) {
220  }
221  $config_default = include $this->path_basic . '/config.php';
222  $config_custom = array();
223  if (XoopsLoad::fileExists($file = $this->path_basic . '/config.custom.php')) {
224  $config_custom = include $file;
225  }
226  return $this->mergeConfig($config_default, $config_custom);
227  }
228 
234  public function mergeConfig($config_default, $config_custom)
235  {
236  if (is_array($config_custom)) {
237  foreach ($config_custom as $key => $val) {
238  if (isset($config_default[$key]) && is_array($config_default[$key])) {
239  $config_default[$key] = $this->mergeConfig($config_default[$key], $config_custom[$key]);
240  } else {
241  $config_default[$key] = $val;
242  }
243  }
244  }
245  return $config_default;
246  }
247 
254  static function getInstance()
255  {
256  static $instance;
257  if (!isset($instance)) {
258  $class = __CLASS__;
259  $instance = new $class();
260  }
261  return $instance;
262  }
263 
270  public function getSmileys($isAll = true)
271  {
272  $smileys = array();
273  XoopsPreload::getInstance()->triggerEvent('core.class.module.textsanitizer.getSmileys', array($isAll, &$smileys, &$this));
274  return $smileys;
275  }
276 
283  public function smiley($message)
284  {
285  XoopsPreload::getInstance()->triggerEvent('core.class.module.textsanitizer.smiley', array(&$message));
286  return $message;
287  }
288 
289 
290 
291  function makeClickableCallback01($match)
292  {
293  return $match[1]."<a href=\"$match[2]://$match[3]\" title=\"$match[2]://$match[3]\" rel=\"external\">$match[2]://".$this->truncate( $match[3] ).'</a>';
294  }
295 
296  function makeClickableCallback02($match)
297  {
298  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>';
299  }
300 
301  function makeClickableCallback03($match)
302  {
303  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>';
304  }
305 
306  function makeClickableCallback04($match)
307  {
308  return $match[1]. "<a href=\"mailto:$match[2]@$match[3]\" title=\"$match[2]@$match[3]\">" .$this->truncate($match[2]."@".$match[3]) .'</a>';
309  }
310 
311 
318  public function makeClickable(&$text) {
319  $valid_chars = "a-z0-9\/\-_+=.~!%@?#&;:$\|";
320  $end_chars = "a-z0-9\/\-_+=~!%@?#&;:$\|";
321 
322 // $patterns = array();
323 // $replacements = array();
324 //
325 // $patterns[] = "/(^|[^]_a-z0-9-=\"'\/])([a-z]+?):\/\/([{$valid_chars}]+[{$end_chars}])/ei";
326 // $replacements[] = "'\\1<a href=\"\\2://\\3\" title=\"\\2://\\3\" rel=\"external\">\\2://'.MyTextSanitizer::truncate( '\\3' ).'</a>'";
327 //
328 //
329 // $patterns[] = "/(^|[^]_a-z0-9-=\"'\/:\.])www\.((([a-zA-Z0-9\-]*\.){1,}){1}([a-zA-Z]{2,6}){1})((\/([a-zA-Z0-9\-\._\?\,\'\/\\+&%\$#\=~])*)*)/ei";
330 // $replacements[] = "'\\1<a href=\"http://www.\\2\\6\" title=\"www.\\2\\6\" rel=\"external\">'.MyTextSanitizer::truncate( 'www.\\2\\6' ).'</a>'";
331 //
332 // $patterns[] = "/(^|[^]_a-z0-9-=\"'\/])ftp\.([a-z0-9\-]+)\.([{$valid_chars}]+[{$end_chars}])/ei";
333 // $replacements[] = "'\\1<a href=\"ftp://ftp.\\2.\\3\" title=\"ftp.\\2.\\3\" rel=\"external\">'.MyTextSanitizer::truncate( 'ftp.\\2.\\3' ).'</a>'";
334 //
335 // $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";
336 // $replacements[] = "'\\1<a href=\"mailto:\\2@\\3\" title=\"\\2@\\3\">'.MyTextSanitizer::truncate( '\\2@\\3' ).'</a>'";
337 //
338 // $text = preg_replace($patterns, $replacements, $text);
339 //
340 //----------------------------------------------------------------------------------
341 
342 
343  $pattern = "/(^|[^]_a-z0-9-=\"'\/])([a-z]+?):\/\/([{$valid_chars}]+[{$end_chars}])/i";
344  $text = preg_replace_callback($pattern, 'self::makeClickableCallback01', $text);
345 
346  $pattern = "/(^|[^]_a-z0-9-=\"'\/:\.])www\.((([a-zA-Z0-9\-]*\.){1,}){1}([a-zA-Z]{2,6}){1})((\/([a-zA-Z0-9\-\._\?\,\'\/\\+&%\$#\=~])*)*)/i";
347  $text = preg_replace_callback($pattern, 'self::makeClickableCallback02', $text);
348 
349 
350  $pattern = "/(^|[^]_a-z0-9-=\"'\/])ftp\.([a-z0-9\-]+)\.([{$valid_chars}]+[{$end_chars}])/i";
351  $text = preg_replace_callback($pattern, 'self::makeClickableCallback03', $text);
352 
353  $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";
354  $text = preg_replace_callback($pattern, 'self::makeClickableCallback04', $text);
355 
356  return $text;
357  }
358 
365  static function truncate($text)
366  {
367  $instance = MyTextSanitizer::getInstance();
368  if (empty($text) || empty($instance->config['truncate_length']) || strlen($text) < $instance->config['truncate_length']) {
369  return $text;
370  }
371  $len = (((strlen($text) - $instance->config['truncate_length']) - 5) / 2);
372  if ($len < 5)
373  $ret = substr($text, 0, $len) . ' ... ' . substr($text, -$len);
374  else
375  $ret = substr($text,0,$instance->config['truncate_length']);
376  return $ret;
377  }
378 
387  public function xoopsCodeDecode(&$text, $allowimage = 1)
388  {
390  $patterns = array();
391  $replacements = array();
392  $patterns[] = "/\[siteurl=(['\"]?)([^\"'<>]*)\\1](.*)\[\/siteurl\]/sU";
393  $replacements[] = '<a href="' . $xoops_url . '/\\2" title="">\\3</a>';
394  $patterns[] = "/\[url=(['\"]?)(http[s]?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
395  $replacements[] = '<a href="\\2" rel="external" title="">\\3</a>';
396  $patterns[] = "/\[url=(['\"]?)(ftp[s]?:\/\/[^\"'<>]*)\\1](.*)\[\/url\]/sU";
397  $replacements[] = '<a href="\\2" rel="external" title="">\\3</a>';
398  $patterns[] = "/\[url=(['\"]?)([^'\"<>]*)\\1](.*)\[\/url\]/sU";
399  $replacements[] = '<a href="http://\\2" rel="external" title="">\\3</a>';
400  $patterns[] = "/\[color=(['\"]?)([a-zA-Z0-9]*)\\1](.*)\[\/color\]/sU";
401  $replacements[] = '<span style="color: #\\2;">\\3</span>';
402  $patterns[] = "/\[size=(['\"]?)([a-z0-9-]*)\\1](.*)\[\/size\]/sU";
403  $replacements[] = '<span style="font-size: \\2;">\\3</span>';
404  $patterns[] = "/\[font=(['\"]?)([^;<>\*\(\)\"']*)\\1](.*)\[\/font\]/sU";
405  $replacements[] = '<span style="font-family: \\2;">\\3</span>';
406  $patterns[] = "/\[email]([^;<>\*\(\)\"']*)\[\/email\]/sU";
407  $replacements[] = '<a href="mailto:\\1" title="">\\1</a>';
408 
409  $patterns[] = "/\[b](.*)\[\/b\]/sU";
410  $replacements[] = '<strong>\\1</strong>';
411  $patterns[] = "/\[i](.*)\[\/i\]/sU";
412  $replacements[] = '<em>\\1</em>';
413  $patterns[] = "/\[u](.*)\[\/u\]/sU";
414  $replacements[] = '<u>\\1</u>';
415  $patterns[] = "/\[d](.*)\[\/d\]/sU";
416  $replacements[] = '<del>\\1</del>';
417  $patterns[] = "/\[center](.*)\[\/center\]/sU";
418  $replacements[] = '<div style="text-align: center;">\\1</div>';
419  $patterns[] = "/\[left](.*)\[\/left\]/sU";
420  $replacements[] = '<div style="text-align: left;">\\1</div>';
421  $patterns[] = "/\[right](.*)\[\/right\]/sU";
422  $replacements[] = '<div style="text-align: right;">\\1</div>';
423 
424  $this->text = $text;
425  $this->patterns = $patterns;
426  $this->replacements = $replacements;
427 
428  $this->config['allowimage'] = $allowimage;
429  $this->executeExtensions();
430 
431  $text = preg_replace($this->patterns, $this->replacements, $this->text);
432 //-------------------------------------------------------------------------------
434 
435  for ($i = 0; $i < $count; ++$i) {
436  $text = preg_replace_callback($this->callbackPatterns[$i], $this->callbacks[$i] , $text);
437  }
438 //------------------------------------------------------------------------------
439  $text = $this->quoteConv($text);
440  return $text;
441  }
442 
449  public function quoteConv($text)
450  {
451  //look for both open and closing tags in the correct order
452  $pattern = "/\[quote](.*)\[\/quote\]/sU";
453  $replacement = XoopsLocale::C_QUOTE . '<div class="xoopsQuote"><blockquote>\\1</blockquote></div>';
454 
455  $text = preg_replace($pattern, $replacement, $text, -1, $count);
456  //no more matches, return now
457  if (!$count) {
458  return $text;
459  }
460  //new matches could have been created, keep doing it until we have no matches
461  return $this->quoteConv($text);
462  }
463 
472  public function filterXss($text)
473  {
474  $patterns = array();
475  $replacements = array();
476  $text = str_replace("\x00", "", $text);
477  $c = "[\x01-\x1f]*";
478  $patterns[] = "/\bj{$c}a{$c}v{$c}a{$c}s{$c}c{$c}r{$c}i{$c}p{$c}t{$c}[\s]*:/si";
479  $replacements[] = "javascript;";
480  $patterns[] = "/\ba{$c}b{$c}o{$c}u{$c}t{$c}[\s]*:/si";
481  $replacements[] = "about;";
482  $patterns[] = "/\bx{$c}s{$c}s{$c}[\s]*:/si";
483  $replacements[] = "xss;";
484  $text = preg_replace($patterns, $replacements, $text);
485  return $text;
486  }
487 
494  public function nl2Br($text)
495  {
496  return preg_replace('/(\015\012)|(\015)|(\012)/', '<br />', $text);
497  }
498 
505  public function addSlashes($text)
506  {
507  if (!get_magic_quotes_gpc()) {
508  $text = addslashes($text);
509  }
510  return $text;
511  }
512 
519  public function stripSlashesGPC($text)
520  {
521  if (get_magic_quotes_gpc()) {
522  $text = stripslashes($text);
523  }
524  return $text;
525  }
526 
536  public function htmlSpecialChars($text, $quote_style = ENT_QUOTES, $charset = null, $double_encode = true)
537  {
538  if (version_compare(phpversion(), '5.2.3', '>=')) {
539  $text = htmlspecialchars($text, $quote_style, $charset ? $charset : (class_exists('xoopslocale', false) ? XoopsLocale::getCharset() : 'UTF-8'), $double_encode);
540  } else {
541  $text = htmlspecialchars($text, $quote_style);
542  }
543  return preg_replace(array('/&amp;/i' , '/&nbsp;/i'), array('&' , '&amp;nbsp;'), $text);
544  }
545 
552  public function undoHtmlSpecialChars($text)
553  {
554  return preg_replace(array('/&gt;/i' , '/&lt;/i' , '/&quot;/i' , '/&#039;/i' , '/&amp;nbsp;/i'), array('>' , '<' , '"' , '\'' , "&nbsp;"), $text);
555  }
556 
568  public function displayTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
569  {
570  if ($html != 1) {
571  // html not allowed
572  $text = $this->htmlSpecialChars($text);
573  }
574  $text = $this->codePreConv($text, $xcode); // Ryuji_edit(2003-11-18)
575  if ($smiley != 0) {
576  // process smiley
577  $text = $this->smiley($text);
578  }
579  if ($xcode != 0) {
580  // decode xcode
581  if ($image != 0) {
582  // image allowed
583  $text = $this->xoopsCodeDecode($text);
584  } else {
585  // image not allowed
586  $text = $this->xoopsCodeDecode($text, 0);
587  }
588  }
589  if ($br != 0) {
590  $text = $this->nl2Br($text);
591  }
592  $text = $this->codeConv($text, $xcode);
593  $text = $this->makeClickable($text);
594  if (!empty($this->config['filterxss_on_display'])) {
595  $text = $this->filterXss($text);
596  }
597  return $text;
598  }
599 
611  public function previewTarea($text, $html = 0, $smiley = 1, $xcode = 1, $image = 1, $br = 1)
612  {
613  $text = $this->stripSlashesGPC($text);
614  $text = $this->displayTarea($text, $html, $smiley, $xcode, $image, $br);
615  return $text;
616  }
617 
624  public function censorString(&$text)
625  {
626  $ret = $this->executeExtension('censor', $text);
627  if ($ret === false) {
628  return $text;
629  }
630  return $ret;
631  }
632 
640  public function codePreConv($text, $xcode = 1)
641  {
642  if ($xcode != 0) {
643 // $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/esU";
644 // $replacements = "'[code\\1]'.base64_encode('\\2').'[/code]'";
645  $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/sU";
646  $text = preg_replace_callback($patterns,
647  function ($matches) {
648  return '[code' . $matches[1] . ']' . base64_encode($matches[2]). '[/code]';
649  },
650  $text
651  );
652  }
653  return $text;
654  }
655 
656 
657 function codeConvCallback($match)
658  {
659  return '<div class=\"xoopsCode\">'. $this->executeExtension('syntaxhighlight', str_replace('\\\"', '\"', base64_decode($match[2])), $match[1]).'</div>';
660  }
661 
662 
670  public function codeConv($text, $xcode = 1)
671  {
672  if (empty($xcode)) {
673  return $text;
674  }
675  $patterns = "/\[code([^\]]*?)\](.*)\[\/code\]/sU";
676 // $replacements = "'<div class=\"xoopsCode\">'.\$this->executeExtension('syntaxhighlight', str_replace('\\\"', '\"', base64_decode('$2')), '$1').'</div>'";
677  $text = preg_replace_callback($patterns, array($this,'codeConvCallback'), $text);
678 
679  return $text;
680  }
681 
687  public function executeExtensions()
688  {
689  $extensions = array_filter($this->config['extensions']);
690  if (empty($extensions)) {
691  return true;
692  }
693  foreach (array_keys($extensions) as $extension) {
694  $this->executeExtension($extension);
695  }
696  return true;
697  }
698 
705  public function loadExtension($name)
706  {
707  if (XoopsLoad::fileExists($file = $this->path_basic . '/' . $name . '/' . $name . '.php')) {
708  include_once $file;
709  } else if (XoopsLoad::fileExists($file = $this->path_plugin . '/' . $name . '/' . $name . '.php')) {
710  include_once $file;
711  } else {
712  return false;
713  }
714  $class = 'Myts' . ucfirst($name);
715  if (!class_exists($class,false)) {
716  trigger_error("Extension '{$name}' does not exist", E_USER_WARNING);
717  return false;
718  }
719  $extension = null;
720  $extension = new $class($this);
721  return $extension;
722  }
723 
730  public function executeExtension($name)
731  {
732  $extension = $this->loadExtension($name);
733  if (!$extension) return false;
734  $args = array_slice(func_get_args(), 1);
735  return call_user_func_array(array($extension , 'load'), array_merge(array(&$this), $args));
736  }
737 
746  public function textFilter($text, $force = false)
747  {
748  $ret = $this->executeExtension('textfilter', $text, $force);
749  if ($ret === false) {
750  return $text;
751  }
752  return $ret;
753  }
754 }
codePreConv($text, $xcode=1)
static decode($url, $width, $height)
previewTarea($text, $html=0, $smiley=1, $xcode=1, $image=1, $br=1)
if(empty($image_id)) $image
Definition: image.php:37
$path
Definition: execute.php:31
$xoops_url
Definition: backend.php:32
textFilter($text, $force=false)
$i
Definition: dialog.php:68
static getInstance()
Definition: Events.php:57
if(!isset($xoops->paths[$path_type])) if($path_type== 'var') $file
Definition: browse.php:55
if(!$xoops->security() ->validateToken(@$_POST['token'], false)) $html
static mergeConfig($config_default, $config_custom)
codeConv($text, $xcode=1)
mergeConfig($config_default, $config_custom)
defined('DS') or define('DS' DIRECTORY_SEPARATOR
Definition: common.php:41
if(DIRECTORY_SEPARATOR!="/") $xoops_root_path
Definition: config.php:7
htmlSpecialChars($text, $quote_style=ENT_QUOTES, $charset=null, $double_encode=true)
__construct(MyTextSanitizer &$ts)
static fileExists($file)
Definition: xoopsload.php:506
static get($name)
const C_QUOTE
Definition: en_US.php:189
$height
$url
Definition: register.php:72
$width
displayTarea($text, $html=0, $smiley=1, $xcode=1, $image=1, $br=1)
xoopsCodeDecode(&$text, $allowimage=1)