Completed
Push — master ( 978178...476e4b )
by cam
04:34
created

texte_mini.php ➔ traiter_echap_pre_dist()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 2
nop 1
dl 0
loc 18
rs 9.3554
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Gestion des textes et échappements (fonctions d'usages fréquents)
15
 *
16
 * @package SPIP\Core\Texte
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
include_spip('inc/filtres');
23
include_spip('inc/lang');
24
25
26
/**
27
 * Retourne une image d'une puce
28
 *
29
 * Le nom de l'image est déterminé par la globale 'puce' ou 'puce_prive'
30
 * ou les mêmes suffixées de '_rtl' pour ce type de langues.
31
 *
32
 * @note
33
 *     On initialise la puce pour éviter `find_in_path()` à chaque rencontre de `\n-`
34
 *     Mais attention elle depend de la direction et de X_fonctions.php, ainsi que
35
 *     de l'espace choisi (public/prive)
36
 *
37
 * @return string
38
 *     Code HTML de la puce
39
 **/
40
function definir_puce() {
41
42
	// Attention au sens, qui n'est pas defini de la meme facon dans
43
	// l'espace prive (spip_lang est la langue de l'interface, lang_dir
44
	// celle du texte) et public (spip_lang est la langue du texte)
45
	$dir = _DIR_RESTREINT ? lang_dir() : lang_dir($GLOBALS['spip_lang']);
46
47
	$p = 'puce' . (test_espace_prive() ? '_prive' : '');
48
	if ($dir == 'rtl') {
49
		$p .= '_rtl';
50
	}
51
52
	if (!isset($GLOBALS[$p])) {
53
		$GLOBALS[$p] = '<span class="spip-puce '.$dir.'"><b>–</b></span>';
54
	}
55
56
	return $GLOBALS[$p];
57
}
58
59
60
// XHTML - Preserver les balises-bloc : on liste ici tous les elements
61
// dont on souhaite qu'ils provoquent un saut de paragraphe
62
63
if (!defined('_BALISES_BLOCS')) {
64
	define('_BALISES_BLOCS',
65
	'address|applet|article|aside|blockquote|button|center|d[ltd]|div|fieldset|fig(ure|caption)|footer|form|h[1-6r]|hgroup|head|header|iframe|li|map|marquee|nav|noscript|object|ol|pre|section|t(able|[rdh]|body|foot|extarea)|ul|script|style'
66
	);
67
}
68
69
if (!defined('_BALISES_BLOCS_REGEXP')) {
70
	define('_BALISES_BLOCS_REGEXP', ',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS');
71
}
72
73
//
74
// Echapper les elements perilleux en les passant en base64
75
//
76
77
// Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
78
// une $source differente ; le script detecte automagiquement si ce qu'on
79
// echappe est un div ou un span
80
// https://code.spip.net/@code_echappement
81
function code_echappement($rempl, $source = '', $no_transform = false, $mode = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $no_transform is not used and could be removed.

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

Loading history...
82
	if (!strlen($rempl)) {
83
		return '';
84
	}
85
86
	// Tester si on echappe en span ou en div
87
	if (is_null($mode) or !in_array($mode, array('div', 'span'))) {
88
		$mode = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $rempl) ? 'div' : 'span';
89
	}
90
91
	// Decouper en morceaux, base64 a des probleme selon la taille de la pile
92
	$taille = 30000;
93
	$return = "";
94
	for ($i = 0; $i < strlen($rempl); $i += $taille) {
95
		// Convertir en base64 et cacher dans un attribut
96
		// utiliser les " pour eviter le re-encodage de ' et &#8217
97
		$base64 = base64_encode(substr($rempl, $i, $taille));
98
		$return .= "<$mode class=\"base64$source\" title=\"$base64\"></$mode>";
99
	}
100
101
	return $return;
102
103
}
104
105
106
// Echapper les <html>...</ html>
107
// https://code.spip.net/@traiter_echap_html_dist
108
function traiter_echap_html_dist($regs) {
109
	return $regs[3];
110
}
111
112
// Echapper les <pre>...</ pre>
113
function traiter_echap_pre_dist($regs) {
114
	// echapper les <code> dans <pre>
115
	$pre = $regs[3];
116
117
	// echapper les < dans <code>
118
	// on utilise _PROTEGE_BLOCS pour simplifier le code et la maintenance, mais on est interesse que par <code>
119
	if (strpos($pre, "<") !== false
120
		and preg_match_all(_PROTEGE_BLOCS, $pre, $matches, PREG_SET_ORDER)){
121
122
		foreach ($matches as $m){
123
			if ($m[1]==='code'){
124
				$code = "<code" . $m[2] . ">" . spip_htmlspecialchars($m[3]) . "</code>";
125
				$pre = str_replace($m[0], $code, $pre);
126
			}
127
		}
128
	}
129
	return "<pre>$pre</pre>";
130
}
131
132
// Echapper les <code>...</ code>
133
// https://code.spip.net/@traiter_echap_code_dist
134
function traiter_echap_code_dist($regs) {
135
	list(, , $att, $corps) = $regs;
136
	$echap = spip_htmlspecialchars($corps); // il ne faut pas passer dans entites_html, ne pas transformer les &#xxx; du code !
137
138
	// ne pas mettre le <div...> s'il n'y a qu'une ligne
139
	if (is_int(strpos($echap, "\n"))) {
140
		// supprimer les sauts de ligne debut/fin
141
		// (mais pas les espaces => ascii art).
142
		$echap = preg_replace("/^[\n\r]+|[\n\r]+$/s", "", $echap);
143
		$echap = nl2br($echap);
144
		$echap = "<div style='text-align: left;' "
145
			. "class='spip_code' dir='ltr'><code$att>"
146
			. $echap . "</code></div>";
147
	} else {
148
		$echap = "<code$att class='spip_code' dir='ltr'>" . $echap . "</code>";
149
	}
150
151
	$echap = str_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
152
	$echap = str_replace("  ", " &nbsp;", $echap);
153
154
	return $echap;
155
}
156
157
// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
158
// https://code.spip.net/@traiter_echap_cadre_dist
159
function traiter_echap_cadre_dist($regs) {
160
	$echap = trim(entites_html($regs[3]));
161
	// compter les lignes un peu plus finement qu'avec les \n
162
	$lignes = explode("\n", trim($echap));
163
	$n = 0;
164
	foreach ($lignes as $l) {
165
		$n += floor(strlen($l) / 60) + 1;
166
	}
167
	$n = max($n, 2);
168
	$echap = "\n<textarea readonly='readonly' cols='40' rows='$n' class='spip_cadre' dir='ltr'>$echap</textarea>";
169
170
	return $echap;
171
}
172
173
// https://code.spip.net/@traiter_echap_frame_dist
174
function traiter_echap_frame_dist($regs) {
175
	return traiter_echap_cadre_dist($regs);
176
}
177
178
// https://code.spip.net/@traiter_echap_script_dist
179
function traiter_echap_script_dist($regs) {
180
	// rendre joli (et inactif) si c'est un script language=php
181
	if (preg_match(',<script\b[^>]+php,ims', $regs[0])) {
182
		return highlight_string($regs[0], true);
183
	}
184
185
	// Cas normal : le script passe tel quel
186
	return $regs[0];
187
}
188
189
define('_PROTEGE_BLOCS', ',<(html|pre|code|cadre|frame|script|style)(\s[^>]*)?>(.*)</\1>,UimsS');
190
191
// - pour $source voir commentaire infra (echappe_retour)
192
// - pour $no_transform voir le filtre post_autobr dans inc/filtres
193
// https://code.spip.net/@echappe_html
194
function echappe_html(
195
	$letexte,
196
	$source = '',
197
	$no_transform = false,
198
	$preg = ''
199
) {
200
	if (!is_string($letexte) or !strlen($letexte)) {
201
		return $letexte;
202
	}
203
204
	// si le texte recu est long PCRE risque d'exploser, on
205
	// fait donc un mic-mac pour augmenter pcre.backtrack_limit
206
	if (($len = strlen($letexte)) > 100000) {
207
		if (!$old = @ini_get('pcre.backtrack_limit')) {
208
			$old = 100000;
209
		}
210
		if ($len > $old) {
211
			$a = @ini_set('pcre.backtrack_limit', $len);
0 ignored issues
show
Unused Code introduced by
$a is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
212
			spip_log("ini_set pcre.backtrack_limit=$len ($old)");
213
		}
214
	}
215
216
	if (($preg or strpos($letexte, "<") !== false)
217
		and preg_match_all($preg ? $preg : _PROTEGE_BLOCS, $letexte, $matches, PREG_SET_ORDER)
218
	) {
219
		foreach ($matches as $regs) {
220
			// echappements tels quels ?
221
			if ($no_transform) {
222
				$echap = $regs[0];
223
			} // sinon les traiter selon le cas
224
			else {
225
				if (function_exists($f = 'traiter_echap_' . strtolower($regs[1]))) {
226
					$echap = $f($regs);
227
				} else {
228
					if (function_exists($f = $f . '_dist')) {
229
						$echap = $f($regs);
230
					}
231
				}
232
			}
233
234
			$p = strpos($letexte, $regs[0]);
235
			$letexte = substr_replace($letexte, code_echappement($echap, $source, $no_transform), $p, strlen($regs[0]));
0 ignored issues
show
Bug introduced by
The variable $echap does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
236
		}
237
	}
238
239
	if ($no_transform) {
240
		return $letexte;
241
	}
242
243
	// Echapper le php pour faire joli (ici, c'est pas pour la securite)
244
	// seulement si on a echappe les <script>
245
	// (derogatoire car on ne peut pas faire passer < ? ... ? >
246
	// dans une callback autonommee
247
	if (strpos($preg ? $preg : _PROTEGE_BLOCS, 'script') !== false) {
248
		if (strpos($letexte, "<" . "?") !== false and preg_match_all(',<[?].*($|[?]>),UisS',
249
				$letexte, $matches, PREG_SET_ORDER)
250
		) {
251
			foreach ($matches as $regs) {
252
				$letexte = str_replace($regs[0],
253
					code_echappement(highlight_string($regs[0], true), $source),
254
					$letexte);
255
			}
256
		}
257
	}
258
259
	return $letexte;
260
}
261
262
//
263
// Traitement final des echappements
264
// Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
265
// par propre() : exemple dans multi et dans typo()
266
// https://code.spip.net/@echappe_retour
267
function echappe_retour($letexte, $source = '', $filtre = "") {
268
	if (strpos($letexte, "base64$source")) {
269
		# spip_log(spip_htmlspecialchars($letexte));  ## pour les curieux
270
		$max_prof = 5;
271
		while (strpos($letexte, "<") !== false
272
			and
273
			preg_match_all(',<(span|div)\sclass=[\'"]base64' . $source . '[\'"]\s(.*)>\s*</\1>,UmsS',
274
				$letexte, $regs, PREG_SET_ORDER)
275
			and $max_prof--) {
276
			foreach ($regs as $reg) {
277
				$rempl = base64_decode(extraire_attribut($reg[0], 'title'));
278
				// recherche d'attributs supplementaires
279
				$at = array();
280
				foreach (array('lang', 'dir') as $attr) {
281
					if ($a = extraire_attribut($reg[0], $attr)) {
282
						$at[$attr] = $a;
283
					}
284
				}
285
				if ($at) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $at of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
286
					$rempl = '<' . $reg[1] . '>' . $rempl . '</' . $reg[1] . '>';
287
					foreach ($at as $attr => $a) {
288
						$rempl = inserer_attribut($rempl, $attr, $a);
289
					}
290
				}
291
				if ($filtre) {
292
					$rempl = $filtre($rempl);
293
				}
294
				$letexte = str_replace($reg[0], $rempl, $letexte);
295
			}
296
		}
297
	}
