Completed
Pull Request — master (#28)
by
unknown
07:09
created

texte_mini.php ➔ echapper_html_suspect()   D

Complexity

Conditions 9
Paths 5

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 13
nc 5
nop 2
dl 0
loc 26
rs 4.909
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2017                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details 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
		$img = find_in_path($p . '.gif');
54
		list(, , , $size) = @getimagesize($img);
55
		$GLOBALS[$p] = '<img src="' . $img . '" ' . $size . ' class="puce" alt="-" />';
56
	}
57
58
	return $GLOBALS[$p];
59
}
60
61
62
// XHTML - Preserver les balises-bloc : on liste ici tous les elements
63
// dont on souhaite qu'ils provoquent un saut de paragraphe
64
65
if (!defined('_BALISES_BLOCS')) {
66
	define('_BALISES_BLOCS',
67
	'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'
68
	);
69
}
70
71
if (!defined('_BALISES_BLOCS_REGEXP')) {
72
	define('_BALISES_BLOCS_REGEXP', ',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS');
73
}
74
75
//
76
// Echapper les elements perilleux en les passant en base64
77
//
78
79
// Creer un bloc base64 correspondant a $rempl ; au besoin en marquant
80
// une $source differente ; le script detecte automagiquement si ce qu'on
81
// echappe est un div ou un span
82
// http://code.spip.net/@code_echappement
83
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...
84
	if (!strlen($rempl)) {
85
		return '';
86
	}
87
88
	// Tester si on echappe en span ou en div
89
	if (is_null($mode) or !in_array($mode, array('div', 'span'))) {
90
		$mode = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $rempl) ? 'div' : 'span';
91
	}
92
93
	// Decouper en morceaux, base64 a des probleme selon la taille de la pile
94
	$taille = 30000;
95
	$return = "";
96
	for ($i = 0; $i < strlen($rempl); $i += $taille) {
97
		// Convertir en base64 et cacher dans un attribut
98
		// utiliser les " pour eviter le re-encodage de ' et &#8217
99
		$base64 = base64_encode(substr($rempl, $i, $taille));
100
		$return .= "<$mode class=\"base64$source\" title=\"$base64\"></$mode>";
101
	}
102
103
	return $return;
