1 | <?php |
---|
2 | |
---|
3 | /***************************************************************************\ |
---|
4 | * SPIP, Systeme de publication pour l'internet * |
---|
5 | * * |
---|
6 | * Copyright (c) 2001-2018 * |
---|
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 raccourcis SPIP |
---|
15 | * |
---|
16 | * @package SPIP\Core\Texte |
---|
17 | **/ |
---|
18 | |
---|
19 | if (!defined('_ECRIRE_INC_VERSION')) { |
---|
20 | return; |
---|
21 | } |
---|
22 | |
---|
23 | include_spip('inc/texte_mini'); |
---|
24 | include_spip('inc/lien'); |
---|
25 | |
---|
26 | /************************************************************************************************************************* |
---|
27 | * Fonctions inutilisees en dehors de inc/texte |
---|
28 | * |
---|
29 | */ |
---|
30 | |
---|
31 | /** |
---|
32 | * Raccourcis dépendant du sens de la langue |
---|
33 | * |
---|
34 | * @return array Tablea ('','') |
---|
35 | */ |
---|
36 | function definir_raccourcis_alineas() { |
---|
37 | return array('', ''); |
---|
38 | } |
---|
39 | |
---|
40 | |
---|
41 | /** |
---|
42 | * Traitement des raccourcis de tableaux |
---|
43 | * |
---|
44 | * Ne fait rien ici. Voir plugin Textwheel. |
---|
45 | * |
---|
46 | * @param string $bloc |
---|
47 | * @return string |
---|
48 | */ |
---|
49 | function traiter_tableau($bloc) { |
---|
50 | return $bloc; |
---|
51 | } |
---|
52 | |
---|
53 | |
---|
54 | /** |
---|
55 | * Traitement des listes |
---|
56 | * |
---|
57 | * Ne fais rien. Voir Plugin Textwheel. |
---|
58 | * (merci a Michael Parienti) |
---|
59 | * |
---|
60 | * @param string $texte |
---|
61 | * @return string |
---|
62 | */ |
---|
63 | function traiter_listes($texte) { |
---|
64 | return $texte; |
---|
65 | } |
---|
66 | |
---|
67 | /** |
---|
68 | * Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc. |
---|
69 | * |
---|
70 | * Ne fais rien ici. Voir plugin Textwheel. |
---|
71 | * |
---|
72 | * @pipeline_appel pre_propre |
---|
73 | * @pipeline_appel post_propre |
---|
74 | * |
---|
75 | * @param string $letexte |
---|
76 | * @return string |
---|
77 | */ |
---|
78 | function traiter_raccourcis($letexte) { |
---|
79 | |
---|
80 | // Appeler les fonctions de pre_traitement |
---|
81 | $letexte = pipeline('pre_propre', $letexte); |
---|
82 | |
---|
83 | // APPELER ICI UN PIPELINE traiter_raccourcis ? |
---|
84 | // $letexte = pipeline('traiter_raccourcis', $letexte); |
---|
85 | |
---|
86 | // Appeler les fonctions de post-traitement |
---|
87 | $letexte = pipeline('post_propre', $letexte); |
---|
88 | |
---|
89 | return $letexte; |
---|
90 | } |
---|
91 | |
---|
92 | /************************************************************************************************************************* |
---|
93 | * Fonctions utilisees en dehors de inc/texte |
---|
94 | */ |
---|
95 | |
---|
96 | |
---|
97 | /** |
---|
98 | * Échapper et affichier joliement les `<script` et `<iframe`... |
---|
99 | * |
---|
100 | * @param string $t |
---|
101 | * @param string $class Attributs HTML du conteneur à ajouter |
---|
102 | * @return string |
---|
103 | */ |
---|
104 | function echappe_js($t, $class = ' class = "echappe-js"') { |
---|
105 | foreach (array('script', 'iframe') as $tag) { |
---|
106 | if (stripos($t, "<$tag") !== false |
---|
107 | and preg_match_all(',<' . $tag . '.*?($|</' . $tag . '.),isS', $t, $r, PREG_SET_ORDER) |
---|
108 | ) { |
---|
109 | foreach ($r as $regs) { |
---|
110 | $t = str_replace($regs[0], |
---|
111 | "<code$class>" . nl2br(spip_htmlspecialchars($regs[0])) . '</code>', |
---|
112 | $t); |
---|
113 | } |
---|
114 | } |
---|
115 | } |
---|
116 | |
---|
117 | return $t; |
---|
118 | } |
---|
119 | |
---|
120 | |
---|
121 | /** |
---|
122 | * Empêcher l'exécution de code PHP et JS |
---|
123 | * |
---|
124 | * Sécurité : empêcher l'exécution de code PHP, en le transformant en joli code |
---|
125 | * dans l'espace privé. Cette fonction est aussi appelée par propre et typo. |
---|
126 | * |
---|
127 | * De la même manière, la fonction empêche l'exécution de JS mais selon le mode |
---|
128 | * de protection passe en argument |
---|
129 | * |
---|
130 | * Il ne faut pas désactiver globalement la fonction dans l'espace privé car elle protège |
---|
131 | * aussi les balises des squelettes qui ne passent pas forcement par propre ou typo après |
---|
132 | * si elles sont appelées en direct |
---|
133 | * |
---|
134 | * @param string $arg |
---|
135 | * Code à protéger |
---|
136 | * @param int $mode_filtre |
---|
137 | * Mode de protection |
---|
138 | * -1 : protection dans l'espace privé et public |
---|
139 | * 0 : protection dans l'espace public |
---|
140 | * 1 : aucune protection |
---|
141 | * utilise la valeur de la globale filtrer_javascript si non fourni |
---|
142 | * @return string |
---|
143 | * Code protégé |
---|
144 | **/ |
---|
145 | function interdire_scripts($arg, $mode_filtre=null) { |
---|
146 | // on memorise le resultat sur les arguments non triviaux |
---|
147 | static $dejavu = array(); |
---|
148 | |
---|
149 | // Attention, si ce n'est pas une chaine, laisser intact |
---|
150 | if (!$arg or !is_string($arg) or !strstr($arg, '<')) { |
---|
151 | return $arg; |
---|
152 | } |
---|
153 | |
---|
154 | if (is_null($mode_filtre) or !in_array($mode_filtre, array(-1, 0, 1))) { |
---|
155 | $mode_filtre = $GLOBALS['filtrer_javascript']; |
---|
156 | } |
---|
157 | |
---|
158 | if (isset($dejavu[$mode_filtre][$arg])) { |
---|
159 | return $dejavu[$mode_filtre][$arg]; |
---|
160 | } |
---|
161 | |
---|
162 | // echapper les tags asp/php |
---|
163 | $t = str_replace('<' . '%', '<%', $arg); |
---|
164 | |
---|
165 | // echapper le php |
---|
166 | $t = str_replace('<' . '?', '<?', $t); |
---|
167 | |
---|
168 | // echapper le < script language=php > |
---|
169 | $t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '<\1', $t); |
---|
170 | |
---|
171 | // Pour le js, trois modes : parano (-1), prive (0), ok (1) |
---|
172 | switch ($mode_filtre) { |
---|
173 | case 0: |
---|
174 | if (!_DIR_RESTREINT) { |
---|
175 | $t = echappe_js($t); |
---|
176 | } |
---|
177 | break; |
---|
178 | case -1: |
---|
179 | $t = echappe_js($t); |
---|
180 | break; |
---|
181 | } |
---|
182 | |
---|
183 | // pas de <base href /> svp ! |
---|
184 | $t = preg_replace(',<(base\b),iS', '<\1', $t); |
---|
185 | |
---|
186 | // Reinserer les echappements des modeles |
---|
187 | if (defined('_PROTEGE_JS_MODELES')) { |
---|
188 | $t = echappe_retour($t, "javascript" . _PROTEGE_JS_MODELES); |
---|
189 | } |
---|
190 | if (defined('_PROTEGE_PHP_MODELES')) { |
---|
191 | $t = echappe_retour($t, "php" . _PROTEGE_PHP_MODELES); |
---|
192 | } |
---|
193 | |
---|
194 | return $dejavu[$mode_filtre][$arg] = $t; |
---|
195 | } |
---|
196 | |
---|
197 | |
---|
198 | /** |
---|
199 | * Applique la typographie générale |
---|
200 | * |
---|
201 | * Effectue un traitement pour que les textes affichés suivent les règles |
---|
202 | * de typographie. Fait une protection préalable des balises HTML et SPIP. |
---|
203 | * Transforme les balises `<multi>` |
---|
204 | * |
---|
205 | * @filtre |
---|
206 | * @uses traiter_modeles() |
---|
207 | * @uses corriger_typo() |
---|
208 | * @uses echapper_faux_tags() |
---|
209 | * @see propre() |
---|
210 | * |
---|
211 | * @param string $letexte |
---|
212 | * Texte d'origine |
---|
213 | * @param bool $echapper |
---|
214 | * Échapper ? |
---|
215 | * @param string|null $connect |
---|
216 | * Nom du connecteur à la bdd |
---|
217 | * @param array $env |
---|
218 | * Environnement (pour les calculs de modèles) |
---|
219 | * @return string $t |
---|
220 | * Texte transformé |
---|
221 | **/ |
---|
222 | function typo($letexte, $echapper = true, $connect = null, $env = array()) { |
---|
223 | // Plus vite ! |
---|
224 | if (!$letexte) { |
---|
225 | return $letexte; |
---|
226 | } |
---|
227 | |
---|
228 | // les appels directs a cette fonction depuis le php de l'espace |
---|
229 | // prive etant historiquement ecrit sans argment $connect |
---|
230 | // on utilise la presence de celui-ci pour distinguer les cas |
---|
231 | // ou il faut passer interdire_script explicitement |
---|
232 | // les appels dans les squelettes (de l'espace prive) fournissant un $connect |
---|
233 | // ne seront pas perturbes |
---|
234 | $interdire_script = false; |
---|
235 | if (is_null($connect)) { |
---|
236 | $connect = ''; |
---|
237 | $interdire_script = true; |
---|
238 | $env['espace_prive'] = test_espace_prive(); |
---|
239 | } |
---|
240 | |
---|
241 | // Echapper les codes <html> etc |
---|
242 | if ($echapper) { |
---|
243 | $letexte = echappe_html($letexte, 'TYPO'); |
---|
244 | } |
---|
245 | |
---|
246 | // |
---|
247 | // Installer les modeles, notamment images et documents ; |
---|
248 | // |
---|
249 | // NOTE : propre() ne passe pas par ici mais directement par corriger_typo |
---|
250 | // cf. inc/lien |
---|
251 | |
---|
252 | $letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '', $connect, null, $env); |
---|
253 | if ($letexte != $mem) { |
---|
254 | $echapper = true; |
---|
255 | } |
---|
256 | unset($mem); |
---|
257 | |
---|
258 | $letexte = corriger_typo($letexte); |
---|
259 | $letexte = echapper_faux_tags($letexte); |
---|
260 | |
---|
261 | // reintegrer les echappements |
---|
262 | if ($echapper) { |
---|
263 | $letexte = echappe_retour($letexte, 'TYPO'); |
---|
264 | } |
---|
265 | |
---|
266 | // Dans les appels directs hors squelette, securiser ici aussi |
---|
267 | if ($interdire_script) { |
---|
268 | $letexte = interdire_scripts($letexte); |
---|
269 | } |
---|
270 | |
---|
271 | // Dans l'espace prive on se mefie de tout contenu dangereux |
---|
272 | // https://core.spip.net/issues/3371 |
---|
273 | // et aussi dans l'espace public si la globale filtrer_javascript = -1 |
---|
274 | // https://core.spip.net/issues/4166 |
---|
275 | if ($GLOBALS['filtrer_javascript'] == -1 |
---|
276 | or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)) { |
---|
277 | $letexte = echapper_html_suspect($letexte); |
---|
278 | } |
---|
279 | |
---|
280 | return $letexte; |
---|
281 | } |
---|
282 | |
---|
283 | // Correcteur typographique |
---|
284 | define('_TYPO_PROTEGER', "!':;?~%-"); |
---|
285 | define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8"); |
---|
286 | |
---|
287 | define('_TYPO_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_TYPO_PROTEGER) . "][^<>]*>,imsS"); |
---|
288 | |
---|
289 | /** |
---|
290 | * Corrige la typographie |
---|
291 | * |
---|
292 | * Applique les corrections typographiques adaptées à la langue indiquée. |
---|
293 | * |
---|
294 | * @pipeline_appel pre_typo |
---|
295 | * @pipeline_appel post_typo |
---|
296 | * @uses corriger_caracteres() |
---|
297 | * @uses corriger_caracteres() |
---|
298 | * |
---|
299 | * @param string $letexte Texte |
---|
300 | * @param string $lang Langue |
---|
301 | * @return string Texte |
---|
302 | */ |
---|
303 | function corriger_typo($letexte, $lang = '') { |
---|
304 | |
---|
305 | // Plus vite ! |
---|
306 | if (!$letexte) { |
---|
307 | return $letexte; |
---|
308 | } |
---|
309 | |
---|
310 | $letexte = pipeline('pre_typo', $letexte); |
---|
311 | |
---|
312 | // Caracteres de controle "illegaux" |
---|
313 | $letexte = corriger_caracteres($letexte); |
---|
314 | |
---|
315 | // Proteger les caracteres typographiques a l'interieur des tags html |
---|
316 | if (preg_match_all(_TYPO_BALISE, $letexte, $regs, PREG_SET_ORDER)) { |
---|
317 | foreach ($regs as $reg) { |
---|
318 | $insert = $reg[0]; |
---|
319 | // hack: on transforme les caracteres a proteger en les remplacant |
---|
320 | // par des caracteres "illegaux". (cf corriger_caracteres()) |
---|
321 | $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR); |
---|
322 | $letexte = str_replace($reg[0], $insert, $letexte); |
---|
323 | } |
---|
324 | } |
---|
325 | |
---|
326 | // trouver les blocs idiomes et les traiter à part |
---|
327 | $letexte = extraire_idiome($ei = $letexte, $lang, true); |
---|
328 | $ei = ($ei !== $letexte); |
---|
329 | |
---|
330 | // trouver les blocs multi et les traiter a part |
---|
331 | $letexte = extraire_multi($em = $letexte, $lang, true); |
---|
332 | $em = ($em !== $letexte); |
---|
333 | |
---|
334 | // Charger & appliquer les fonctions de typographie |
---|
335 | $typographie = charger_fonction(lang_typo($lang), 'typographie'); |
---|
336 | $letexte = $typographie($letexte); |
---|
337 | |
---|
338 | // Les citations en une autre langue, s'il y a lieu |
---|
339 | if ($em) { |
---|
340 | $letexte = echappe_retour($letexte, 'multi'); |
---|
341 | } |
---|
342 | if ($ei) { |
---|
343 | $letexte = echappe_retour($letexte, 'idiome'); |
---|
344 | } |
---|
345 | |
---|
346 | // Retablir les caracteres proteges |
---|
347 | $letexte = strtr($letexte, _TYPO_PROTECTEUR, _TYPO_PROTEGER); |
---|
348 | |
---|
349 | // pipeline |
---|
350 | $letexte = pipeline('post_typo', $letexte); |
---|
351 | |
---|
352 | # un message pour abs_url - on est passe en mode texte |
---|
353 | $GLOBALS['mode_abs_url'] = 'texte'; |
---|
354 | |
---|
355 | return $letexte; |
---|
356 | } |
---|
357 | |
---|
358 | |
---|
359 | /** |
---|
360 | * Paragrapher seulement |
---|
361 | * |
---|
362 | * /!\ appelée dans inc/filtres et public/composer |
---|
363 | * |
---|
364 | * Ne fait rien ici. Voir plugin Textwheel |
---|
365 | * |
---|
366 | * @param string $letexte |
---|
367 | * @param null $forcer |
---|
368 | * @return string |
---|
369 | */ |
---|
370 | function paragrapher($letexte, $forcer = true) { |
---|
371 | return $letexte; |
---|
372 | } |
---|
373 | |
---|
374 | /** |
---|
375 | * Harmonise les retours chariots et mange les paragraphes HTML |
---|
376 | * |
---|
377 | * Ne sert plus |
---|
378 | * |
---|
379 | * @param string $letexte Texte |
---|
380 | * @return string Texte |
---|
381 | **/ |
---|
382 | function traiter_retours_chariots($letexte) { |
---|
383 | $letexte = preg_replace(",\r\n?,S", "\n", $letexte); |
---|
384 | $letexte = preg_replace(",<p[>[:space:]],iS", "\n\n\\0", $letexte); |
---|
385 | $letexte = preg_replace(",</p[>[:space:]],iS", "\\0\n\n", $letexte); |
---|
386 | |
---|
387 | return $letexte; |
---|
388 | } |
---|
389 | |
---|
390 | |
---|
391 | /** |
---|
392 | * Transforme les raccourcis SPIP, liens et modèles d'un texte en code HTML |
---|
393 | * |
---|
394 | * Filtre à appliquer aux champs du type `#TEXTE*` |
---|
395 | * |
---|
396 | * @filtre |
---|
397 | * @uses echappe_html() |
---|
398 | * @uses expanser_liens() |
---|
399 | * @uses traiter_raccourcis() |
---|
400 | * @uses echappe_retour_modeles() |
---|
401 | * @see typo() |
---|
402 | * |
---|
403 | * @param string $t |
---|
404 | * Texte avec des raccourcis SPIP |
---|
405 | * @param string|null $connect |
---|
406 | * Nom du connecteur à la bdd |
---|
407 | * @param array $env |
---|
408 | * Environnement (pour les calculs de modèles) |
---|
409 | * @return string $t |
---|
410 | * Texte transformé |
---|
411 | **/ |
---|
412 | function propre($t, $connect = null, $env = array()) { |
---|
413 | // les appels directs a cette fonction depuis le php de l'espace |
---|
414 | // prive etant historiquement ecrits sans argment $connect |
---|
415 | // on utilise la presence de celui-ci pour distinguer les cas |
---|
416 | // ou il faut passer interdire_script explicitement |
---|
417 | // les appels dans les squelettes (de l'espace prive) fournissant un $connect |
---|
418 | // ne seront pas perturbes |
---|
419 | $interdire_script = false; |
---|
420 | if (is_null($connect)) { |
---|
421 | $connect = ''; |
---|
422 | $interdire_script = true; |
---|
423 | } |
---|
424 | |
---|
425 | if (!$t) { |
---|
426 | return strval($t); |
---|
427 | } |
---|
428 | |
---|
429 | // Dans l'espace prive on se mefie de tout contenu dangereux |
---|
430 | // avant echappement des balises <html> |
---|
431 | // https://core.spip.net/issues/3371 |
---|
432 | // et aussi dans l'espace public si la globale filtrer_javascript = -1 |
---|
433 | // https://core.spip.net/issues/4166 |
---|
434 | if ($interdire_script |
---|
435 | or $GLOBALS['filtrer_javascript'] == -1 |
---|
436 | or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0) |
---|
437 | or (isset($env['wysiwyg']) and $env['wysiwyg'] and $GLOBALS['filtrer_javascript']<=0)) { |
---|
438 | $t = echapper_html_suspect($t, false); |
---|
439 | } |
---|
440 | $t = echappe_html($t); |
---|
441 | $t = expanser_liens($t, $connect, $env); |
---|
442 | $t = traiter_raccourcis($t); |
---|
443 | $t = echappe_retour_modeles($t, $interdire_script); |
---|
444 | |
---|
445 | return $t; |
---|
446 | } |
---|