298
299
	return $letexte;
300
}
301
302
// Reinserer le javascript de confiance (venant des modeles)
303
304
// https://code.spip.net/@echappe_retour_modeles
305
function echappe_retour_modeles($letexte, $interdire_scripts = false) {
306
	$letexte = echappe_retour($letexte);
307
308
	// Dans les appels directs hors squelette, securiser aussi ici
309
	if ($interdire_scripts) {
310
		$letexte = interdire_scripts($letexte);
311
	}
312
313
	return trim($letexte);
314
}
315
316
317
/**
318
 * Coupe un texte à une certaine longueur.
319
 *
320
 * Il essaie de ne pas couper les mots et enlève le formatage du texte.
321
 * Si le texte original est plus long que l’extrait coupé, alors des points
322
 * de suite sont ajoutés à l'extrait, tel que ` (...)`.
323
 *
324
 * @note
325
 *     Les points de suite ne sont pas ajoutés sur les extraits
326
 *     très courts.
327
 *
328
 * @filtre
329
 * @link https://www.spip.net/4275
330
 *
331
 * @param string $texte
332
 *     Texte à couper
333
 * @param int $taille
334
 *     Taille de la coupe
335
 * @param string $suite
0 ignored issues
show
Documentation introduced by
Should the type for parameter $suite not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
336
 *     Points de suite ajoutés.
337
 * @return string
338
 *     Texte coupé
339
 **/