104
105
}
106
107
108
// Echapper les <html>...</ html>
109
// http://code.spip.net/@traiter_echap_html_dist
110
function traiter_echap_html_dist($regs) {
111
	return $regs[3];
112
}
113
114
// Echapper les <code>...</ code>
115
// http://code.spip.net/@traiter_echap_code_dist
116
function traiter_echap_code_dist($regs) {
117
	list(, , $att, $corps) = $regs;
118
	$echap = spip_htmlspecialchars($corps); // il ne faut pas passer dans entites_html, ne pas transformer les &#xxx; du code !
119
120
	// ne pas mettre le <div...> s'il n'y a qu'une ligne
121
	if (is_int(strpos($echap, "\n"))) {
122
		// supprimer les sauts de ligne debut/fin
123
		// (mais pas les espaces => ascii art).
124
		$echap = preg_replace("/^[\n\r]+|[\n\r]+$/s", "", $echap);
125
		$echap = nl2br($echap);
126
		$echap = "<div style='text-align: left;' "
127
			. "class='spip_code' dir='ltr'><code$att>"
128
			. $echap . "</code></div>";
129
	} else {
130
		$echap = "<code$att class='spip_code' dir='ltr'>" . $echap . "</code>";
131
	}
132
133
	$echap = str_replace("\t", "&nbsp; &nbsp; &nbsp; &nbsp; ", $echap);
134
	$echap = str_replace("  ", " &nbsp;", $echap);
135
136
	return $echap;
137
}
138
139
// Echapper les <cadre>...</ cadre> aka <frame>...</ frame>
140
// http://code.spip.net/@traiter_echap_cadre_dist
141
function traiter_echap_cadre_dist($regs) {
142
	$echap = trim(entites_html($regs[3]));
143
	// compter les lignes un peu plus finement qu'avec les \n
144
	$lignes = explode("\n", trim($echap));
145
	$n = 0;
146
	foreach ($lignes as $l) {
147
		$n += floor(strlen($l) / 60) + 1;
148
	}
149
	$n = max($n, 2);
150
	$echap = "\n<textarea readonly='readonly' cols='40' rows='$n' class='spip_cadre' dir='ltr'>$echap</textarea>";
151
152
	return $echap;
153
}
154
155
// http://code.spip.net/@traiter_echap_frame_dist
156
function traiter_echap_frame_dist($regs) {
157
	return traiter_echap_cadre_dist($regs);
158
}
159
160
// http://code.spip.net/@traiter_echap_script_dist
161
function traiter_echap_script_dist($regs) {
162
	// rendre joli (et inactif) si c'est un script language=php
163
	if (preg_match(',<script\b[^>]+php,ims', $regs[0])) {
164
		return highlight_string($regs[0], true);
165
	}
166
167
	// Cas normal : le script passe tel quel
168
	return $regs[0];
169
}
170
171
define('_PROTEGE_BLOCS', ',<(html|code|cadre|frame|script|style)(\s[^>]*)?>(.*)</\1>,UimsS');
172
173
// - pour $source voir commentaire infra (echappe_retour)
174
// - pour $no_transform voir le filtre post_autobr dans inc/filtres
175
// http://code.spip.net/@echappe_html
176
function echappe_html(
177
	$letexte,
178
	$source = '',
179
	$no_transform = false,
180
	$preg = ''
181
) {
182
	if (!is_string($letexte) or !strlen($letexte)) {
183
		return $letexte;
184
	}
185
186
	// si le texte recu est long PCRE risque d'exploser, on
187
	// fait donc un mic-mac pour augmenter pcre.backtrack_limit
188
	if (($len = strlen($letexte)) > 100000) {
189
		if (!$old = @ini_get('pcre.backtrack_limit')) {
190
			$old = 100000;
191
		}
192
		if ($len > $old) {
193
			$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...
194
			spip_log("ini_set pcre.backtrack_limit=$len ($old)");
195
		}
196
	}
197
198
	if (($preg or strpos($letexte, "<") !== false)
199
		and preg_match_all($preg ? $preg : _PROTEGE_BLOCS, $letexte, $matches, PREG_SET_ORDER)
200
	) {
201
		foreach ($matches as $regs) {
202
			// echappements tels quels ?
203
			if ($no_transform) {
204
				$echap = $regs[0];
205
			} // sinon les traiter selon le cas
206
			else {
207
				if (function_exists($f = 'traiter_echap_' . strtolower($regs[1]))) {
208
					$echap = $f($regs);
209
				} else {
210
					if (function_exists($f = $f . '_dist')) {
211
						$echap = $f($regs);
212
					}
213
				}
214
			}
215
216
			$p = strpos($letexte, $regs[0]);
217
			$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...
218
		}
219
	}
220
221
	if ($no_transform) {
222
		return $letexte;
223
	}
224
225
	// Echapper le php pour faire joli (ici, c'est pas pour la securite)
226
	// seulement si on a echappe les <script>
227
	// (derogatoire car on ne peut pas faire passer < ? ... ? >
228
	// dans une callback autonommee
229
	if (strpos($preg ? $preg : _PROTEGE_BLOCS, 'script') !== false) {
230
		if (strpos($letexte, "<" . "?") !== false and preg_match_all(',<[?].*($|[?]>),UisS',
231
				$letexte, $matches, PREG_SET_ORDER)
232
		) {
233
			foreach ($matches as $regs) {
234
				$letexte = str_replace($regs[0],
235
					code_echappement(highlight_string($regs[0], true), $source),
236
					$letexte);
237
			}
238
		}
239
	}
240
241
	return $letexte;
242
}
243
244
//
245
// Traitement final des echappements
246
// Rq: $source sert a faire des echappements "a soi" qui ne sont pas nettoyes
247
// par propre() : exemple dans multi et dans typo()
248
// http://code.spip.net/@echappe_retour
249
function echappe_retour($letexte, $source = '', $filtre = "") {
250
	if (strpos($letexte, "base64$source")) {
251
		# spip_log(spip_htmlspecialchars($letexte));  ## pour les curieux
252
		$max_prof = 5;
253
		while (strpos($letexte, "<") !== false
254
			and
255
			preg_match_all(',<(span|div)\sclass=[\'"]base64' . $source . '[\'"]\s(.*)>\s*</\1>,UmsS',
256
				$letexte, $regs, PREG_SET_ORDER)
257
			and $max_prof--) {
258
			foreach ($regs as $reg) {
259
				$rempl = base64_decode(extraire_attribut($reg[0], 'title'));
260
				// recherche d'attributs supplementaires
261
				$at = array();
262
				foreach (array('lang', 'dir') as $attr) {
263
					if ($a = extraire_attribut($reg[0], $attr)) {
264
						$at[$attr] = $a;
265
					}
266
				}
267
				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...
268
					$rempl = '<' . $reg[1] . '>' . $rempl . '</' . $reg[1] . '>';
269
					foreach ($at as $attr => $a) {
270
						$rempl = inserer_attribut($rempl, $attr, $a);
271
					}
272
				}
273
				if ($filtre) {
274
					$rempl = $filtre($rempl);
275
				}
276
				$letexte = str_replace($reg[0], $rempl, $letexte);
277
			}
278
		}
279
	}
