Completed
Push — spip-3.0 ( 0bbd73...b24c06 )
by cam
06:45
created

distant.php ➔ valider_url_distante()   F

Complexity

Conditions 34
Paths 464

Size

Total Lines 84

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 34
nc 464
nop 2
dl 0
loc 84
rs 0.7443
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2016                                                *
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
 * Ce fichier gère l'obtention de données distantes
15
 *
16
 * @package SPIP\Core\Distant
17
**/
18
if (!defined('_ECRIRE_INC_VERSION')) return;
19
20
if (!defined('_INC_DISTANT_VERSION_HTTP')) define('_INC_DISTANT_VERSION_HTTP', "HTTP/1.0");
21
if (!defined('_INC_DISTANT_CONTENT_ENCODING')) define('_INC_DISTANT_CONTENT_ENCODING', "gzip");
22
if (!defined('_INC_DISTANT_USER_AGENT')) define('_INC_DISTANT_USER_AGENT', 'SPIP-' . $GLOBALS['spip_version_affichee'] . " (" . $GLOBALS['home_server'] . ")");
23
if (!defined('_INC_DISTANT_MAX_SIZE')) define('_INC_DISTANT_MAX_SIZE',2097152);
24
if (!defined('_INC_DISTANT_CONNECT_TIMEOUT')) define('_INC_DISTANT_CONNECT_TIMEOUT',10);
25
26
define('_REGEXP_COPIE_LOCALE', ',' .
27
       preg_replace('@^https?:@', 'https?:', $GLOBALS['meta']['adresse_site'])
28
       . "/?spip.php[?]action=acceder_document.*file=(.*)$,");
29
30
//@define('_COPIE_LOCALE_MAX_SIZE',2097152); // poids (inc/utils l'a fait)
31
32
/**
33
 * Crée au besoin la copie locale d'un fichier distant
34
 *
35
 * Prend en argument un chemin relatif au rep racine, ou une URL
36
 * Renvoie un chemin relatif au rep racine, ou false
37
 *
38
 * @link http://www.spip.net/4155
39
 *
40
 * @param $source
41
 * @param string $mode
42
 *   'test' - ne faire que tester
43
 *   'auto' - charger au besoin
44
 *   'modif' - Si deja present, ne charger que si If-Modified-Since
45
 *   'force' - charger toujours (mettre a jour)
46
 * @param string $local
0 ignored issues
show
Documentation introduced by
Should the type for parameter $local 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...
47
 *   permet de specifier le nom du fichier local (stockage d'un cache par exemple, et non document IMG)
48
 * @param int $taille_max
0 ignored issues
show
Documentation introduced by
Should the type for parameter $taille_max not be integer|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...
49
 *   taille maxi de la copie local, par defaut _COPIE_LOCALE_MAX_SIZE
50
 * @return bool|string
51
 */
52
function copie_locale($source, $mode='auto', $local=null, $taille_max=null){
53
54
	// si c'est la protection de soi-meme, retourner le path
55
	if ($mode !== 'force' AND preg_match(_REGEXP_COPIE_LOCALE, $source, $match)) {
56
		$source = substr(_DIR_IMG,strlen(_DIR_RACINE)) . urldecode($match[1]);
57
		return @file_exists($source) ? $source : false;
58
	}
59
60
	if (is_null($local))
61
		$local = fichier_copie_locale($source);
62 View Code Duplication
	else {
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...
63
		if (_DIR_RACINE AND strncmp(_DIR_RACINE, $local, strlen(_DIR_RACINE))==0) {
64
			$local = substr($local, strlen(_DIR_RACINE));
65
		}
66
	}
67
68
	// si $local = '' c'est un fichier refuse par fichier_copie_locale(),
69
	// par exemple un fichier qui ne figure pas dans nos documents ;
70
	// dans ce cas on n'essaie pas de le telecharger pour ensuite echouer
71
	if (!$local) return false;
72
73
	$localrac = _DIR_RACINE.$local;
74
	$t = ($mode=='force') ? false  : @file_exists($localrac);
75
76
	// test d'existence du fichier
77
	if ($mode=='test') return $t ? $local : '';
78
79
	// sinon voir si on doit/peut le telecharger
80
	if ($local==$source OR !preg_match(',^\w+://,', $source))
81
		return $local;
82
83
	if ($mode=='modif' OR !$t){
84
		// passer par un fichier temporaire unique pour gerer les echecs en cours de recuperation
85
		// et des eventuelles recuperations concurantes
86
		include_spip("inc/acces");
87
		if (!$taille_max) $taille_max = _COPIE_LOCALE_MAX_SIZE;
0 ignored issues
show
Bug Best Practice introduced by
The expression $taille_max of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
88
		$res = recuperer_page($source, $localrac, false, $taille_max, '', '', false, $t ? filemtime($localrac) : '');
89
		if (!$res) {
90
			if (!$t) // si $t c'est sans doute juste un not-modified-since qui fait renvoyer false
91
				spip_log("copie_locale : Echec recuperation $source sur $localrac",_LOG_INFO_IMPORTANTE);
92
			return $t ? $local : false;
93
		}
94
		spip_log("copie_locale : recuperation $source sur $localrac taille $res OK");
95
96
		// pour une eventuelle indexation
97
		pipeline('post_edition',
98
			array(
99
				'args' => array(
100
					'operation' => 'copie_locale',
101
					'source' => $source,
102
					'fichier' => $local
103
				),
104
				'data' => null
105
			)
106
		);
107
	}
108
109
	return $local;
110
}
111
112
/**
113
 * Valider qu'une URL d'un document distant est bien distante
114
 * et pas une url localhost qui permet d'avoir des infos sur le serveur
115
 * inspiree de https://core.trac.wordpress.org/browser/trunk/src/wp-includes/http.php?rev=36435#L500
116
 * 
117
 * @param string $url
118
 * @param array $known_hosts
119
 *   url/hosts externes connus et acceptes
120
 * @return false|string 
121
 *   url ou false en cas d'echec
122
 */