340
function couper($texte, $taille = 50, $suite = null) {
341
	if (!($length = strlen($texte)) or $taille <= 0) {
342
		return '';
343
	}
344
	$offset = 400 + 2 * $taille;
345
	while ($offset < $length
346
		and strlen(preg_replace(",<(!--|\w|/)[^>]+>,Uims", "", substr($texte, 0, $offset))) < $taille) {
347
		$offset = 2 * $offset;
348
	}
349
	if ($offset < $length
350
		&& ($p_tag_ouvrant = strpos($texte, '<', $offset)) !== null
351
	) {
352
		$p_tag_fermant = strpos($texte, '>', $offset);
353
		if ($p_tag_fermant && ($p_tag_fermant < $p_tag_ouvrant)) {
354
			$offset = $p_tag_fermant + 1;
355
		} // prolonger la coupe jusqu'au tag fermant suivant eventuel
356
	}
357
	$texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
358
359
	if (!function_exists('nettoyer_raccourcis_typo')) {
360
		include_spip('inc/lien');
361
	}
362
	$texte = nettoyer_raccourcis_typo($texte);
363
364
	// balises de sauts de ligne et paragraphe
365
	$texte = preg_replace("/<p( [^>]*)?" . ">/", "\r", $texte);
366
	$texte = preg_replace("/<br( [^>]*)?" . ">/", "\n", $texte);
367
368
	// on repasse les doubles \n en \r que nettoyer_raccourcis_typo() a pu modifier
369
	$texte = str_replace("\n\n", "\r", $texte);
370
371
	// supprimer les tags
372
	$texte = supprimer_tags($texte);
373
	$texte = trim(str_replace("\n", " ", $texte));
374
	$texte .= "\n";  // marquer la fin
375
376
	// corriger la longueur de coupe
377
	// en fonction de la presence de caracteres utf
378
	if ($GLOBALS['meta']['charset'] == 'utf-8') {
379
		$long = charset2unicode($texte);
380
		$long = spip_substr($long, 0, max($taille, 1));
381
		$nbcharutf = preg_match_all('/(&#[0-9]{3,6};)/S', $long, $matches);
382
		$taille += $nbcharutf;
383
	}
384
385
386
	// couper au mot precedent
387
	$long = spip_substr($texte, 0, max($taille - 4, 1));
388
	$u = $GLOBALS['meta']['pcre_u'];
389
	$court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
390
	if (is_null($suite)) {
391
		$suite = (defined('_COUPER_SUITE') ? _COUPER_SUITE : '&nbsp;(...)');
392
	}
393
	$points = $suite;
394
395
	// trop court ? ne pas faire de (...)
396
	if (spip_strlen($court) < max(0.75 * $taille, 2)) {
397
		$points = '';
398
		$long = spip_substr($texte, 0, $taille);
399
		$texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
400
		// encore trop court ? couper au caractere
401
		if (spip_strlen($texte) < 0.75 * $taille) {
402
			$texte = $long;
403
		}
404
	} else {
405
		$texte = $court;
406
	}
407
408
	if (strpos($texte, "\n"))  // la fin est encore la : c'est qu'on n'a pas de texte de suite
409
	{
410
		$points = '';
411
	}
412
413
	// remettre les paragraphes
414
	$texte = preg_replace("/\r+/", "\n\n", $texte);
415
416
	// supprimer l'eventuelle entite finale mal coupee
417
	$texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
418
419
	return quote_amp(trim($texte)) . $points;
420
}
421
422
423
// https://code.spip.net/@protege_js_modeles
424
function protege_js_modeles($t) {
425
	if (isset($GLOBALS['visiteur_session'])) {
426 View Code Duplication
		if (preg_match_all(',<script.*?($|</script.),isS', $t, $r, PREG_SET_ORDER)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
427
			if (!defined('_PROTEGE_JS_MODELES')) {
428
				include_spip('inc/acces');
429
				define('_PROTEGE_JS_MODELES', creer_uniqid());
430
			}
431
			foreach ($r as $regs) {
432
				$t = str_replace($regs[0], code_echappement($regs[0], 'javascript' . _PROTEGE_JS_MODELES), $t);
433
			}
434
		}
435 View Code Duplication
		if (preg_match_all(',<\?php.*?($|\?' . '>),isS', $t, $r, PREG_SET_ORDER)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
436
			if (!defined('_PROTEGE_PHP_MODELES')) {
437
				include_spip('inc/acces');
438
				define('_PROTEGE_PHP_MODELES', creer_uniqid());
439
			}
440
			foreach ($r as $regs) {
441
				$t = str_replace($regs[0], code_echappement($regs[0], 'php' . _PROTEGE_PHP_MODELES), $t);
442
			}
443
		}
444
	}
445
446
	return $t;
447
}
448
449
450
function echapper_faux_tags($letexte) {
451
	if (strpos($letexte, '<') === false) {
452
		return $letexte;
453
	}
454
	$textMatches = preg_split(',(</?[a-z!][^<>]*>),', $letexte, null, PREG_SPLIT_DELIM_CAPTURE);
455
456
	$letexte = "";
457
	while (count($textMatches)) {
458
		// un texte a echapper
459
		$letexte .= str_replace("<", '&lt;', array_shift($textMatches));
460
		// un tag html qui a servit a faite le split
461
		$letexte .= array_shift($textMatches);
462
	}
463
464
	return $letexte;
465
}
466
467
/**
468
 * Si le html contenu dans un texte ne passe pas sans transformation a travers safehtml
469
 * on l'echappe
470
 * si safehtml ne renvoie pas la meme chose on echappe les < en &lt; pour montrer le contenu brut
471
 *
472
 * @param string $texte
473
 * @param bool $strict
474
 * @return string
475
 */