280
281
	return $letexte;
282
}
283
284
// Reinserer le javascript de confiance (venant des modeles)
285
286
// http://code.spip.net/@echappe_retour_modeles
287
function echappe_retour_modeles($letexte, $interdire_scripts = false) {
288
	$letexte = echappe_retour($letexte);
289
290
	// Dans les appels directs hors squelette, securiser aussi ici
291
	if ($interdire_scripts) {
292
		$letexte = interdire_scripts($letexte);
293
	}
294
295
	return trim($letexte);
296
}
297
298
299
/**
300
 * Coupe un texte à une certaine longueur.
301
 *
302
 * Il essaie de ne pas couper les mots et enlève le formatage du texte.
303
 * Si le texte original est plus long que l’extrait coupé, alors des points
304
 * de suite sont ajoutés à l'extrait, tel que ` (...)`.
305
 *
306
 * @note
307
 *     Les points de suite ne sont pas ajoutés sur les extraits
308
 *     très courts.
309
 *
310
 * @filtre
311
 * @link http://www.spip.net/4275
312
 *
313
 * @param string $texte
314
 *     Texte à couper
315
 * @param int $taille
316
 *     Taille de la coupe
317
 * @param string $suite
318
 *     Points de suite ajoutés.
319
 * @return string
320
 *     Texte coupé
321
 **/
