Newer
Older
<?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2015 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
/**
* Ce fichier gère l'obtention de données distantes
*
* @package SPIP\Core\Distant
cerdic
a validé
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
cerdic
a validé
if (!defined('_INC_DISTANT_VERSION_HTTP')) {
define('_INC_DISTANT_VERSION_HTTP', "HTTP/1.0");
}
if (!defined('_INC_DISTANT_CONTENT_ENCODING')) {
define('_INC_DISTANT_CONTENT_ENCODING', "gzip");
}
if (!defined('_INC_DISTANT_USER_AGENT')) {
define('_INC_DISTANT_USER_AGENT', 'SPIP-' . $GLOBALS['spip_version_affichee'] . " (" . $GLOBALS['home_server'] . ")");
}
if (!defined('_INC_DISTANT_MAX_SIZE')) {
define('_INC_DISTANT_MAX_SIZE', 2097152);
}
if (!defined('_INC_DISTANT_CONNECT_TIMEOUT')) {
define('_INC_DISTANT_CONNECT_TIMEOUT', 10);
}
define('_REGEXP_COPIE_LOCALE', ',' .
preg_replace('@^https?:@', 'https?:',
(isset($GLOBALS['meta']["adresse_site"]) ? $GLOBALS['meta']["adresse_site"] : ''))
. "/?spip.php[?]action=acceder_document.*file=(.*)$,");
cerdic
a validé
//@define('_COPIE_LOCALE_MAX_SIZE',2097152); // poids (inc/utils l'a fait)
/**
* Crée au besoin la copie locale d'un fichier distant
*
* Prend en argument un chemin relatif au rep racine, ou une URL
* Renvoie un chemin relatif au rep racine, ou false
*
* @link http://www.spip.net/4155
*
* @param string $mode
* - 'test' - ne faire que tester
* - 'auto' - charger au besoin
* - 'modif' - Si deja present, ne charger que si If-Modified-Since
* - 'force' - charger toujours (mettre a jour)
* @param string $local
* permet de specifier le nom du fichier local (stockage d'un cache par exemple, et non document IMG)
* @param int $taille_max
* taille maxi de la copie local, par defaut _COPIE_LOCALE_MAX_SIZE
* @return bool|string
*/
cerdic
a validé
function copie_locale($source, $mode = 'auto', $local = null, $taille_max = null) {
// si c'est la protection de soi-meme, retourner le path
marcimat
a validé
if ($mode !== 'force' and preg_match(_REGEXP_COPIE_LOCALE, $source, $match)) {
$source = substr(_DIR_IMG, strlen(_DIR_RACINE)) . urldecode($match[1]);
cerdic
a validé
return @file_exists($source) ? $source : false;
}
cerdic
a validé
if (is_null($local)) {
$local = fichier_copie_locale($source);
cerdic
a validé
} else {
marcimat
a validé
if (_DIR_RACINE and strncmp(_DIR_RACINE, $local, strlen(_DIR_RACINE)) == 0) {
$local = substr($local, strlen(_DIR_RACINE));
}
}
// si $local = '' c'est un fichier refuse par fichier_copie_locale(),
// par exemple un fichier qui ne figure pas dans nos documents ;
// dans ce cas on n'essaie pas de le telecharger pour ensuite echouer
cerdic
a validé
if (!$local) {
return false;
}
cerdic
a validé
$t = ($mode == 'force') ? false : @file_exists($localrac);
// test d'existence du fichier
cerdic
a validé
if ($mode == 'test') {
return $t ? $local : '';
}
// sinon voir si on doit/peut le telecharger
marcimat
a validé
if ($local == $source or !tester_url_absolue($source)) {
return $local;
cerdic
a validé
}
marcimat
a validé
if ($mode == 'modif' or !$t) {
// passer par un fichier temporaire unique pour gerer les echecs en cours de recuperation
// et des eventuelles recuperations concurantes
include_spip("inc/acces");
cerdic
a validé
if (!$taille_max) {
$taille_max = _COPIE_LOCALE_MAX_SIZE;
}
cerdic
a validé
$res = recuperer_url($source,
array('file' => $localrac, 'taille_max' => $taille_max, 'if_modified_since' => $t ? filemtime($localrac) : ''));
marcimat
a validé
if (!$res or (!$res["length"] and $res["status"] != 304)) {
cerdic
a validé
spip_log("copie_locale : Echec recuperation $source sur $localrac status : " . $res["status"],
_LOG_INFO_IMPORTANTE);
}
if (!$res['length']) {
// si $t c'est sans doute juste un not-modified-since
return $t ? $local : false;
}
spip_log("copie_locale : recuperation $source sur $localrac taille " . $res['length'] . " OK");
// pour une eventuelle indexation
pipeline('post_edition',
array(
'args' => array(
'operation' => 'copie_locale',
'source' => $source,
'fichier' => $local,
'http_res' => $res['length'],
}
return $local;
}
/**
* Preparer les donnes pour un POST
* si $donnees est une chaine
* - charge a l'envoyeur de la boundariser, de gerer le Content-Type etc...
* - on traite les retour ligne pour les mettre au bon format
* - on decoupe en entete/corps (separes par ligne vide)
* si $donnees est un tableau
* - structuration en chaine avec boundary si necessaire ou fournie et bon Content-Type
*
* @param string|array $donnees
* @param string $boundary
* @return array
* entete,corps
*/
cerdic
a validé
function prepare_donnees_post($donnees, $boundary = '') {
// permettre a la fonction qui a demande le post de formater elle meme ses donnees
// pour un appel soap par exemple
// l'entete est separe des donnees par un double retour a la ligne
// on s'occupe ici de passer tous les retours lignes (\r\n, \r ou \n) en \r\n
cerdic
a validé
if (is_string($donnees) && strlen($donnees)) {
$entete = "";
// on repasse tous les \r\n et \r en simples \n
$donnees = str_replace("\r\n", "\n", $donnees);
$donnees = str_replace("\r", "\n", $donnees);
// un double retour a la ligne signifie la fin de l'entete et le debut des donnees
cerdic
a validé
if ($p !== false) {
$entete = str_replace("\n", "\r\n", substr($donnees, 0, $p+1));
$donnees = substr($donnees, $p+2);
}
$chaine = str_replace("\n", "\r\n", $donnees);
/* boundary automatique */
// Si on a plus de 500 octects de donnees, on "boundarise"
cerdic
a validé
if ($boundary === '') {
cerdic
a validé
foreach ($donnees as $cle => $valeur) {
if (is_array($valeur)) {
foreach ($valeur as $val2) {
$taille += strlen($val2);
}
} else {
// faut-il utiliser spip_strlen() dans inc/charsets ?
$taille += strlen($valeur);
}
}
cerdic
a validé
if ($taille > 500) {
cerdic
a validé
if (is_string($boundary) and strlen($boundary)) {
// fabrique une chaine HTTP pour un POST avec boundary
$entete = "Content-Type: multipart/form-data; boundary=$boundary\r\n";
$chaine = '';
cerdic
a validé
if (is_array($donnees)) {
foreach ($donnees as $cle => $valeur) {
if (is_array($valeur)) {
foreach ($valeur as $val2) {
$chaine .= "\r\n--$boundary\r\n";
$chaine .= "Content-Disposition: form-data; name=\"{$cle}[]\"\r\n";
$chaine .= "\r\n";
$chaine .= $val2;
}
} else {
$chaine .= "\r\n--$boundary\r\n";
$chaine .= "Content-Disposition: form-data; name=\"$cle\"\r\n";
$chaine .= "\r\n";
$chaine .= $valeur;
}
}
JamesRezo
a validé
$chaine .= "\r\n--$boundary\r\n";
}
} else {
// fabrique une chaine HTTP simple pour un POST
$entete = 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
$chaine = array();
cerdic
a validé
if (is_array($donnees)) {
foreach ($donnees as $cle => $valeur) {
if (is_array($valeur)) {
foreach ($valeur as $val2) {
}
} else {
JamesRezo
a validé
}
}
$chaine = implode('&', $chaine);
} else {
$chaine = $donnees;
JamesRezo
a validé
}
}
cerdic
a validé
JamesRezo
a validé
return array($entete, $chaine);
}
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/**
* Récupère le contenu d'une URL
* au besoin encode son contenu dans le charset local
*
* @uses init_http()
* @uses recuperer_entetes()
* @uses recuperer_body()
* @uses transcoder_page()
*
* @param string $url
* @param array $options
* bool transcoder : true si on veut transcoder la page dans le charset du site
* string methode : Type de requête HTTP à faire (HEAD, GET ou POST)
* int taille_max : Arrêter le contenu au-delà (0 = seulement les entetes ==> requête HEAD). Par defaut taille_max = 1Mo ou 16Mo si copie dans un fichier
* string|array datas : Pour faire un POST de données (force la methode POST si non vide)
* string boundary : boundary pour formater les datas au format array
* bool refuser_gz : Pour forcer le refus de la compression (cas des serveurs orthographiques)
* int if_modified_since : Un timestamp unix pour arrêter la récuperation si la page distante n'a pas été modifiée depuis une date donnée
* string uri_referer : Pour préciser un référer différent
* string file : nom du fichier dans lequel copier le contenu
* int follow_location : nombre de redirections a suivre (0 pour ne rien suivre)
* string version_http : version du protocole HTTP a utiliser (par defaut defini par la constante _INC_DISTANT_VERSION_HTTP)
* @return array|bool
* false si echec
* array sinon :
* int status : le status de la page
* string headers : les entetes de la page
* string page : le contenu de la page (vide si copie dans un fichier)
* int last_modified : timestamp de derniere modification
* string location : url de redirection envoyee par la page
* string url : url reelle de la page recuperee
* int length : taille du contenu ou du fichier
*
* string file : nom du fichier si enregistre dans un fichier
*/
cerdic
a validé
function recuperer_url($url, $options = array()) {
$default = array(
'transcoder' => false,
'methode' => 'GET',
'taille_max' => null,
'datas' => '',
'boundary' => '',
'refuser_gz' => false,
'if_modified_since' => '',
'uri_referer' => '',
'file' => '',
'follow_location' => 10,
'version_http' => _INC_DISTANT_VERSION_HTTP,
);
// copier directement dans un fichier ?
$copy = $options['file'];
cerdic
a validé
if ($options['methode'] == "HEAD") {
$options['taille_max'] = 0;
cerdic
a validé
}
if (is_null($options['taille_max'])) {
$options['taille_max'] = $copy ? _COPIE_LOCALE_MAX_SIZE : _INC_DISTANT_MAX_SIZE;
cerdic
a validé
}
cerdic
a validé
if (!empty($options['datas'])) {
$options['methode'] = 'POST';
list($head, $postdata) = prepare_donnees_post($options['datas'], $options['boundary']);
cerdic
a validé
if (stripos($head, "Content-Length:") === false) {
$head .= 'Content-Length: ' . strlen($postdata);
cerdic
a validé
}
$options['datas'] = $head . "\r\n\r\n" . $postdata;
}
// Accepter les URLs au format feed:// ou qui ont oublie le http:// ou les urls relatives au protocole
$url = preg_replace(',^feed://,i', 'http://', $url);
cerdic
a validé
if (!tester_url_absolue($url)) {
$url = 'http://' . $url;
} elseif (strncmp($url, "//", 2) == 0) {
$url = 'http:' . $url;
}
cerdic
a validé
$result = array(
'status' => 0,
'headers' => '',
'page' => '',
'length' => 0,
'last_modified' => '',
'location' => '',
'url' => $url
);
// si on ecrit directement dans un fichier, pour ne pas manipuler en memoire refuser gz
marcimat
a validé
$refuser_gz = (($options['refuser_gz'] or $copy) ? true : false);
// ouvrir la connexion et envoyer la requete et ses en-tetes
cerdic
a validé
list($handle, $fopen) = init_http($options['methode'], $url, $refuser_gz, $options['uri_referer'], $options['datas'],
$options['version_http'], $options['if_modified_since']);
if (!$handle) {
spip_log("ECHEC init_http $url");
cerdic
a validé
return false;
}
// Sauf en fopen, envoyer le flux d'entree
// et recuperer les en-tetes de reponses
cerdic
a validé
if (!$fopen) {
$res = recuperer_entetes_complets($handle, $options['if_modified_since']);
cerdic
a validé
if (!$res) {
fclose($handle);
$t = @parse_url($url);
$host = $t['host'];
// Chinoisierie inexplicable pour contrer
// les actions liberticides de l'empire du milieu
if (!need_proxy($host)
marcimat
a validé
and $res = @file_get_contents($url)
cerdic
a validé
) {
$result['length'] = strlen($res);
cerdic
a validé
if ($copy) {
$result['page'] = $res;
}
$res = array(
'status' => 200,
);
cerdic
a validé
} else {
return false;
cerdic
a validé
}
marcimat
a validé
} elseif ($res['location'] and $options['follow_location']) {
$options['follow_location']--;
fclose($handle);
include_spip('inc/filtres');
$url = suivre_lien($url, $res['location']);
spip_log("recuperer_url recommence sur $url");
cerdic
a validé
cerdic
a validé
} elseif ($res['status'] !== 200) {
}
$result['status'] = $res['status'];
cerdic
a validé
if (isset($res['headers'])) {
$result['headers'] = $res['headers'];
cerdic
a validé
}
if (isset($res['last_modified'])) {
$result['last_modified'] = $res['last_modified'];
cerdic
a validé
}
if (isset($res['location'])) {
$result['location'] = $res['location'];
cerdic
a validé
}
}
// on ne veut que les entetes
marcimat
a validé
if (!$options['taille_max'] or $options['methode'] == 'HEAD' or $result['status'] == "304") {
return $result;
cerdic
a validé
}
// s'il faut deballer, le faire via un fichier temporaire
// sinon la memoire explose pour les gros flux
$gz = false;
cerdic
a validé
if (preg_match(",\bContent-Encoding: .*gzip,is", $result['headers'])) {
$gz = (_DIR_TMP . md5(uniqid(mt_rand())) . '.tmp.gz');
cerdic
a validé
}
// si on a pas deja recuperer le contenu par une methode detournee
cerdic
a validé
if (!$result['length']) {
$res = recuperer_body($handle, $options['taille_max'], $gz ? $gz : $copy);
fclose($handle);
cerdic
a validé
if ($copy) {
$result['length'] = $res;
$result['file'] = $copy;
$result['page'] = &$res;
$result['length'] = strlen($result['page']);
}
cerdic
a validé
if (!$result['page']) {
return $result;
cerdic
a validé
}
// Decompresser au besoin
cerdic
a validé
if ($gz) {
$result['page'] = implode('', gzfile($gz));
supprimer_fichier($gz);
}
// Faut-il l'importer dans notre charset local ?
cerdic
a validé
if ($options['transcoder']) {
include_spip('inc/charsets');
$result['page'] = transcoder_page($result['page'], $result['headers']);
}
return $result;
}
/**
* Recuperer une URL si on l'a pas deja dans un cache fichier
* le delai de cache est fourni par l'option delai_cache
* les autres options et le format de retour sont identiques a recuperer_url_cache
cerdic
a validé
*
* @uses recuperer_url()
*
* @param string $url
* @param array $options
* int delai_cache : anciennete acceptable pour le contenu (en seconde)
* @return array|bool|mixed
*/
cerdic
a validé
function recuperer_url_cache($url, $options = array()) {
if (!defined('_DELAI_RECUPERER_URL_CACHE')) {
define('_DELAI_RECUPERER_URL_CACHE', 3600);
}
$default = array(
'transcoder' => false,
'methode' => 'GET',
'taille_max' => null,
'datas' => '',
'boundary' => '',
'refuser_gz' => false,
'if_modified_since' => '',
'uri_referer' => '',
'file' => '',
'follow_location' => 10,
'version_http' => _INC_DISTANT_VERSION_HTTP,
'delai_cache' => _DELAI_RECUPERER_URL_CACHE,
);
// cas ou il n'est pas possible de cacher
marcimat
a validé
if (!empty($options['data']) or $options['methode'] == 'POST') {
cerdic
a validé
}
// ne pas tenter plusieurs fois la meme url en erreur (non cachee donc)
static $errors = array();
cerdic
a validé
if (isset($errors[$url])) {
cerdic
a validé
}
$sig = $options;
unset($sig['if_modified_since']);
unset($sig['delai_cache']);
$dir = sous_repertoire(_DIR_CACHE, 'curl');
$cache = md5(serialize($sig)) . "-" . substr(preg_replace(",\W+,", "_", $url), 80);
$sub = sous_repertoire($dir, substr($cache, 0, 2));
$cache = "$sub$cache";
$res = false;
$is_cached = file_exists($cache);
marcimat
a validé
and (filemtime($cache) > $_SERVER['REQUEST_TIME']-$options['delai_cache'])
cerdic
a validé
) {
cerdic
a validé
if ($res = unserialize($res)) {
// mettre le last_modified et le status=304 ?
}
cerdic
a validé
if (!$res) {
$res = recuperer_url($url, $options);
// ne pas recharger cette url non cachee dans le meme hit puisque non disponible
cerdic
a validé
if (!$res) {
if ($is_cached) {
// on a pas reussi a recuperer mais on avait un cache : l'utiliser
lire_fichier($cache, $res);
$res = unserialize($res);
}
cerdic
a validé
return $errors[$url] = $res;
}
ecrire_fichier($cache, serialize($res));
}
return $res;
* Obosolete : Récupère une page sur le net et au besoin l'encode dans le charset local
*
* Gère les redirections de page (301) sur l'URL demandée (maximum 10 redirections)
*
* @deprecated
* @uses recuperer_url()
*
* @param string $url
* URL de la page à récupérer
* @param bool|string $trans
* - chaîne longue : c'est un nom de fichier (nom pour sa copie locale)
* - true : demande d'encodage/charset
* - null : ne retourner que les headers
* @param bool $get_headers
* Si on veut récupérer les entêtes
* @param int|null $taille_max
* Arrêter le contenu au-delà (0 = seulement les entetes ==> requête HEAD).
* Par defaut taille_max = 1Mo.
* @param string|array $datas
* Pour faire un POST de données
* @param string $boundary
* Pour forcer l'envoi par cette méthode
* @param bool $refuser_gz
* Pour forcer le refus de la compression (cas des serveurs orthographiques)
* @param string $date_verif
* Un timestamp unix pour arrêter la récuperation si la page distante
* n'a pas été modifiée depuis une date donnée
* @param string $uri_referer
* Pour préciser un référer différent
* @return string|bool
* - Code de la page obtenue (avec ou sans entête)
* - false si la page n'a pu être récupérée (status different de 200)
cerdic
a validé
function recuperer_page(
$url,
$trans = false,
$get_headers = false,
$taille_max = null,
$datas = '',
$boundary = '',
$refuser_gz = false,
$date_verif = '',
$uri_referer = ''
) {
// $copy = copier le fichier ?
marcimat
a validé
$copy = (is_string($trans) and strlen($trans) > 5); // eviter "false" :-)
cerdic
a validé
if (!is_null($taille_max) and ($taille_max == 0)) {
$get = 'HEAD';
cerdic
a validé
} else {
$get = 'GET';
cerdic
a validé
}
$options = array(
cerdic
a validé
'transcoder' => $trans === true,
'methode' => $get,
'datas' => $datas,
'boundary' => $boundary,
'refuser_gz' => $refuser_gz,
'if_modified_since' => $date_verif,
'uri_referer' => $uri_referer,
'follow_location' => 10,
);
cerdic
a validé
if (!is_null($taille_max)) {
cerdic
a validé
}
// dix tentatives maximum en cas d'entetes 301...
cerdic
a validé
if (!$res) {
return false;
}
if ($res['status'] !== 200) {
return false;
}
if ($get_headers) {
return $res['headers'] . "\n" . $res['page'];
}
return $res['page'];
* Obsolete Récupère une page sur le net et au besoin l'encode dans le charset local
*
* @deprecated
*
* @uses recuperer_url()
*
* @param string $url
* URL de la page à récupérer
* @param bool|null|string $trans
* - chaîne longue : c'est un nom de fichier (nom pour sa copie locale)
* - true : demande d'encodage/charset
* - null : ne retourner que les headers
* @param string $get
* Type de requête HTTP à faire (HEAD, GET ou POST)
* @param int|bool $taille_max
* Arrêter le contenu au-delà (0 = seulement les entetes ==> requête HEAD).
* Par defaut taille_max = 1Mo.
* @param string|array $datas
* Pour faire un POST de données
* @param bool $refuser_gz
* Pour forcer le refus de la compression (cas des serveurs orthographiques)
* @param string $date_verif
* Un timestamp unix pour arrêter la récuperation si la page distante
* n'a pas été modifiée depuis une date donnée
* @param string $uri_referer
* Pour préciser un référer différent
* @return string|array|bool
* - Retourne l'URL en cas de 301,
* - Un tableau (entête, corps) si ok,
* - false sinon
cerdic
a validé
function recuperer_lapage(
$url,
$trans = false,
$get = 'GET',
$taille_max = 1048576,
$datas = '',
$refuser_gz = false,
$date_verif = '',
$uri_referer = ''
) {
// $copy = copier le fichier ?
marcimat
a validé
$copy = (is_string($trans) and strlen($trans) > 5); // eviter "false" :-)
// si on ecrit directement dans un fichier, pour ne pas manipuler
// en memoire refuser gz
cerdic
a validé
if ($copy) {
$refuser_gz = true;
cerdic
a validé
}
$options = array(
cerdic
a validé
'transcoder' => $trans === true,
'methode' => $get,
'datas' => $datas,
'refuser_gz' => $refuser_gz,
'if_modified_since' => $date_verif,
'uri_referer' => $uri_referer,
'follow_location' => false,
);
cerdic
a validé
if (!is_null($taille_max)) {
cerdic
a validé
}
// dix tentatives maximum en cas d'entetes 301...
cerdic
a validé
if ($res) {
return false;
}
if ($res['status'] !== 200) {
return false;
cerdic
a validé
}
return array($res['headers'], $res['result']);
}
/**
* Recuperer le contenu sur lequel pointe la resource passee en argument
* $taille_max permet de tronquer
* de l'url dont on a deja recupere les en-tetes
*
* @param resource $handle
* @param int $taille_max
* @param string $fichier
* fichier dans lequel copier le contenu de la resource
* @return bool|int|string
* bool false si echec
* int taille du fichier si argument fichier fourni
* string contenu de la resource
*/
cerdic
a validé
function recuperer_body($handle, $taille_max = _INC_DISTANT_MAX_SIZE, $fichier = '') {
$taille = 0;
$result = '';
cerdic
a validé
if ($fichier) {
include_spip("inc/acces");
$fp = spip_fopen_lock($tmpfile, 'w', LOCK_EX);
marcimat
a validé
if (!$fp and file_exists($fichier)) {
return filesize($fichier);
}
cerdic
a validé
if (!$fp) {
cerdic
a validé
}
$result = 0; // on renvoie la taille du fichier
}
marcimat
a validé
while (!feof($handle) and $taille < $taille_max) {
$res = fread($handle, 16384);
$taille += strlen($res);
cerdic
a validé
if ($fp) {
$result = $taille;
cerdic
a validé
} else {
$result .= $res;
cerdic
a validé
}
cerdic
a validé
if ($fp) {
spip_fclose_unlock($fp);
spip_unlink($fichier);
@rename($tmpfile, $fichier);
cerdic
a validé
if (!file_exists($fichier)) {
return false;
cerdic
a validé
}
}
cerdic
a validé
return $result;
}
/**
* Lit les entetes de reponse HTTP sur la socket $handle
* et retourne
* false en cas d'echec,
* un tableau associatif en cas de succes, contenant :
* - le status
* - le tableau complet des headers
* - la date de derniere modif si connue
* - l'url de redirection si specifiee
*
* @param resource $handle
* @param int|bool $if_modified_since
* @return bool|array
* int status
* string headers
* int last_modified
* string location
*/
cerdic
a validé
function recuperer_entetes_complets($handle, $if_modified_since = false) {
$result = array('status' => 0, 'headers' => array(), 'last_modified' => 0, 'location' => '');
$s = @trim(fgets($handle, 16384));
cerdic
a validé
if (!preg_match(',^HTTP/[0-9]+\.[0-9]+ ([0-9]+),', $s, $r)) {
return false;
}
$result['status'] = intval($r[1]);
cerdic
a validé
while ($s = trim(fgets($handle, 16384))) {
$result['headers'][] = $s . "\n";
preg_match(',^([^:]*): *(.*)$,i', $s, $r);
list(, $d, $v) = $r;
marcimat
a validé
if (strtolower(trim($d)) == 'location' and $result['status'] >= 300 and $result['status'] < 400) {
$result['location'] = $v;
cerdic
a validé
} elseif ($d == 'Last-Modified') {
$result['last_modified'] = strtotime($v);
}
}
if ($if_modified_since
marcimat
a validé
and $result['last_modified']
and $if_modified_since > $result['last_modified']
and $result['status'] == 200
cerdic
a validé
) {
$result['status'] = 304;
cerdic
a validé
}
$result['headers'] = implode('', $result['headers']);
return $result;
}
/**
* Obsolete : version simplifiee de recuperer_entetes_complets
* Retourne les informations d'entête HTTP d'un socket
*
* Lit les entêtes de reponse HTTP sur la socket $f
*
* @uses recuperer_entetes_complets()
* @deprecated
*
* @param resource $f
* Socket d'un fichier (issu de fopen)
* @param int|string $date_verif
* Pour tester une date de dernière modification
* @return string|int|array
* - la valeur (chaîne) de l'en-tete Location si on l'a trouvée
* - la valeur (numerique) du statut si different de 200, notamment Not-Modified
* - le tableau des entetes dans tous les autres cas
cerdic
a validé
function recuperer_entetes($f, $date_verif = '') {
//Cas ou la page distante n'a pas bouge depuis
//la derniere visite
$res = recuperer_entetes_complets($f, $date_verif);
cerdic
a validé
if (!$res) {
return false;
}
if ($res['location']) {
return $res['location'];
}
if ($res['status'] != 200) {
return $res['status'];
}
/**
* Calcule le nom canonique d'une copie local d'un fichier distant
*
* Si on doit conserver une copie locale des fichiers distants, autant que ca
* soit à un endroit canonique
*
* @note
* Si ca peut être bijectif c'est encore mieux,
* mais là tout de suite je ne trouve pas l'idee, étant donné les limitations
* des filesystems
*
* @param string $source
* URL de la source
* @param string $extension
* Extension du fichier
* @return string
* Nom du fichier pour copie locale
cerdic
a validé
function nom_fichier_copie_locale($source, $extension) {
include_spip('inc/documents');
$d = creer_repertoire_documents('distant'); # IMG/distant/
$d = sous_repertoire($d, $extension); # IMG/distant/pdf/
// on se place tout le temps comme si on etait a la racine
cerdic
a validé
if (_DIR_RACINE) {
cerdic
a validé
}
$m = md5($source);
return $d
. substr(preg_replace(',[^\w-],', '', basename($source)) . '-' . $m, 0, 12)
. substr($m, 0, 4)
. ".$extension";
}
/**
* Donne le nom de la copie locale de la source
*
* Soit obtient l'extension du fichier directement de l'URL de la source,
* soit tente de le calculer.
*
* @uses nom_fichier_copie_locale()
* @uses recuperer_infos_distantes()
*
* @param string $source
* URL de la source distante
* @return string
* Nom du fichier calculé
cerdic
a validé
function fichier_copie_locale($source) {
// Si c'est deja local pas de souci
cerdic
a validé
if (!tester_url_absolue($source)) {
if (_DIR_RACINE) {
$source = preg_replace(',^' . preg_quote(_DIR_RACINE) . ',', '', $source);
cerdic
a validé
}
return $source;
// optimisation : on regarde si on peut deviner l'extension dans l'url et si le fichier
// a deja ete copie en local avec cette extension
// dans ce cas elle est fiable, pas la peine de requeter en base
$path_parts = pathinfo($source);
if (!isset($path_parts['extension'])) {
$path_parts['extension'] = '';
}
$ext = $path_parts ? $path_parts['extension'] : '';
if ($ext
marcimat
a validé
and preg_match(',^\w+$,', $ext) // pas de php?truc=1&...
and $f = nom_fichier_copie_locale($source, $ext)
and file_exists(_DIR_RACINE . $f)
cerdic
a validé
) {
cerdic
a validé
}
// Si c'est deja dans la table des documents,
// ramener le nom de sa copie potentielle
cerdic
a validé
$ext = sql_getfetsel("extension", "spip_documents",
"fichier=" . sql_quote($source) . " AND distant='oui' AND extension <> ''");
cerdic
a validé
if ($ext) {
return nom_fichier_copie_locale($source, $ext);
}
// voir si l'extension indiquee dans le nom du fichier est ok
// et si il n'aurait pas deja ete rapatrie
$ext = $path_parts ? $path_parts['extension'] : '';
marcimat
a validé
if ($ext and sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))) {
$f = nom_fichier_copie_locale($source, $ext);
cerdic
a validé
if (file_exists(_DIR_RACINE . $f)) {
cerdic
a validé
}
// Ping pour voir si son extension est connue et autorisee
// avec mise en cache du resultat du ping
if (!@file_exists($cache)
marcimat
a validé
or !$path_parts = @unserialize(spip_file_get_contents($cache))
or _request('var_mode') == 'recalcul'
cerdic
a validé
) {
ecrire_fichier($cache, serialize($path_parts));
}
$ext = !empty($path_parts['extension']) ? $path_parts['extension'] : '';
marcimat
a validé
if ($ext and sql_getfetsel("extension", "spip_types_documents", "extension=" . sql_quote($ext))) {
return nom_fichier_copie_locale($source, $ext);
}
spip_log("pas de copie locale pour $source");
}
/**
* Récupérer les infos d'un document distant, sans trop le télécharger
*
* @param string $source
* URL de la source
* @param int $max
* Taille maximum du fichier à télécharger
* @param bool $charger_si_petite_image
* Pour télécharger le document s'il est petit
* @return array
* Couples des informations obtenues parmis :
*
* - 'body' = chaine
* - 'type_image' = booleen
* - 'titre' = chaine
* - 'largeur' = intval
* - 'hauteur' = intval
* - 'taille' = intval
* - 'extension' = chaine
* - 'fichier' = chaine
cerdic
a validé
function recuperer_infos_distantes($source, $max = 0, $charger_si_petite_image = true) {
# charger les alias des types mime
include_spip('base/typedoc');
$a = array();
$mime_type = '';
// On va directement charger le debut des images et des fichiers html,
// de maniere a attrapper le maximum d'infos (titre, taille, etc). Si
// ca echoue l'utilisateur devra les entrer...
cerdic
a validé
if ($headers = recuperer_page($source, false, true, $max, '', '', true)) {
list($headers, $a['body']) = preg_split(',\n\n,', $headers, 2);
cerdic
a validé
if (preg_match(",\nContent-Type: *([^[:space:];]*),i", "\n$headers", $regs)) {
$mime_type = (trim($regs[1]));
cerdic
a validé
} else {
$mime_type = '';
} // inconnu
// Appliquer les alias
cerdic
a validé
while (isset($GLOBALS['mime_alias'][$mime_type])) {
$mime_type = $GLOBALS['mime_alias'][$mime_type];
cerdic
a validé
}
// Si on a un mime-type insignifiant
// text/plain,application/octet-stream ou vide
// c'est peut-etre que le serveur ne sait pas
// ce qu'il sert ; on va tenter de detecter via l'extension de l'url
// ou le Content-Disposition: attachment; filename=...
$t = null;
cerdic
a validé
if (in_array($mime_type, array('text/plain', '', 'application/octet-stream'))) {
marcimat
a validé
and preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $rext)
cerdic
a validé
) {
$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1], '', 'text'));
}
if (!$t
marcimat
a validé
and preg_match(",^Content-Disposition:\s*attachment;\s*filename=(.*)$,Uims", $headers, $m)
and preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $m[1], $rext)
cerdic
a validé
) {
$t = sql_fetsel("extension", "spip_types_documents", "extension=" . sql_quote($rext[1], '', 'text'));
// Autre mime/type (ou text/plain avec fichier d'extension inconnue)
cerdic
a validé
if (!$t) {
$t = sql_fetsel("extension", "spip_types_documents", "mime_type=" . sql_quote($mime_type));
cerdic
a validé
}