123
function valider_url_distante($url, $known_hosts = array()) {
124
	if (!function_exists('protocole_verifier')){
125
		include_spip('inc/filtres_mini');
126
	}
127
128
	if (!protocole_verifier($url, array('http', 'https'))) {
129
		return false;
130
	}
131
	
132
	$parsed_url = parse_url($url);
133
	if (!$parsed_url or empty($parsed_url['host']) ) {
134
		return false;
135
	}
136
137
	if (isset($parsed_url['user']) or isset($parsed_url['pass'])) {
138
		return false;
139
	}
140
141
	if (false !== strpbrk($parsed_url['host'], ':#?[]')) {
142
		return false;
143
	}
144
145
	if (!is_array($known_hosts)) {
146
		$known_hosts = array($known_hosts);
147
	}
148
	$known_hosts[] = $GLOBALS['meta']['adresse_site'];
149
	$known_hosts[] = url_de_base();
150
	$known_hosts = pipeline('declarer_hosts_distants', $known_hosts);
151
152
	$is_known_host = false;
153
	foreach ($known_hosts as $known_host) {
154
		$parse_known = parse_url($known_host);
155
		if ($parse_known
156
		  and strtolower($parse_known['host']) === strtolower($parsed_url['host'])) {
157
			$is_known_host = true;
158
			break;
159
		}
160
	}
161
162
	if (!$is_known_host) {
163
		$host = trim($parsed_url['host'], '.');
164
		if (preg_match('#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host)) {
165
			$ip = $host;
166
		} else {
167
			$ip = gethostbyname($host);
168
			if ($ip === $host) {
169
				// Error condition for gethostbyname()
170
				$ip = false;
171
			}
172
		}
173
		if ($ip) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ip of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
174
			$parts = array_map('intval', explode( '.', $ip ));
175
			if (127 === $parts[0] or 10 === $parts[0] or 0 === $parts[0]
176
			  or ( 172 === $parts[0] and 16 <= $parts[1] and 31 >= $parts[1] )
177
			  or ( 192 === $parts[0] && 168 === $parts[1] )
178
			) {
179
				return false;
180
			}
181
		}
182
	}
183
184
	if (empty($parsed_url['port'])) {
185
		return $url;
186
	}
187
188
	$port = $parsed_url['port'];
189
	if ($port === 80  or $port === 443  or $port === 8080) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $port (string) and 80 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
190
		return $url;
191
	}
192
193
	if ($is_known_host) {
194
		foreach ($known_hosts as $known_host) {
195
			$parse_known = parse_url($known_host);
196
			if ($parse_known
197
				and !empty($parse_known['port'])
198
			  and strtolower($parse_known['host']) === strtolower($parsed_url['host'])
199
			  and $parse_known['port'] == $port) {
200
				return $url;
201
			}
202
		}
203
	}
204
205
	return false;