322
function couper($texte, $taille = 50, $suite = '&nbsp;(...)') {
323
	if (!($length = strlen($texte)) or $taille <= 0) {
324
		return '';
325
	}
326
	$offset = 400 + 2 * $taille;
327
	while ($offset < $length
328
		and strlen(preg_replace(",<(!--|\w|/)[^>]+>,Uims", "", substr($texte, 0, $offset))) < $taille) {
329
		$offset = 2 * $offset;
330
	}
331
	if ($offset < $length
332
		&& ($p_tag_ouvrant = strpos($texte, '<', $offset)) !== null
333
	) {
334
		$p_tag_fermant = strpos($texte, '>', $offset);
335
		if ($p_tag_fermant && ($p_tag_fermant < $p_tag_ouvrant)) {
336
			$offset = $p_tag_fermant + 1;
337
		} // prolonger la coupe jusqu'au tag fermant suivant eventuel
338
	}
339
	$texte = substr($texte, 0, $offset); /* eviter de travailler sur 10ko pour extraire 150 caracteres */
340
341
	if (!function_exists('nettoyer_raccourcis_typo')) {
342
		include_spip('inc/lien');
343
	}
344
	$texte = nettoyer_raccourcis_typo($texte);
345
346
	// balises de sauts de ligne et paragraphe
347
	$texte = preg_replace("/<p( [^>]*)?" . ">/", "\r", $texte);
348
	$texte = preg_replace("/<br( [^>]*)?" . ">/", "\n", $texte);
349
350
	// on repasse les doubles \n en \r que nettoyer_raccourcis_typo() a pu modifier
351
	$texte = str_replace("\n\n", "\r", $texte);
352
353
	// supprimer les tags
354
	$texte = supprimer_tags($texte);
355
	$texte = trim(str_replace("\n", " ", $texte));
356
	$texte .= "\n";  // marquer la fin
357
358
	// corriger la longueur de coupe
359
	// en fonction de la presence de caracteres utf
360
	if ($GLOBALS['meta']['charset'] == 'utf-8') {
361
		$long = charset2unicode($texte);
362
		$long = spip_substr($long, 0, max($taille, 1));
363
		$nbcharutf = preg_match_all('/(&#[0-9]{3,6};)/S', $long, $matches);
364
		$taille += $nbcharutf;
365
	}
366
367
368
	// couper au mot precedent
369
	$long = spip_substr($texte, 0, max($taille - 4, 1));
370
	$u = $GLOBALS['meta']['pcre_u'];
371
	$court = preg_replace("/([^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
372
	$points = $suite;
373
374
	// trop court ? ne pas faire de (...)
375
	if (spip_strlen($court) < max(0.75 * $taille, 2)) {
376
		$points = '';
377
		$long = spip_substr($texte, 0, $taille);
378
		$texte = preg_replace("/([^\s][\s]+)[^\s]*\n?$/" . $u, "\\1", $long);
379
		// encore trop court ? couper au caractere
380
		if (spip_strlen($texte) < 0.75 * $taille) {
381
			$texte = $long;
382
		}
383
	} else {
384
		$texte = $court;
385
	}
386
387
	if (strpos($texte, "\n"))  // la fin est encore la : c'est qu'on n'a pas de texte de suite
388
	{
389
		$points = '';
390
	}
391
392
	// remettre les paragraphes
393
	$texte = preg_replace("/\r+/", "\n\n", $texte);
394
395
	// supprimer l'eventuelle entite finale mal coupee
396
	$texte = preg_replace('/&#?[a-z0-9]*$/S', '', $texte);
397
398
	return quote_amp(trim($texte)) . $points;
399
}
400
401
402
// http://code.spip.net/@protege_js_modeles
403
function protege_js_modeles($t) {
404
	if (isset($GLOBALS['visiteur_session'])) {
405 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...
406
			if (!defined('_PROTEGE_JS_MODELES')) {
407
				include_spip('inc/acces');
408
				define('_PROTEGE_JS_MODELES', creer_uniqid());
409
			}
410
			foreach ($r as $regs) {
411
				$t = str_replace($regs[0], code_echappement($regs[0], 'javascript' . _PROTEGE_JS_MODELES), $t);
412
			}
413
		}
414 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...
415
			if (!defined('_PROTEGE_PHP_MODELES')) {
416
				include_spip('inc/acces');
417
				define('_PROTEGE_PHP_MODELES', creer_uniqid());
418
			}
419
			foreach ($r as $regs) {
420
				$t = str_replace($regs[0], code_echappement($regs[0], 'php' . _PROTEGE_PHP_MODELES), $t);
421
			}
422
		}
423
	}
424
425
	return $t;