476
function echapper_html_suspect($texte, $strict=true) {
477
	static $echapper_html_suspect;
478
	if (!$texte or !is_string($texte)) {
479
		return $texte;
480
	}
481
482
	if (!isset($echapper_html_suspect)) {
483
		$echapper_html_suspect = charger_fonction('echapper_html_suspect', 'inc', true);
484
	}
485
	// si fonction personalisee, on delegue
486
	if ($echapper_html_suspect) {
487
		return $echapper_html_suspect($texte, $strict);
488
	}
489
490
	if (strpos($texte, '<') === false
491
	  or strpos($texte, '=') === false) {
492
		return $texte;
493
	}
494
495
	// quand c'est du texte qui passe par propre on est plus coulant tant qu'il y a pas d'attribut du type onxxx=
496
	// car sinon on declenche sur les modeles ou ressources
497
	if (!$strict and
498
	  (strpos($texte,'on') === false or !preg_match(",<\w+.*\bon\w+\s*=,UimsS", $texte))
499
	  ){
500
		return $texte;
501
	}
502
503
	// on teste sur strlen car safehtml supprime le contenu dangereux
504
	// mais il peut aussi changer des ' en " sur les attributs html,
505
	// donc un test d'egalite est trop strict
506
	if (strlen(safehtml($texte)) !== strlen($texte)) {
507
		$texte = str_replace("<", "&lt;", $texte);
508
		if (!function_exists('attribut_html')) {
509
			include_spip('inc/filtres');
510
		}
511
		$texte = "<mark class='danger-js' title='".attribut_html(_T('erreur_contenu_suspect'))."'>⚠️</mark> ".$texte;
512
	}
513
514
	return $texte;
515
}
516
517
518
/**
519
 * Sécurise un texte HTML
520
 *
521
 * Échappe le code PHP et JS.
522
 * Applique en plus safehtml si un plugin le définit dans inc/safehtml.php
523
 *
524
 * Permet de protéger les textes issus d'une origine douteuse (forums, syndications...)
525
 *
526
 * @filtre
527
 * @link https://www.spip.net/4310
528
 *
529
 * @param string $t
530
 *      Texte à sécuriser
531
 * @return string
532
 *      Texte sécurisé
533
 **/