206
}
207
208
// http://doc.spip.org/@prepare_donnees_post
209
function prepare_donnees_post($donnees, $boundary = '') {
210
211
	// permettre a la fonction qui a demande le post de formater elle meme ses donnees
212
	// pour un appel soap par exemple
213
	// l'entete est separe des donnees par un double retour a la ligne
214
	// on s'occupe ici de passer tous les retours lignes (\r\n, \r ou \n) en \r\n
215
	if (is_string($donnees) && strlen($donnees)){
216
		$entete = "";
217
		// on repasse tous les \r\n et \r en simples \n
218
		$donnees = str_replace("\r\n","\n",$donnees);
219
		$donnees = str_replace("\r","\n",$donnees);
220
		// un double retour a la ligne signifie la fin de l'entete et le debut des donnees
221
		$p = strpos($donnees, "\n\n");
222
		if ($p!==FALSE){
223
			$entete = str_replace("\n", "\r\n", substr($donnees, 0, $p+1));
224
			$donnees = substr($donnees, $p+2);
225
		}
226
		$chaine = str_replace("\n", "\r\n", $donnees);
227
	}
228
	else {
229
		/* boundary automatique */
230
		// Si on a plus de 500 octects de donnees, on "boundarise"
231
		if ($boundary===''){
232
			$taille = 0;
233
			foreach ($donnees as $cle => $valeur){
234
				if (is_array($valeur)){
235
					foreach ($valeur as $val2){
236
						$taille += strlen($val2);
237
					}
238
				} else {
239
					// faut-il utiliser spip_strlen() dans inc/charsets ?
240
					$taille += strlen($valeur);
241
				}
242
			}
243
			if ($taille>500){
244
				$boundary = substr(md5(rand() . 'spip'), 0, 8);
245
			}
246
		}
247
248
		if (is_string($boundary) and strlen($boundary)){
249
			// fabrique une chaine HTTP pour un POST avec boundary
250
			$entete = "Content-Type: multipart/form-data; boundary=$boundary\r\n";
251
			$chaine = '';
252
			if (is_array($donnees)) {
253
				foreach ($donnees as $cle => $valeur) {
254
					if (is_array($valeur)) {
255
						foreach ($valeur as $val2) {
256
							$chaine .= "\r\n--$boundary\r\n";
257
							$chaine .= "Content-Disposition: form-data; name=\"{$cle}[]\"\r\n";
258
							$chaine .= "\r\n";
259
							$chaine .= $val2;
260
						}
261
					} else {
262
						$chaine .= "\r\n--$boundary\r\n";
263
						$chaine .= "Content-Disposition: form-data; name=\"$cle\"\r\n";
264
						$chaine .= "\r\n";
265
						$chaine .= $valeur;
266
					}
267
				}
268
				$chaine .= "\r\n--$boundary\r\n";
269
			}
270
		} else {
271
			// fabrique une chaine HTTP simple pour un POST
272
			$entete = 'Content-Type: application/x-www-form-urlencoded'."\r\n";
273
			$chaine = array();
274
			if (is_array($donnees)) {
275
				foreach ($donnees as $cle => $valeur) {
276
					if (is_array($valeur)) {
277
						foreach ($valeur as $val2) {
278
							$chaine[] = rawurlencode($cle).'[]='.rawurlencode($val2);
279
						}
280
					} else {
281
						$chaine[] = rawurlencode($cle).'='.rawurlencode($valeur);
282
					}
283
				}
284
				$chaine = implode('&', $chaine);
285
			} else {
286
				$chaine = $donnees;
287
			}
288
		}
289
	}
290
	return array($entete, $chaine);
291
}
292
293
/**
294
 * Convertir une URL dont le host est en utf8 en ascii
295
 * Utilise la librairie https://github.com/phlylabs/idna-convert/tree/v0.9.1
296
 * dans sa derniere version compatible toutes version PHP 5
297
 * La fonction PHP idn_to_ascii depend d'un package php5-intl et est rarement disponible
298
 *
299
 * @param string $url_idn
300
 * @return array|string
301
 */