426
}
427
428
429
function echapper_faux_tags($letexte) {
430
	if (strpos($letexte, '<') === false) {
431
		return $letexte;
432
	}
433
	$textMatches = preg_split(',(</?[a-z!][^<>]*>),', $letexte, null, PREG_SPLIT_DELIM_CAPTURE);
434
435
	$letexte = "";
436
	while (count($textMatches)) {
437
		// un texte a echapper
438
		$letexte .= str_replace("<", '&lt;', array_shift($textMatches));
439
		// un tag html qui a servit a faite le split
440
		$letexte .= array_shift($textMatches);
441
	}
442
443
	return $letexte;
444
}
445
446
/**
447
 * Si le html contenu dans un texte ne passe pas sans transformation a travers safehtml
448
 * on l'echappe
449
 * si safehtml ne renvoie pas la meme chose on echappe les < en &lt; pour montrer le contenu brut
450
 *
451
 * @param string $texte
452
 * @param bool $strict
453
 * @return string
454
 */
455
function echapper_html_suspect($texte, $strict=true) {
456
	if (!$texte
457
		or strpos($texte, '<') === false or strpos($texte, '=') === false) {
458
		return $texte;
459
	}
460
	// quand c'est du texte qui passe par propre on est plus coulant tant qu'il y a pas d'attribut du type onxxx=
461
	// car sinon on declenche sur les modeles ou ressources
462
	if (!$strict and
463
	  (strpos($texte,'on') === false or !preg_match(",<\w+.*\bon\w+\s*=,UimsS", $texte))
464
	  ){
465
		return $texte;
466
	}
467
468
	// on teste sur strlen car safehtml supprime le contenu dangereux
469
	// mais il peut aussi changer des ' en " sur les attributs html,
470
	// donc un test d'egalite est trop strict
471
	if (strlen(safehtml($texte)) !== strlen($texte)) {
472
		$texte = str_replace("<", "&lt;", $texte);
473
		if (!function_exists('attribut_html')) {
474
			include_spip('inc/filtres');
475
		}
476
		$texte = "<mark title='".attribut_html(_T('erreur_contenu_suspect'))."'>⚠️</mark> ".$texte;
477
	}
478
479
	return $texte;
480
}
481
482
483
/**
484
 * Sécurise un texte HTML
485
 *
486
 * Échappe le code PHP et JS.
487
 * Applique en plus safehtml si un plugin le définit dans inc/safehtml.php
488
 *
489
 * Permet de protéger les textes issus d'une origine douteuse (forums, syndications...)
490
 *
491
 * @filtre
492
 * @link http://www.spip.net/4310
493
 *
494
 * @param string $t
495
 *      Texte à sécuriser
496
 * @return string
497
 *      Texte sécurisé
498
 **/
499
function safehtml($t) {
500
	static $safehtml;
501
502
	if (!$t or !is_string($t)) {
503
		return $t;
504
	}
505
	# attention safehtml nettoie deux ou trois caracteres de plus. A voir
506
	if (strpos($t, '<') === false) {
507
		return str_replace("\x00", '', $t);
508
	}
509
510
	$t = interdire_scripts($t); // jolifier le php
511
	$t = echappe_js($t);
512
513
	if (!isset($safehtml)) {
514
		$safehtml = charger_fonction('safehtml', 'inc', true);
515
	}
516
	if ($safehtml) {
517
		$t = $safehtml($t);
518
	}
519
520
	return interdire_scripts($t); // interdire le php (2 precautions)
521
}
522
523
524
/**
525
 * Supprime les modèles d'image d'un texte
526
 *
527
 * Fonction en cas de texte extrait d'un serveur distant:
528
 * on ne sait pas (encore) rapatrier les documents joints
529
 * Sert aussi à nettoyer un texte qu'on veut mettre dans un `<a>` etc.
530
 *
531
 * @todo
532
 *     gérer les autres modèles ?
533
 *
534
 * @param string $letexte
535
 *     Texte à nettoyer
536
 * @param string|null $message
537
 *     Message de remplacement pour chaque image enlevée
538
 * @return string
539
 *     Texte sans les modèles d'image
540
 **/
541
function supprime_img($letexte, $message = null) {
542
	if ($message === null) {
543
		$message = '(' . _T('img_indisponible') . ')';
544
	}
545
546
	return preg_replace(',<(img|doc|emb)([0-9]+)(\|([^>]*))?' . '\s*/?' . '>,i',
547
		$message, $letexte);
548
}
549