b7fcda555cf342b7bdad10499662b463c9236643
[lhc/web/www.git] / www / ecrire / inc / texte.php
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 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('<' . '%', '&lt;%', $arg);
164
165 // echapper le php
166 $t = str_replace('<' . '?', '&lt;?', $t);
167
168 // echapper le < script language=php >
169 $t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '&lt;\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', '&lt;\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 if (isset($env['espace_prive']) and $env['espace_prive']) {
274 $letexte = echapper_html_suspect($letexte);
275 }
276
277 return $letexte;
278 }
279
280 // Correcteur typographique
281 define('_TYPO_PROTEGER', "!':;?~%-");
282 define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8");
283
284 define('_TYPO_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_TYPO_PROTEGER) . "][^<>]*>,imsS");
285
286 /**
287 * Corrige la typographie
288 *
289 * Applique les corrections typographiques adaptées à la langue indiquée.
290 *
291 * @pipeline_appel pre_typo
292 * @pipeline_appel post_typo
293 * @uses corriger_caracteres()
294 * @uses corriger_caracteres()
295 *
296 * @param string $letexte Texte
297 * @param string $lang Langue
298 * @return string Texte
299 */
300 function corriger_typo($letexte, $lang = '') {
301
302 // Plus vite !
303 if (!$letexte) {
304 return $letexte;
305 }
306
307 $letexte = pipeline('pre_typo', $letexte);
308
309 // Caracteres de controle "illegaux"
310 $letexte = corriger_caracteres($letexte);
311
312 // Proteger les caracteres typographiques a l'interieur des tags html
313 if (preg_match_all(_TYPO_BALISE, $letexte, $regs, PREG_SET_ORDER)) {
314 foreach ($regs as $reg) {
315 $insert = $reg[0];
316 // hack: on transforme les caracteres a proteger en les remplacant
317 // par des caracteres "illegaux". (cf corriger_caracteres())
318 $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
319 $letexte = str_replace($reg[0], $insert, $letexte);
320 }
321 }
322
323 // trouver les blocs idiomes et les traiter à part
324 $letexte = extraire_idiome($ei = $letexte, $lang, true);
325 $ei = ($ei !== $letexte);
326
327 // trouver les blocs multi et les traiter a part
328 $letexte = extraire_multi($em = $letexte, $lang, true);
329 $em = ($em !== $letexte);
330
331 // Charger & appliquer les fonctions de typographie
332 $typographie = charger_fonction(lang_typo($lang), 'typographie');
333 $letexte = $typographie($letexte);
334
335 // Les citations en une autre langue, s'il y a lieu
336 if ($em) {
337 $letexte = echappe_retour($letexte, 'multi');
338 }
339 if ($ei) {
340 $letexte = echappe_retour($letexte, 'idiome');
341 }
342
343 // Retablir les caracteres proteges
344 $letexte = strtr($letexte, _TYPO_PROTECTEUR, _TYPO_PROTEGER);
345
346 // pipeline
347 $letexte = pipeline('post_typo', $letexte);
348
349 # un message pour abs_url - on est passe en mode texte
350 $GLOBALS['mode_abs_url'] = 'texte';
351
352 return $letexte;
353 }
354
355
356 /**
357 * Paragrapher seulement
358 *
359 * /!\ appelée dans inc/filtres et public/composer
360 *
361 * Ne fait rien ici. Voir plugin Textwheel
362 *
363 * @param string $letexte
364 * @param null $forcer
365 * @return string
366 */
367 function paragrapher($letexte, $forcer = true) {
368 return $letexte;
369 }
370
371 /**
372 * Harmonise les retours chariots et mange les paragraphes HTML
373 *
374 * Ne sert plus
375 *
376 * @param string $letexte Texte
377 * @return string Texte
378 **/
379 function traiter_retours_chariots($letexte) {
380 $letexte = preg_replace(",\r\n?,S", "\n", $letexte);
381 $letexte = preg_replace(",<p[>[:space:]],iS", "\n\n\\0", $letexte);
382 $letexte = preg_replace(",</p[>[:space:]],iS", "\\0\n\n", $letexte);
383
384 return $letexte;
385 }
386
387
388 /**
389 * Transforme les raccourcis SPIP, liens et modèles d'un texte en code HTML
390 *
391 * Filtre à appliquer aux champs du type `#TEXTE*`
392 *
393 * @filtre
394 * @uses echappe_html()
395 * @uses expanser_liens()
396 * @uses traiter_raccourcis()
397 * @uses echappe_retour_modeles()
398 * @see typo()
399 *
400 * @param string $t
401 * Texte avec des raccourcis SPIP
402 * @param string|null $connect
403 * Nom du connecteur à la bdd
404 * @param array $env
405 * Environnement (pour les calculs de modèles)
406 * @return string $t
407 * Texte transformé
408 **/
409 function propre($t, $connect = null, $env = array()) {
410 // les appels directs a cette fonction depuis le php de l'espace
411 // prive etant historiquement ecrits sans argment $connect
412 // on utilise la presence de celui-ci pour distinguer les cas
413 // ou il faut passer interdire_script explicitement
414 // les appels dans les squelettes (de l'espace prive) fournissant un $connect
415 // ne seront pas perturbes
416 $interdire_script = false;
417 if (is_null($connect)) {
418 $connect = '';
419 $interdire_script = true;
420 }
421
422 if (!$t) {
423 return strval($t);
424 }
425
426 $t = echappe_html($t);
427 $t = expanser_liens($t, $connect, $env);
428 $t = traiter_raccourcis($t);
429 $t = echappe_retour_modeles($t, $interdire_script);
430
431 return $t;
432 }