302
function url_to_ascii($url_idn) {
303
304
	if ($parts = parse_url($url_idn)) {
305
		$host = $parts['host'];
306
		if (!preg_match(',^[a-z0-9_\.\-]+$,i', $host)) {
307
			include_spip('inc/idna_convert.class');
308
			$IDN = new idna_convert();
309
			$host_ascii = $IDN->encode($host);
310
			$url_idn = explode($host, $url_idn, 2);
311
			$url_idn = implode($host_ascii, $url_idn);
312
		}
313
	}
314
315
	return $url_idn;
316
}
317
318
//
319
// Recupere une page sur le net
320
// et au besoin l'encode dans le charset local
321
//
322
// options : get_headers si on veut recuperer les entetes
323
// taille_max : arreter le contenu au-dela (0 = seulement les entetes ==>HEAD)
324
// Par defaut taille_max = 1Mo.
325
// datas, une chaine ou un tableau pour faire un POST de donnees
326
// boundary, pour forcer l'envoi par cette methode
327
// et refuser_gz pour forcer le refus de la compression (cas des serveurs orthographiques)
328
// date_verif, un timestamp unix pour arreter la recuperation si la page distante n'a pas ete modifiee depuis une date donnee
329
// uri_referer, preciser un referer different
330
// Le second argument ($trans) :
331
// * si c'est une chaine longue, alors c'est un nom de fichier
332
//   dans lequel on ecrit directement la page
333
// * si c'est true/null ca correspond a une demande d'encodage/charset
334
// http://doc.spip.org/@recuperer_page
335
function recuperer_page($url, $trans = false, $get_headers = false,
336
                        $taille_max = null, $datas = '', $boundary = '', $refuser_gz = false,
337
                        $date_verif = '', $uri_referer = ''){
338
	$gz = false;
0 ignored issues
show
Unused Code introduced by
$gz 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...
339
340
	// $copy = copier le fichier ?
341
	$copy = (is_string($trans) AND strlen($trans)>5); // eviter "false" :-)
342
343
	if (is_null($taille_max))
344
		$taille_max = $copy ? _COPIE_LOCALE_MAX_SIZE : _INC_DISTANT_MAX_SIZE;
345
346
	// Accepter les URLs au format feed:// ou qui ont oublie le http://
347
	$url = preg_replace(',^feed://,i', 'http://', $url);
348
	if (!preg_match(',^[a-z]+://,i', $url)) $url = 'http://' . $url;
349
	$url = url_to_ascii($url);
350
351
	if ($taille_max==0)
352
		$get = 'HEAD';
353
	else
354
		$get = 'GET';
355
356
	if (!empty($datas)) {
357
		$get = 'POST';
358
		list($type, $postdata) = prepare_donnees_post($datas, $boundary);
359
		$datas = $type . 'Content-Length: ' . strlen($postdata) . "\r\n\r\n" . $postdata;
360
	}
361
362
	// dix tentatives maximum en cas d'entetes 301...
363
	for ($i = 0; $i<10; $i++){
364
		$url = recuperer_lapage($url, $trans, $get, $taille_max, $datas, $refuser_gz, $date_verif, $uri_referer);
0 ignored issues
show
Bug introduced by
It seems like $trans can also be of type string; however, recuperer_lapage() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
365
		if (!$url) return false;
366
		if (is_array($url)){
367
			list($headers, $result) = $url;
368
			return ($get_headers ? $headers . "\n" : '') . $result;
369
		} else spip_log("recuperer page recommence sur $url");
370
	}
371
}
372
373
// args comme ci-dessus (presque)
374
// retourne l'URL en cas de 301, un tableau (entete, corps) si ok, false sinon
375
// si $trans est null -> on ne veut que les headers
376
// si $trans est une chaine, c'est un nom de fichier pour ecrire directement dedans
377
// http://doc.spip.org/@recuperer_lapage
378
function recuperer_lapage($url, $trans = false, $get = 'GET', $taille_max = _INC_DISTANT_MAX_SIZE, $datas = '', $refuser_gz = false, $date_verif = '', $uri_referer = ''){
379
	// $copy = copier le fichier ?
380
	$copy = (is_string($trans) AND strlen($trans)>5); // eviter "false" :-)
381
382
	// si on ecrit directement dans un fichier, pour ne pas manipuler
383
	// en memoire refuser gz
384
	if ($copy)
385
		$refuser_gz = true;
386
387
	// ouvrir la connexion et envoyer la requete et ses en-tetes
388
	list($f, $fopen) = init_http($get, $url, $refuser_gz, $uri_referer, $datas, _INC_DISTANT_VERSION_HTTP, $date_verif);
389
	if (!$f){
390
		spip_log("ECHEC init_http $url");
391
		return false;
392
	}
393
394
	$result = '';
395
	// Sauf en fopen, envoyer le flux d'entree
396
	// et recuperer les en-tetes de reponses
397
	if ($fopen)
398
		$headers = '';
399
	else {
400
		$headers = recuperer_entetes($f, $date_verif);
401
		if (is_numeric($headers)){
402
			fclose($f);
403
			// Chinoisierie inexplicable pour contrer
404
			// les actions liberticides de l'empire du milieu
405
			if ($headers){
406
				spip_log("HTTP status $headers pour $url");
407
				return false;
408
			}
409
			$t = @parse_url($url);
410
			$host = $t['host'];
411
			if (!need_proxy($host)
412
				AND $result = @file_get_contents($url)){
413
				// on peuple les headers de vide et on continue
414
				$headers = array('');
415
			}
416
			else
417
				return false;
418
		}
419
		if (!is_array($headers)){ // cas Location
420
			fclose($f);
421
			include_spip('inc/filtres');
422
			return suivre_lien($url, $headers);
423
		}
424
		$headers = join('', $headers);
425
	}
426
427
	if ($trans===NULL) return array($headers, '');
428
429
	// s'il faut deballer, le faire via un fichier temporaire
430
	// sinon la memoire explose pour les gros flux
431
432
	$gz = preg_match(",\bContent-Encoding: .*gzip,is", $headers) ?
433
		(_DIR_TMP . md5(uniqid(mt_rand())) . '.tmp.gz') : '';
434
435
#	spip_log("entete ($trans $copy $gz)\n$headers");
436
	if (!$result){
437
		$result = recuperer_body($f, $taille_max, $gz ? $gz : ($copy ? $trans : ''));
438
		fclose($f);
439
	}
440
	if (!$result)
441
		return array($headers, $result);
442
443
	// Decompresser au besoin
444
	if ($gz){
445
		$result = join('', gzfile($gz));
446
		supprimer_fichier($gz);
447
	}
448
	// Faut-il l'importer dans notre charset local ?
449
	if ($trans===true){
450
		include_spip('inc/charsets');
451
		$result = transcoder_page($result, $headers);
452
	}
453
454
	return array($headers, $result);
455
}
456
457
// http://doc.spip.org/@recuperer_body
458
function recuperer_body($f, $taille_max = _INC_DISTANT_MAX_SIZE, $fichier = ''){
459
	$taille = 0;
460
	$result = '';
461
	$fp = false;
462
	if ($fichier){
463
		include_spip("inc/acces");
464
		$tmpfile = "$fichier.".creer_uniqid().".tmp";
465
		$fp = spip_fopen_lock($tmpfile, 'w', LOCK_EX);
466
		if (!$fp AND file_exists($fichier)){
467
			return filesize($fichier);
468
		}
469
		if (!$fp)
470
			return false;
471
		$result = 0; // on renvoie la taille du fichier
472
	}
473
	while (!feof($f) AND $taille<$taille_max){
474
		$res = fread($f, 16384);
475
		$taille += strlen($res);
476
		if ($fp){
477
			fwrite($fp, $res);
478
			$result = $taille;
479
		}
480
		else
481
			$result .= $res;
482
	}
483
	if ($fp){
484
		spip_fclose_unlock($fp);
485
		spip_unlink($fichier);
486
		@rename($tmpfile, $fichier);
0 ignored issues
show
Bug introduced by
The variable $tmpfile 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...
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
487
		if (!file_exists($fichier))
488
			return false;
489
	}
490
	return $result;
491
}
492
493
// Lit les entetes de reponse HTTP sur la socket $f et retourne:
494
// la valeur (chaine) de l'en-tete Location si on l'a trouvee
495
// la valeur (numerique) du statut si different de 200, notamment Not-Modified
496
// le tableau des entetes dans tous les autres cas
497
498
// http://doc.spip.org/@recuperer_entetes
499
function recuperer_entetes($f, $date_verif = ''){
500
	$s = @trim(fgets($f, 16384));
501
502
	if (!preg_match(',^HTTP/[0-9]+\.[0-9]+ ([0-9]+),', $s, $r)){
503
		return 0;
504
	}
505
	$status = intval($r[1]);
506
	$headers = array();
507
	$not_modif = $location = false;
508
	while ($s = trim(fgets($f, 16384))){
509
		$headers[] = $s . "\n";
510
		preg_match(',^([^:]*): *(.*)$,i', $s, $r);
511
		list(, $d, $v) = $r;
512
		if (strtolower(trim($d))=='location' AND $status>=300 AND $status<400){
513
			$location = $v;
514
		}
515
		elseif ($date_verif AND ($d=='Last-Modified')) {
516
			if ($date_verif>=strtotime($v)){
517
				//Cas ou la page distante n'a pas bouge depuis
518
				//la derniere visite
519
				$not_modif = true;
520
			}
521
		}
522
	}
523
524
	if ($location)
525
		return $location;
526
	if ($status!=200 or $not_modif)
527
		return $status;
528
	return $headers;
529
}
530
531
// Si on doit conserver une copie locale des fichiers distants, autant que ca
532
// soit a un endroit canonique -- si ca peut etre bijectif c'est encore mieux,
533
// mais la tout de suite je ne trouve pas l'idee, etant donne les limitations
534
// des filesystems
535
// http://doc.spip.org/@nom_fichier_copie_locale
536
function nom_fichier_copie_locale($source, $extension){
537
	include_spip('inc/documents');
538
539
	$d = creer_repertoire_documents('distant'); # IMG/distant/
540
	$d = sous_repertoire($d, $extension); # IMG/distant/pdf/
541
542
	// on se place tout le temps comme si on etait a la racine
543
	if (_DIR_RACINE)
544
		$d = preg_replace(',^' . preg_quote(_DIR_RACINE) . ',', '', $d);
545
546
	$m = md5($source);
547
548
	return $d
549
		. substr(preg_replace(',[^\w-],', '', basename($source)) . '-' . $m, 0, 12)
550
		. substr($m, 0, 4)
551
		. ".$extension";
552
}
553
554
//
555
// Donne le nom de la copie locale de la source
556
//
557
// http://doc.spip.org/@fichier_copie_locale
558
function fichier_copie_locale($source){
559
	// Si c'est deja local pas de souci
560
	if (!preg_match(',^\w+://,', $source)){
561
		if (_DIR_RACINE)
562
			$source = preg_replace(',^' . preg_quote(_DIR_RACINE) . ',', '', $source);
563
		return $source;
564
	}
565
566
	// optimisation : on regarde si on peut deviner l'extension dans l'url et si le fichier
567
	// a deja ete copie en local avec cette extension
568
	// dans ce cas elle est fiable, pas la peine de requeter en base
569
	$path_parts = pathinfo($source);
570
	$ext = $path_parts ? $path_parts['extension'] : '';
571
	if ($ext
572
	AND preg_match(',^\w+$,', $ext) // pas de php?truc=1&...
573
	AND $f = nom_fichier_copie_locale($source, $ext)
574
	AND file_exists(_DIR_RACINE . $f)
575
	)
576
		return $f;
577
578
579
	// Si c'est deja dans la table des documents,
580
	// ramener le nom de sa copie potentielle
581
582
	$ext = sql_getfetsel("extension", "spip_documents", "fichier=" . sql_quote($source) . " AND distant='oui' AND extension <> ''");
583
584
585
	if ($ext) return nom_fichier_copie_locale($source, $ext);
586
587
	// voir si l'extension indiquee dans le nom du fichier est ok
588
	// et si il n'aurait pas deja ete rapatrie
589
590
	$ext = $path_parts ? $path_parts['extension'] : '';
591
592
	if ($ext AND sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))){
593
		$f = nom_fichier_copie_locale($source, $ext);
594
		if (file_exists(_DIR_RACINE . $f))
595
			return $f;
596
	}
