Rev 264 | Blame | Last modification | View Log | RSS feed
<?php/*** La classe OpenIdClient est une implémentation d'un client OpenId, depuis une classe Zend.* Elle permet d'établir une connexion avec un serveur, en fonction d'un identifiant OpenId.* Elle permet de communiquer de manière sécurisée avec ce serveur, et doit aboutir a une* identification centralisée.* */class OpenIdClient {//OpenID 2.0 namespace. Tous les messages OpenID 2.0 DOIVENT contenir la variable openid.ns et sa valeurconst NS_2_0 = 'http://specs.openid.net/auth/2.0';// TODO : remplacer _storage par une gestion par cache ?/*** Variable permettant le stockage d'informations, notammenent à propos des clés DiffieHellmann* @var Storage $_storage*/protected $_storage = null;/*** Tableau "cache" interne permettant d'éviter des accès inutiles au fichier storage* @var array $_cache*/protected $_cache = array();// Client pour les requetes.private $client;/*** Constructeur de l'application* */function __construct() {$this->client = new Client();$this->_storage = new StorageFile();}/*** Fonction login** Return true ou false* > Ne retourne rien si true car redirige vers l'adresse du serveur OID* *///FIXME : le paramètre immediate ?// A vérifier mais ça doit permettre de passer directement le mot de passe. Il reste plus qu'à trouver le nom de la variable mot de passe.function login($id, $immediate = false) {// L'original retourne la fonction checkId, avec le parametre immediate = true// Je ne comprends pas l'utilité, je fusionne les deux pour l'instant// FIXME : si pas de comportement étrange, valider.//Tests et arrêt si non validé ://Normaliser (traite si XRI ou URL, normalize URL)//FIXME : voir avec JP pour équivalent dans frameworkif (!$this->normalize($id)) {return false;}//Discovery// Récupérer les informations sur le serveur OPEN ID/*FIXME : ca change la valeur de l'id !!!if (!$this->_discovery($id, $server, $version)) {trigger_error('Discovery failed');return false;}*/$retour_url = $this->client->consulter($id);//Le retour contient les balises suivantes :/*** <link rel="openid.server" href="http://www.myopenid.com/server" />* <link rel="openid2.provider" href="http://www.myopenid.com/server" />*/$metaServeur = $this->verifierVersion($retour_url);//TODO : Voir avec JP : la classe client ne permet pas de vérifer le statut ??if ($retour_url === false) {trigger_error('L\'adresse $id est inacessible', E_USER_ERROR);return false;}if ($metaServeur === false) {return false;}if (!$this->_associate($metaServeur['serveur'], $metaServeur['version'])) {trigger_error('Impossible de s\'associer avec le serveur');}/*TODO : fonctionnement différentif (!$this->_getAssociation($server,$handle,$macFunc,$secret,$expires)) {/* Use dumb mode *unset($handle);unset($macFunc);unset($secret);unset($expires);*}*///on a la version, l'adresse du serveur et le realId si c'est une 2.0 dans metaServeurif (isset($metaServeur['realId'])) {$id = $metaServeur['realId'];}//Associate//getAssociation//Organisation des paramètres :$params = array();if ($metaServeur['version'] >= 2.0) {$params['openid.ns'] = self::NS_2_0;}$params['openid.mode'] = $immediate ?'checkid_immediate' : 'checkid_setup';$params['openid.identity'] = $id;//FIXME : Ex : $params['openid.claimed_id'] = $claimedId; > jvois pas l'intéret$params['openid.claimed_id'] = $id;/** TODO : gérer les sessions et namespace* if ($metaServeur['version'] <= 2.0) {if ($this->_session !== null) {$this->_session->identity = $id;$this->_session->claimed_id = $claimedId;} else if (defined('SID')) {$_SESSION["zend_openid"] = array("identity" => $id,"claimed_id" => $claimedId);} else {require_once "Zend/Session/Namespace.php";$this->_session = new Zend_Session_Namespace("zend_openid");$this->_session->identity = $id;$this->_session->claimed_id = $claimedId;}}*/if (isset($handle)) {$params['openid.assoc_handle'] = $handle;}//FIXME : $params['openid.return_to'] = $this->absoluteUrl($returnTo);$params['openid.return_to'] = $this->absoluteUrl(null);if (empty($root)) {$root = $this->selfUrl();if ($root[strlen($root)-1] != '/') {$root = dirname($root);}}if ($metaServeur['version'] >= 2.0) {$params['openid.realm'] = $root;} else {$params['openid.trust_root'] = $root;}/*FIXME ::if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) {$this->_setError("Extension::prepareRequest failure");return false;}*/$this->redirect($metaServeur['serveur'], $params);return true;//Renvoyer vers l'url}/*** Verifies authentication response from OpenID server.** This is the second step of OpenID authentication process.* The function returns true on successful authentication and false on* failure.** @param array $params HTTP query data from OpenID server* @param string &$identity this argument is set to end-user's claimed* identifier or OpenID provider local identifier.* @param mixed $extensions extension object or array of extensions objects* @return bool*/public function verify($params, &$identity = "", $extensions = null){if (isset($params['openid_ns']) &&$params['openid_ns'] == $this->NS_2_0) {$version = 2.0;}if (isset($params["openid_claimed_id"])) {$identity = $params["openid_claimed_id"];} else if (isset($params["openid_identity"])){$identity = $params["openid_identity"];} else {$identity = "";}if ($version < 2.0 && !isset($params["openid_claimed_id"])) {if ($this->_session !== null) {if ($this->_session->identity === $identity) {$identity = $this->_session->claimed_id;}} else if (defined('SID')) {if (isset($_SESSION["zend_openid"]["identity"]) &&isset($_SESSION["zend_openid"]["claimed_id"]) &&$_SESSION["zend_openid"]["identity"] === $identity) {$identity = $_SESSION["zend_openid"]["claimed_id"];}} else {require_once "Zend/Session/Namespace.php";$this->_session = new Zend_Session_Namespace("zend_openid");if ($this->_session->identity === $identity) {$identity = $this->_session->claimed_id;}}}if (empty($params['openid_mode'])) {$this->_setError("Missing openid.mode");return false;}if (empty($params['openid_return_to'])) {$this->_setError("Missing openid.return_to");return false;}if (empty($params['openid_signed'])) {$this->_setError("Missing openid.signed");return false;}if (empty($params['openid_sig'])) {$this->_setError("Missing openid.sig");return false;}if ($params['openid_mode'] != 'id_res') {$this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'");return false;}if (empty($params['openid_assoc_handle'])) {$this->_setError("Missing openid.assoc_handle");return false;}if ($params['openid_return_to'] != $this->selfUrl()) {/* Ignore query part in openid.return_to */$pos = strpos($params['openid_return_to'], '?');if ($pos === false ||SUBSTR($params['openid_return_to'], 0 , $pos) != $this->selfUrl()) {/*$this->_setError("Wrong openid.return_to '".$params['openid_return_to']."' != '" . $this->selfUrl() ."'");*/trigger_error('Wrong openid.return_to', E_USER_ERROR);return false;}}if ($version >= 2.0) {if (empty($params['openid_response_nonce'])) {trigger_error('Missing openid.response_nonce', E_USER_ERROR);return false;}if (empty($params['openid_op_endpoint'])) {trigger_error('Missing openid.op_endpoint', E_USER_ERROR);return false;/* OpenID 2.0 (11.3) Checking the Nonce */} else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) {trigger_error('Duplicate openid.response_nonce', E_USER_ERROR);return false;}}if (!empty($params['openid_invalidate_handle'])) {if ($this->_storage->getAssociationByHandle($params['openid_invalidate_handle'],$url,$macFunc,$secret,$expires)) {$this->_storage->delAssociation($url);}}if ($this->_storage->getAssociationByHandle($params['openid_assoc_handle'],$url,$macFunc,$secret,$expires)) {$signed = explode(',', $params['openid_signed']);$data = '';foreach ($signed as $key) {$data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n";}if (base64_decode($params['openid_sig']) ==Zend_OpenId::hashHmac($macFunc, $data, $secret)) {/** FIXME dépendance je sais pas pour quoi : a voir :* if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {$this->_setError("Extension::parseResponse failure");return false;}*//* OpenID 2.0 (11.2) Verifying Discovered Information */if (isset($params['openid_claimed_id'])) {$id = $params['openid_claimed_id'];if (!$this->normalize($id)) {$this->_setError("Normalization failed");return false;} else if (!$this->_discovery($id, $discovered_server, $discovered_version)) {$this->_setError("Discovery failed: " . $this->getError());return false;} else if ((!empty($params['openid_identity']) &&$params["openid_identity"] != $id) ||(!empty($params['openid_op_endpoint']) &&$params['openid_op_endpoint'] != $discovered_server) ||$discovered_version != $version) {$this->_setError("Discovery information verification failed");return false;}}return true;}$this->_storage->delAssociation($url);$this->_setError("Signature check failed");return false;}else{/* Use dumb mode */if (isset($params['openid_claimed_id'])) {$id = $params['openid_claimed_id'];} else if (isset($params['openid_identity'])) {$id = $params['openid_identity'];} else {$this->_setError("Missing openid.claimed_id and openid.identity");return false;}if (!$this->normalize($id)) {trigger_error('Normalization failed', E_USER_ERROR);return false;} else if (!$this->_discovery($id, $server, $discovered_version)) {trigger_error('Discovery failed', E_USER_ERROR);return false;}/* OpenID 2.0 (11.2) Verifying Discovered Information */if ((isset($params['openid_identity']) &&$params["openid_identity"] != $id) ||(isset($params['openid_op_endpoint']) &&$params['openid_op_endpoint'] != $server) ||$discovered_version != $version) {trigger_error('Discovery information verification failed', E_USER_ERROR);return false;}$params2 = array();foreach ($params as $key => $val) {if (strpos($key, 'openid_ns_') === 0) {$key = 'openid.ns.' . substr($key, strlen('openid_ns_'));} else if (strpos($key, 'openid_sreg_') === 0) {$key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));} else if (strpos($key, 'openid_') === 0) {$key = 'openid.' . substr($key, strlen('openid_'));}$params2[$key] = $val;}$params2['openid.mode'] = 'check_authentication';$ret = $this->client->modifier($serveur, $params2);//_httpRequest($server, 'POST', $params2, $status);if ($ret === false) {trigger_error("'Dumb' signature verification HTTP request failed", E_USER_ERROR);return false;}$r = array();if (is_string($ret)) {foreach(explode("\n", $ret) as $line) {$line = trim($line);if (!empty($line)) {$x = explode(':', $line, 2);if (is_array($x) && count($x) == 2) {list($key, $value) = $x;$r[trim($key)] = trim($value);}}}}$ret = $r;if (!empty($ret['invalidate_handle'])) {if ($this->_storage->getAssociationByHandle($ret['invalidate_handle'],$url,$macFunc,$secret,$expires)) {$this->_storage->delAssociation($url);}}if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') {if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {$this->_setError("Extension::parseResponse failure");return false;}return true;}$this->_setError("'Dumb' signature verification failed");return false;}}/*** Performs discovery of identity and finds OpenID URL, OpenID server URL* and OpenID protocol version. Returns true on succees and false on* failure.** @param string &$id OpenID identity URL* @param string &$server OpenID server URL* @param float &$version OpenID protocol version* @return bool* @todo OpenID 2.0 (7.3) XRI and Yadis discovery*/protected function _discovery(&$id, &$server, &$version){$realId = $id;if ($this->_storage->getDiscoveryInfo($id,$realId,$server,$version,$expire)) {$id = $realId;return true;}/* TODO: OpenID 2.0 (7.3) XRI and Yadis discovery *//* HTML-based discovery */$clientDiscovery = new Client();//TODO : rajouter un test sur le statut de la réponse// Nécessite la prise en compte des entetes dans le framework/*if ($status != 200 || !is_string($response)) {return false;}*/$reponse = $clientDiscovery->consulter($id);$metaServeur = $this->verifierVersion($reponse);if (!isset($metaServeur) || empty($reponse)) {trigger_error('Aucune donnée OpenId', E_USER_ERROR);return false;}$expire = time() + 60 * 60;$this->_storage->addDiscoveryInfo($id, $metaServeur['realId'], $metaServeur['serveur'], $metaServeur['version'], $expire);if (!empty($metaServeur['realId'])) {$id = $metaServeur['realId'];}return true;}//Parser l'HTML de réponse pour trouver la version du serveur OPEN IDfunction verifierVersion($reponseHtml) {// TODO : remplacer l'arlgorythme suivant par cette solution ://1. Chercher l'existence d'une balise openidN.provider//2. Déterminer la version en fonction de la chaine : openid2.provider => 2.0; openid.provider => 1.1//3. Récupérer l'url du serveur href="serveur"//4. SI 2.0, récupérer la valeur réelle de l'ID//TODO : penser à tester les deux versions du serveur$metaServeur = Array();if (preg_match('/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['version'] = 2.0;$metaServeur['serveur'] = $r[3];} else if (preg_match('/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['version'] = 2.0;$metaServeur['serveur'] = $r[1];} else if (preg_match('/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['version'] = 1.1;$metaServeur['serveur'] = $r[3];} else if (preg_match('/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['version'] = 1.1;$metaServeur['serveur'] = $r[2];} else {return false;}if ($metaServeur['version'] >= 2.0) {if (preg_match('/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['realId'] = $r[3];} else if (preg_match('/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['realId'] = $r[2];}} else {if (preg_match('/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['realId'] = $r[3];} else if (preg_match('/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i',$reponseHtml,$r)) {$metaServeur['realId'] = $r[2];}}return $metaServeur;}/*** Create (or reuse existing) association between OpenID consumer and* OpenID server based on Diffie-Hellman key agreement. Returns true* on success and false on failure.** @param string $url OpenID server url* @param float $version OpenID protocol version* @param string $priv_key for testing only* @return bool*/protected function _associate($url, $version, $priv_key=null){/* Check if we already have association in chace or storage *//** TODO : Utiliser le stockage plutot* */if ($this->_getAssociation($url,$handle,$macFunc,$secret,$expires)) {return true;}/** TODO : utiliser le fichier de config* if ($this->_dumbMode) {return true;}*/$params = array();if ($version >= 2.0) {$params = array('openid.ns' => $this->NS_2_0,'openid.mode' => 'associate','openid.assoc_type' => 'HMAC-SHA256','openid.session_type' => 'DH-SHA256',);} else {$params = array('openid.mode' => 'associate','openid.assoc_type' => 'HMAC-SHA1','openid.session_type' => 'DH-SHA1',);}$dh = DiffieHellmanUtil::createDhKey(pack('H*', DiffieHellmanUtil::DH_P),pack('H*', DiffieHellmanUtil::DH_G),$priv_key);$dh_details = DiffieHellmanUtil::getDhKeyDetails($dh);$params['openid.dh_modulus'] = base64_encode(DiffieHellmanUtil::btwoc($dh_details['p']));$params['openid.dh_gen'] = base64_encode(DiffieHellmanUtil::btwoc($dh_details['g']));$params['openid.dh_consumer_public'] = base64_encode(DiffieHellmanUtil::btwoc($dh_details['pub_key']));while(1) {//FIXME : c'est pas une modification ...$ret = $this->client->modifier($url, $params); // FIXME : a quoi sert status ?, $status);if ($ret === false) {//$this->_setError("HTTP request failed");trigger_error('La requête a échoué', E_USER_ERROR);return false;}$r = array();$bad_response = false;foreach(explode("\n", $ret) as $line) {$line = trim($line);if (!empty($line)) {$x = explode(':', $line, 2);if (is_array($x) && count($x) == 2) {list($key, $value) = $x;$r[trim($key)] = trim($value);} else {$bad_response = true;}}}if ($bad_response && strpos($ret, 'Unknown session type') !== false) {$r['error_code'] = 'unsupported-type';}$ret = $r;if (isset($ret['error_code']) &&$ret['error_code'] == 'unsupported-type') {if ($params['openid.session_type'] == 'DH-SHA256') {$params['openid.session_type'] = 'DH-SHA1';$params['openid.assoc_type'] = 'HMAC-SHA1';} else if ($params['openid.session_type'] == 'DH-SHA1') {$params['openid.session_type'] = 'no-encryption';} else {trigger_error("The OpenID service responded with: " . $ret['error_code'], E_USER_ERROR);return false;}} else {break;}}/*FIXME : gestion du statut avec la classe client ??if ($status != 200) {$this->_setError("The server responded with status code: " . $status);return false;}*/if ($version >= 2.0 &&isset($ret['ns']) &&$ret['ns'] != $this->NS_2_0) {$this->_setError("Wrong namespace definition in the server response");return false;}if (!isset($ret['assoc_handle']) ||!isset($ret['expires_in']) ||!isset($ret['assoc_type']) ||$params['openid.assoc_type'] != $ret['assoc_type']) {if ($params['openid.assoc_type'] != $ret['assoc_type']) {$this->_setError("The returned assoc_type differed from the supplied openid.assoc_type");} else {$this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)");}return false;}$handle = $ret['assoc_handle'];$expiresIn = $ret['expires_in'];if ($ret['assoc_type'] == 'HMAC-SHA1') {$macFunc = 'sha1';} else if ($ret['assoc_type'] == 'HMAC-SHA256' &&$version >= 2.0) {$macFunc = 'sha256';} else {$this->_setError("Unsupported assoc_type");return false;}if ((empty($ret['session_type']) ||($version >= 2.0 && $ret['session_type'] == 'no-encryption')) &&isset($ret['mac_key'])) {$secret = base64_decode($ret['mac_key']);} else if (isset($ret['session_type']) &&$ret['session_type'] == 'DH-SHA1' &&!empty($ret['dh_server_public']) &&!empty($ret['enc_mac_key'])) {$dhFunc = 'sha1';} else if (isset($ret['session_type']) &&$ret['session_type'] == 'DH-SHA256' &&$version >= 2.0 &&!empty($ret['dh_server_public']) &&!empty($ret['enc_mac_key'])) {$dhFunc = 'sha256';} else {$this->_setError("Unsupported session_type");return false;}if (isset($dhFunc)) {$serverPub = base64_decode($ret['dh_server_public']);$dhSec = DiffieHellmanUtil::computeDhSecret($serverPub, $dh);if ($dhSec === false) {$this->_setError("DH secret comutation failed");return false;}$sec = $this->digest($dhFunc, $dhSec);if ($sec === false) {$this->_setError("Could not create digest");return false;}$secret = $sec ^ base64_decode($ret['enc_mac_key']);}if ($macFunc == 'sha1') {if (DiffieHellmanUtil::strlen($secret) != 20) {$this->_setError("The length of the sha1 secret must be 20");return false;}} else if ($macFunc == 'sha256') {if (DiffieHellmanUtil::strlen($secret) != 32) {$this->_setError("The length of the sha256 secret must be 32");return false;}}$this->_addAssociation($url,$handle,$macFunc,$secret,time() + $expiresIn);/* $this->association['url'] = $url;$this->association['handle'] = $handle;$this->association['macFunc'] = $macFunc;$this->association['secret'] = $secret;$this->association['expiresIn'] = time() + $expiresIn;*/return true;}/*** Store assiciation in internal chace and external storage** @param string $url OpenID server url* @param string $handle association handle* @param string $macFunc HMAC function (sha1 or sha256)* @param string $secret shared secret* @param integer $expires expiration UNIX time* @return void*/protected function _addAssociation($url, $handle, $macFunc, $secret, $expires){$this->_cache[$url] = array($handle, $macFunc, $secret, $expires);return $this->_storage->addAssociation($url,$handle,$macFunc,$secret,$expires);}/*** Retrive assiciation information for given $url from internal cahce or* external storage** @param string $url OpenID server url* @param string &$handle association handle* @param string &$macFunc HMAC function (sha1 or sha256)* @param string &$secret shared secret* @param integer &$expires expiration UNIX time* @return void*/protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires){if (isset($this->_cache[$url])) {$handle = $this->_cache[$url][0];$macFunc = $this->_cache[$url][1];$secret = $this->_cache[$url][2];$expires = $this->_cache[$url][3];return true;}if ($this->_storage->getAssociation($url,$handle,$macFunc,$secret,$expires)) {$this->_cache[$url] = array($handle, $macFunc, $secret, $expires);return true;}return false;}/*** Normalizes URL according to RFC 3986 to use it in comparison operations.* The function gets URL argument by reference and modifies it.* It returns true on success and false of failure.** @param string &$id url to be normalized* @return bool*/static public function normalizeUrl(&$id){// RFC 3986, 6.2.2. Syntax-Based Normalization// RFC 3986, 6.2.2.2 Percent-Encoding Normalization$i = 0;$n = strlen($id);$res = '';while ($i < $n) {if ($id[$i] == '%') {if ($i + 2 >= $n) {return false;}++$i;if ($id[$i] >= '0' && $id[$i] <= '9') {$c = ord($id[$i]) - ord('0');} else if ($id[$i] >= 'A' && $id[$i] <= 'F') {$c = ord($id[$i]) - ord('A') + 10;} else if ($id[$i] >= 'a' && $id[$i] <= 'f') {$c = ord($id[$i]) - ord('a') + 10;} else {return false;}++$i;if ($id[$i] >= '0' && $id[$i] <= '9') {$c = ($c << 4) | (ord($id[$i]) - ord('0'));} else if ($id[$i] >= 'A' && $id[$i] <= 'F') {$c = ($c << 4) | (ord($id[$i]) - ord('A') + 10);} else if ($id[$i] >= 'a' && $id[$i] <= 'f') {$c = ($c << 4) | (ord($id[$i]) - ord('a') + 10);} else {return false;}++$i;$ch = chr($c);if (($ch >= 'A' && $ch <= 'Z') ||($ch >= 'a' && $ch <= 'z') ||$ch == '-' ||$ch == '.' ||$ch == '_' ||$ch == '~') {$res .= $ch;} else {$res .= '%';if (($c >> 4) < 10) {$res .= chr(($c >> 4) + ord('0'));} else {$res .= chr(($c >> 4) - 10 + ord('A'));}$c = $c & 0xf;if ($c < 10) {$res .= chr($c + ord('0'));} else {$res .= chr($c - 10 + ord('A'));}}} else {$res .= $id[$i++];}}if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) {return false;}$scheme = $reg[1];$auth = $reg[2];$host = $reg[3];$port = $reg[4];$path = $reg[5];$query = $reg[6];$fragment = $reg[7]; /* strip it */if (empty($scheme) || empty($host)) {return false;}// RFC 3986, 6.2.2.1. Case Normalization$scheme = strtolower($scheme);$host = strtolower($host);// RFC 3986, 6.2.2.3. Path Segment Normalizationif (!empty($path)) {$i = 0;$n = strlen($path);$res = "";while ($i < $n) {if ($path[$i] == '/') {++$i;while ($i < $n && $path[$i] == '/') {++$i;}if ($i < $n && $path[$i] == '.') {++$i;if ($i < $n && $path[$i] == '.') {++$i;if ($i == $n || $path[$i] == '/') {if (($pos = strrpos($res, '/')) !== false) {$res = substr($res, 0, $pos);}} else {$res .= '/..';}} else if ($i != $n && $path[$i] != '/') {$res .= '/.';}} else {$res .= '/';}} else {$res .= $path[$i++];}}$path = $res;}// RFC 3986,6.2.3. Scheme-Based Normalizationif ($scheme == 'http') {if ($port == 80) {$port = '';}} else if ($scheme == 'https') {if ($port == 443) {$port = '';}}if (empty($path)) {$path = '/';}$id = $scheme. '://'. $auth. $host. (empty($port) ? '' : (':' . $port)). $path. $query;return true;}/*** Normaliser l'identifiant OpenId qui peut être une URL ou nom XRI* Retourne true ou false en cas d'erreur.** Règles de normalisation :* 1. If the user's input starts with one of the "xri://", "xri://$ip*",* or "xri://$dns*" prefixes, they MUST be stripped off, so that XRIs* are used in the canonical form, and URI-authority XRIs are further* considered URL identifiers.* 2. If the first character of the resulting string is an XRI Global* Context Symbol ("=", "@", "+", "$", "!"), then the input SHOULD be* treated as an XRI.* 3. Otherwise, the input SHOULD be treated as an http URL; if it does* not include a "http" or "https" scheme, the Identifier MUST be* prefixed with the string "http://".* 4. URL identifiers MUST then be further normalized by both following* redirects when retrieving their content and finally applying the* rules in Section 6 of [RFC3986] to the final destination URL.* @param string &$id identifier to be normalized* @return bool*/static public function normalize(&$id){$id = trim($id);if (strlen($id) === 0) {return true;}// 7.2.1if (strpos($id, 'xri://$ip*') === 0) {$id = substr($id, strlen('xri://$ip*'));} else if (strpos($id, 'xri://$dns*') === 0) {$id = substr($id, strlen('xri://$dns*'));} else if (strpos($id, 'xri://') === 0) {$id = substr($id, strlen('xri://'));}// 7.2.2if ($id[0] == '=' ||$id[0] == '@' ||$id[0] == '+' ||$id[0] == '$' ||$id[0] == '!') {return true;}// 7.2.3if (strpos($id, "://") === false) {$id = 'http://' . $id;}// 7.2.4return self::normalizeURL($id);}/*** Generates a hash value (message digest) according to given algorithm.* It returns RAW binary string.** This is a wrapper function that uses one of available internal function* dependent on given PHP configuration. It may use various functions from* ext/openssl, ext/hash, ext/mhash or ext/standard.** @param string $func digest algorithm* @param string $data data to sign* @return string RAW digital signature* @throws Zend_OpenId_Exception*/public function digest($func, $data){if (function_exists('openssl_digest')) {return openssl_digest($data, $func, true);} else if (function_exists('hash')) {return hash($func, $data, true);} else if ($func === 'sha1') {return sha1($data, true);} else if ($func === 'sha256') {if (function_exists('mhash')) {return mhash(MHASH_SHA256 , $data);}}/*require_once "Zend/OpenId/Exception.php";throw new Zend_OpenId_Exception('Unsupported digest algorithm "' . $func . '".',Zend_OpenId_Exception::UNSUPPORTED_DIGEST);*/trigger_error('Unsupported digest algorithm '.$func , E_USER_ERROR);}/*** Returns a full URL that was requested on current HTTP request.** @return string*/public function selfUrl(){/*FIXME :* if ($this->$selfUrl !== null) {return $this->$selfUrl;} */if (isset($_SERVER['SCRIPT_URI'])) {return $_SERVER['SCRIPT_URI'];}$url = '';$port = '';if (isset($_SERVER['HTTP_HOST'])) {if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) {if (isset($_SERVER['SERVER_PORT'])) {$port = ':' . $_SERVER['SERVER_PORT'];}$url = $_SERVER['HTTP_HOST'];} else {$url = substr($_SERVER['HTTP_HOST'], 0, $pos);$port = substr($_SERVER['HTTP_HOST'], $pos);}} else if (isset($_SERVER['SERVER_NAME'])) {$url = $_SERVER['SERVER_NAME'];if (isset($_SERVER['SERVER_PORT'])) {$port = ':' . $_SERVER['SERVER_PORT'];}}if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {$url = 'https://' . $url;if ($port == ':443') {$port = '';}} else {$url = 'http://' . $url;if ($port == ':80') {$port = '';}}$url .= $port;if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {$url .= $_SERVER['HTTP_X_REWRITE_URL'];} elseif (isset($_SERVER['REQUEST_URI'])) {$query = strpos($_SERVER['REQUEST_URI'], '?');if ($query === false) {$url .= $_SERVER['REQUEST_URI'];} else {$url .= substr($_SERVER['REQUEST_URI'], 0, $query);}} else if (isset($_SERVER['SCRIPT_URL'])) {$url .= $_SERVER['SCRIPT_URL'];} else if (isset($_SERVER['REDIRECT_URL'])) {$url .= $_SERVER['REDIRECT_URL'];} else if (isset($_SERVER['PHP_SELF'])) {$url .= $_SERVER['PHP_SELF'];} else if (isset($_SERVER['SCRIPT_NAME'])) {$url .= $_SERVER['SCRIPT_NAME'];if (isset($_SERVER['PATH_INFO'])) {$url .= $_SERVER['PATH_INFO'];}}return $url;}//TODO : vérifier si les fonctions FWK & ZEND sont bien équivalente/*** Retourne l'url absolue d'une url donnée** @param string $url absilute or relative URL* @return string*/public function absoluteUrl($url){if (!empty($ur)) {$urlAbsolue = new Url($url);$urlAbsolue->normaliser();$url = $urlAbsolue->getUrl();} else {$url = $this->selfUrl();}return $url;/*if (empty($url)) {return $this->selfUrl();} else if (!preg_match('|^([^:]+)://|', $url)) {if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', $this->selfUrl(), $reg)) {$scheme = $reg[1];$auth = $reg[2];$host = $reg[3];$port = $reg[4];$path = $reg[5];$query = $reg[6];if ($url[0] == '/') {return $scheme. '://'. $auth. $host. (empty($port) ? '' : (':' . $port)). $url;} else {$dir = dirname($path);return $scheme. '://'. $auth. $host. (empty($port) ? '' : (':' . $port)). (strlen($dir) > 1 ? $dir : ''). '/'. $url;}}}return $url;*/}//TODO : voir si on ne peut pas glisser ça dans client ?//FIXME : je met une fonction SIMPLISSIME a améliorer et reécrire// La fonction de Zend est plus poussée est prend en compte le cas ou l'header ne peut pas etre envoyé/*** Rediriger vers la page du serveur avec les paramètres de confiration** @param string $url URL de retour* @param array $params paramètres additionnels*/public function redirect($url, $params) {//1. fabriquer l'url Get$urlRedirection = new Url($url);$urlRedirection->setRequete($params);//echo $urlRedirection->getUrl();try {header('Location:'.$urlRedirection->getUrl());} catch (Exception $e) {//TODO : voir autres méthodes de redirection// > balise META// > formulaire HTML// > JS}}}?>