534
function safehtml($t) {
535
	static $safehtml;
536
537
	if (!$t or !is_string($t)) {
538
		return $t;
539
	}
540
	# attention safehtml nettoie deux ou trois caracteres de plus. A voir
541
	if (strpos($t, '<') === false) {
542
		return str_replace("\x00", '', $t);
543
	}
544
545
	$t = interdire_scripts($t); // jolifier le php
546
	$t = echappe_js($t);
547
548
	if (!isset($safehtml)) {
549
		$safehtml = charger_fonction('safehtml', 'inc', true);
550
	}
551
	if ($safehtml) {
552
		$t = $safehtml($t);
553
	}
554
555
	return interdire_scripts($t); // interdire le php (2 precautions)
556
}
557
558
559
/**
560
 * Supprime les modèles d'image d'un texte
561
 *
562
 * Fonction en cas de texte extrait d'un serveur distant:
563
 * on ne sait pas (encore) rapatrier les documents joints
564
 * Sert aussi à nettoyer un texte qu'on veut mettre dans un `<a>` etc.
565
 *
566
 * @todo
567
 *     gérer les autres modèles ?
568
 *
569
 * @param string $letexte
570
 *     Texte à nettoyer
571
 * @param string|null $message
572
 *     Message de remplacement pour chaque image enlevée
573
 * @return string
574
 *     Texte sans les modèles d'image
575
 **/
576
function supprime_img($letexte, $message = null) {
577
	if ($message === null) {
578
		$message = '(' . _T('img_indisponible') . ')';
579
	}
580
581
	return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?' . '\s*/?' . '>,i',
582
		$message, $letexte);
583
}
584