597
598
	// Ping  pour voir si son extension est connue et autorisee
599
	// avec mise en cache du resultat du ping
600
601
	$cache = sous_repertoire(_DIR_CACHE, 'rid') . md5($source);
602
	if (!@file_exists($cache)
603
		OR !$path_parts = @unserialize(spip_file_get_contents($cache))
604
		OR _request('var_mode')=='recalcul'
605
	){
606
		$path_parts = recuperer_infos_distantes($source, 0, false);
607
		ecrire_fichier($cache, serialize($path_parts));
608
	}
609
	$ext = $path_parts ? $path_parts['extension'] : '';
610
	if ($ext AND sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))){
611
		return nom_fichier_copie_locale($source, $ext);
612
	}
613
	spip_log("pas de copie locale pour $source");
614
}
615
616
617
// Recuperer les infos d'un document distant, sans trop le telecharger
618
#$a['body'] = chaine
619
#$a['type_image'] = booleen
620
#$a['titre'] = chaine
621
#$a['largeur'] = intval
622
#$a['hauteur'] = intval
623
#$a['taille'] = intval
624
#$a['extension'] = chaine
625
#$a['fichier'] = chaine
626
#$a['mime_type'] = chaine
627
628
// http://doc.spip.org/@recuperer_infos_distantes
629
function recuperer_infos_distantes($source, $max = 0, $charger_si_petite_image = true){
630
631
	// pas la peine de perdre son temps
632
	if (!tester_url_absolue($source)) {
633
		return false;
634
	}
635
	
636
	# charger les alias des types mime
637
	include_spip('base/typedoc');
638
	global $mime_alias;
639
640
	$a = array();
641
	$mime_type = '';
642
	// On va directement charger le debut des images et des fichiers html,
643
	// de maniere a attrapper le maximum d'infos (titre, taille, etc). Si
644
	// ca echoue l'utilisateur devra les entrer...
645
	if ($headers = recuperer_page($source, false, true, $max, '', '', true)){
646
		list($headers, $a['body']) = preg_split(',\n\n,', $headers, 2);
647
648
		if (preg_match(",\nContent-Type: *([^[:space:];]*),i", "\n$headers", $regs))
649
			$mime_type = (trim($regs[1]));
650
		else
651
			$mime_type = ''; // inconnu
652
653
		// Appliquer les alias
654
		while (isset($mime_alias[$mime_type]))
655
			$mime_type = $mime_alias[$mime_type];
656
657
		// Si on a un mime-type insignifiant
658
		// text/plain,application/octet-stream ou vide
659
		// c'est peut-etre que le serveur ne sait pas
660
		// ce qu'il sert ; on va tenter de detecter via l'extension de l'url
661
		// ou le Content-Disposition: attachment; filename=...
662
		$t = null;
663
		if (in_array($mime_type, array('text/plain', '', 'application/octet-stream'))){
664
			if (!$t
665
				AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $rext)
666
			){
667
				$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text'));
668
			}
669 View Code Duplication
			if (!$t
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...
670
				AND preg_match(",^Content-Disposition:\s*attachment;\s*filename=(.*)$,Uims", $headers, $m)
671
				AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $m[1], $rext)
672
			){
673
				$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text'));
674
			}
675
		}
676
677
		// Autre mime/type (ou text/plain avec fichier d'extension inconnue)
678
		if (!$t)
679
			$t = sql_fetsel("extension", "spip_types_documents", "mime_type=" . sql_quote($mime_type));
680
681
		// Toujours rien ? (ex: audio/x-ogg au lieu de application/ogg)
682
		// On essaie de nouveau avec l'extension
683 View Code Duplication
		if (!$t
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...
684
			AND $mime_type!='text/plain'
685
			AND preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $rext)
686
		){
687
			$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1],'','text')); # eviter xxx.3 => 3gp (> SPIP 3)
688
		}
689
690
691
		if ($t){
0 ignored issues
show
Bug Best Practice introduced by
The expression $t 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...
692
			spip_log("mime-type $mime_type ok, extension " . $t['extension']);
693
			$a['extension'] = $t['extension'];
694
		} else {
695
			# par defaut on retombe sur '.bin' si c'est autorise
696
			spip_log("mime-type $mime_type inconnu");
697
			$t = sql_fetsel("extension", "spip_types_documents", "extension='bin'");
698
			if (!$t) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $t 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...
699
			$a['extension'] = $t['extension'];
700
		}
701
702
		if (preg_match(",\nContent-Length: *([^[:space:]]*),i",
703
			"\n$headers", $regs)
704
		)
705
			$a['taille'] = intval($regs[1]);
706
	}
707
708
	// Echec avec HEAD, on tente avec GET
709
	if (!$a AND !$max){
710
		spip_log("tenter GET $source");
711
		$a = recuperer_infos_distantes($source, _INC_DISTANT_MAX_SIZE);
712
	}
713
714
	// si on a rien trouve pas la peine d'insister
715
	if (!$a) {
716
		return false;
717
	}
718
719
	// S'il s'agit d'une image pas trop grosse ou d'un fichier html, on va aller
720
	// recharger le document en GET et recuperer des donnees supplementaires...
721
	if (preg_match(',^image/(jpeg|gif|png|swf),', $mime_type)){
722
		if ($max==0
723
			AND $a['taille']<_INC_DISTANT_MAX_SIZE
724
				AND (strpos($GLOBALS['meta']['formats_graphiques'], $a['extension'])!==false)
725
					AND $charger_si_petite_image
726
		){
727
			$a = recuperer_infos_distantes($source, _INC_DISTANT_MAX_SIZE);
728
		}
729
		else if ($a['body']){
730
			$a['fichier'] = _DIR_RACINE . nom_fichier_copie_locale($source, $a['extension']);
731
			ecrire_fichier($a['fichier'], $a['body']);
732
			$size_image = @getimagesize($a['fichier']);
733
			$a['largeur'] = intval($size_image[0]);
734
			$a['hauteur'] = intval($size_image[1]);
735
			$a['type_image'] = true;
736
		}
737
	}
738
739
	// Fichier swf, si on n'a pas la taille, on va mettre 425x350 par defaut
740
	// ce sera mieux que 0x0
741
	if ($a AND $a['extension']=='swf'
742
		AND !$a['largeur']
743
	){
744
		$a['largeur'] = 425;
745
		$a['hauteur'] = 350;
746
	}
747
748
	if ($mime_type=='text/html'){
749
		include_spip('inc/filtres');
750
		$page = recuperer_page($source, true, false, _INC_DISTANT_MAX_SIZE);
751
		if (preg_match(',<title>(.*?)</title>,ims', $page, $regs))
752
			$a['titre'] = corriger_caracteres(trim($regs[1]));
753
		if (!$a['taille']) $a['taille'] = strlen($page); # a peu pres
754
	}
755
	$a['mime_type']=$mime_type;
756
757
	return $a;
758
}
759
760
761
/**
762
 * Tester si un host peut etre recuperer directement ou doit passer par un proxy
763
 * on peut passer en parametre le proxy et la liste des host exclus,
764
 * pour les besoins des tests, lors de la configuration
765
 *
766
 * @param string $host
767
 * @param string $http_proxy
0 ignored issues
show
Documentation introduced by
Should the type for parameter $http_proxy 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...
768
 * @param string $http_noproxy
0 ignored issues
show
Documentation introduced by
Should the type for parameter $http_noproxy 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...
769
 * @return string
770
 */
771
function need_proxy($host, $http_proxy = null, $http_noproxy = null){
772
	if (is_null($http_proxy))
773
		$http_proxy = @$GLOBALS['meta']["http_proxy"];
774
	if (is_null($http_noproxy))
775
		$http_noproxy = @$GLOBALS['meta']["http_noproxy"];
776
777
	$domain = substr($host, strpos($host, '.'));
778
779
	return ($http_proxy
780
		AND (strpos(" $http_noproxy ", " $host ")===false
781
		AND (strpos(" $http_noproxy ", " $domain ")===false)))
782
		? $http_proxy : '';
783
}
784
785
//
786
// Lance une requete HTTP avec entetes
787
// retourne le descripteur sur lequel lire la reponse
788
//
789
// http://doc.spip.org/@init_http
790
function init_http($method, $url, $refuse_gz = false, $referer = '', $datas = "", $vers = "HTTP/1.0", $date = ''){
791
	$user = $via_proxy = $proxy_user = '';
0 ignored issues
show
Unused Code introduced by
$proxy_user 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...
Unused Code introduced by
$via_proxy 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...
792
	$fopen = false;
793
794
	$t = @parse_url($url);
795
	$host = $t['host'];
796
	if ($t['scheme']=='http'){
797
		$scheme = 'http';
798
		$noproxy = '';
799
	} elseif ($t['scheme']=='https') {
800
		$scheme = 'ssl';
801
		$noproxy = 'ssl://';
802 View Code Duplication
		if (!isset($t['port']) || !($port = $t['port'])) $t['port'] = 443;
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...
803
	}
804
	else {
805
		$scheme = $t['scheme'];
806
		$noproxy = $scheme . '://';
807
	}
808
	if (isset($t['user']))
809
		$user = array($t['user'], $t['pass']);
810
811 View Code Duplication
	if (!isset($t['port']) || !($port = $t['port'])) $port = 80;
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...
812 View Code Duplication
	if (!isset($t['path']) || !($path = $t['path'])) $path = "/";
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...
813
	if (@$t['query']) $path .= "?" . $t['query'];
814
815
	$f = lance_requete($method, $scheme, $user, $host, $path, $port, $noproxy, $refuse_gz, $referer, $datas, $vers, $date);
816
	if (!$f){
817
		// fallback : fopen
818
		if (!need_proxy($host)
819
		  AND !_request('tester_proxy')
820
		  AND (!isset($GLOBALS['inc_distant_allow_fopen']) OR $GLOBALS['inc_distant_allow_fopen'])){
821
			$f = @fopen($url, "rb");
822
			spip_log("connexion vers $url par simple fopen");
823
			$fopen = true;
824
		}
825
		else
826
			$f = false;
827
		// echec total
828
	}
829
830
	return array($f, $fopen);
831
}
832
833
// http://doc.spip.org/@lance_requete
834
function lance_requete($method, $scheme, $user, $host, $path, $port, $noproxy, $refuse_gz = false, $referer = '', $datas = "", $vers = "HTTP/1.0", $date = ''){
835
836
	$proxy_user = '';
837
	$http_proxy = need_proxy($host);
838
	if ($user) $user = urlencode($user[0]) . ":" . urlencode($user[1]);
839
840
	$connect = '';
841
	if ($http_proxy) {
842
		if (defined('_PROXY_HTTPS_VIA_CONNECT') and in_array($scheme , array('tls','ssl'))) {
843
			$path_host = (!$user ? '' : "$user@") . $host . (($port != 80) ? ":$port" : '');
844
			$connect = 'CONNECT ' . $path_host . " $vers\r\n"
845
				. "Host: $path_host\r\n"
846
				. "Proxy-Connection: Keep-Alive\r\n";
847
		} else {
848
			$path = (in_array($scheme , array('tls','ssl')) ? 'https://' : "$scheme://")
849
				. (!$user ? '' : "$user@")
850
				. "$host" . (($port!=80) ? ":$port" : "") . $path;
851
		}
852
		$t2 = @parse_url($http_proxy);
853
		$first_host = $t2['host'];
854
		if (!($port = $t2['port'])) $port = 80;
855
		if ($t2['user'])
856
			$proxy_user = base64_encode($t2['user'] . ":" . $t2['pass']);
857
	}
858
	else
859
		$first_host = $noproxy . $host;
860
861
	if ($connect){
862
		$streamContext = stream_context_create(array('ssl' => array('verify_peer' => false, 'allow_self_signed' => true)));
863
		$f = @stream_socket_client("tcp://$first_host:$port", $nError, $sError, _INC_DISTANT_CONNECT_TIMEOUT, STREAM_CLIENT_CONNECT, $streamContext);
864
		spip_log("Recuperer $path sur $first_host:$port par $f (via CONNECT)","connect");
865
		if (!$f) return false;
866
		stream_set_timeout($f, _INC_DISTANT_CONNECT_TIMEOUT);
867
868
		fputs($f, $connect);
869
		fputs($f, "\r\n");
870
		$res = fread($f, 1024);
871
		if (!$res
872
		  OR !count($res = explode(' ',$res))
873
		  OR $res[1]!=='200'){
874
			spip_log("Echec CONNECT sur $first_host:$port","connect"._LOG_INFO_IMPORTANTE);
875
			fclose($f);
876
			return false;
877
		}
878
		// important, car sinon on lit trop vite et les donnees ne sont pas encore dispo
879
		stream_set_blocking($f, true);
880
		// envoyer le handshake
881
		stream_socket_enable_crypto($f, true,	STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
882
		spip_log("OK CONNECT sur $first_host:$port","connect");
883
	}
884
	else {
885
		$f = @fsockopen($first_host, $port, $errno, $errstr, _INC_DISTANT_CONNECT_TIMEOUT);
886
		spip_log("Recuperer $path sur $first_host:$port par $f");
887
		if (!$f) {
888
			spip_log("Erreur connexion $errno $errstr",_LOG_ERREUR);
889
			return false;
890
		}
891
		stream_set_timeout($f, _INC_DISTANT_CONNECT_TIMEOUT);
892
	}
893
894
	$site = $GLOBALS['meta']["adresse_site"];
895
896
	$req = "$method $path $vers\r\n"
897
		. "Host: $host\r\n"
898
		. "User-Agent: " . _INC_DISTANT_USER_AGENT . "\r\n"
899
		. ($refuse_gz ? '' : ("Accept-Encoding: " . _INC_DISTANT_CONTENT_ENCODING . "\r\n"))
900
		. (!$site ? '' : "Referer: $site/$referer\r\n")
901
		. (!$date ? '' : "If-Modified-Since: " . (gmdate("D, d M Y H:i:s", $date) . " GMT\r\n"))
902
		. (!$user ? '' : ("Authorization: Basic " . base64_encode($user) . "\r\n"))
903
		. (!$proxy_user ? '' : "Proxy-Authorization: Basic $proxy_user\r\n")
904
		. (!strpos($vers, '1.1') ? '' : "Keep-Alive: 300\r\nConnection: keep-alive\r\n");
905
906
#	spip_log("Requete\n$req");
907
	fputs($f, $req);
908
	fputs($f, $datas ? $datas : "\r\n");
909
	return $f;
910
}
911
912