/trunk/composants/openid/Services/Yadis/PlainHTTPFetcher.php |
---|
New file |
0,0 → 1,245 |
<?php |
/** |
* This module contains the plain non-curl HTTP fetcher |
* implementation. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Interface import |
*/ |
require_once "Services/Yadis/HTTPFetcher.php"; |
/** |
* This class implements a plain, hand-built socket-based fetcher |
* which will be used in the event that CURL is unavailable. |
* |
* @package Yadis |
*/ |
class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher { |
function get($url, $extra_headers = null) |
{ |
if (!$this->allowedURL($url)) { |
trigger_error("Bad URL scheme in url: " . $url, |
E_USER_WARNING); |
return null; |
} |
$redir = true; |
$stop = time() + $this->timeout; |
$off = $this->timeout; |
while ($redir && ($off > 0)) { |
$parts = parse_url($url); |
$specify_port = true; |
// Set a default port. |
if (!array_key_exists('port', $parts)) { |
$specify_port = false; |
if ($parts['scheme'] == 'http') { |
$parts['port'] = 80; |
} elseif ($parts['scheme'] == 'https') { |
$parts['port'] = 443; |
} else { |
trigger_error("fetcher post method doesn't support " . |
" scheme '" . $parts['scheme'] . |
"', no default port available", |
E_USER_WARNING); |
return null; |
} |
} |
$host = $parts['host']; |
if ($parts['scheme'] == 'https') { |
$host = 'ssl://' . $host; |
} |
$user_agent = "PHP Yadis Library Fetcher"; |
$headers = array( |
"GET ".$parts['path']. |
(array_key_exists('query', $parts) ? |
"?".$parts['query'] : ""). |
" HTTP/1.0", |
"User-Agent: $user_agent", |
"Host: ".$parts['host']. |
($specify_port ? ":".$parts['port'] : ""), |
"Port: ".$parts['port']); |
$errno = 0; |
$errstr = ''; |
if ($extra_headers) { |
foreach ($extra_headers as $h) { |
$headers[] = $h; |
} |
} |
@$sock = fsockopen($host, $parts['port'], $errno, $errstr, |
$this->timeout); |
if ($sock === false) { |
return false; |
} |
stream_set_timeout($sock, $this->timeout); |
fputs($sock, implode("\r\n", $headers) . "\r\n\r\n"); |
$data = ""; |
while (!feof($sock)) { |
$data .= fgets($sock, 1024); |
} |
fclose($sock); |
// Split response into header and body sections |
list($headers, $body) = explode("\r\n\r\n", $data, 2); |
$headers = explode("\r\n", $headers); |
$http_code = explode(" ", $headers[0]); |
$code = $http_code[1]; |
if (in_array($code, array('301', '302'))) { |
$url = $this->_findRedirect($headers); |
$redir = true; |
} else { |
$redir = false; |
} |
$off = $stop - time(); |
} |
$new_headers = array(); |
foreach ($headers as $header) { |
if (preg_match("/:/", $header)) { |
list($name, $value) = explode(": ", $header, 2); |
$new_headers[$name] = $value; |
} |
} |
return new Services_Yadis_HTTPResponse($url, $code, $new_headers, $body); |
} |
function post($url, $body, $extra_headers = null) |
{ |
if (!$this->allowedURL($url)) { |
trigger_error("Bad URL scheme in url: " . $url, |
E_USER_WARNING); |
return null; |
} |
$parts = parse_url($url); |
$headers = array(); |
$post_path = $parts['path']; |
if (isset($parts['query'])) { |
$post_path .= '?' . $parts['query']; |
} |
$headers[] = "POST ".$post_path." HTTP/1.0"; |
$headers[] = "Host: " . $parts['host']; |
$headers[] = "Content-type: application/x-www-form-urlencoded"; |
$headers[] = "Content-length: " . strval(strlen($body)); |
if ($extra_headers && |
is_array($extra_headers)) { |
$headers = array_merge($headers, $extra_headers); |
} |
// Join all headers together. |
$all_headers = implode("\r\n", $headers); |
// Add headers, two newlines, and request body. |
$request = $all_headers . "\r\n\r\n" . $body; |
// Set a default port. |
if (!array_key_exists('port', $parts)) { |
if ($parts['scheme'] == 'http') { |
$parts['port'] = 80; |
} elseif ($parts['scheme'] == 'https') { |
$parts['port'] = 443; |
} else { |
trigger_error("fetcher post method doesn't support scheme '" . |
$parts['scheme'] . |
"', no default port available", |
E_USER_WARNING); |
return null; |
} |
} |
if ($parts['scheme'] == 'https') { |
$parts['host'] = sprintf("ssl://%s", $parts['host']); |
} |
// Connect to the remote server. |
$errno = 0; |
$errstr = ''; |
$sock = fsockopen($parts['host'], $parts['port'], $errno, $errstr, |
$this->timeout); |
if ($sock === false) { |
trigger_error("Could not connect to " . $parts['host'] . |
" port " . $parts['port'], |
E_USER_WARNING); |
return null; |
} |
stream_set_timeout($sock, $this->timeout); |
// Write the POST request. |
fputs($sock, $request); |
// Get the response from the server. |
$response = ""; |
while (!feof($sock)) { |
if ($data = fgets($sock, 128)) { |
$response .= $data; |
} else { |
break; |
} |
} |
// Split the request into headers and body. |
list($headers, $response_body) = explode("\r\n\r\n", $response, 2); |
$headers = explode("\r\n", $headers); |
// Expect the first line of the headers data to be something |
// like HTTP/1.1 200 OK. Split the line on spaces and take |
// the second token, which should be the return code. |
$http_code = explode(" ", $headers[0]); |
$code = $http_code[1]; |
$new_headers = array(); |
foreach ($headers as $header) { |
if (preg_match("/:/", $header)) { |
list($name, $value) = explode(": ", $header, 2); |
$new_headers[$name] = $value; |
} |
} |
return new Services_Yadis_HTTPResponse($url, $code, |
$headers, $response_body); |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/Yadis.php |
---|
New file |
0,0 → 1,313 |
<?php |
/** |
* The core PHP Yadis implementation. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Need both fetcher types so we can use the right one based on the |
* presence or absence of CURL. |
*/ |
require_once "Services/Yadis/PlainHTTPFetcher.php"; |
require_once "Services/Yadis/ParanoidHTTPFetcher.php"; |
/** |
* Need this for parsing HTML (looking for META tags). |
*/ |
require_once "Services/Yadis/ParseHTML.php"; |
/** |
* Need this to parse the XRDS document during Yadis discovery. |
*/ |
require_once "Services/Yadis/XRDS.php"; |
/** |
* This is the core of the PHP Yadis library. This is the only class |
* a user needs to use to perform Yadis discovery. This class |
* performs the discovery AND stores the result of the discovery. |
* |
* First, require this library into your program source: |
* |
* <pre> require_once "Services/Yadis/Yadis.php";</pre> |
* |
* To perform Yadis discovery, first call the "discover" method |
* statically with a URI parameter: |
* |
* <pre> $http_response = array(); |
* $fetcher = Services_Yadis_Yadis::getHTTPFetcher(); |
* $yadis_object = Services_Yadis_Yadis::discover($uri, |
* $http_response, $fetcher);</pre> |
* |
* If the discovery succeeds, $yadis_object will be an instance of |
* {@link Services_Yadis_Yadis}. If not, it will be null. The XRDS |
* document found during discovery should have service descriptions, |
* which can be accessed by calling |
* |
* <pre> $service_list = $yadis_object->services();</pre> |
* |
* which returns an array of objects which describe each service. |
* These objects are instances of Services_Yadis_Service. Each object |
* describes exactly one whole Service element, complete with all of |
* its Types and URIs (no expansion is performed). The common use |
* case for using the service objects returned by services() is to |
* write one or more filter functions and pass those to services(): |
* |
* <pre> $service_list = $yadis_object->services( |
* array("filterByURI", |
* "filterByExtension"));</pre> |
* |
* The filter functions (whose names appear in the array passed to |
* services()) take the following form: |
* |
* <pre> function myFilter(&$service) { |
* // Query $service object here. Return true if the service |
* // matches your query; false if not. |
* }</pre> |
* |
* This is an example of a filter which uses a regular expression to |
* match the content of URI tags (note that the Services_Yadis_Service |
* class provides a getURIs() method which you should use instead of |
* this contrived example): |
* |
* <pre> |
* function URIMatcher(&$service) { |
* foreach ($service->getElements('xrd:URI') as $uri) { |
* if (preg_match("/some_pattern/", |
* $service->parser->content($uri))) { |
* return true; |
* } |
* } |
* return false; |
* }</pre> |
* |
* The filter functions you pass will be called for each service |
* object to determine which ones match the criteria your filters |
* specify. The default behavior is that if a given service object |
* matches ANY of the filters specified in the services() call, it |
* will be returned. You can specify that a given service object will |
* be returned ONLY if it matches ALL specified filters by changing |
* the match mode of services(): |
* |
* <pre> $yadis_object->services(array("filter1", "filter2"), |
* SERVICES_YADIS_MATCH_ALL);</pre> |
* |
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link |
* SERVICES_YADIS_MATCH_ANY}. |
* |
* Services described in an XRDS should have a library which you'll |
* probably be using. Those libraries are responsible for defining |
* filters that can be used with the "services()" call. If you need |
* to write your own filter, see the documentation for {@link |
* Services_Yadis_Service}. |
* |
* @package Yadis |
*/ |
class Services_Yadis_Yadis { |
/** |
* Returns an HTTP fetcher object. If the CURL extension is |
* present, an instance of {@link Services_Yadis_ParanoidHTTPFetcher} |
* is returned. If not, an instance of |
* {@link Services_Yadis_PlainHTTPFetcher} is returned. |
*/ |
function getHTTPFetcher($timeout = 20) |
{ |
if (Services_Yadis_Yadis::curlPresent()) { |
$fetcher = new Services_Yadis_ParanoidHTTPFetcher($timeout); |
} else { |
$fetcher = new Services_Yadis_PlainHTTPFetcher($timeout); |
} |
return $fetcher; |
} |
function curlPresent() |
{ |
return function_exists('curl_init'); |
} |
/** |
* @access private |
*/ |
function _getHeader($header_list, $names) |
{ |
foreach ($header_list as $name => $value) { |
foreach ($names as $n) { |
if (strtolower($name) == strtolower($n)) { |
return $value; |
} |
} |
} |
return null; |
} |
/** |
* @access private |
*/ |
function _getContentType($content_type_header) |
{ |
if ($content_type_header) { |
$parts = explode(";", $content_type_header); |
return strtolower($parts[0]); |
} |
} |
/** |
* This should be called statically and will build a Yadis |
* instance if the discovery process succeeds. This implements |
* Yadis discovery as specified in the Yadis specification. |
* |
* @param string $uri The URI on which to perform Yadis discovery. |
* |
* @param array $http_response An array reference where the HTTP |
* response object will be stored (see {@link |
* Services_Yadis_HTTPResponse}. |
* |
* @param Services_Yadis_HTTPFetcher $fetcher An instance of a |
* Services_Yadis_HTTPFetcher subclass. |
* |
* @param array $extra_ns_map An array which maps namespace names |
* to namespace URIs to be used when parsing the Yadis XRDS |
* document. |
* |
* @param integer $timeout An optional fetcher timeout, in seconds. |
* |
* @return mixed $obj Either null or an instance of |
* Services_Yadis_Yadis, depending on whether the discovery |
* succeeded. |
*/ |
function discover($uri, &$http_response, &$fetcher, |
$extra_ns_map = null, $timeout = 20) |
{ |
if (!$uri) { |
return null; |
} |
$request_uri = $uri; |
$headers = array("Accept: application/xrds+xml"); |
if (!$fetcher) { |
$fetcher = Services_Yadis_Yadis::getHTTPFetcher($timeout); |
} |
$response = $fetcher->get($uri, $headers); |
$http_response = $response; |
if (!$response) { |
return null; |
} |
if ($response->status != 200) { |
return null; |
} |
$xrds_uri = $response->final_url; |
$uri = $response->final_url; |
$body = $response->body; |
$xrds_header_uri = Services_Yadis_Yadis::_getHeader( |
$response->headers, |
array('x-xrds-location', |
'x-yadis-location')); |
$content_type = Services_Yadis_Yadis::_getHeader($response->headers, |
array('content-type')); |
if ($xrds_header_uri) { |
$xrds_uri = $xrds_header_uri; |
$response = $fetcher->get($xrds_uri); |
$http_response = $response; |
if (!$response) { |
return null; |
} else { |
$body = $response->body; |
$headers = $response->headers; |
$content_type = Services_Yadis_Yadis::_getHeader($headers, |
array('content-type')); |
} |
} |
if (Services_Yadis_Yadis::_getContentType($content_type) != |
'application/xrds+xml') { |
// Treat the body as HTML and look for a META tag. |
$parser = new Services_Yadis_ParseHTML(); |
$new_uri = $parser->getHTTPEquiv($body); |
$xrds_uri = null; |
if ($new_uri) { |
$response = $fetcher->get($new_uri); |
if ($response->status != 200) { |
return null; |
} |
$http_response = $response; |
$body = $response->body; |
$xrds_uri = $new_uri; |
$content_type = Services_Yadis_Yadis::_getHeader( |
$response->headers, |
array('content-type')); |
} |
} |
$xrds = Services_Yadis_XRDS::parseXRDS($body, $extra_ns_map); |
if ($xrds !== null) { |
$y = new Services_Yadis_Yadis(); |
$y->request_uri = $request_uri; |
$y->xrds = $xrds; |
$y->uri = $uri; |
$y->xrds_uri = $xrds_uri; |
$y->body = $body; |
$y->content_type = $content_type; |
return $y; |
} else { |
return null; |
} |
} |
/** |
* Instantiates an empty Services_Yadis_Yadis object. This |
* constructor should not be used by any user of the library. |
* This constructor results in a completely useless object which |
* must be populated with valid discovery information. Instead of |
* using this constructor, call |
* Services_Yadis_Yadis::discover($uri). |
*/ |
function Services_Yadis_Yadis() |
{ |
$this->request_uri = null; |
$this->uri = null; |
$this->xrds = null; |
$this->xrds_uri = null; |
$this->body = null; |
$this->content_type = null; |
} |
/** |
* Returns the list of service objects as described by the XRDS |
* document, if this yadis object represents a successful Yadis |
* discovery. |
* |
* @return array $services An array of {@link Services_Yadis_Service} |
* objects |
*/ |
function services() |
{ |
if ($this->xrds) { |
return $this->xrds->services(); |
} |
return null; |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/Manager.php |
---|
New file |
0,0 → 1,496 |
<?php |
/** |
* Yadis service manager to be used during yadis-driven authentication |
* attempts. |
* |
* @package Yadis |
*/ |
/** |
* The base session class used by the Services_Yadis_Manager. This |
* class wraps the default PHP session machinery and should be |
* subclassed if your application doesn't use PHP sessioning. |
* |
* @package Yadis |
*/ |
class Services_Yadis_PHPSession { |
/** |
* Set a session key/value pair. |
* |
* @param string $name The name of the session key to add. |
* @param string $value The value to add to the session. |
*/ |
function set($name, $value) |
{ |
$_SESSION[$name] = $value; |
} |
/** |
* Get a key's value from the session. |
* |
* @param string $name The name of the key to retrieve. |
* @param string $default The optional value to return if the key |
* is not found in the session. |
* @return string $result The key's value in the session or |
* $default if it isn't found. |
*/ |
function get($name, $default=null) |
{ |
if (array_key_exists($name, $_SESSION)) { |
return $_SESSION[$name]; |
} else { |
return $default; |
} |
} |
/** |
* Remove a key/value pair from the session. |
* |
* @param string $name The name of the key to remove. |
*/ |
function del($name) |
{ |
unset($_SESSION[$name]); |
} |
/** |
* Return the contents of the session in array form. |
*/ |
function contents() |
{ |
return $_SESSION; |
} |
} |
/** |
* A session helper class designed to translate between arrays and |
* objects. Note that the class used must have a constructor that |
* takes no parameters. This is not a general solution, but it works |
* for dumb objects that just need to have attributes set. The idea |
* is that you'll subclass this and override $this->check($data) -> |
* bool to implement your own session data validation. |
*/ |
class Services_Yadis_SessionLoader { |
/** |
* Override this. |
*/ |
function check($data) |
{ |
return true; |
} |
/** |
* Given a session data value (an array), this creates an object |
* (returned by $this->newObject()) whose attributes and values |
* are those in $data. Returns null if $data lacks keys found in |
* $this->requiredKeys(). Returns null if $this->check($data) |
* evaluates to false. Returns null if $this->newObject() |
* evaluates to false. |
*/ |
function fromSession($data) |
{ |
if (!$data) { |
return null; |
} |
$required = $this->requiredKeys(); |
foreach ($required as $k) { |
if (!array_key_exists($k, $data)) { |
return null; |
} |
} |
if (!$this->check($data)) { |
return null; |
} |
$data = array_merge($data, $this->prepareForLoad($data)); |
$obj = $this->newObject($data); |
if (!$obj) { |
return null; |
} |
foreach ($required as $k) { |
$obj->$k = $data[$k]; |
} |
return $obj; |
} |
/** |
* Prepares the data array by making any necessary changes. |
* Returns an array whose keys and values will be used to update |
* the original data array before calling $this->newObject($data). |
*/ |
function prepareForLoad($data) |
{ |
return array(); |
} |
/** |
* Returns a new instance of this loader's class, using the |
* session data to construct it if necessary. The object need |
* only be created; $this->fromSession() will take care of setting |
* the object's attributes. |
*/ |
function newObject($data) |
{ |
return null; |
} |
/** |
* Returns an array of keys and values built from the attributes |
* of $obj. If $this->prepareForSave($obj) returns an array, its keys |
* and values are used to update the $data array of attributes |
* from $obj. |
*/ |
function toSession($obj) |
{ |
$data = array(); |
foreach ($obj as $k => $v) { |
$data[$k] = $v; |
} |
$extra = $this->prepareForSave($obj); |
if ($extra && is_array($extra)) { |
foreach ($extra as $k => $v) { |
$data[$k] = $v; |
} |
} |
return $data; |
} |
/** |
* Override this. |
*/ |
function prepareForSave($obj) |
{ |
return array(); |
} |
} |
class Auth_OpenID_ServiceEndpointLoader extends Services_Yadis_SessionLoader { |
function newObject($data) |
{ |
return new Auth_OpenID_ServiceEndpoint(); |
} |
function requiredKeys() |
{ |
$obj = new Auth_OpenID_ServiceEndpoint(); |
$data = array(); |
foreach ($obj as $k => $v) { |
$data[] = $k; |
} |
return $data; |
} |
function check($data) |
{ |
return is_array($data['type_uris']); |
} |
} |
class Services_Yadis_ManagerLoader extends Services_Yadis_SessionLoader { |
function requiredKeys() |
{ |
return array('starting_url', |
'yadis_url', |
'services', |
'session_key', |
'_current', |
'stale'); |
} |
function newObject($data) |
{ |
return new Services_Yadis_Manager($data['starting_url'], |
$data['yadis_url'], |
$data['services'], |
$data['session_key']); |
} |
function check($data) |
{ |
return is_array($data['services']); |
} |
function prepareForLoad($data) |
{ |
$loader = new Auth_OpenID_ServiceEndpointLoader(); |
$services = array(); |
foreach ($data['services'] as $s) { |
$services[] = $loader->fromSession($s); |
} |
return array('services' => $services); |
} |
function prepareForSave($obj) |
{ |
$loader = new Auth_OpenID_ServiceEndpointLoader(); |
$services = array(); |
foreach ($obj->services as $s) { |
$services[] = $loader->toSession($s); |
} |
return array('services' => $services); |
} |
} |
/** |
* The Yadis service manager which stores state in a session and |
* iterates over <Service> elements in a Yadis XRDS document and lets |
* a caller attempt to use each one. This is used by the Yadis |
* library internally. |
* |
* @package Yadis |
*/ |
class Services_Yadis_Manager { |
/** |
* Intialize a new yadis service manager. |
* |
* @access private |
*/ |
function Services_Yadis_Manager($starting_url, $yadis_url, |
$services, $session_key) |
{ |
// The URL that was used to initiate the Yadis protocol |
$this->starting_url = $starting_url; |
// The URL after following redirects (the identifier) |
$this->yadis_url = $yadis_url; |
// List of service elements |
$this->services = $services; |
$this->session_key = $session_key; |
// Reference to the current service object |
$this->_current = null; |
// Stale flag for cleanup if PHP lib has trouble. |
$this->stale = false; |
} |
/** |
* @access private |
*/ |
function length() |
{ |
// How many untried services remain? |
return count($this->services); |
} |
/** |
* Return the next service |
* |
* $this->current() will continue to return that service until the |
* next call to this method. |
*/ |
function nextService() |
{ |
if ($this->services) { |
$this->_current = array_shift($this->services); |
} else { |
$this->_current = null; |
} |
return $this->_current; |
} |
/** |
* @access private |
*/ |
function current() |
{ |
// Return the current service. |
// Returns None if there are no services left. |
return $this->_current; |
} |
/** |
* @access private |
*/ |
function forURL($url) |
{ |
return in_array($url, array($this->starting_url, $this->yadis_url)); |
} |
/** |
* @access private |
*/ |
function started() |
{ |
// Has the first service been returned? |
return $this->_current !== null; |
} |
} |
/** |
* State management for discovery. |
* |
* High-level usage pattern is to call .getNextService(discover) in |
* order to find the next available service for this user for this |
* session. Once a request completes, call .finish() to clean up the |
* session state. |
* |
* @package Yadis |
*/ |
class Services_Yadis_Discovery { |
/** |
* @access private |
*/ |
var $DEFAULT_SUFFIX = 'auth'; |
/** |
* @access private |
*/ |
var $PREFIX = '_yadis_services_'; |
/** |
* Initialize a discovery object. |
* |
* @param Services_Yadis_PHPSession $session An object which |
* implements the Services_Yadis_PHPSession API. |
* @param string $url The URL on which to attempt discovery. |
* @param string $session_key_suffix The optional session key |
* suffix override. |
*/ |
function Services_Yadis_Discovery(&$session, $url, |
$session_key_suffix = null) |
{ |
/// Initialize a discovery object |
$this->session =& $session; |
$this->url = $url; |
if ($session_key_suffix === null) { |
$session_key_suffix = $this->DEFAULT_SUFFIX; |
} |
$this->session_key_suffix = $session_key_suffix; |
$this->session_key = $this->PREFIX . $this->session_key_suffix; |
} |
/** |
* Return the next authentication service for the pair of |
* user_input and session. This function handles fallback. |
*/ |
function getNextService($discover_cb, &$fetcher) |
{ |
$manager = $this->getManager(); |
if (!$manager || (!$manager->services)) { |
$this->destroyManager(); |
$http_response = array(); |
$services = call_user_func($discover_cb, $this->url, |
$fetcher); |
$manager = $this->createManager($services, $this->url); |
} |
if ($manager) { |
$loader = new Services_Yadis_ManagerLoader(); |
$service = $manager->nextService(); |
$this->session->set($this->session_key, |
serialize($loader->toSession($manager))); |
} else { |
$service = null; |
} |
return $service; |
} |
/** |
* Clean up Yadis-related services in the session and return the |
* most-recently-attempted service from the manager, if one |
* exists. |
*/ |
function cleanup() |
{ |
$manager = $this->getManager(); |
if ($manager) { |
$service = $manager->current(); |
$this->destroyManager(); |
} else { |
$service = null; |
} |
return $service; |
} |
/** |
* @access private |
*/ |
function getSessionKey() |
{ |
// Get the session key for this starting URL and suffix |
return $this->PREFIX . $this->session_key_suffix; |
} |
/** |
* @access private |
*/ |
function &getManager() |
{ |
// Extract the YadisServiceManager for this object's URL and |
// suffix from the session. |
$manager_str = $this->session->get($this->getSessionKey()); |
$manager = null; |
if ($manager_str !== null) { |
$loader = new Services_Yadis_ManagerLoader(); |
$manager = $loader->fromSession(unserialize($manager_str)); |
} |
if ($manager && $manager->forURL($this->url)) { |
return $manager; |
} else { |
$unused = null; |
return $unused; |
} |
} |
/** |
* @access private |
*/ |
function &createManager($services, $yadis_url = null) |
{ |
$key = $this->getSessionKey(); |
if ($this->getManager()) { |
return $this->getManager(); |
} |
if ($services) { |
$loader = new Services_Yadis_ManagerLoader(); |
$manager = new Services_Yadis_Manager($this->url, $yadis_url, |
$services, $key); |
$this->session->set($this->session_key, |
serialize($loader->toSession($manager))); |
return $manager; |
} else { |
// Oh, PHP. |
$unused = null; |
return $unused; |
} |
} |
/** |
* @access private |
*/ |
function destroyManager() |
{ |
if ($this->getManager() !== null) { |
$key = $this->getSessionKey(); |
$this->session->del($key); |
} |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/Misc.php |
---|
New file |
0,0 → 1,59 |
<?php |
/** |
* Miscellaneous utility values and functions for OpenID and Yadis. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
function Services_Yadis_getUCSChars() |
{ |
return array( |
array(0xA0, 0xD7FF), |
array(0xF900, 0xFDCF), |
array(0xFDF0, 0xFFEF), |
array(0x10000, 0x1FFFD), |
array(0x20000, 0x2FFFD), |
array(0x30000, 0x3FFFD), |
array(0x40000, 0x4FFFD), |
array(0x50000, 0x5FFFD), |
array(0x60000, 0x6FFFD), |
array(0x70000, 0x7FFFD), |
array(0x80000, 0x8FFFD), |
array(0x90000, 0x9FFFD), |
array(0xA0000, 0xAFFFD), |
array(0xB0000, 0xBFFFD), |
array(0xC0000, 0xCFFFD), |
array(0xD0000, 0xDFFFD), |
array(0xE1000, 0xEFFFD) |
); |
} |
function Services_Yadis_getIPrivateChars() |
{ |
return array( |
array(0xE000, 0xF8FF), |
array(0xF0000, 0xFFFFD), |
array(0x100000, 0x10FFFD) |
); |
} |
function Services_Yadis_pct_escape_unicode($char_match) |
{ |
$c = $char_match[0]; |
$result = ""; |
for ($i = 0; $i < strlen($c); $i++) { |
$result .= "%".sprintf("%X", ord($c[$i])); |
} |
return $result; |
} |
function Services_Yadis_startswith($s, $stuff) |
{ |
return strpos($s, $stuff) === 0; |
} |
?> |
/trunk/composants/openid/Services/Yadis/XRIRes.php |
---|
New file |
0,0 → 1,68 |
<?php |
require_once 'Services/Yadis/XRDS.php'; |
require_once 'Services/Yadis/XRI.php'; |
class Services_Yadis_ProxyResolver { |
function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null) |
{ |
$this->fetcher =& $fetcher; |
$this->proxy_url = $proxy_url; |
if (!$this->proxy_url) { |
$this->proxy_url = Services_Yadis_getDefaultProxy(); |
} |
} |
function queryURL($xri, $service_type = null) |
{ |
// trim off the xri:// prefix |
$qxri = substr(Services_Yadis_toURINormal($xri), 6); |
$hxri = $this->proxy_url . $qxri; |
$args = array( |
'_xrd_r' => 'application/xrds+xml' |
); |
if ($service_type) { |
$args['_xrd_t'] = $service_type; |
} else { |
// Don't perform service endpoint selection. |
$args['_xrd_r'] .= ';sep=false'; |
} |
$query = Services_Yadis_XRIAppendArgs($hxri, $args); |
return $query; |
} |
function query($xri, $service_types, $filters = array()) |
{ |
$services = array(); |
$canonicalID = null; |
foreach ($service_types as $service_type) { |
$url = $this->queryURL($xri, $service_type); |
$response = $this->fetcher->get($url); |
if ($response->status != 200) { |
continue; |
} |
$xrds = Services_Yadis_XRDS::parseXRDS($response->body); |
if (!$xrds) { |
continue; |
} |
$canonicalID = Services_Yadis_getCanonicalID($xri, |
$xrds); |
if ($canonicalID === false) { |
return null; |
} |
$some_services = $xrds->services($filters); |
$services = array_merge($services, $some_services); |
// TODO: |
// * If we do get hits for multiple service_types, we're |
// almost certainly going to have duplicated service |
// entries and broken priority ordering. |
} |
return array($canonicalID, $services); |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/ParanoidHTTPFetcher.php |
---|
New file |
0,0 → 1,177 |
<?php |
/** |
* This module contains the CURL-based HTTP fetcher implementation. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Interface import |
*/ |
require_once "Services/Yadis/HTTPFetcher.php"; |
/** |
* A paranoid {@link Services_Yadis_HTTPFetcher} class which uses CURL |
* for fetching. |
* |
* @package Yadis |
*/ |
class Services_Yadis_ParanoidHTTPFetcher extends Services_Yadis_HTTPFetcher { |
function Services_Yadis_ParanoidHTTPFetcher() |
{ |
$this->reset(); |
} |
function reset() |
{ |
$this->headers = array(); |
$this->data = ""; |
} |
/** |
* @access private |
*/ |
function _writeHeader($ch, $header) |
{ |
array_push($this->headers, rtrim($header)); |
return strlen($header); |
} |
/** |
* @access private |
*/ |
function _writeData($ch, $data) |
{ |
$this->data .= $data; |
return strlen($data); |
} |
function get($url, $extra_headers = null) |
{ |
$stop = time() + $this->timeout; |
$off = $this->timeout; |
$redir = true; |
while ($redir && ($off > 0)) { |
$this->reset(); |
$c = curl_init(); |
if (defined('CURLOPT_NOSIGNAL')) { |
curl_setopt($c, CURLOPT_NOSIGNAL, true); |
} |
if (!$this->allowedURL($url)) { |
trigger_error(sprintf("Fetching URL not allowed: %s", $url), |
E_USER_WARNING); |
return null; |
} |
curl_setopt($c, CURLOPT_WRITEFUNCTION, |
array(&$this, "_writeData")); |
curl_setopt($c, CURLOPT_HEADERFUNCTION, |
array(&$this, "_writeHeader")); |
if ($extra_headers) { |
curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers); |
} |
curl_setopt($c, CURLOPT_TIMEOUT, $off); |
curl_setopt($c, CURLOPT_URL, $url); |
curl_exec($c); |
$code = curl_getinfo($c, CURLINFO_HTTP_CODE); |
$body = $this->data; |
$headers = $this->headers; |
if (!$code) { |
return null; |
} |
if (in_array($code, array(301, 302, 303, 307))) { |
$url = $this->_findRedirect($headers); |
$redir = true; |
} else { |
$redir = false; |
curl_close($c); |
$new_headers = array(); |
foreach ($headers as $header) { |
if (preg_match("/:/", $header)) { |
list($name, $value) = explode(": ", $header, 2); |
$new_headers[$name] = $value; |
} |
} |
return new Services_Yadis_HTTPResponse($url, $code, |
$new_headers, $body); |
} |
$off = $stop - time(); |
} |
trigger_error(sprintf("Timed out fetching: %s", $url), |
E_USER_WARNING); |
return null; |
} |
function post($url, $body) |
{ |
$this->reset(); |
if (!$this->allowedURL($url)) { |
trigger_error(sprintf("Fetching URL not allowed: %s", $url), |
E_USER_WARNING); |
return null; |
} |
$c = curl_init(); |
curl_setopt($c, CURLOPT_NOSIGNAL, true); |
curl_setopt($c, CURLOPT_POST, true); |
curl_setopt($c, CURLOPT_POSTFIELDS, $body); |
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout); |
curl_setopt($c, CURLOPT_URL, $url); |
curl_setopt($c, CURLOPT_WRITEFUNCTION, |
array(&$this, "_writeData")); |
curl_exec($c); |
$code = curl_getinfo($c, CURLINFO_HTTP_CODE); |
if (!$code) { |
trigger_error("No HTTP code returned", E_USER_WARNING); |
return null; |
} |
$body = $this->data; |
curl_close($c); |
$new_headers = array(); |
foreach ($this->headers as $header) { |
if (preg_match("/:/", $header)) { |
list($name, $value) = explode(": ", $header, 2); |
$new_headers[$name] = $value; |
} |
} |
return new Services_Yadis_HTTPResponse($url, $code, |
$new_headers, $body); |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/ParseHTML.php |
---|
New file |
0,0 → 1,258 |
<?php |
/** |
* This is the HTML pseudo-parser for the Yadis library. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* This class is responsible for scanning an HTML string to find META |
* tags and their attributes. This is used by the Yadis discovery |
* process. This class must be instantiated to be used. |
* |
* @package Yadis |
*/ |
class Services_Yadis_ParseHTML { |
/** |
* @access private |
*/ |
var $_re_flags = "si"; |
/** |
* @access private |
*/ |
var $_removed_re = |
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>"; |
/** |
* @access private |
*/ |
var $_tag_expr = "<%s%s(?:\s.*?)?%s>"; |
/** |
* @access private |
*/ |
var $_attr_find = '\b([-\w]+)=(".*?"|\'.*?\'|.+?)[\s>]'; |
function Services_Yadis_ParseHTML() |
{ |
$this->_attr_find = sprintf("/%s/%s", |
$this->_attr_find, |
$this->_re_flags); |
$this->_removed_re = sprintf("/%s/%s", |
$this->_removed_re, |
$this->_re_flags); |
$this->_entity_replacements = array( |
'amp' => '&', |
'lt' => '<', |
'gt' => '>', |
'quot' => '"' |
); |
$this->_ent_replace = |
sprintf("&(%s);", implode("|", |
$this->_entity_replacements)); |
} |
/** |
* Replace HTML entities (amp, lt, gt, and quot) as well as |
* numeric entities (e.g. #x9f;) with their actual values and |
* return the new string. |
* |
* @access private |
* @param string $str The string in which to look for entities |
* @return string $new_str The new string entities decoded |
*/ |
function replaceEntities($str) |
{ |
foreach ($this->_entity_replacements as $old => $new) { |
$str = preg_replace(sprintf("/&%s;/", $old), $new, $str); |
} |
// Replace numeric entities because html_entity_decode doesn't |
// do it for us. |
$str = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $str); |
$str = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $str); |
return $str; |
} |
/** |
* Strip single and double quotes off of a string, if they are |
* present. |
* |
* @access private |
* @param string $str The original string |
* @return string $new_str The new string with leading and |
* trailing quotes removed |
*/ |
function removeQuotes($str) |
{ |
$matches = array(); |
$double = '/^"(.*)"$/'; |
$single = "/^\'(.*)\'$/"; |
if (preg_match($double, $str, $matches)) { |
return $matches[1]; |
} else if (preg_match($single, $str, $matches)) { |
return $matches[1]; |
} else { |
return $str; |
} |
} |
/** |
* Create a regular expression that will match an opening |
* or closing tag from a set of names. |
* |
* @access private |
* @param mixed $tag_names Tag names to match |
* @param mixed $close false/0 = no, true/1 = yes, other = maybe |
* @param mixed $self_close false/0 = no, true/1 = yes, other = maybe |
* @return string $regex A regular expression string to be used |
* in, say, preg_match. |
*/ |
function tagPattern($tag_names, $close, $self_close) |
{ |
if (is_array($tag_names)) { |
$tag_names = '(?:'.implode('|',$tag_names).')'; |
} |
if ($close) { |
$close = '\/' . (($close == 1)? '' : '?'); |
} else { |
$close = ''; |
} |
if ($self_close) { |
$self_close = '(?:\/\s*)' . (($self_close == 1)? '' : '?'); |
} else { |
$self_close = ''; |
} |
$expr = sprintf($this->_tag_expr, $close, $tag_names, $self_close); |
return sprintf("/%s/%s", $expr, $this->_re_flags); |
} |
/** |
* Given an HTML document string, this finds all the META tags in |
* the document, provided they are found in the |
* <HTML><HEAD>...</HEAD> section of the document. The <HTML> tag |
* may be missing. |
* |
* @access private |
* @param string $html_string An HTMl document string |
* @return array $tag_list Array of tags; each tag is an array of |
* attribute -> value. |
*/ |
function getMetaTags($html_string) |
{ |
$html_string = preg_replace($this->_removed_re, |
"", |
$html_string); |
$key_tags = array($this->tagPattern('html', false, false), |
$this->tagPattern('head', false, false), |
$this->tagPattern('head', true, false), |
$this->tagPattern('html', true, false), |
$this->tagPattern(array( |
'body', 'frameset', 'frame', 'p', 'div', |
'table','span','a'), 'maybe', 'maybe')); |
$key_tags_pos = array(); |
foreach ($key_tags as $pat) { |
$matches = array(); |
preg_match($pat, $html_string, $matches, PREG_OFFSET_CAPTURE); |
if($matches) { |
$key_tags_pos[] = $matches[0][1]; |
} else { |
$key_tags_pos[] = null; |
} |
} |
// no opening head tag |
if (is_null($key_tags_pos[1])) { |
return array(); |
} |
// the effective </head> is the min of the following |
if (is_null($key_tags_pos[2])) { |
$key_tags_pos[2] = strlen($html_string); |
} |
foreach (array($key_tags_pos[3], $key_tags_pos[4]) as $pos) { |
if (!is_null($pos) && $pos < $key_tags_pos[2]) { |
$key_tags_pos[2] = $pos; |
} |
} |
// closing head tag comes before opening head tag |
if ($key_tags_pos[1] > $key_tags_pos[2]) { |
return array(); |
} |
// if there is an opening html tag, make sure the opening head tag |
// comes after it |
if (!is_null($key_tags_pos[0]) && $key_tags_pos[1] < $key_tags_pos[0]) { |
return array(); |
} |
$html_string = substr($html_string, $key_tags_pos[1], ($key_tags_pos[2]-$key_tags_pos[1])); |
$link_data = array(); |
$link_matches = array(); |
if (!preg_match_all($this->tagPattern('meta', false, 'maybe'), |
$html_string, $link_matches)) { |
return array(); |
} |
foreach ($link_matches[0] as $link) { |
$attr_matches = array(); |
preg_match_all($this->_attr_find, $link, $attr_matches); |
$link_attrs = array(); |
foreach ($attr_matches[0] as $index => $full_match) { |
$name = $attr_matches[1][$index]; |
$value = $this->replaceEntities( |
$this->removeQuotes($attr_matches[2][$index])); |
$link_attrs[strtolower($name)] = $value; |
} |
$link_data[] = $link_attrs; |
} |
return $link_data; |
} |
/** |
* Looks for a META tag with an "http-equiv" attribute whose value |
* is one of ("x-xrds-location", "x-yadis-location"), ignoring |
* case. If such a META tag is found, its "content" attribute |
* value is returned. |
* |
* @param string $html_string An HTML document in string format |
* @return mixed $content The "content" attribute value of the |
* META tag, if found, or null if no such tag was found. |
*/ |
function getHTTPEquiv($html_string) |
{ |
$meta_tags = $this->getMetaTags($html_string); |
if ($meta_tags) { |
foreach ($meta_tags as $tag) { |
if (array_key_exists('http-equiv', $tag) && |
(in_array(strtolower($tag['http-equiv']), |
array('x-xrds-location', 'x-yadis-location'))) && |
array_key_exists('content', $tag)) { |
return $tag['content']; |
} |
} |
} |
return null; |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/HTTPFetcher.php |
---|
New file |
0,0 → 1,92 |
<?php |
/** |
* This module contains the HTTP fetcher interface |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
class Services_Yadis_HTTPResponse { |
function Services_Yadis_HTTPResponse($final_url = null, $status = null, |
$headers = null, $body = null) |
{ |
$this->final_url = $final_url; |
$this->status = $status; |
$this->headers = $headers; |
$this->body = $body; |
} |
} |
/** |
* This class is the interface for HTTP fetchers the Yadis library |
* uses. This interface is only important if you need to write a new |
* fetcher for some reason. |
* |
* @access private |
* @package Yadis |
*/ |
class Services_Yadis_HTTPFetcher { |
var $timeout = 20; // timeout in seconds. |
/** |
* Return whether a URL should be allowed. Override this method to |
* conform to your local policy. |
* |
* By default, will attempt to fetch any http or https URL. |
*/ |
function allowedURL($url) |
{ |
return $this->URLHasAllowedScheme($url); |
} |
/** |
* Is this an http or https URL? |
* |
* @access private |
*/ |
function URLHasAllowedScheme($url) |
{ |
return (bool)preg_match('/^https?:\/\//i', $url); |
} |
/** |
* @access private |
*/ |
function _findRedirect($headers) |
{ |
foreach ($headers as $line) { |
if (strpos($line, "Location: ") === 0) { |
$parts = explode(" ", $line, 2); |
return $parts[1]; |
} |
} |
return null; |
} |
/** |
* Fetches the specified URL using optional extra headers and |
* returns the server's response. |
* |
* @param string $url The URL to be fetched. |
* @param array $extra_headers An array of header strings |
* (e.g. "Accept: text/html"). |
* @return mixed $result An array of ($code, $url, $headers, |
* $body) if the URL could be fetched; null if the URL does not |
* pass the URLHasAllowedScheme check or if the server's response |
* is malformed. |
*/ |
function get($url, $headers) |
{ |
trigger_error("not implemented", E_USER_ERROR); |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/XML.php |
---|
New file |
0,0 → 1,365 |
<?php |
/** |
* XML-parsing classes to wrap the domxml and DOM extensions for PHP 4 |
* and 5, respectively. |
* |
* @package Yadis |
*/ |
/** |
* The base class for wrappers for available PHP XML-parsing |
* extensions. To work with this Yadis library, subclasses of this |
* class MUST implement the API as defined in the remarks for this |
* class. Subclasses of Services_Yadis_XMLParser are used to wrap |
* particular PHP XML extensions such as 'domxml'. These are used |
* internally by the library depending on the availability of |
* supported PHP XML extensions. |
* |
* @package Yadis |
*/ |
class Services_Yadis_XMLParser { |
/** |
* Initialize an instance of Services_Yadis_XMLParser with some |
* XML and namespaces. This SHOULD NOT be overridden by |
* subclasses. |
* |
* @param string $xml_string A string of XML to be parsed. |
* @param array $namespace_map An array of ($ns_name => $ns_uri) |
* to be registered with the XML parser. May be empty. |
* @return boolean $result True if the initialization and |
* namespace registration(s) succeeded; false otherwise. |
*/ |
function init($xml_string, $namespace_map) |
{ |
if (!$this->setXML($xml_string)) { |
return false; |
} |
foreach ($namespace_map as $prefix => $uri) { |
if (!$this->registerNamespace($prefix, $uri)) { |
return false; |
} |
} |
return true; |
} |
/** |
* Register a namespace with the XML parser. This should be |
* overridden by subclasses. |
* |
* @param string $prefix The namespace prefix to appear in XML tag |
* names. |
* |
* @param string $uri The namespace URI to be used to identify the |
* namespace in the XML. |
* |
* @return boolean $result True if the registration succeeded; |
* false otherwise. |
*/ |
function registerNamespace($prefix, $uri) |
{ |
// Not implemented. |
} |
/** |
* Set this parser object's XML payload. This should be |
* overridden by subclasses. |
* |
* @param string $xml_string The XML string to pass to this |
* object's XML parser. |
* |
* @return boolean $result True if the initialization succeeded; |
* false otherwise. |
*/ |
function setXML($xml_string) |
{ |
// Not implemented. |
} |
/** |
* Evaluate an XPath expression and return the resulting node |
* list. This should be overridden by subclasses. |
* |
* @param string $xpath The XPath expression to be evaluated. |
* |
* @param mixed $node A node object resulting from a previous |
* evalXPath call. This node, if specified, provides the context |
* for the evaluation of this xpath expression. |
* |
* @return array $node_list An array of matching opaque node |
* objects to be used with other methods of this parser class. |
*/ |
function evalXPath($xpath, $node = null) |
{ |
// Not implemented. |
} |
/** |
* Return the textual content of a specified node. |
* |
* @param mixed $node A node object from a previous call to |
* $this->evalXPath(). |
* |
* @return string $content The content of this node. |
*/ |
function content($node) |
{ |
// Not implemented. |
} |
/** |
* Return the attributes of a specified node. |
* |
* @param mixed $node A node object from a previous call to |
* $this->evalXPath(). |
* |
* @return array $attrs An array mapping attribute names to |
* values. |
*/ |
function attributes($node) |
{ |
// Not implemented. |
} |
} |
/** |
* This concrete implementation of Services_Yadis_XMLParser implements |
* the appropriate API for the 'domxml' extension which is typically |
* packaged with PHP 4. This class will be used whenever the 'domxml' |
* extension is detected. See the Services_Yadis_XMLParser class for |
* details on this class's methods. |
* |
* @package Yadis |
*/ |
class Services_Yadis_domxml extends Services_Yadis_XMLParser { |
function Services_Yadis_domxml() |
{ |
$this->xml = null; |
$this->doc = null; |
$this->xpath = null; |
$this->errors = array(); |
} |
function setXML($xml_string) |
{ |
$this->xml = $xml_string; |
$this->doc = @domxml_open_mem($xml_string, DOMXML_LOAD_PARSING, |
$this->errors); |
if (!$this->doc) { |
return false; |
} |
$this->xpath = $this->doc->xpath_new_context(); |
return true; |
} |
function registerNamespace($prefix, $uri) |
{ |
return xpath_register_ns($this->xpath, $prefix, $uri); |
} |
function &evalXPath($xpath, $node = null) |
{ |
if ($node) { |
$result = @$this->xpath->xpath_eval($xpath, $node); |
} else { |
$result = @$this->xpath->xpath_eval($xpath); |
} |
if (!$result->nodeset) { |
$n = array(); |
return $n; |
} |
return $result->nodeset; |
} |
function content($node) |
{ |
if ($node) { |
return $node->get_content(); |
} |
} |
function attributes($node) |
{ |
if ($node) { |
$arr = $node->attributes(); |
$result = array(); |
if ($arr) { |
foreach ($arr as $attrnode) { |
$result[$attrnode->name] = $attrnode->value; |
} |
} |
return $result; |
} |
} |
} |
/** |
* This concrete implementation of Services_Yadis_XMLParser implements |
* the appropriate API for the 'dom' extension which is typically |
* packaged with PHP 5. This class will be used whenever the 'dom' |
* extension is detected. See the Services_Yadis_XMLParser class for |
* details on this class's methods. |
* |
* @package Yadis |
*/ |
class Services_Yadis_dom extends Services_Yadis_XMLParser { |
function Services_Yadis_dom() |
{ |
$this->xml = null; |
$this->doc = null; |
$this->xpath = null; |
$this->errors = array(); |
} |
function setXML($xml_string) |
{ |
$this->xml = $xml_string; |
$this->doc = new DOMDocument; |
if (!$this->doc) { |
return false; |
} |
if (!@$this->doc->loadXML($xml_string)) { |
return false; |
} |
$this->xpath = new DOMXPath($this->doc); |
if ($this->xpath) { |
return true; |
} else { |
return false; |
} |
} |
function registerNamespace($prefix, $uri) |
{ |
return $this->xpath->registerNamespace($prefix, $uri); |
} |
function &evalXPath($xpath, $node = null) |
{ |
if ($node) { |
$result = @$this->xpath->query($xpath, $node); |
} else { |
$result = @$this->xpath->query($xpath); |
} |
$n = array(); |
for ($i = 0; $i < $result->length; $i++) { |
$n[] = $result->item($i); |
} |
return $n; |
} |
function content($node) |
{ |
if ($node) { |
return $node->textContent; |
} |
} |
function attributes($node) |
{ |
if ($node) { |
$arr = $node->attributes; |
$result = array(); |
if ($arr) { |
for ($i = 0; $i < $arr->length; $i++) { |
$node = $arr->item($i); |
$result[$node->nodeName] = $node->nodeValue; |
} |
} |
return $result; |
} |
} |
} |
global $__Services_Yadis_defaultParser; |
$__Services_Yadis_defaultParser = null; |
/** |
* Set a default parser to override the extension-driven selection of |
* available parser classes. This is helpful in a test environment or |
* one in which multiple parsers can be used but one is more |
* desirable. |
* |
* @param Services_Yadis_XMLParser $parser An instance of a |
* Services_Yadis_XMLParser subclass. |
*/ |
function Services_Yadis_setDefaultParser(&$parser) |
{ |
global $__Services_Yadis_defaultParser; |
$__Services_Yadis_defaultParser =& $parser; |
} |
function Services_Yadis_getSupportedExtensions() |
{ |
return array( |
'dom' => array('classname' => 'Services_Yadis_dom', |
'libname' => array('dom.so', 'dom.dll')), |
'domxml' => array('classname' => 'Services_Yadis_domxml', |
'libname' => array('domxml.so', 'php_domxml.dll')), |
); |
} |
/** |
* Returns an instance of a Services_Yadis_XMLParser subclass based on |
* the availability of PHP extensions for XML parsing. If |
* Services_Yadis_setDefaultParser has been called, the parser used in |
* that call will be returned instead. |
*/ |
function &Services_Yadis_getXMLParser() |
{ |
global $__Services_Yadis_defaultParser; |
if (isset($__Services_Yadis_defaultParser)) { |
return $__Services_Yadis_defaultParser; |
} |
$p = null; |
$classname = null; |
$extensions = Services_Yadis_getSupportedExtensions(); |
// Return a wrapper for the resident implementation, if any. |
foreach ($extensions as $name => $params) { |
if (!extension_loaded($name)) { |
foreach ($params['libname'] as $libname) { |
if (@dl($libname)) { |
$classname = $params['classname']; |
} |
} |
} else { |
$classname = $params['classname']; |
} |
if (isset($classname)) { |
$p = new $classname(); |
return $p; |
} |
} |
if (!isset($p)) { |
trigger_error('No XML parser was found', E_USER_ERROR); |
} else { |
Services_Yadis_setDefaultParser($p); |
} |
return $p; |
} |
?> |
/trunk/composants/openid/Services/Yadis/XRDS.php |
---|
New file |
0,0 → 1,425 |
<?php |
/** |
* This module contains the XRDS parsing code. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Require the XPath implementation. |
*/ |
require_once 'Services/Yadis/XML.php'; |
/** |
* This match mode means a given service must match ALL filters passed |
* to the Services_Yadis_XRDS::services() call. |
*/ |
define('SERVICES_YADIS_MATCH_ALL', 101); |
/** |
* This match mode means a given service must match ANY filters (at |
* least one) passed to the Services_Yadis_XRDS::services() call. |
*/ |
define('SERVICES_YADIS_MATCH_ANY', 102); |
/** |
* The priority value used for service elements with no priority |
* specified. |
*/ |
define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30)); |
function Services_Yadis_getNSMap() |
{ |
return array('xrds' => 'xri://$xrds', |
'xrd' => 'xri://$xrd*($v*2.0)'); |
} |
/** |
* @access private |
*/ |
function Services_Yadis_array_scramble($arr) |
{ |
$result = array(); |
while (count($arr)) { |
$index = array_rand($arr, 1); |
$result[] = $arr[$index]; |
unset($arr[$index]); |
} |
return $result; |
} |
/** |
* This class represents a <Service> element in an XRDS document. |
* Objects of this type are returned by |
* Services_Yadis_XRDS::services() and |
* Services_Yadis_Yadis::services(). Each object corresponds directly |
* to a <Service> element in the XRDS and supplies a |
* getElements($name) method which you should use to inspect the |
* element's contents. See {@link Services_Yadis_Yadis} for more |
* information on the role this class plays in Yadis discovery. |
* |
* @package Yadis |
*/ |
class Services_Yadis_Service { |
/** |
* Creates an empty service object. |
*/ |
function Services_Yadis_Service() |
{ |
$this->element = null; |
$this->parser = null; |
} |
/** |
* Return the URIs in the "Type" elements, if any, of this Service |
* element. |
* |
* @return array $type_uris An array of Type URI strings. |
*/ |
function getTypes() |
{ |
$t = array(); |
foreach ($this->getElements('xrd:Type') as $elem) { |
$c = $this->parser->content($elem); |
if ($c) { |
$t[] = $c; |
} |
} |
return $t; |
} |
/** |
* Return the URIs in the "URI" elements, if any, of this Service |
* element. The URIs are returned sorted in priority order. |
* |
* @return array $uris An array of URI strings. |
*/ |
function getURIs() |
{ |
$uris = array(); |
$last = array(); |
foreach ($this->getElements('xrd:URI') as $elem) { |
$uri_string = $this->parser->content($elem); |
$attrs = $this->parser->attributes($elem); |
if ($attrs && |
array_key_exists('priority', $attrs)) { |
$priority = intval($attrs['priority']); |
if (!array_key_exists($priority, $uris)) { |
$uris[$priority] = array(); |
} |
$uris[$priority][] = $uri_string; |
} else { |
$last[] = $uri_string; |
} |
} |
$keys = array_keys($uris); |
sort($keys); |
// Rebuild array of URIs. |
$result = array(); |
foreach ($keys as $k) { |
$new_uris = Services_Yadis_array_scramble($uris[$k]); |
$result = array_merge($result, $new_uris); |
} |
$result = array_merge($result, |
Services_Yadis_array_scramble($last)); |
return $result; |
} |
/** |
* Returns the "priority" attribute value of this <Service> |
* element, if the attribute is present. Returns null if not. |
* |
* @return mixed $result Null or integer, depending on whether |
* this Service element has a 'priority' attribute. |
*/ |
function getPriority() |
{ |
$attributes = $this->parser->attributes($this->element); |
if (array_key_exists('priority', $attributes)) { |
return intval($attributes['priority']); |
} |
return null; |
} |
/** |
* Used to get XML elements from this object's <Service> element. |
* |
* This is what you should use to get all custom information out |
* of this element. This is used by service filter functions to |
* determine whether a service element contains specific tags, |
* etc. NOTE: this only considers elements which are direct |
* children of the <Service> element for this object. |
* |
* @param string $name The name of the element to look for |
* @return array $list An array of elements with the specified |
* name which are direct children of the <Service> element. The |
* nodes returned by this function can be passed to $this->parser |
* methods (see {@link Services_Yadis_XMLParser}). |
*/ |
function getElements($name) |
{ |
return $this->parser->evalXPath($name, $this->element); |
} |
} |
/** |
* This class performs parsing of XRDS documents. |
* |
* You should not instantiate this class directly; rather, call |
* parseXRDS statically: |
* |
* <pre> $xrds = Services_Yadis_XRDS::parseXRDS($xml_string);</pre> |
* |
* If the XRDS can be parsed and is valid, an instance of |
* Services_Yadis_XRDS will be returned. Otherwise, null will be |
* returned. This class is used by the Services_Yadis_Yadis::discover |
* method. |
* |
* @package Yadis |
*/ |
class Services_Yadis_XRDS { |
/** |
* Instantiate a Services_Yadis_XRDS object. Requires an XPath |
* instance which has been used to parse a valid XRDS document. |
*/ |
function Services_Yadis_XRDS(&$xmlParser, &$xrdNodes) |
{ |
$this->parser =& $xmlParser; |
$this->xrdNode = $xrdNodes[count($xrdNodes) - 1]; |
$this->allXrdNodes =& $xrdNodes; |
$this->serviceList = array(); |
$this->_parse(); |
} |
/** |
* Parse an XML string (XRDS document) and return either a |
* Services_Yadis_XRDS object or null, depending on whether the |
* XRDS XML is valid. |
* |
* @param string $xml_string An XRDS XML string. |
* @return mixed $xrds An instance of Services_Yadis_XRDS or null, |
* depending on the validity of $xml_string |
*/ |
function &parseXRDS($xml_string, $extra_ns_map = null) |
{ |
$_null = null; |
if (!$xml_string) { |
return $_null; |
} |
$parser = Services_Yadis_getXMLParser(); |
$ns_map = Services_Yadis_getNSMap(); |
if ($extra_ns_map && is_array($extra_ns_map)) { |
$ns_map = array_merge($ns_map, $extra_ns_map); |
} |
if (!($parser && $parser->init($xml_string, $ns_map))) { |
return $_null; |
} |
// Try to get root element. |
$root = $parser->evalXPath('/xrds:XRDS[1]'); |
if (!$root) { |
return $_null; |
} |
if (is_array($root)) { |
$root = $root[0]; |
} |
$attrs = $parser->attributes($root); |
if (array_key_exists('xmlns:xrd', $attrs) && |
$attrs['xmlns:xrd'] != 'xri://$xrd*($v*2.0)') { |
return $_null; |
} else if (array_key_exists('xmlns', $attrs) && |
preg_match('/xri/', $attrs['xmlns']) && |
$attrs['xmlns'] != 'xri://$xrd*($v*2.0)') { |
return $_null; |
} |
// Get the last XRD node. |
$xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD'); |
if (!$xrd_nodes) { |
return $_null; |
} |
$xrds = new Services_Yadis_XRDS($parser, $xrd_nodes); |
return $xrds; |
} |
/** |
* @access private |
*/ |
function _addService($priority, $service) |
{ |
$priority = intval($priority); |
if (!array_key_exists($priority, $this->serviceList)) { |
$this->serviceList[$priority] = array(); |
} |
$this->serviceList[$priority][] = $service; |
} |
/** |
* Creates the service list using nodes from the XRDS XML |
* document. |
* |
* @access private |
*/ |
function _parse() |
{ |
$this->serviceList = array(); |
$services = $this->parser->evalXPath('xrd:Service', $this->xrdNode); |
foreach ($services as $node) { |
$s =& new Services_Yadis_Service(); |
$s->element = $node; |
$s->parser =& $this->parser; |
$priority = $s->getPriority(); |
if ($priority === null) { |
$priority = SERVICES_YADIS_MAX_PRIORITY; |
} |
$this->_addService($priority, $s); |
} |
} |
/** |
* Returns a list of service objects which correspond to <Service> |
* elements in the XRDS XML document for this object. |
* |
* Optionally, an array of filter callbacks may be given to limit |
* the list of returned service objects. Furthermore, the default |
* mode is to return all service objects which match ANY of the |
* specified filters, but $filter_mode may be |
* SERVICES_YADIS_MATCH_ALL if you want to be sure that the |
* returned services match all the given filters. See {@link |
* Services_Yadis_Yadis} for detailed usage information on filter |
* functions. |
* |
* @param mixed $filters An array of callbacks to filter the |
* returned services, or null if all services are to be returned. |
* @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or |
* SERVICES_YADIS_MATCH_ANY, depending on whether the returned |
* services should match ALL or ANY of the specified filters, |
* respectively. |
* @return mixed $services An array of {@link |
* Services_Yadis_Service} objects if $filter_mode is a valid |
* mode; null if $filter_mode is an invalid mode (i.e., not |
* SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL). |
*/ |
function services($filters = null, |
$filter_mode = SERVICES_YADIS_MATCH_ANY) |
{ |
$pri_keys = array_keys($this->serviceList); |
sort($pri_keys, SORT_NUMERIC); |
// If no filters are specified, return the entire service |
// list, ordered by priority. |
if (!$filters || |
(!is_array($filters))) { |
$result = array(); |
foreach ($pri_keys as $pri) { |
$result = array_merge($result, $this->serviceList[$pri]); |
} |
return $result; |
} |
// If a bad filter mode is specified, return null. |
if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY, |
SERVICES_YADIS_MATCH_ALL))) { |
return null; |
} |
// Otherwise, use the callbacks in the filter list to |
// determine which services are returned. |
$filtered = array(); |
foreach ($pri_keys as $priority_value) { |
$service_obj_list = $this->serviceList[$priority_value]; |
foreach ($service_obj_list as $service) { |
$matches = 0; |
foreach ($filters as $filter) { |
if (call_user_func_array($filter, array($service))) { |
$matches++; |
if ($filter_mode == SERVICES_YADIS_MATCH_ANY) { |
$pri = $service->getPriority(); |
if ($pri === null) { |
$pri = SERVICES_YADIS_MAX_PRIORITY; |
} |
if (!array_key_exists($pri, $filtered)) { |
$filtered[$pri] = array(); |
} |
$filtered[$pri][] = $service; |
break; |
} |
} |
} |
if (($filter_mode == SERVICES_YADIS_MATCH_ALL) && |
($matches == count($filters))) { |
$pri = $service->getPriority(); |
if ($pri === null) { |
$pri = SERVICES_YADIS_MAX_PRIORITY; |
} |
if (!array_key_exists($pri, $filtered)) { |
$filtered[$pri] = array(); |
} |
$filtered[$pri][] = $service; |
} |
} |
} |
$pri_keys = array_keys($filtered); |
sort($pri_keys, SORT_NUMERIC); |
$result = array(); |
foreach ($pri_keys as $pri) { |
$result = array_merge($result, $filtered[$pri]); |
} |
return $result; |
} |
} |
?> |
/trunk/composants/openid/Services/Yadis/XRI.php |
---|
New file |
0,0 → 1,233 |
<?php |
/** |
* Routines for XRI resolution. |
* |
* @package Yadis |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
require_once 'Services/Yadis/Misc.php'; |
require_once 'Services/Yadis/Yadis.php'; |
require_once 'Auth/OpenID.php'; |
function Services_Yadis_getDefaultProxy() |
{ |
return 'http://proxy.xri.net/'; |
} |
function Services_Yadis_getXRIAuthorities() |
{ |
return array('!', '=', '@', '+', '$', '('); |
} |
function Services_Yadis_getEscapeRE() |
{ |
$parts = array(); |
foreach (array_merge(Services_Yadis_getUCSChars(), |
Services_Yadis_getIPrivateChars()) as $pair) { |
list($m, $n) = $pair; |
$parts[] = sprintf("%s-%s", chr($m), chr($n)); |
} |
return sprintf('/[%s]/', implode('', $parts)); |
} |
function Services_Yadis_getXrefRE() |
{ |
return '/\((.*?)\)/'; |
} |
function Services_Yadis_identifierScheme($identifier) |
{ |
if (Services_Yadis_startswith($identifier, 'xri://') || |
(in_array($identifier[0], Services_Yadis_getXRIAuthorities()))) { |
return "XRI"; |
} else { |
return "URI"; |
} |
} |
function Services_Yadis_toIRINormal($xri) |
{ |
if (!Services_Yadis_startswith($xri, 'xri://')) { |
$xri = 'xri://' . $xri; |
} |
return Services_Yadis_escapeForIRI($xri); |
} |
function _escape_xref($xref_match) |
{ |
$xref = $xref_match[0]; |
$xref = str_replace('/', '%2F', $xref); |
$xref = str_replace('?', '%3F', $xref); |
$xref = str_replace('#', '%23', $xref); |
return $xref; |
} |
function Services_Yadis_escapeForIRI($xri) |
{ |
$xri = str_replace('%', '%25', $xri); |
$xri = preg_replace_callback(Services_Yadis_getXrefRE(), |
'_escape_xref', $xri); |
return $xri; |
} |
function Services_Yadis_toURINormal($xri) |
{ |
return Services_Yadis_iriToURI(Services_Yadis_toIRINormal($xri)); |
} |
function Services_Yadis_iriToURI($iri) |
{ |
if (1) { |
return $iri; |
} else { |
// According to RFC 3987, section 3.1, "Mapping of IRIs to URIs" |
return preg_replace_callback(Services_Yadis_getEscapeRE(), |
'Services_Yadis_pct_escape_unicode', $iri); |
} |
} |
function Services_Yadis_XRIAppendArgs($url, $args) |
{ |
// Append some arguments to an HTTP query. Yes, this is just like |
// OpenID's appendArgs, but with special seasoning for XRI |
// queries. |
if (count($args) == 0) { |
return $url; |
} |
// Non-empty array; if it is an array of arrays, use multisort; |
// otherwise use sort. |
if (array_key_exists(0, $args) && |
is_array($args[0])) { |
// Do nothing here. |
} else { |
$keys = array_keys($args); |
sort($keys); |
$new_args = array(); |
foreach ($keys as $key) { |
$new_args[] = array($key, $args[$key]); |
} |
$args = $new_args; |
} |
// According to XRI Resolution section "QXRI query parameters": |
// |
// "If the original QXRI had a null query component (only a |
// leading question mark), or a query component consisting of |
// only question marks, one additional leading question mark MUST |
// be added when adding any XRI resolution parameters." |
if (strpos(rtrim($url, '?'), '?') !== false) { |
$sep = '&'; |
} else { |
$sep = '?'; |
} |
return $url . $sep . Auth_OpenID::httpBuildQuery($args); |
} |
function Services_Yadis_providerIsAuthoritative($providerID, $canonicalID) |
{ |
$lastbang = strrpos($canonicalID, '!'); |
$p = substr($canonicalID, 0, $lastbang); |
return $p == $providerID; |
} |
function Services_Yadis_rootAuthority($xri) |
{ |
// Return the root authority for an XRI. |
$root = null; |
if (Services_Yadis_startswith($xri, 'xri://')) { |
$xri = substr($xri, 6); |
} |
$authority = explode('/', $xri, 2); |
$authority = $authority[0]; |
if ($authority[0] == '(') { |
// Cross-reference. |
// XXX: This is incorrect if someone nests cross-references so |
// there is another close-paren in there. Hopefully nobody |
// does that before we have a real xriparse function. |
// Hopefully nobody does that *ever*. |
$root = substr($authority, 0, strpos($authority, ')') + 1); |
} else if (in_array($authority[0], Services_Yadis_getXRIAuthorities())) { |
// Other XRI reference. |
$root = $authority[0]; |
} else { |
// IRI reference. |
$_segments = explode("!", $authority); |
$segments = array(); |
foreach ($_segments as $s) { |
$segments = array_merge($segments, explode("*", $s)); |
} |
$root = $segments[0]; |
} |
return Services_Yadis_XRI($root); |
} |
function Services_Yadis_XRI($xri) |
{ |
if (!Services_Yadis_startswith($xri, 'xri://')) { |
$xri = 'xri://' . $xri; |
} |
return $xri; |
} |
function Services_Yadis_getCanonicalID($iname, $xrds) |
{ |
// Returns FALSE or a canonical ID value. |
// Now nodes are in reverse order. |
$xrd_list = array_reverse($xrds->allXrdNodes); |
$parser =& $xrds->parser; |
$node = $xrd_list[0]; |
$canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node); |
if (!$canonicalID_nodes) { |
return false; |
} |
$canonicalID = $canonicalID_nodes[count($canonicalID_nodes) - 1]; |
$canonicalID = Services_Yadis_XRI($parser->content($canonicalID)); |
$childID = $canonicalID; |
for ($i = 1; $i < count($xrd_list); $i++) { |
$xrd = $xrd_list[$i]; |
$parent_sought = substr($childID, 0, strrpos($childID, '!')); |
$parent_list = array(); |
foreach ($parser->evalXPath('xrd:CanonicalID', $xrd) as $c) { |
$parent_list[] = Services_Yadis_XRI($parser->content($c)); |
} |
if (!in_array($parent_sought, $parent_list)) { |
// raise XRDSFraud. |
return false; |
} |
$childID = $parent_sought; |
} |
$root = Services_Yadis_rootAuthority($iname); |
if (!Services_Yadis_providerIsAuthoritative($root, $childID)) { |
// raise XRDSFraud. |
return false; |
} |
return $canonicalID; |
} |
?> |
/trunk/composants/openid/OpenId.php |
---|
New file |
0,0 → 1,85 |
<? // fichiers inclus |
require_once "Auth/OpenID/Consumer.php"; |
require_once "Auth/OpenID/FileStore.php"; |
// démarrage de la session (requis pour YADIS) |
session_start(); |
// crée une zone de stockage pour les données OpenID |
$store = new Auth_OpenID_FileStore('./oid_store'); |
// crée un consommateur OpenID |
$consumer = new Auth_OpenID_Consumer($store); |
// commence le process d'authentification |
// crée une requête d'authentification pour le fournisseur OpenID |
$auth = $consumer->begin($_POST['id']); |
if (!$auth) { |
die("ERROR: Entrez un OpenID valide svp."); |
} |
// redirige vers le fournisseur OpenID pour l'authentification |
$url = $auth->redirectURL('http://consumer.example.com/', 'http://consumer.example.com/oid_return.php'); |
header('Location: ' . $url); |
// fichiers inclus |
require_once "Auth/OpenID/Consumer.php"; |
require_once "Auth/OpenID/FileStore.php"; |
require_once "Auth/OpenID/SReg.php"; |
// démarrage de session (requis pour YADIS) |
session_start(); |
// crée une zone de stockage pour les données OpenID |
$store = new Auth_OpenID_FileStore('./oid_store'); |
// crée un consommateur OpenID |
// lit la réponse depuis e fournisseur OPenID |
$consumer = new Auth_OpenID_Consumer($store); |
$response = $consumer->complete('http://consumer.example.com/oid_return.php'); |
// crée une variable de session qui dépend de l'authentification |
if ($response->status == Auth_OpenID_SUCCESS) { |
$_SESSION['OPENID_AUTH'] = true; |
// récupère les informations d'enregistrement |
$sreg = new Auth_OpenID_SRegResponse(); |
$obj = $sreg->fromSuccessResponse($response); |
$data = $obj->contents(); |
if (isset($data['email'])) { |
// Si l'adresse mail est disponible |
// Vérifie si l'utilisateur a déjà un compte sur le système |
// ouvre une connexion a la base |
$conn = mysql_connect('localhost', 'user', 'pass') or die('ERROR: Connexion serveur impossible'); |
mysql_select_db('test') or die('ERROR: Impossible de sélectionner une base'); |
// exécute la requête |
$result = mysql_query("SELECT DISTINCT COUNT(*) FROM users WHERE email = '" . $data['email'] . "'") or die('ERROR: La requête ne peut pas être exécutée'); |
$row = mysql_fetch_array($result); |
if ($row[0] == 1) { |
// si oui affiche un message personnalisé |
$newUser = false; |
echo 'Bonjour et bienvenue, ' . $data['email']; |
exit(); |
} else { |
// si non avertit que l'utilisateur est nouveau |
$newUser = true; |
} |
// ferme la connexion |
mysql_free_result($result); |
mysql_close($conn); |
} else { |
// si l'adresse email n'est pas disponible |
// avertit que l'utilisateur est nouveau |
$newUser = true; |
} |
} else { |
$_SESSION['OPENID_AUTH'] = false; |
die ('Vous n'avez pas la permission d'accéder a cette page! Re-loggez vous svp.'); |
} |
?> |
/trunk/composants/openid/Auth/OpenID/DumbStore.php |
---|
New file |
0,0 → 1,116 |
<?php |
/** |
* This file supplies a dumb store backend for OpenID servers and |
* consumers. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Import the interface for creating a new store class. |
*/ |
require_once 'Auth/OpenID/Interface.php'; |
require_once 'Auth/OpenID/HMACSHA1.php'; |
/** |
* This is a store for use in the worst case, when you have no way of |
* saving state on the consumer site. Using this store makes the |
* consumer vulnerable to replay attacks, as it's unable to use |
* nonces. Avoid using this store if it is at all possible. |
* |
* Most of the methods of this class are implementation details. |
* Users of this class need to worry only about the constructor. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore { |
/** |
* Creates a new {@link Auth_OpenID_DumbStore} instance. For the security |
* of the tokens generated by the library, this class attempts to |
* at least have a secure implementation of getAuthKey. |
* |
* When you create an instance of this class, pass in a secret |
* phrase. The phrase is hashed with sha1 to make it the correct |
* length and form for an auth key. That allows you to use a long |
* string as the secret phrase, which means you can make it very |
* difficult to guess. |
* |
* Each {@link Auth_OpenID_DumbStore} instance that is created for use by |
* your consumer site needs to use the same $secret_phrase. |
* |
* @param string secret_phrase The phrase used to create the auth |
* key returned by getAuthKey |
*/ |
function Auth_OpenID_DumbStore($secret_phrase) |
{ |
$this->auth_key = Auth_OpenID_SHA1($secret_phrase); |
} |
/** |
* This implementation does nothing. |
*/ |
function storeAssociation($server_url, $association) |
{ |
} |
/** |
* This implementation always returns null. |
*/ |
function getAssociation($server_url, $handle = null) |
{ |
return null; |
} |
/** |
* This implementation always returns false. |
*/ |
function removeAssociation($server_url, $handle) |
{ |
return false; |
} |
/** |
* This implementation does nothing. |
*/ |
function storeNonce($nonce) |
{ |
} |
/** |
* In a system truly limited to dumb mode, nonces must all be |
* accepted. This therefore always returns true, which makes |
* replay attacks feasible. |
*/ |
function useNonce($nonce) |
{ |
return true; |
} |
/** |
* This method returns the auth key generated by the constructor. |
*/ |
function getAuthKey() |
{ |
return $this->auth_key; |
} |
/** |
* This store is a dumb mode store, so this method is overridden |
* to return true. |
*/ |
function isDumb() |
{ |
return true; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/KVForm.php |
---|
New file |
0,0 → 1,112 |
<?php |
/** |
* OpenID protocol key-value/comma-newline format parsing and |
* serialization |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Container for key-value/comma-newline OpenID format and parsing |
*/ |
class Auth_OpenID_KVForm { |
/** |
* Convert an OpenID colon/newline separated string into an |
* associative array |
* |
* @static |
* @access private |
*/ |
function toArray($kvs, $strict=false) |
{ |
$lines = explode("\n", $kvs); |
$last = array_pop($lines); |
if ($last !== '') { |
array_push($lines, $last); |
if ($strict) { |
return false; |
} |
} |
$values = array(); |
for ($lineno = 0; $lineno < count($lines); $lineno++) { |
$line = $lines[$lineno]; |
$kv = explode(':', $line, 2); |
if (count($kv) != 2) { |
if ($strict) { |
return false; |
} |
continue; |
} |
$key = $kv[0]; |
$tkey = trim($key); |
if ($tkey != $key) { |
if ($strict) { |
return false; |
} |
} |
$value = $kv[1]; |
$tval = trim($value); |
if ($tval != $value) { |
if ($strict) { |
return false; |
} |
} |
$values[$tkey] = $tval; |
} |
return $values; |
} |
/** |
* Convert an array into an OpenID colon/newline separated string |
* |
* @static |
* @access private |
*/ |
function fromArray($values) |
{ |
if ($values === null) { |
return null; |
} |
ksort($values); |
$serialized = ''; |
foreach ($values as $key => $value) { |
if (is_array($value)) { |
list($key, $value) = array($value[0], $value[1]); |
} |
if (strpos($key, ':') !== false) { |
return null; |
} |
if (strpos($key, "\n") !== false) { |
return null; |
} |
if (strpos($value, "\n") !== false) { |
return null; |
} |
$serialized .= "$key:$value\n"; |
} |
return $serialized; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/Server.php |
---|
New file |
0,0 → 1,1307 |
<?php |
/** |
* OpenID server protocol and logic. |
* |
* Overview |
* |
* An OpenID server must perform three tasks: |
* |
* 1. Examine the incoming request to determine its nature and validity. |
* 2. Make a decision about how to respond to this request. |
* 3. Format the response according to the protocol. |
* |
* The first and last of these tasks may performed by the |
* 'decodeRequest' and 'encodeResponse' methods of the |
* Auth_OpenID_Server object. Who gets to do the intermediate task -- |
* deciding how to respond to the request -- will depend on what type |
* of request it is. |
* |
* If it's a request to authenticate a user (a 'checkid_setup' or |
* 'checkid_immediate' request), you need to decide if you will assert |
* that this user may claim the identity in question. Exactly how you |
* do that is a matter of application policy, but it generally |
* involves making sure the user has an account with your system and |
* is logged in, checking to see if that identity is hers to claim, |
* and verifying with the user that she does consent to releasing that |
* information to the party making the request. |
* |
* Examine the properties of the Auth_OpenID_CheckIDRequest object, |
* and if and when you've come to a decision, form a response by |
* calling Auth_OpenID_CheckIDRequest::answer. |
* |
* Other types of requests relate to establishing associations between |
* client and server and verifing the authenticity of previous |
* communications. Auth_OpenID_Server contains all the logic and data |
* necessary to respond to such requests; just pass it to |
* Auth_OpenID_Server::handleRequest. |
* |
* OpenID Extensions |
* |
* Do you want to provide other information for your users in addition |
* to authentication? Version 1.2 of the OpenID protocol allows |
* consumers to add extensions to their requests. For example, with |
* sites using the Simple Registration |
* Extension |
* (http://www.openidenabled.com/openid/simple-registration-extension/), |
* a user can agree to have their nickname and e-mail address sent to |
* a site when they sign up. |
* |
* Since extensions do not change the way OpenID authentication works, |
* code to handle extension requests may be completely separate from |
* the Auth_OpenID_Request class here. But you'll likely want data |
* sent back by your extension to be signed. Auth_OpenID_Response |
* provides methods with which you can add data to it which can be |
* signed with the other data in the OpenID signature. |
* |
* For example: |
* |
* // when request is a checkid_* request |
* response = request.answer(True) |
* // this will a signed 'openid.sreg.timezone' parameter to the response |
* response.addField('sreg', 'timezone', 'America/Los_Angeles') |
* |
* Stores |
* |
* The OpenID server needs to maintain state between requests in order |
* to function. Its mechanism for doing this is called a store. The |
* store interface is defined in Interface.php. Additionally, several |
* concrete store implementations are provided, so that most sites |
* won't need to implement a custom store. For a store backed by flat |
* files on disk, see Auth_OpenID_FileStore. For stores based on |
* MySQL, SQLite, or PostgreSQL, see the Auth_OpenID_SQLStore |
* subclasses. |
* |
* Upgrading |
* |
* The keys by which a server looks up associations in its store have |
* changed in version 1.2 of this library. If your store has entries |
* created from version 1.0 code, you should empty it. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Required imports |
*/ |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID/Association.php"; |
require_once "Auth/OpenID/CryptUtil.php"; |
require_once "Auth/OpenID/BigMath.php"; |
require_once "Auth/OpenID/DiffieHellman.php"; |
require_once "Auth/OpenID/KVForm.php"; |
require_once "Auth/OpenID/TrustRoot.php"; |
require_once "Auth/OpenID/ServerRequest.php"; |
define('AUTH_OPENID_HTTP_OK', 200); |
define('AUTH_OPENID_HTTP_REDIRECT', 302); |
define('AUTH_OPENID_HTTP_ERROR', 400); |
global $_Auth_OpenID_Request_Modes, |
$_Auth_OpenID_OpenID_Prefix, |
$_Auth_OpenID_Encode_Kvform, |
$_Auth_OpenID_Encode_Url; |
/** |
* @access private |
*/ |
$_Auth_OpenID_Request_Modes = array('checkid_setup', |
'checkid_immediate'); |
/** |
* @access private |
*/ |
$_Auth_OpenID_OpenID_Prefix = "openid."; |
/** |
* @access private |
*/ |
$_Auth_OpenID_Encode_Kvform = array('kfvorm'); |
/** |
* @access private |
*/ |
$_Auth_OpenID_Encode_Url = array('URL/redirect'); |
/** |
* @access private |
*/ |
function _isError($obj, $cls = 'Auth_OpenID_ServerError') |
{ |
return is_a($obj, $cls); |
} |
/** |
* An error class which gets instantiated and returned whenever an |
* OpenID protocol error occurs. Be prepared to use this in place of |
* an ordinary server response. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_ServerError { |
/** |
* @access private |
*/ |
function Auth_OpenID_ServerError($query = null, $message = null) |
{ |
$this->message = $message; |
$this->query = $query; |
} |
/** |
* Returns the return_to URL for the request which caused this |
* error. |
*/ |
function hasReturnTo() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
if ($this->query) { |
return array_key_exists($_Auth_OpenID_OpenID_Prefix . |
'return_to', $this->query); |
} else { |
return false; |
} |
} |
/** |
* Encodes this error's response as a URL suitable for |
* redirection. If the response has no return_to, another |
* Auth_OpenID_ServerError is returned. |
*/ |
function encodeToURL() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$return_to = Auth_OpenID::arrayGet($this->query, |
$_Auth_OpenID_OpenID_Prefix . |
'return_to'); |
if (!$return_to) { |
return new Auth_OpenID_ServerError(null, "no return_to URL"); |
} |
return Auth_OpenID::appendArgs($return_to, |
array('openid.mode' => 'error', |
'openid.error' => $this->toString())); |
} |
/** |
* Encodes the response to key-value form. This is a |
* machine-readable format used to respond to messages which came |
* directly from the consumer and not through the user-agent. See |
* the OpenID specification. |
*/ |
function encodeToKVForm() |
{ |
return Auth_OpenID_KVForm::fromArray( |
array('mode' => 'error', |
'error' => $this->toString())); |
} |
/** |
* Returns one of $_Auth_OpenID_Encode_Url, |
* $_Auth_OpenID_Encode_Kvform, or null, depending on the type of |
* encoding expected for this error's payload. |
*/ |
function whichEncoding() |
{ |
global $_Auth_OpenID_Encode_Url, |
$_Auth_OpenID_Encode_Kvform, |
$_Auth_OpenID_Request_Modes; |
if ($this->hasReturnTo()) { |
return $_Auth_OpenID_Encode_Url; |
} |
$mode = Auth_OpenID::arrayGet($this->query, 'openid.mode'); |
if ($mode) { |
if (!in_array($mode, $_Auth_OpenID_Request_Modes)) { |
return $_Auth_OpenID_Encode_Kvform; |
} |
} |
return null; |
} |
/** |
* Returns this error message. |
*/ |
function toString() |
{ |
if ($this->message) { |
return $this->message; |
} else { |
return get_class($this) . " error"; |
} |
} |
} |
/** |
* An error indicating that the return_to URL is malformed. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError { |
function Auth_OpenID_MalformedReturnURL($query, $return_to) |
{ |
$this->return_to = $return_to; |
parent::Auth_OpenID_ServerError($query, "malformed return_to URL"); |
} |
} |
/** |
* This error is returned when the trust_root value is malformed. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError { |
function toString() |
{ |
return "Malformed trust root"; |
} |
} |
/** |
* The base class for all server request classes. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_Request { |
var $mode = null; |
} |
/** |
* A request to verify the validity of a previous response. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request { |
var $mode = "check_authentication"; |
var $invalidate_handle = null; |
function Auth_OpenID_CheckAuthRequest($assoc_handle, $sig, $signed, |
$invalidate_handle = null) |
{ |
$this->assoc_handle = $assoc_handle; |
$this->sig = $sig; |
$this->signed = $signed; |
if ($invalidate_handle !== null) { |
$this->invalidate_handle = $invalidate_handle; |
} |
} |
function fromQuery($query) |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$required_keys = array('assoc_handle', 'sig', 'signed'); |
foreach ($required_keys as $k) { |
if (!array_key_exists($_Auth_OpenID_OpenID_Prefix . $k, |
$query)) { |
return new Auth_OpenID_ServerError($query, |
sprintf("%s request missing required parameter %s from \ |
query", "check_authentication", $k)); |
} |
} |
$assoc_handle = $query[$_Auth_OpenID_OpenID_Prefix . 'assoc_handle']; |
$sig = $query[$_Auth_OpenID_OpenID_Prefix . 'sig']; |
$signed_list = $query[$_Auth_OpenID_OpenID_Prefix . 'signed']; |
$signed_list = explode(",", $signed_list); |
$signed_pairs = array(); |
foreach ($signed_list as $field) { |
if ($field == 'mode') { |
// XXX KLUDGE HAX WEB PROTOCoL BR0KENNN |
// |
// openid.mode is currently check_authentication |
// because that's the mode of this request. But the |
// signature was made on something with a different |
// openid.mode. |
$value = "id_res"; |
} else { |
if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, |
$query)) { |
$value = $query[$_Auth_OpenID_OpenID_Prefix . $field]; |
} else { |
return new Auth_OpenID_ServerError($query, |
sprintf("Couldn't find signed field %r in query %s", |
$field, var_export($query, true))); |
} |
} |
$signed_pairs[] = array($field, $value); |
} |
$result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $sig, |
$signed_pairs); |
$result->invalidate_handle = Auth_OpenID::arrayGet($query, |
$_Auth_OpenID_OpenID_Prefix . 'invalidate_handle'); |
return $result; |
} |
function answer(&$signatory) |
{ |
$is_valid = $signatory->verify($this->assoc_handle, $this->sig, |
$this->signed); |
// Now invalidate that assoc_handle so it this checkAuth |
// message cannot be replayed. |
$signatory->invalidate($this->assoc_handle, true); |
$response = new Auth_OpenID_ServerResponse($this); |
$response->fields['is_valid'] = $is_valid ? "true" : "false"; |
if ($this->invalidate_handle) { |
$assoc = $signatory->getAssociation($this->invalidate_handle, |
false); |
if (!$assoc) { |
$response->fields['invalidate_handle'] = |
$this->invalidate_handle; |
} |
} |
return $response; |
} |
} |
class Auth_OpenID_PlainTextServerSession { |
/** |
* An object that knows how to handle association requests with no |
* session type. |
*/ |
var $session_type = 'plaintext'; |
function fromQuery($unused_request) |
{ |
return new Auth_OpenID_PlainTextServerSession(); |
} |
function answer($secret) |
{ |
return array('mac_key' => base64_encode($secret)); |
} |
} |
class Auth_OpenID_DiffieHellmanServerSession { |
/** |
* An object that knows how to handle association requests with |
* the Diffie-Hellman session type. |
*/ |
var $session_type = 'DH-SHA1'; |
function Auth_OpenID_DiffieHellmanServerSession($dh, $consumer_pubkey) |
{ |
$this->dh = $dh; |
$this->consumer_pubkey = $consumer_pubkey; |
} |
function fromQuery($query) |
{ |
$dh_modulus = Auth_OpenID::arrayGet($query, 'openid.dh_modulus'); |
$dh_gen = Auth_OpenID::arrayGet($query, 'openid.dh_gen'); |
if ((($dh_modulus === null) && ($dh_gen !== null)) || |
(($dh_gen === null) && ($dh_modulus !== null))) { |
if ($dh_modulus === null) { |
$missing = 'modulus'; |
} else { |
$missing = 'generator'; |
} |
return new Auth_OpenID_ServerError( |
'If non-default modulus or generator is '. |
'supplied, both must be supplied. Missing '. |
$missing); |
} |
$lib =& Auth_OpenID_getMathLib(); |
if ($dh_modulus || $dh_gen) { |
$dh_modulus = $lib->base64ToLong($dh_modulus); |
$dh_gen = $lib->base64ToLong($dh_gen); |
if ($lib->cmp($dh_modulus, 0) == 0 || |
$lib->cmp($dh_gen, 0) == 0) { |
return new Auth_OpenID_ServerError( |
$query, "Failed to parse dh_mod or dh_gen"); |
} |
$dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen); |
} else { |
$dh = new Auth_OpenID_DiffieHellman(); |
} |
$consumer_pubkey = Auth_OpenID::arrayGet($query, |
'openid.dh_consumer_public'); |
if ($consumer_pubkey === null) { |
return new Auth_OpenID_ServerError( |
'Public key for DH-SHA1 session '. |
'not found in query'); |
} |
$consumer_pubkey = |
$lib->base64ToLong($consumer_pubkey); |
if ($consumer_pubkey === false) { |
return new Auth_OpenID_ServerError($query, |
"dh_consumer_public is not base64"); |
} |
return new Auth_OpenID_DiffieHellmanServerSession($dh, |
$consumer_pubkey); |
} |
function answer($secret) |
{ |
$lib =& Auth_OpenID_getMathLib(); |
$mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret); |
return array( |
'dh_server_public' => |
$lib->longToBase64($this->dh->public), |
'enc_mac_key' => base64_encode($mac_key)); |
} |
} |
/** |
* A request to associate with the server. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request { |
var $mode = "associate"; |
var $assoc_type = 'HMAC-SHA1'; |
function Auth_OpenID_AssociateRequest(&$session) |
{ |
$this->session =& $session; |
} |
function fromQuery($query) |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$session_classes = array( |
'DH-SHA1' => 'Auth_OpenID_DiffieHellmanServerSession', |
null => 'Auth_OpenID_PlainTextServerSession'); |
$session_type = null; |
if (array_key_exists($_Auth_OpenID_OpenID_Prefix . 'session_type', |
$query)) { |
$session_type = $query[$_Auth_OpenID_OpenID_Prefix . |
'session_type']; |
} |
if (!array_key_exists($session_type, $session_classes)) { |
return new Auth_OpenID_ServerError($query, |
"Unknown session type $session_type"); |
} |
$session_cls = $session_classes[$session_type]; |
$session = call_user_func_array(array($session_cls, 'fromQuery'), |
array($query)); |
if (($session === null) || (_isError($session))) { |
return new Auth_OpenID_ServerError($query, |
"Error parsing $session_type session"); |
} |
return new Auth_OpenID_AssociateRequest($session); |
} |
function answer($assoc) |
{ |
$ml =& Auth_OpenID_getMathLib(); |
$response = new Auth_OpenID_ServerResponse($this); |
$response->fields = array('expires_in' => $assoc->getExpiresIn(), |
'assoc_type' => 'HMAC-SHA1', |
'assoc_handle' => $assoc->handle); |
$r = $this->session->answer($assoc->secret); |
foreach ($r as $k => $v) { |
$response->fields[$k] = $v; |
} |
if ($this->session->session_type != 'plaintext') { |
$response->fields['session_type'] = $this->session->session_type; |
} |
return $response; |
} |
} |
/** |
* A request to confirm the identity of a user. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request { |
var $mode = "checkid_setup"; // or "checkid_immediate" |
var $immediate = false; |
var $trust_root = null; |
function make($query, $identity, $return_to, $trust_root = null, |
$immediate = false, $assoc_handle = null) |
{ |
if (!Auth_OpenID_TrustRoot::_parse($return_to)) { |
return new Auth_OpenID_MalformedReturnURL($query, $return_to); |
} |
$r = new Auth_OpenID_CheckIDRequest($identity, $return_to, |
$trust_root, $immediate, |
$assoc_handle); |
if (!$r->trustRootValid()) { |
return new Auth_OpenID_UntrustedReturnURL($return_to, |
$trust_root); |
} else { |
return $r; |
} |
} |
function Auth_OpenID_CheckIDRequest($identity, $return_to, |
$trust_root = null, $immediate = false, |
$assoc_handle = null) |
{ |
$this->identity = $identity; |
$this->return_to = $return_to; |
$this->trust_root = $trust_root; |
$this->assoc_handle = $assoc_handle; |
if ($immediate) { |
$this->immediate = true; |
$this->mode = "checkid_immediate"; |
} else { |
$this->immediate = false; |
$this->mode = "checkid_setup"; |
} |
} |
function fromQuery($query) |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$mode = $query[$_Auth_OpenID_OpenID_Prefix . 'mode']; |
$immediate = null; |
if ($mode == "checkid_immediate") { |
$immediate = true; |
$mode = "checkid_immediate"; |
} else { |
$immediate = false; |
$mode = "checkid_setup"; |
} |
$required = array('identity', |
'return_to'); |
$optional = array('trust_root', |
'assoc_handle'); |
$values = array(); |
foreach ($required as $field) { |
if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, |
$query)) { |
$value = $query[$_Auth_OpenID_OpenID_Prefix . $field]; |
} else { |
return new Auth_OpenID_ServerError($query, |
sprintf("Missing required field %s from request", |
$field)); |
} |
$values[$field] = $value; |
} |
foreach ($optional as $field) { |
$value = null; |
if (array_key_exists($_Auth_OpenID_OpenID_Prefix . $field, |
$query)) { |
$value = $query[$_Auth_OpenID_OpenID_Prefix. $field]; |
} |
if ($value) { |
$values[$field] = $value; |
} |
} |
if (!Auth_OpenID_TrustRoot::_parse($values['return_to'])) { |
return new Auth_OpenID_MalformedReturnURL($query, |
$values['return_to']); |
} |
$obj = Auth_OpenID_CheckIDRequest::make($query, |
$values['identity'], |
$values['return_to'], |
Auth_OpenID::arrayGet($values, |
'trust_root', null), |
$immediate); |
if (is_a($obj, 'Auth_OpenID_ServerError')) { |
return $obj; |
} |
if (Auth_OpenID::arrayGet($values, 'assoc_handle')) { |
$obj->assoc_handle = $values['assoc_handle']; |
} |
return $obj; |
} |
function trustRootValid() |
{ |
if (!$this->trust_root) { |
return true; |
} |
$tr = Auth_OpenID_TrustRoot::_parse($this->trust_root); |
if ($tr === false) { |
return new Auth_OpenID_MalformedTrustRoot(null, $this->trust_root); |
} |
return Auth_OpenID_TrustRoot::match($this->trust_root, |
$this->return_to); |
} |
function answer($allow, $server_url = null) |
{ |
if ($allow || $this->immediate) { |
$mode = 'id_res'; |
} else { |
$mode = 'cancel'; |
} |
$response = new Auth_OpenID_CheckIDResponse($this, $mode); |
if ($allow) { |
$response->fields['identity'] = $this->identity; |
$response->fields['return_to'] = $this->return_to; |
if (!$this->trustRootValid()) { |
return new Auth_OpenID_UntrustedReturnURL($this->return_to, |
$this->trust_root); |
} |
} else { |
$response->signed = array(); |
if ($this->immediate) { |
if (!$server_url) { |
return new Auth_OpenID_ServerError(null, |
'setup_url is required for $allow=false \ |
in immediate mode.'); |
} |
$setup_request =& new Auth_OpenID_CheckIDRequest( |
$this->identity, |
$this->return_to, |
$this->trust_root, |
false, |
$this->assoc_handle); |
$setup_url = $setup_request->encodeToURL($server_url); |
$response->fields['user_setup_url'] = $setup_url; |
} |
} |
return $response; |
} |
function encodeToURL($server_url) |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
// Imported from the alternate reality where these classes are |
// used in both the client and server code, so Requests are |
// Encodable too. That's right, code imported from alternate |
// realities all for the love of you, id_res/user_setup_url. |
$q = array('mode' => $this->mode, |
'identity' => $this->identity, |
'return_to' => $this->return_to); |
if ($this->trust_root) { |
$q['trust_root'] = $this->trust_root; |
} |
if ($this->assoc_handle) { |
$q['assoc_handle'] = $this->assoc_handle; |
} |
$_q = array(); |
foreach ($q as $k => $v) { |
$_q[$_Auth_OpenID_OpenID_Prefix . $k] = $v; |
} |
return Auth_OpenID::appendArgs($server_url, $_q); |
} |
function getCancelURL() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
if ($this->immediate) { |
return new Auth_OpenID_ServerError(null, |
"Cancel is not an appropriate \ |
response to immediate mode \ |
requests."); |
} |
return Auth_OpenID::appendArgs($this->return_to, |
array($_Auth_OpenID_OpenID_Prefix . 'mode' => |
'cancel')); |
} |
} |
/** |
* This class encapsulates the response to an OpenID server request. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_ServerResponse { |
function Auth_OpenID_ServerResponse($request) |
{ |
$this->request = $request; |
$this->fields = array(); |
} |
function whichEncoding() |
{ |
global $_Auth_OpenID_Encode_Kvform, |
$_Auth_OpenID_Request_Modes, |
$_Auth_OpenID_Encode_Url; |
if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) { |
return $_Auth_OpenID_Encode_Url; |
} else { |
return $_Auth_OpenID_Encode_Kvform; |
} |
} |
function encodeToURL() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$fields = array(); |
foreach ($this->fields as $k => $v) { |
$fields[$_Auth_OpenID_OpenID_Prefix . $k] = $v; |
} |
return Auth_OpenID::appendArgs($this->request->return_to, $fields); |
} |
function encodeToKVForm() |
{ |
return Auth_OpenID_KVForm::fromArray($this->fields); |
} |
} |
/** |
* A response to a checkid request. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_CheckIDResponse extends Auth_OpenID_ServerResponse { |
function Auth_OpenID_CheckIDResponse(&$request, $mode = 'id_res') |
{ |
parent::Auth_OpenID_ServerResponse($request); |
$this->fields['mode'] = $mode; |
$this->signed = array(); |
if ($mode == 'id_res') { |
array_push($this->signed, 'mode', 'identity', 'return_to'); |
} |
} |
function addField($namespace, $key, $value, $signed = true) |
{ |
if ($namespace) { |
$key = sprintf('%s.%s', $namespace, $key); |
} |
$this->fields[$key] = $value; |
if ($signed && !in_array($key, $this->signed)) { |
$this->signed[] = $key; |
} |
} |
function addFields($namespace, $fields, $signed = true) |
{ |
foreach ($fields as $k => $v) { |
$this->addField($namespace, $k, $v, $signed); |
} |
} |
function update($namespace, $other) |
{ |
$namespaced_fields = array(); |
foreach ($other->fields as $k => $v) { |
$name = sprintf('%s.%s', $namespace, $k); |
$namespaced_fields[$name] = $v; |
} |
$this->fields = array_merge($this->fields, $namespaced_fields); |
$this->signed = array_merge($this->signed, $other->signed); |
} |
} |
/** |
* A web-capable response object which you can use to generate a |
* user-agent response. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_WebResponse { |
var $code = AUTH_OPENID_HTTP_OK; |
var $body = ""; |
function Auth_OpenID_WebResponse($code = null, $headers = null, |
$body = null) |
{ |
if ($code) { |
$this->code = $code; |
} |
if ($headers !== null) { |
$this->headers = $headers; |
} else { |
$this->headers = array(); |
} |
if ($body !== null) { |
$this->body = $body; |
} |
} |
} |
/** |
* Responsible for the signature of query data and the verification of |
* OpenID signature values. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Signatory { |
// = 14 * 24 * 60 * 60; # 14 days, in seconds |
var $SECRET_LIFETIME = 1209600; |
// keys have a bogus server URL in them because the filestore |
// really does expect that key to be a URL. This seems a little |
// silly for the server store, since I expect there to be only one |
// server URL. |
var $normal_key = 'http://localhost/|normal'; |
var $dumb_key = 'http://localhost/|dumb'; |
/** |
* Create a new signatory using a given store. |
*/ |
function Auth_OpenID_Signatory(&$store) |
{ |
// assert store is not None |
$this->store =& $store; |
} |
/** |
* Verify, using a given association handle, a signature with |
* signed key-value pairs from an HTTP request. |
*/ |
function verify($assoc_handle, $sig, $signed_pairs) |
{ |
$assoc = $this->getAssociation($assoc_handle, true); |
if (!$assoc) { |
// oidutil.log("failed to get assoc with handle %r to verify sig %r" |
// % (assoc_handle, sig)) |
return false; |
} |
$expected_sig = base64_encode($assoc->sign($signed_pairs)); |
return $sig == $expected_sig; |
} |
/** |
* Given a response, sign the fields in the response's 'signed' |
* list, and insert the signature into the response. |
*/ |
function sign($response) |
{ |
$signed_response = $response; |
$assoc_handle = $response->request->assoc_handle; |
if ($assoc_handle) { |
// normal mode |
$assoc = $this->getAssociation($assoc_handle, false); |
if (!$assoc) { |
// fall back to dumb mode |
$signed_response->fields['invalidate_handle'] = $assoc_handle; |
$assoc = $this->createAssociation(true); |
} |
} else { |
// dumb mode. |
$assoc = $this->createAssociation(true); |
} |
$signed_response->fields['assoc_handle'] = $assoc->handle; |
$assoc->addSignature($signed_response->signed, |
$signed_response->fields, ''); |
return $signed_response; |
} |
/** |
* Make a new association. |
*/ |
function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1') |
{ |
$secret = Auth_OpenID_CryptUtil::getBytes(20); |
$uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4)); |
$handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq); |
$assoc = Auth_OpenID_Association::fromExpiresIn( |
$this->SECRET_LIFETIME, $handle, $secret, $assoc_type); |
if ($dumb) { |
$key = $this->dumb_key; |
} else { |
$key = $this->normal_key; |
} |
$this->store->storeAssociation($key, $assoc); |
return $assoc; |
} |
/** |
* Given an association handle, get the association from the |
* store, or return a ServerError or null if something goes wrong. |
*/ |
function getAssociation($assoc_handle, $dumb) |
{ |
if ($assoc_handle === null) { |
return new Auth_OpenID_ServerError(null, |
"assoc_handle must not be null"); |
} |
if ($dumb) { |
$key = $this->dumb_key; |
} else { |
$key = $this->normal_key; |
} |
$assoc = $this->store->getAssociation($key, $assoc_handle); |
if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) { |
$this->store->removeAssociation($key, $assoc_handle); |
$assoc = null; |
} |
return $assoc; |
} |
/** |
* Invalidate a given association handle. |
*/ |
function invalidate($assoc_handle, $dumb) |
{ |
if ($dumb) { |
$key = $this->dumb_key; |
} else { |
$key = $this->normal_key; |
} |
$this->store->removeAssociation($key, $assoc_handle); |
} |
} |
/** |
* Encode an Auth_OpenID_Response to an Auth_OpenID_WebResponse. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Encoder { |
var $responseFactory = 'Auth_OpenID_WebResponse'; |
/** |
* Encode an Auth_OpenID_Response and return an |
* Auth_OpenID_WebResponse. |
*/ |
function encode(&$response) |
{ |
global $_Auth_OpenID_Encode_Kvform, |
$_Auth_OpenID_Encode_Url; |
$cls = $this->responseFactory; |
$encode_as = $response->whichEncoding(); |
if ($encode_as == $_Auth_OpenID_Encode_Kvform) { |
$wr = new $cls(null, null, $response->encodeToKVForm()); |
if (is_a($response, 'Auth_OpenID_ServerError')) { |
$wr->code = AUTH_OPENID_HTTP_ERROR; |
} |
} else if ($encode_as == $_Auth_OpenID_Encode_Url) { |
$location = $response->encodeToURL(); |
$wr = new $cls(AUTH_OPENID_HTTP_REDIRECT, |
array('location' => $location)); |
} else { |
return new Auth_OpenID_EncodingError($response); |
} |
return $wr; |
} |
} |
/** |
* Returns true if the given response needs a signature. |
* |
* @access private |
*/ |
function needsSigning($response) |
{ |
return (in_array($response->request->mode, array('checkid_setup', |
'checkid_immediate')) && |
$response->signed); |
} |
/** |
* An encoder which also takes care of signing fields when required. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder { |
function Auth_OpenID_SigningEncoder(&$signatory) |
{ |
$this->signatory =& $signatory; |
} |
/** |
* Sign an Auth_OpenID_Response and return an |
* Auth_OpenID_WebResponse. |
*/ |
function encode(&$response) |
{ |
// the isinstance is a bit of a kludge... it means there isn't |
// really an adapter to make the interfaces quite match. |
if (!is_a($response, 'Auth_OpenID_ServerError') && |
needsSigning($response)) { |
if (!$this->signatory) { |
return new Auth_OpenID_ServerError(null, |
"Must have a store to sign request"); |
} |
if (array_key_exists('sig', $response->fields)) { |
return new Auth_OpenID_AlreadySigned($response); |
} |
$response = $this->signatory->sign($response); |
} |
return parent::encode($response); |
} |
} |
/** |
* Decode an incoming Auth_OpenID_WebResponse into an |
* Auth_OpenID_Request. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Decoder { |
function Auth_OpenID_Decoder() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$this->prefix = $_Auth_OpenID_OpenID_Prefix; |
$this->handlers = array( |
'checkid_setup' => 'Auth_OpenID_CheckIDRequest', |
'checkid_immediate' => 'Auth_OpenID_CheckIDRequest', |
'check_authentication' => 'Auth_OpenID_CheckAuthRequest', |
'associate' => 'Auth_OpenID_AssociateRequest' |
); |
} |
/** |
* Given an HTTP query in an array (key-value pairs), decode it |
* into an Auth_OpenID_Request object. |
*/ |
function decode($query) |
{ |
if (!$query) { |
return null; |
} |
$myquery = array(); |
foreach ($query as $k => $v) { |
if (strpos($k, $this->prefix) === 0) { |
$myquery[$k] = $v; |
} |
} |
if (!$myquery) { |
return null; |
} |
$mode = Auth_OpenID::arrayGet($myquery, $this->prefix . 'mode'); |
if (!$mode) { |
return new Auth_OpenID_ServerError($query, |
sprintf("No %s mode found in query", $this->prefix)); |
} |
$handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode, |
$this->defaultDecoder($query)); |
if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) { |
return call_user_func_array(array($handlerCls, 'fromQuery'), |
array($query)); |
} else { |
return $handlerCls; |
} |
} |
function defaultDecoder($query) |
{ |
$mode = $query[$this->prefix . 'mode']; |
return new Auth_OpenID_ServerError($query, |
sprintf("No decoder for mode %s", $mode)); |
} |
} |
/** |
* An error that indicates an encoding problem occurred. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_EncodingError { |
function Auth_OpenID_EncodingError(&$response) |
{ |
$this->response =& $response; |
} |
} |
/** |
* An error that indicates that a response was already signed. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError { |
// This response is already signed. |
} |
/** |
* An error that indicates that the given return_to is not under the |
* given trust_root. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError { |
function Auth_OpenID_UntrustedReturnURL($return_to, $trust_root) |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$query = array( |
$_Auth_OpenID_OpenID_Prefix . 'return_to' => $return_to, |
$_Auth_OpenID_OpenID_Prefix . 'trust_root' => $trust_root); |
parent::Auth_OpenID_ServerError($query); |
} |
function toString() |
{ |
global $_Auth_OpenID_OpenID_Prefix; |
$return_to = $this->query[$_Auth_OpenID_OpenID_Prefix . 'return_to']; |
$trust_root = $this->query[$_Auth_OpenID_OpenID_Prefix . 'trust_root']; |
return sprintf("return_to %s not under trust_root %s", |
$return_to, $trust_root); |
} |
} |
/** |
* An object that implements the OpenID protocol for a single URL. |
* |
* Use this object by calling getOpenIDResponse when you get any |
* request for the server URL. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Server { |
function Auth_OpenID_Server(&$store) |
{ |
$this->store =& $store; |
$this->signatory =& new Auth_OpenID_Signatory($this->store); |
$this->encoder =& new Auth_OpenID_SigningEncoder($this->signatory); |
$this->decoder =& new Auth_OpenID_Decoder(); |
} |
/** |
* Handle a request. Given an Auth_OpenID_Request object, call |
* the appropriate Auth_OpenID_Server method to process the |
* request and generate a response. |
* |
* @param Auth_OpenID_Request $request An Auth_OpenID_Request |
* returned by Auth_OpenID_Server::decodeRequest. |
* |
* @return Auth_OpenID_Response $response A response object |
* capable of generating a user-agent reply. |
*/ |
function handleRequest($request) |
{ |
if (method_exists($this, "openid_" . $request->mode)) { |
$handler = array($this, "openid_" . $request->mode); |
return call_user_func($handler, $request); |
} |
return null; |
} |
/** |
* The callback for 'check_authentication' messages. |
* |
* @access private |
*/ |
function openid_check_authentication(&$request) |
{ |
return $request->answer($this->signatory); |
} |
/** |
* The callback for 'associate' messages. |
* |
* @access private |
*/ |
function openid_associate(&$request) |
{ |
$assoc = $this->signatory->createAssociation(false); |
return $request->answer($assoc); |
} |
/** |
* Encodes as response in the appropriate format suitable for |
* sending to the user agent. |
*/ |
function encodeResponse(&$response) |
{ |
return $this->encoder->encode($response); |
} |
/** |
* Decodes a query args array into the appropriate |
* Auth_OpenID_Request object. |
*/ |
function decodeRequest(&$query) |
{ |
return $this->decoder->decode($query); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/Parse.php |
---|
New file |
0,0 → 1,308 |
<?php |
/** |
* This module implements a VERY limited parser that finds <link> tags |
* in the head of HTML or XHTML documents and parses out their |
* attributes according to the OpenID spec. It is a liberal parser, |
* but it requires these things from the data in order to work: |
* |
* - There must be an open <html> tag |
* |
* - There must be an open <head> tag inside of the <html> tag |
* |
* - Only <link>s that are found inside of the <head> tag are parsed |
* (this is by design) |
* |
* - The parser follows the OpenID specification in resolving the |
* attributes of the link tags. This means that the attributes DO |
* NOT get resolved as they would by an XML or HTML parser. In |
* particular, only certain entities get replaced, and href |
* attributes do not get resolved relative to a base URL. |
* |
* From http://openid.net/specs.bml: |
* |
* - The openid.server URL MUST be an absolute URL. OpenID consumers |
* MUST NOT attempt to resolve relative URLs. |
* |
* - The openid.server URL MUST NOT include entities other than &, |
* <, >, and ". |
* |
* The parser ignores SGML comments and <![CDATA[blocks]]>. Both kinds |
* of quoting are allowed for attributes. |
* |
* The parser deals with invalid markup in these ways: |
* |
* - Tag names are not case-sensitive |
* |
* - The <html> tag is accepted even when it is not at the top level |
* |
* - The <head> tag is accepted even when it is not a direct child of |
* the <html> tag, but a <html> tag must be an ancestor of the |
* <head> tag |
* |
* - <link> tags are accepted even when they are not direct children |
* of the <head> tag, but a <head> tag must be an ancestor of the |
* <link> tag |
* |
* - If there is no closing tag for an open <html> or <head> tag, the |
* remainder of the document is viewed as being inside of the |
* tag. If there is no closing tag for a <link> tag, the link tag is |
* treated as a short tag. Exceptions to this rule are that <html> |
* closes <html> and <body> or <head> closes <head> |
* |
* - Attributes of the <link> tag are not required to be quoted. |
* |
* - In the case of duplicated attribute names, the attribute coming |
* last in the tag will be the value returned. |
* |
* - Any text that does not parse as an attribute within a link tag |
* will be ignored. (e.g. <link pumpkin rel='openid.server' /> will |
* ignore pumpkin) |
* |
* - If there are more than one <html> or <head> tag, the parser only |
* looks inside of the first one. |
* |
* - The contents of <script> tags are ignored entirely, except |
* unclosed <script> tags. Unclosed <script> tags are ignored. |
* |
* - Any other invalid markup is ignored, including unclosed SGML |
* comments and unclosed <![CDATA[blocks. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Require Auth_OpenID::arrayGet(). |
*/ |
require_once "Auth/OpenID.php"; |
class Auth_OpenID_Parse { |
/** |
* Specify some flags for use with regex matching. |
*/ |
var $_re_flags = "si"; |
/** |
* Stuff to remove before we start looking for tags |
*/ |
var $_removed_re = |
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>"; |
/** |
* Starts with the tag name at a word boundary, where the tag name |
* is not a namespace |
*/ |
var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*?)(?:<\/?%s\s*>|\Z))"; |
var $_attr_find = '\b(\w+)=("[^"]*"|\'[^\']*\'|[^\'"\s\/<>]+)'; |
function Auth_OpenID_Parse() |
{ |
$this->_link_find = sprintf("/<link\b(?!:)([^>]*)(?!<)>/%s", |
$this->_re_flags); |
$this->_entity_replacements = array( |
'amp' => '&', |
'lt' => '<', |
'gt' => '>', |
'quot' => '"' |
); |
$this->_attr_find = sprintf("/%s/%s", |
$this->_attr_find, |
$this->_re_flags); |
$this->_removed_re = sprintf("/%s/%s", |
$this->_removed_re, |
$this->_re_flags); |
$this->_ent_replace = |
sprintf("&(%s);", implode("|", |
$this->_entity_replacements)); |
} |
/** |
* Returns a regular expression that will match a given tag in an |
* SGML string. |
*/ |
function tagMatcher($tag_name, $close_tags = null) |
{ |
if ($close_tags) { |
$options = implode("|", array_merge(array($tag_name), $close_tags)); |
$closer = sprintf("(?:%s)", $options); |
} else { |
$closer = $tag_name; |
} |
$expr = sprintf($this->_tag_expr, $tag_name, $closer); |
return sprintf("/%s/%s", $expr, $this->_re_flags); |
} |
function htmlFind() |
{ |
return $this->tagMatcher('html'); |
} |
function headFind() |
{ |
return $this->tagMatcher('head', array('body')); |
} |
function replaceEntities($str) |
{ |
foreach ($this->_entity_replacements as $old => $new) { |
$str = preg_replace(sprintf("/&%s;/", $old), $new, $str); |
} |
return $str; |
} |
function removeQuotes($str) |
{ |
$matches = array(); |
$double = '/^"(.*)"$/'; |
$single = "/^\'(.*)\'$/"; |
if (preg_match($double, $str, $matches)) { |
return $matches[1]; |
} else if (preg_match($single, $str, $matches)) { |
return $matches[1]; |
} else { |
return $str; |
} |
} |
/** |
* Find all link tags in a string representing a HTML document and |
* return a list of their attributes. |
* |
* @param string $html The text to parse |
* @return array $list An array of arrays of attributes, one for each |
* link tag |
*/ |
function parseLinkAttrs($html) |
{ |
$stripped = preg_replace($this->_removed_re, |
"", |
$html); |
// Try to find the <HTML> tag. |
$html_re = $this->htmlFind(); |
$html_matches = array(); |
if (!preg_match($html_re, $stripped, $html_matches)) { |
return array(); |
} |
// Try to find the <HEAD> tag. |
$head_re = $this->headFind(); |
$head_matches = array(); |
if (!preg_match($head_re, $html_matches[0], $head_matches)) { |
return array(); |
} |
$link_data = array(); |
$link_matches = array(); |
if (!preg_match_all($this->_link_find, $head_matches[0], |
$link_matches)) { |
return array(); |
} |
foreach ($link_matches[0] as $link) { |
$attr_matches = array(); |
preg_match_all($this->_attr_find, $link, $attr_matches); |
$link_attrs = array(); |
foreach ($attr_matches[0] as $index => $full_match) { |
$name = $attr_matches[1][$index]; |
$value = $this->replaceEntities( |
$this->removeQuotes($attr_matches[2][$index])); |
$link_attrs[strtolower($name)] = $value; |
} |
$link_data[] = $link_attrs; |
} |
return $link_data; |
} |
function relMatches($rel_attr, $target_rel) |
{ |
// Does this target_rel appear in the rel_str? |
// XXX: TESTME |
$rels = preg_split("/\s+/", trim($rel_attr)); |
foreach ($rels as $rel) { |
$rel = strtolower($rel); |
if ($rel == $target_rel) { |
return 1; |
} |
} |
return 0; |
} |
function linkHasRel($link_attrs, $target_rel) |
{ |
// Does this link have target_rel as a relationship? |
// XXX: TESTME |
$rel_attr = Auth_OpeniD::arrayGet($link_attrs, 'rel', null); |
return ($rel_attr && $this->relMatches($rel_attr, |
$target_rel)); |
} |
function findLinksRel($link_attrs_list, $target_rel) |
{ |
// Filter the list of link attributes on whether it has |
// target_rel as a relationship. |
// XXX: TESTME |
$result = array(); |
foreach ($link_attrs_list as $attr) { |
if ($this->linkHasRel($attr, $target_rel)) { |
$result[] = $attr; |
} |
} |
return $result; |
} |
function findFirstHref($link_attrs_list, $target_rel) |
{ |
// Return the value of the href attribute for the first link |
// tag in the list that has target_rel as a relationship. |
// XXX: TESTME |
$matches = $this->findLinksRel($link_attrs_list, |
$target_rel); |
if (!$matches) { |
return null; |
} |
$first = $matches[0]; |
return Auth_OpenID::arrayGet($first, 'href', null); |
} |
} |
function Auth_OpenID_legacy_discover($html_text) |
{ |
$p = new Auth_OpenID_Parse(); |
$link_attrs = $p->parseLinkAttrs($html_text); |
$server_url = $p->findFirstHref($link_attrs, |
'openid.server'); |
if ($server_url === null) { |
return false; |
} else { |
$delegate_url = $p->findFirstHref($link_attrs, |
'openid.delegate'); |
return array($delegate_url, $server_url); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/BigMath.php |
---|
New file |
0,0 → 1,444 |
<?php |
/** |
* BigMath: A math library wrapper that abstracts out the underlying |
* long integer library. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Needed for random number generation |
*/ |
require_once 'Auth/OpenID/CryptUtil.php'; |
/** |
* The superclass of all big-integer math implementations |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_MathLibrary { |
/** |
* Given a long integer, returns the number converted to a binary |
* string. This function accepts long integer values of arbitrary |
* magnitude and uses the local large-number math library when |
* available. |
* |
* @param integer $long The long number (can be a normal PHP |
* integer or a number created by one of the available long number |
* libraries) |
* @return string $binary The binary version of $long |
*/ |
function longToBinary($long) |
{ |
$cmp = $this->cmp($long, 0); |
if ($cmp < 0) { |
$msg = __FUNCTION__ . " takes only positive integers."; |
trigger_error($msg, E_USER_ERROR); |
return null; |
} |
if ($cmp == 0) { |
return "\x00"; |
} |
$bytes = array(); |
while ($this->cmp($long, 0) > 0) { |
array_unshift($bytes, $this->mod($long, 256)); |
$long = $this->div($long, pow(2, 8)); |
} |
if ($bytes && ($bytes[0] > 127)) { |
array_unshift($bytes, 0); |
} |
$string = ''; |
foreach ($bytes as $byte) { |
$string .= pack('C', $byte); |
} |
return $string; |
} |
/** |
* Given a binary string, returns the binary string converted to a |
* long number. |
* |
* @param string $binary The binary version of a long number, |
* probably as a result of calling longToBinary |
* @return integer $long The long number equivalent of the binary |
* string $str |
*/ |
function binaryToLong($str) |
{ |
if ($str === null) { |
return null; |
} |
// Use array_merge to return a zero-indexed array instead of a |
// one-indexed array. |
$bytes = array_merge(unpack('C*', $str)); |
$n = $this->init(0); |
if ($bytes && ($bytes[0] > 127)) { |
trigger_error("bytesToNum works only for positive integers.", |
E_USER_WARNING); |
return null; |
} |
foreach ($bytes as $byte) { |
$n = $this->mul($n, pow(2, 8)); |
$n = $this->add($n, $byte); |
} |
return $n; |
} |
function base64ToLong($str) |
{ |
$b64 = base64_decode($str); |
if ($b64 === false) { |
return false; |
} |
return $this->binaryToLong($b64); |
} |
function longToBase64($str) |
{ |
return base64_encode($this->longToBinary($str)); |
} |
/** |
* Returns a random number in the specified range. This function |
* accepts $start, $stop, and $step values of arbitrary magnitude |
* and will utilize the local large-number math library when |
* available. |
* |
* @param integer $start The start of the range, or the minimum |
* random number to return |
* @param integer $stop The end of the range, or the maximum |
* random number to return |
* @param integer $step The step size, such that $result - ($step |
* * N) = $start for some N |
* @return integer $result The resulting randomly-generated number |
*/ |
function rand($stop) |
{ |
static $duplicate_cache = array(); |
// Used as the key for the duplicate cache |
$rbytes = $this->longToBinary($stop); |
if (array_key_exists($rbytes, $duplicate_cache)) { |
list($duplicate, $nbytes) = $duplicate_cache[$rbytes]; |
} else { |
if ($rbytes[0] == "\x00") { |
$nbytes = strlen($rbytes) - 1; |
} else { |
$nbytes = strlen($rbytes); |
} |
$mxrand = $this->pow(256, $nbytes); |
// If we get a number less than this, then it is in the |
// duplicated range. |
$duplicate = $this->mod($mxrand, $stop); |
if (count($duplicate_cache) > 10) { |
$duplicate_cache = array(); |
} |
$duplicate_cache[$rbytes] = array($duplicate, $nbytes); |
} |
do { |
$bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes); |
$n = $this->binaryToLong($bytes); |
// Keep looping if this value is in the low duplicated range |
} while ($this->cmp($n, $duplicate) < 0); |
return $this->mod($n, $stop); |
} |
} |
/** |
* Exposes BCmath math library functionality. |
* |
* {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided |
* by the BCMath extension. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{ |
var $type = 'bcmath'; |
function add($x, $y) |
{ |
return bcadd($x, $y); |
} |
function sub($x, $y) |
{ |
return bcsub($x, $y); |
} |
function pow($base, $exponent) |
{ |
return bcpow($base, $exponent); |
} |
function cmp($x, $y) |
{ |
return bccomp($x, $y); |
} |
function init($number, $base = 10) |
{ |
return $number; |
} |
function mod($base, $modulus) |
{ |
return bcmod($base, $modulus); |
} |
function mul($x, $y) |
{ |
return bcmul($x, $y); |
} |
function div($x, $y) |
{ |
return bcdiv($x, $y); |
} |
/** |
* Same as bcpowmod when bcpowmod is missing |
* |
* @access private |
*/ |
function _powmod($base, $exponent, $modulus) |
{ |
$square = $this->mod($base, $modulus); |
$result = 1; |
while($this->cmp($exponent, 0) > 0) { |
if ($this->mod($exponent, 2)) { |
$result = $this->mod($this->mul($result, $square), $modulus); |
} |
$square = $this->mod($this->mul($square, $square), $modulus); |
$exponent = $this->div($exponent, 2); |
} |
return $result; |
} |
function powmod($base, $exponent, $modulus) |
{ |
if (function_exists('bcpowmod')) { |
return bcpowmod($base, $exponent, $modulus); |
} else { |
return $this->_powmod($base, $exponent, $modulus); |
} |
} |
function toString($num) |
{ |
return $num; |
} |
} |
/** |
* Exposes GMP math library functionality. |
* |
* {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided |
* by the GMP extension. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{ |
var $type = 'gmp'; |
function add($x, $y) |
{ |
return gmp_add($x, $y); |
} |
function sub($x, $y) |
{ |
return gmp_sub($x, $y); |
} |
function pow($base, $exponent) |
{ |
return gmp_pow($base, $exponent); |
} |
function cmp($x, $y) |
{ |
return gmp_cmp($x, $y); |
} |
function init($number, $base = 10) |
{ |
return gmp_init($number, $base); |
} |
function mod($base, $modulus) |
{ |
return gmp_mod($base, $modulus); |
} |
function mul($x, $y) |
{ |
return gmp_mul($x, $y); |
} |
function div($x, $y) |
{ |
return gmp_div_q($x, $y); |
} |
function powmod($base, $exponent, $modulus) |
{ |
return gmp_powm($base, $exponent, $modulus); |
} |
function toString($num) |
{ |
return gmp_strval($num); |
} |
} |
/** |
* Define the supported extensions. An extension array has keys |
* 'modules', 'extension', and 'class'. 'modules' is an array of PHP |
* module names which the loading code will attempt to load. These |
* values will be suffixed with a library file extension (e.g. ".so"). |
* 'extension' is the name of a PHP extension which will be tested |
* before 'modules' are loaded. 'class' is the string name of a |
* {@link Auth_OpenID_MathWrapper} subclass which should be |
* instantiated if a given extension is present. |
* |
* You can define new math library implementations and add them to |
* this array. |
*/ |
global $_Auth_OpenID_math_extensions; |
$_Auth_OpenID_math_extensions = array( |
array('modules' => array('gmp', 'php_gmp'), |
'extension' => 'gmp', |
'class' => 'Auth_OpenID_GmpMathWrapper'), |
array('modules' => array('bcmath', 'php_bcmath'), |
'extension' => 'bcmath', |
'class' => 'Auth_OpenID_BcMathWrapper') |
); |
/** |
* Detect which (if any) math library is available |
*/ |
function Auth_OpenID_detectMathLibrary($exts) |
{ |
$loaded = false; |
foreach ($exts as $extension) { |
// See if the extension specified is already loaded. |
if ($extension['extension'] && |
extension_loaded($extension['extension'])) { |
$loaded = true; |
} |
// Try to load dynamic modules. |
if (!$loaded) { |
foreach ($extension['modules'] as $module) { |
if (@dl($module . "." . PHP_SHLIB_SUFFIX)) { |
$loaded = true; |
break; |
} |
} |
} |
// If the load succeeded, supply an instance of |
// Auth_OpenID_MathWrapper which wraps the specified |
// module's functionality. |
if ($loaded) { |
return $extension; |
} |
} |
return false; |
} |
/** |
* {@link Auth_OpenID_getMathLib} checks for the presence of long |
* number extension modules and returns an instance of |
* {@link Auth_OpenID_MathWrapper} which exposes the module's |
* functionality. |
* |
* Checks for the existence of an extension module described by the |
* local {@link Auth_OpenID_math_extensions} array and returns an |
* instance of a wrapper for that extension module. If no extension |
* module is found, an instance of {@link Auth_OpenID_MathWrapper} is |
* returned, which wraps the native PHP integer implementation. The |
* proper calling convention for this method is $lib =& |
* Auth_OpenID_getMathLib(). |
* |
* This function checks for the existence of specific long number |
* implementations in the following order: GMP followed by BCmath. |
* |
* @return Auth_OpenID_MathWrapper $instance An instance of |
* {@link Auth_OpenID_MathWrapper} or one of its subclasses |
* |
* @package OpenID |
*/ |
function &Auth_OpenID_getMathLib() |
{ |
// The instance of Auth_OpenID_MathWrapper that we choose to |
// supply will be stored here, so that subseqent calls to this |
// method will return a reference to the same object. |
static $lib = null; |
if (isset($lib)) { |
return $lib; |
} |
if (defined('Auth_OpenID_NO_MATH_SUPPORT')) { |
$null = null; |
return $null; |
} |
// If this method has not been called before, look at |
// $Auth_OpenID_math_extensions and try to find an extension that |
// works. |
global $_Auth_OpenID_math_extensions; |
$ext = Auth_OpenID_detectMathLibrary($_Auth_OpenID_math_extensions); |
if ($ext === false) { |
$tried = array(); |
foreach ($_Auth_OpenID_math_extensions as $extinfo) { |
$tried[] = $extinfo['extension']; |
} |
$triedstr = implode(", ", $tried); |
define('Auth_OpenID_NO_MATH_SUPPORT', true); |
return null; |
} |
// Instantiate a new wrapper |
$class = $ext['class']; |
$lib = new $class(); |
return $lib; |
} |
?> |
/trunk/composants/openid/Auth/OpenID/Association.php |
---|
New file |
0,0 → 1,308 |
<?php |
/** |
* This module contains code for dealing with associations between |
* consumers and servers. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* @access private |
*/ |
require_once 'Auth/OpenID/CryptUtil.php'; |
/** |
* @access private |
*/ |
require_once 'Auth/OpenID/KVForm.php'; |
/** |
* This class represents an association between a server and a |
* consumer. In general, users of this library will never see |
* instances of this object. The only exception is if you implement a |
* custom {@link Auth_OpenID_OpenIDStore}. |
* |
* If you do implement such a store, it will need to store the values |
* of the handle, secret, issued, lifetime, and assoc_type instance |
* variables. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Association { |
/** |
* This is a HMAC-SHA1 specific value. |
* |
* @access private |
*/ |
var $SIG_LENGTH = 20; |
/** |
* The ordering and name of keys as stored by serialize. |
* |
* @access private |
*/ |
var $assoc_keys = array( |
'version', |
'handle', |
'secret', |
'issued', |
'lifetime', |
'assoc_type' |
); |
/** |
* This is an alternate constructor (factory method) used by the |
* OpenID consumer library to create associations. OpenID store |
* implementations shouldn't use this constructor. |
* |
* @access private |
* |
* @param integer $expires_in This is the amount of time this |
* association is good for, measured in seconds since the |
* association was issued. |
* |
* @param string $handle This is the handle the server gave this |
* association. |
* |
* @param string secret This is the shared secret the server |
* generated for this association. |
* |
* @param assoc_type This is the type of association this |
* instance represents. The only valid value of this field at |
* this time is 'HMAC-SHA1', but new types may be defined in the |
* future. |
* |
* @return association An {@link Auth_OpenID_Association} |
* instance. |
*/ |
function fromExpiresIn($expires_in, $handle, $secret, $assoc_type) |
{ |
$issued = time(); |
$lifetime = $expires_in; |
return new Auth_OpenID_Association($handle, $secret, |
$issued, $lifetime, $assoc_type); |
} |
/** |
* This is the standard constructor for creating an association. |
* The library should create all of the necessary associations, so |
* this constructor is not part of the external API. |
* |
* @access private |
* |
* @param string $handle This is the handle the server gave this |
* association. |
* |
* @param string $secret This is the shared secret the server |
* generated for this association. |
* |
* @param integer $issued This is the time this association was |
* issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a |
* unix timestamp) |
* |
* @param integer $lifetime This is the amount of time this |
* association is good for, measured in seconds since the |
* association was issued. |
* |
* @param string $assoc_type This is the type of association this |
* instance represents. The only valid value of this field at |
* this time is 'HMAC-SHA1', but new types may be defined in the |
* future. |
*/ |
function Auth_OpenID_Association( |
$handle, $secret, $issued, $lifetime, $assoc_type) |
{ |
if ($assoc_type != 'HMAC-SHA1') { |
$fmt = 'HMAC-SHA1 is the only supported association type (got %s)'; |
trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR); |
} |
$this->handle = $handle; |
$this->secret = $secret; |
$this->issued = $issued; |
$this->lifetime = $lifetime; |
$this->assoc_type = $assoc_type; |
} |
/** |
* This returns the number of seconds this association is still |
* valid for, or 0 if the association is no longer valid. |
* |
* @return integer $seconds The number of seconds this association |
* is still valid for, or 0 if the association is no longer valid. |
*/ |
function getExpiresIn($now = null) |
{ |
if ($now == null) { |
$now = time(); |
} |
return max(0, $this->issued + $this->lifetime - $now); |
} |
/** |
* This checks to see if two {@link Auth_OpenID_Association} |
* instances represent the same association. |
* |
* @return bool $result true if the two instances represent the |
* same association, false otherwise. |
*/ |
function equal($other) |
{ |
return ((gettype($this) == gettype($other)) |
&& ($this->handle == $other->handle) |
&& ($this->secret == $other->secret) |
&& ($this->issued == $other->issued) |
&& ($this->lifetime == $other->lifetime) |
&& ($this->assoc_type == $other->assoc_type)); |
} |
/** |
* Convert an association to KV form. |
* |
* @return string $result String in KV form suitable for |
* deserialization by deserialize. |
*/ |
function serialize() |
{ |
$data = array( |
'version' => '2', |
'handle' => $this->handle, |
'secret' => base64_encode($this->secret), |
'issued' => strval(intval($this->issued)), |
'lifetime' => strval(intval($this->lifetime)), |
'assoc_type' => $this->assoc_type |
); |
assert(array_keys($data) == $this->assoc_keys); |
return Auth_OpenID_KVForm::fromArray($data, $strict = true); |
} |
/** |
* Parse an association as stored by serialize(). This is the |
* inverse of serialize. |
* |
* @param string $assoc_s Association as serialized by serialize() |
* @return Auth_OpenID_Association $result instance of this class |
*/ |
function deserialize($class_name, $assoc_s) |
{ |
$pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true); |
$keys = array(); |
$values = array(); |
foreach ($pairs as $key => $value) { |
if (is_array($value)) { |
list($key, $value) = $value; |
} |
$keys[] = $key; |
$values[] = $value; |
} |
$class_vars = get_class_vars($class_name); |
$class_assoc_keys = $class_vars['assoc_keys']; |
sort($keys); |
sort($class_assoc_keys); |
if ($keys != $class_assoc_keys) { |
trigger_error('Unexpected key values: ' . strval($keys), |
E_USER_WARNING); |
return null; |
} |
$version = $pairs['version']; |
$handle = $pairs['handle']; |
$secret = $pairs['secret']; |
$issued = $pairs['issued']; |
$lifetime = $pairs['lifetime']; |
$assoc_type = $pairs['assoc_type']; |
if ($version != '2') { |
trigger_error('Unknown version: ' . $version, E_USER_WARNING); |
return null; |
} |
$issued = intval($issued); |
$lifetime = intval($lifetime); |
$secret = base64_decode($secret); |
return new $class_name( |
$handle, $secret, $issued, $lifetime, $assoc_type); |
} |
/** |
* Generate a signature for a sequence of (key, value) pairs |
* |
* @access private |
* @param array $pairs The pairs to sign, in order. This is an |
* array of two-tuples. |
* @return string $signature The binary signature of this sequence |
* of pairs |
*/ |
function sign($pairs) |
{ |
$kv = Auth_OpenID_KVForm::fromArray($pairs); |
return Auth_OpenID_HMACSHA1($this->secret, $kv); |
} |
/** |
* Generate a signature for some fields in a dictionary |
* |
* @access private |
* @param array $fields The fields to sign, in order; this is an |
* array of strings. |
* @param array $data Dictionary of values to sign (an array of |
* string => string pairs). |
* @return string $signature The signature, base64 encoded |
*/ |
function signDict($fields, $data, $prefix = 'openid.') |
{ |
$pairs = array(); |
foreach ($fields as $field) { |
$pairs[] = array($field, $data[$prefix . $field]); |
} |
return base64_encode($this->sign($pairs)); |
} |
/** |
* Add a signature to an array of fields |
* |
* @access private |
*/ |
function addSignature($fields, &$data, $prefix = 'openid.') |
{ |
$sig = $this->signDict($fields, $data, $prefix); |
$signed = implode(",", $fields); |
$data[$prefix . 'sig'] = $sig; |
$data[$prefix . 'signed'] = $signed; |
} |
/** |
* Confirm that the signature of these fields matches the |
* signature contained in the data |
* |
* @access private |
*/ |
function checkSignature($data, $prefix = 'openid.') |
{ |
$signed = $data[$prefix . 'signed']; |
$fields = explode(",", $signed); |
$expected_sig = $this->signDict($fields, $data, $prefix); |
$request_sig = $data[$prefix . 'sig']; |
return ($request_sig == $expected_sig); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/SQLStore.php |
---|
New file |
0,0 → 1,658 |
<?php |
/** |
* SQL-backed OpenID stores. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Require the PEAR DB module because we'll need it for the SQL-based |
* stores implemented here. We silence any errors from the inclusion |
* because it might not be present, and a user of the SQL stores may |
* supply an Auth_OpenID_DatabaseConnection instance that implements |
* its own storage. |
*/ |
global $__Auth_OpenID_PEAR_AVAILABLE; |
$__Auth_OpenID_PEAR_AVAILABLE = @include_once 'DB.php'; |
/** |
* @access private |
*/ |
require_once 'Auth/OpenID/Interface.php'; |
/** |
* This is the parent class for the SQL stores, which contains the |
* logic common to all of the SQL stores. |
* |
* The table names used are determined by the class variables |
* settings_table_name, associations_table_name, and |
* nonces_table_name. To change the name of the tables used, pass new |
* table names into the constructor. |
* |
* To create the tables with the proper schema, see the createTables |
* method. |
* |
* This class shouldn't be used directly. Use one of its subclasses |
* instead, as those contain the code necessary to use a specific |
* database. If you're an OpenID integrator and you'd like to create |
* an SQL-driven store that wraps an application's database |
* abstraction, be sure to create a subclass of |
* {@link Auth_OpenID_DatabaseConnection} that calls the application's |
* database abstraction calls. Then, pass an instance of your new |
* database connection class to your SQLStore subclass constructor. |
* |
* All methods other than the constructor and createTables should be |
* considered implementation details. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore { |
/** |
* This creates a new SQLStore instance. It requires an |
* established database connection be given to it, and it allows |
* overriding the default table names. |
* |
* @param connection $connection This must be an established |
* connection to a database of the correct type for the SQLStore |
* subclass you're using. This must either be an PEAR DB |
* connection handle or an instance of a subclass of |
* Auth_OpenID_DatabaseConnection. |
* |
* @param string $settings_table This is an optional parameter to |
* specify the name of the table used for this store's settings. |
* The default value is 'oid_settings'. |
* |
* @param associations_table: This is an optional parameter to |
* specify the name of the table used for storing associations. |
* The default value is 'oid_associations'. |
* |
* @param nonces_table: This is an optional parameter to specify |
* the name of the table used for storing nonces. The default |
* value is 'oid_nonces'. |
*/ |
function Auth_OpenID_SQLStore($connection, $settings_table = null, |
$associations_table = null, |
$nonces_table = null) |
{ |
global $__Auth_OpenID_PEAR_AVAILABLE; |
$this->settings_table_name = "oid_settings"; |
$this->associations_table_name = "oid_associations"; |
$this->nonces_table_name = "oid_nonces"; |
// Check the connection object type to be sure it's a PEAR |
// database connection. |
if (!(is_object($connection) && |
(is_subclass_of($connection, 'db_common') || |
is_subclass_of($connection, |
'auth_openid_databaseconnection')))) { |
trigger_error("Auth_OpenID_SQLStore expected PEAR connection " . |
"object (got ".get_class($connection).")", |
E_USER_ERROR); |
return; |
} |
$this->connection = $connection; |
// Be sure to set the fetch mode so the results are keyed on |
// column name instead of column index. This is a PEAR |
// constant, so only try to use it if PEAR is present. Note |
// that Auth_Openid_Databaseconnection instances need not |
// implement ::setFetchMode for this reason. |
if ($__Auth_OpenID_PEAR_AVAILABLE) { |
$this->connection->setFetchMode(DB_FETCHMODE_ASSOC); |
} |
if ($settings_table) { |
$this->settings_table_name = $settings_table; |
} |
if ($associations_table) { |
$this->associations_table_name = $associations_table; |
} |
if ($nonces_table) { |
$this->nonces_table_name = $nonces_table; |
} |
$this->max_nonce_age = 6 * 60 * 60; |
// Be sure to run the database queries with auto-commit mode |
// turned OFF, because we want every function to run in a |
// transaction, implicitly. As a rule, methods named with a |
// leading underscore will NOT control transaction behavior. |
// Callers of these methods will worry about transactions. |
$this->connection->autoCommit(false); |
// Create an empty SQL strings array. |
$this->sql = array(); |
// Call this method (which should be overridden by subclasses) |
// to populate the $this->sql array with SQL strings. |
$this->setSQL(); |
// Verify that all required SQL statements have been set, and |
// raise an error if any expected SQL strings were either |
// absent or empty. |
list($missing, $empty) = $this->_verifySQL(); |
if ($missing) { |
trigger_error("Expected keys in SQL query list: " . |
implode(", ", $missing), |
E_USER_ERROR); |
return; |
} |
if ($empty) { |
trigger_error("SQL list keys have no SQL strings: " . |
implode(", ", $empty), |
E_USER_ERROR); |
return; |
} |
// Add table names to queries. |
$this->_fixSQL(); |
} |
function tableExists($table_name) |
{ |
return !$this->isError( |
$this->connection->query("SELECT * FROM %s LIMIT 0", |
$table_name)); |
} |
/** |
* Returns true if $value constitutes a database error; returns |
* false otherwise. |
*/ |
function isError($value) |
{ |
return PEAR::isError($value); |
} |
/** |
* Converts a query result to a boolean. If the result is a |
* database error according to $this->isError(), this returns |
* false; otherwise, this returns true. |
*/ |
function resultToBool($obj) |
{ |
if ($this->isError($obj)) { |
return false; |
} else { |
return true; |
} |
} |
/** |
* This method should be overridden by subclasses. This method is |
* called by the constructor to set values in $this->sql, which is |
* an array keyed on sql name. |
*/ |
function setSQL() |
{ |
} |
/** |
* Resets the store by removing all records from the store's |
* tables. |
*/ |
function reset() |
{ |
$this->connection->query(sprintf("DELETE FROM %s", |
$this->associations_table_name)); |
$this->connection->query(sprintf("DELETE FROM %s", |
$this->nonces_table_name)); |
$this->connection->query(sprintf("DELETE FROM %s", |
$this->settings_table_name)); |
} |
/** |
* @access private |
*/ |
function _verifySQL() |
{ |
$missing = array(); |
$empty = array(); |
$required_sql_keys = array( |
'nonce_table', |
'assoc_table', |
'settings_table', |
'get_auth', |
'create_auth', |
'set_assoc', |
'get_assoc', |
'get_assocs', |
'remove_assoc', |
'add_nonce', |
'get_nonce', |
'remove_nonce' |
); |
foreach ($required_sql_keys as $key) { |
if (!array_key_exists($key, $this->sql)) { |
$missing[] = $key; |
} else if (!$this->sql[$key]) { |
$empty[] = $key; |
} |
} |
return array($missing, $empty); |
} |
/** |
* @access private |
*/ |
function _fixSQL() |
{ |
$replacements = array( |
array( |
'value' => $this->nonces_table_name, |
'keys' => array('nonce_table', |
'add_nonce', |
'get_nonce', |
'remove_nonce') |
), |
array( |
'value' => $this->associations_table_name, |
'keys' => array('assoc_table', |
'set_assoc', |
'get_assoc', |
'get_assocs', |
'remove_assoc') |
), |
array( |
'value' => $this->settings_table_name, |
'keys' => array('settings_table', |
'get_auth', |
'create_auth') |
) |
); |
foreach ($replacements as $item) { |
$value = $item['value']; |
$keys = $item['keys']; |
foreach ($keys as $k) { |
if (is_array($this->sql[$k])) { |
foreach ($this->sql[$k] as $part_key => $part_value) { |
$this->sql[$k][$part_key] = sprintf($part_value, |
$value); |
} |
} else { |
$this->sql[$k] = sprintf($this->sql[$k], $value); |
} |
} |
} |
} |
function blobDecode($blob) |
{ |
return $blob; |
} |
function blobEncode($str) |
{ |
return $str; |
} |
function createTables() |
{ |
$this->connection->autoCommit(true); |
$n = $this->create_nonce_table(); |
$a = $this->create_assoc_table(); |
$s = $this->create_settings_table(); |
$this->connection->autoCommit(false); |
if ($n && $a && $s) { |
return true; |
} else { |
return false; |
} |
} |
function create_nonce_table() |
{ |
if (!$this->tableExists($this->nonces_table_name)) { |
$r = $this->connection->query($this->sql['nonce_table']); |
return $this->resultToBool($r); |
} |
return true; |
} |
function create_assoc_table() |
{ |
if (!$this->tableExists($this->associations_table_name)) { |
$r = $this->connection->query($this->sql['assoc_table']); |
return $this->resultToBool($r); |
} |
return true; |
} |
function create_settings_table() |
{ |
if (!$this->tableExists($this->settings_table_name)) { |
$r = $this->connection->query($this->sql['settings_table']); |
return $this->resultToBool($r); |
} |
return true; |
} |
/** |
* @access private |
*/ |
function _get_auth() |
{ |
return $this->connection->getOne($this->sql['get_auth']); |
} |
/** |
* @access private |
*/ |
function _create_auth($str) |
{ |
return $this->connection->query($this->sql['create_auth'], |
array($str)); |
} |
function getAuthKey() |
{ |
$value = $this->_get_auth(); |
if (!$value) { |
$auth_key = |
Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN); |
$auth_key_s = $this->blobEncode($auth_key); |
$this->_create_auth($auth_key_s); |
} else { |
$auth_key_s = $value; |
$auth_key = $this->blobDecode($auth_key_s); |
} |
$this->connection->commit(); |
if (strlen($auth_key) != $this->AUTH_KEY_LEN) { |
$fmt = "Expected %d-byte string for auth key. Got key of length %d"; |
trigger_error(sprintf($fmt, $this->AUTH_KEY_LEN, strlen($auth_key)), |
E_USER_WARNING); |
return null; |
} |
return $auth_key; |
} |
/** |
* @access private |
*/ |
function _set_assoc($server_url, $handle, $secret, $issued, |
$lifetime, $assoc_type) |
{ |
return $this->connection->query($this->sql['set_assoc'], |
array( |
$server_url, |
$handle, |
$secret, |
$issued, |
$lifetime, |
$assoc_type)); |
} |
function storeAssociation($server_url, $association) |
{ |
if ($this->resultToBool($this->_set_assoc( |
$server_url, |
$association->handle, |
$this->blobEncode( |
$association->secret), |
$association->issued, |
$association->lifetime, |
$association->assoc_type |
))) { |
$this->connection->commit(); |
} else { |
$this->connection->rollback(); |
} |
} |
/** |
* @access private |
*/ |
function _get_assoc($server_url, $handle) |
{ |
$result = $this->connection->getRow($this->sql['get_assoc'], |
array($server_url, $handle)); |
if ($this->isError($result)) { |
return null; |
} else { |
return $result; |
} |
} |
/** |
* @access private |
*/ |
function _get_assocs($server_url) |
{ |
$result = $this->connection->getAll($this->sql['get_assocs'], |
array($server_url)); |
if ($this->isError($result)) { |
return array(); |
} else { |
return $result; |
} |
} |
function removeAssociation($server_url, $handle) |
{ |
if ($this->_get_assoc($server_url, $handle) == null) { |
return false; |
} |
if ($this->resultToBool($this->connection->query( |
$this->sql['remove_assoc'], |
array($server_url, $handle)))) { |
$this->connection->commit(); |
} else { |
$this->connection->rollback(); |
} |
return true; |
} |
function getAssociation($server_url, $handle = null) |
{ |
if ($handle !== null) { |
$assoc = $this->_get_assoc($server_url, $handle); |
$assocs = array(); |
if ($assoc) { |
$assocs[] = $assoc; |
} |
} else { |
$assocs = $this->_get_assocs($server_url); |
} |
if (!$assocs || (count($assocs) == 0)) { |
return null; |
} else { |
$associations = array(); |
foreach ($assocs as $assoc_row) { |
$assoc = new Auth_OpenID_Association($assoc_row['handle'], |
$assoc_row['secret'], |
$assoc_row['issued'], |
$assoc_row['lifetime'], |
$assoc_row['assoc_type']); |
$assoc->secret = $this->blobDecode($assoc->secret); |
if ($assoc->getExpiresIn() == 0) { |
$this->removeAssociation($server_url, $assoc->handle); |
} else { |
$associations[] = array($assoc->issued, $assoc); |
} |
} |
if ($associations) { |
$issued = array(); |
$assocs = array(); |
foreach ($associations as $key => $assoc) { |
$issued[$key] = $assoc[0]; |
$assocs[$key] = $assoc[1]; |
} |
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC, |
$associations); |
// return the most recently issued one. |
list($issued, $assoc) = $associations[0]; |
return $assoc; |
} else { |
return null; |
} |
} |
} |
/** |
* @access private |
*/ |
function _add_nonce($nonce, $expires) |
{ |
$sql = $this->sql['add_nonce']; |
$result = $this->connection->query($sql, array($nonce, $expires)); |
return $this->resultToBool($result); |
} |
/** |
* @access private |
*/ |
function storeNonce($nonce) |
{ |
if ($this->_add_nonce($nonce, time())) { |
$this->connection->commit(); |
} else { |
$this->connection->rollback(); |
} |
} |
/** |
* @access private |
*/ |
function _get_nonce($nonce) |
{ |
$result = $this->connection->getRow($this->sql['get_nonce'], |
array($nonce)); |
if ($this->isError($result)) { |
return null; |
} else { |
return $result; |
} |
} |
/** |
* @access private |
*/ |
function _remove_nonce($nonce) |
{ |
$this->connection->query($this->sql['remove_nonce'], |
array($nonce)); |
} |
function useNonce($nonce) |
{ |
$row = $this->_get_nonce($nonce); |
if ($row !== null) { |
$nonce = $row['nonce']; |
$timestamp = $row['expires']; |
$nonce_age = time() - $timestamp; |
if ($nonce_age > $this->max_nonce_age) { |
$present = 0; |
} else { |
$present = 1; |
} |
$this->_remove_nonce($nonce); |
} else { |
$present = 0; |
} |
$this->connection->commit(); |
return $present; |
} |
/** |
* "Octifies" a binary string by returning a string with escaped |
* octal bytes. This is used for preparing binary data for |
* PostgreSQL BYTEA fields. |
* |
* @access private |
*/ |
function _octify($str) |
{ |
$result = ""; |
for ($i = 0; $i < strlen($str); $i++) { |
$ch = substr($str, $i, 1); |
if ($ch == "\\") { |
$result .= "\\\\\\\\"; |
} else if (ord($ch) == 0) { |
$result .= "\\\\000"; |
} else { |
$result .= "\\" . strval(decoct(ord($ch))); |
} |
} |
return $result; |
} |
/** |
* "Unoctifies" octal-escaped data from PostgreSQL and returns the |
* resulting ASCII (possibly binary) string. |
* |
* @access private |
*/ |
function _unoctify($str) |
{ |
$result = ""; |
$i = 0; |
while ($i < strlen($str)) { |
$char = $str[$i]; |
if ($char == "\\") { |
// Look to see if the next char is a backslash and |
// append it. |
if ($str[$i + 1] != "\\") { |
$octal_digits = substr($str, $i + 1, 3); |
$dec = octdec($octal_digits); |
$char = chr($dec); |
$i += 4; |
} else { |
$char = "\\"; |
$i += 2; |
} |
} else { |
$i += 1; |
} |
$result .= $char; |
} |
return $result; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/SQLiteStore.php |
---|
New file |
0,0 → 1,66 |
<?php |
/** |
* An SQLite store. |
* |
* @package OpenID |
*/ |
/** |
* Require the base class file. |
*/ |
require_once "Auth/OpenID/SQLStore.php"; |
/** |
* An SQL store that uses SQLite as its backend. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore { |
function setSQL() |
{ |
$this->sql['nonce_table'] = |
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ". |
"expires INTEGER)"; |
$this->sql['assoc_table'] = |
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ". |
"secret BLOB(128), issued INTEGER, lifetime INTEGER, ". |
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle))"; |
$this->sql['settings_table'] = |
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ". |
"value BLOB(20))"; |
$this->sql['create_auth'] = |
"INSERT INTO %s VALUES ('auth_key', ?)"; |
$this->sql['get_auth'] = |
"SELECT value FROM %s WHERE setting = 'auth_key'"; |
$this->sql['set_assoc'] = |
"INSERT OR REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)"; |
$this->sql['get_assocs'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ?"; |
$this->sql['get_assoc'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ? AND handle = ?"; |
$this->sql['remove_assoc'] = |
"DELETE FROM %s WHERE server_url = ? AND handle = ?"; |
$this->sql['add_nonce'] = |
"INSERT OR REPLACE INTO %s (nonce, expires) VALUES (?, ?)"; |
$this->sql['get_nonce'] = |
"SELECT * FROM %s WHERE nonce = ?"; |
$this->sql['remove_nonce'] = |
"DELETE FROM %s WHERE nonce = ?"; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/Discover.php |
---|
New file |
0,0 → 1,258 |
<?php |
/** |
* The OpenID and Yadis discovery implementation for OpenID 1.2. |
*/ |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID/Parse.php"; |
require_once "Services/Yadis/XRIRes.php"; |
require_once "Services/Yadis/Yadis.php"; |
define('_OPENID_1_0_NS', 'http://openid.net/xmlns/1.0'); |
define('_OPENID_1_2_TYPE', 'http://openid.net/signon/1.2'); |
define('_OPENID_1_1_TYPE', 'http://openid.net/signon/1.1'); |
define('_OPENID_1_0_TYPE', 'http://openid.net/signon/1.0'); |
/** |
* Object representing an OpenID service endpoint. |
*/ |
class Auth_OpenID_ServiceEndpoint { |
function Auth_OpenID_ServiceEndpoint() |
{ |
$this->identity_url = null; |
$this->server_url = null; |
$this->type_uris = array(); |
$this->delegate = null; |
$this->canonicalID = null; |
$this->used_yadis = false; // whether this came from an XRDS |
} |
function usesExtension($extension_uri) |
{ |
return in_array($extension_uri, $this->type_uris); |
} |
function parseService($yadis_url, $uri, $type_uris, $service_element) |
{ |
// Set the state of this object based on the contents of the |
// service element. |
$this->type_uris = $type_uris; |
$this->identity_url = $yadis_url; |
$this->server_url = $uri; |
$this->delegate = Auth_OpenID_ServiceEndpoint::findDelegate( |
$service_element); |
$this->used_yadis = true; |
} |
function findDelegate($service) |
{ |
// Extract a openid:Delegate value from a Yadis Service |
// element. If no delegate is found, returns null. |
// Try to register new namespace. |
$service->parser->registerNamespace('openid', |
'http://openid.net/xmlns/1.0'); |
// XXX: should this die if there is more than one delegate |
// element? |
$delegates = $service->getElements("openid:Delegate"); |
if ($delegates) { |
return $service->parser->content($delegates[0]); |
} else { |
return null; |
} |
} |
function getServerID() |
{ |
// Return the identifier that should be sent as the |
// openid.identity_url parameter to the server. |
if ($this->delegate === null) { |
if ($this->canonicalID) { |
return $this->canonicalID; |
} else { |
return $this->identity_url; |
} |
} else { |
return $this->delegate; |
} |
} |
function fromHTML($uri, $html) |
{ |
// Parse the given document as HTML looking for an OpenID <link |
// rel=...> |
$urls = Auth_OpenID_legacy_discover($html); |
if ($urls === false) { |
return null; |
} |
list($delegate_url, $server_url) = $urls; |
$service = new Auth_OpenID_ServiceEndpoint(); |
$service->identity_url = $uri; |
$service->delegate = $delegate_url; |
$service->server_url = $server_url; |
$service->type_uris = array(_OPENID_1_0_TYPE); |
return $service; |
} |
} |
function filter_MatchesAnyOpenIDType(&$service) |
{ |
$uris = $service->getTypes(); |
foreach ($uris as $uri) { |
if (in_array($uri, |
array(_OPENID_1_0_TYPE, |
_OPENID_1_1_TYPE, |
_OPENID_1_2_TYPE))) { |
return true; |
} |
} |
return false; |
} |
function Auth_OpenID_makeOpenIDEndpoints($uri, $endpoints) |
{ |
$s = array(); |
if (!$endpoints) { |
return $s; |
} |
foreach ($endpoints as $service) { |
$type_uris = $service->getTypes(); |
$uris = $service->getURIs(); |
// If any Type URIs match and there is an endpoint URI |
// specified, then this is an OpenID endpoint |
if ($type_uris && |
$uris) { |
foreach ($uris as $service_uri) { |
$openid_endpoint = new Auth_OpenID_ServiceEndpoint(); |
$openid_endpoint->parseService($uri, |
$service_uri, |
$type_uris, |
$service); |
$s[] = $openid_endpoint; |
} |
} |
} |
return $s; |
} |
function Auth_OpenID_discoverWithYadis($uri, &$fetcher) |
{ |
// Discover OpenID services for a URI. Tries Yadis and falls back |
// on old-style <link rel='...'> discovery if Yadis fails. |
// Might raise a yadis.discover.DiscoveryFailure if no document |
// came back for that URI at all. I don't think falling back to |
// OpenID 1.0 discovery on the same URL will help, so don't bother |
// to catch it. |
$openid_services = array(); |
$http_response = null; |
$response = Services_Yadis_Yadis::discover($uri, $http_response, |
$fetcher); |
if ($response) { |
$identity_url = $response->uri; |
$openid_services = |
$response->xrds->services(array('filter_MatchesAnyOpenIDType')); |
} |
if (!$openid_services) { |
return @Auth_OpenID_discoverWithoutYadis($uri, |
$fetcher); |
} |
if (!$openid_services) { |
$body = $response->body; |
// Try to parse the response as HTML to get OpenID 1.0/1.1 |
// <link rel="..."> |
$service = Auth_OpenID_ServiceEndpoint::fromHTML($identity_url, |
$body); |
if ($service !== null) { |
$openid_services = array($service); |
} |
} else { |
$openid_services = Auth_OpenID_makeOpenIDEndpoints($response->uri, |
$openid_services); |
} |
return array($identity_url, $openid_services, $http_response); |
} |
function _Auth_OpenID_discoverServiceList($uri, &$fetcher) |
{ |
list($url, $services, $resp) = Auth_OpenID_discoverWithYadis($uri, |
$fetcher); |
return $services; |
} |
function _Auth_OpenID_discoverXRIServiceList($uri, &$fetcher) |
{ |
list($url, $services, $resp) = _Auth_OpenID_discoverXRI($uri, |
$fetcher); |
return $services; |
} |
function Auth_OpenID_discoverWithoutYadis($uri, &$fetcher) |
{ |
$http_resp = @$fetcher->get($uri); |
if ($http_resp->status != 200) { |
return array(null, array(), $http_resp); |
} |
$identity_url = $http_resp->final_url; |
// Try to parse the response as HTML to get OpenID 1.0/1.1 <link |
// rel="..."> |
$endpoint =& new Auth_OpenID_ServiceEndpoint(); |
$service = $endpoint->fromHTML($identity_url, $http_resp->body); |
if ($service === null) { |
$openid_services = array(); |
} else { |
$openid_services = array($service); |
} |
return array($identity_url, $openid_services, $http_resp); |
} |
function _Auth_OpenID_discoverXRI($iname, &$fetcher) |
{ |
$services = new Services_Yadis_ProxyResolver($fetcher); |
list($canonicalID, $service_list) = $services->query($iname, |
array(_OPENID_1_0_TYPE, |
_OPENID_1_1_TYPE, |
_OPENID_1_2_TYPE), |
array('filter_MatchesAnyOpenIDType')); |
$endpoints = Auth_OpenID_makeOpenIDEndpoints($iname, $service_list); |
for ($i = 0; $i < count($endpoints); $i++) { |
$endpoints[$i]->canonicalID = $canonicalID; |
} |
// FIXME: returned xri should probably be in some normal form |
return array($iname, $endpoints, null); |
} |
function Auth_OpenID_discover($uri, &$fetcher) |
{ |
return @Auth_OpenID_discoverWithYadis($uri, $fetcher); |
} |
?> |
/trunk/composants/openid/Auth/OpenID/ServerRequest.php |
---|
New file |
0,0 → 1,37 |
<?php |
/** |
* OpenID Server Request |
* |
* @see Auth_OpenID_Server |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Imports |
*/ |
require_once "Auth/OpenID.php"; |
/** |
* Object that holds the state of a request to the OpenID server |
* |
* With accessor functions to get at the internal request data. |
* |
* @see Auth_OpenID_Server |
* @package OpenID |
*/ |
class Auth_OpenID_ServerRequest { |
function Auth_OpenID_ServerRequest() |
{ |
$this->mode = null; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/PostgreSQLStore.php |
---|
New file |
0,0 → 1,136 |
<?php |
/** |
* A PostgreSQL store. |
* |
* @package OpenID |
*/ |
/** |
* Require the base class file. |
*/ |
require_once "Auth/OpenID/SQLStore.php"; |
/** |
* An SQL store that uses PostgreSQL as its backend. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore { |
/** |
* @access private |
*/ |
function setSQL() |
{ |
$this->sql['nonce_table'] = |
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ". |
"expires INTEGER)"; |
$this->sql['assoc_table'] = |
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ". |
"secret BYTEA, issued INTEGER, lifetime INTEGER, ". |
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle), ". |
"CONSTRAINT secret_length_constraint CHECK ". |
"(LENGTH(secret) <= 128))"; |
$this->sql['settings_table'] = |
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ". |
"value BYTEA, ". |
"CONSTRAINT value_length_constraint CHECK (LENGTH(value) <= 20))"; |
$this->sql['create_auth'] = |
"INSERT INTO %s VALUES ('auth_key', '!')"; |
$this->sql['get_auth'] = |
"SELECT value FROM %s WHERE setting = 'auth_key'"; |
$this->sql['set_assoc'] = |
array( |
'insert_assoc' => "INSERT INTO %s (server_url, handle, ". |
"secret, issued, lifetime, assoc_type) VALUES ". |
"(?, ?, '!', ?, ?, ?)", |
'update_assoc' => "UPDATE %s SET secret = '!', issued = ?, ". |
"lifetime = ?, assoc_type = ? WHERE server_url = ? AND ". |
"handle = ?" |
); |
$this->sql['get_assocs'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ?"; |
$this->sql['get_assoc'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ? AND handle = ?"; |
$this->sql['remove_assoc'] = |
"DELETE FROM %s WHERE server_url = ? AND handle = ?"; |
$this->sql['add_nonce'] = |
array( |
'insert_nonce' => "INSERT INTO %s (nonce, expires) VALUES ". |
"(?, ?)", |
'update_nonce' => "UPDATE %s SET expires = ? WHERE nonce = ?" |
); |
$this->sql['get_nonce'] = |
"SELECT * FROM %s WHERE nonce = ?"; |
$this->sql['remove_nonce'] = |
"DELETE FROM %s WHERE nonce = ?"; |
} |
/** |
* @access private |
*/ |
function _set_assoc($server_url, $handle, $secret, $issued, $lifetime, |
$assoc_type) |
{ |
$result = $this->_get_assoc($server_url, $handle); |
if ($result) { |
// Update the table since this associations already exists. |
$this->connection->query($this->sql['set_assoc']['update_assoc'], |
array($secret, $issued, $lifetime, |
$assoc_type, $server_url, $handle)); |
} else { |
// Insert a new record because this association wasn't |
// found. |
$this->connection->query($this->sql['set_assoc']['insert_assoc'], |
array($server_url, $handle, $secret, |
$issued, $lifetime, $assoc_type)); |
} |
} |
/** |
* @access private |
*/ |
function _add_nonce($nonce, $expires) |
{ |
if ($this->_get_nonce($nonce)) { |
return $this->resultToBool($this->connection->query( |
$this->sql['add_nonce']['update_nonce'], |
array($expires, $nonce))); |
} else { |
return $this->resultToBool($this->connection->query( |
$this->sql['add_nonce']['insert_nonce'], |
array($nonce, $expires))); |
} |
} |
/** |
* @access private |
*/ |
function blobEncode($blob) |
{ |
return $this->_octify($blob); |
} |
/** |
* @access private |
*/ |
function blobDecode($blob) |
{ |
return $this->_unoctify($blob); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/MySQLStore.php |
---|
New file |
0,0 → 1,78 |
<?php |
/** |
* A MySQL store. |
* |
* @package OpenID |
*/ |
/** |
* Require the base class file. |
*/ |
require_once "Auth/OpenID/SQLStore.php"; |
/** |
* An SQL store that uses MySQL as its backend. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore { |
/** |
* @access private |
*/ |
function setSQL() |
{ |
$this->sql['nonce_table'] = |
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ". |
"expires INTEGER) TYPE=InnoDB"; |
$this->sql['assoc_table'] = |
"CREATE TABLE %s (server_url BLOB, handle VARCHAR(255), ". |
"secret BLOB, issued INTEGER, lifetime INTEGER, ". |
"assoc_type VARCHAR(64), PRIMARY KEY (server_url(255), handle)) ". |
"TYPE=InnoDB"; |
$this->sql['settings_table'] = |
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ". |
"value BLOB) TYPE=InnoDB"; |
$this->sql['create_auth'] = |
"INSERT INTO %s VALUES ('auth_key', !)"; |
$this->sql['get_auth'] = |
"SELECT value FROM %s WHERE setting = 'auth_key'"; |
$this->sql['set_assoc'] = |
"REPLACE INTO %s VALUES (?, ?, !, ?, ?, ?)"; |
$this->sql['get_assocs'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ?"; |
$this->sql['get_assoc'] = |
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ". |
"WHERE server_url = ? AND handle = ?"; |
$this->sql['remove_assoc'] = |
"DELETE FROM %s WHERE server_url = ? AND handle = ?"; |
$this->sql['add_nonce'] = |
"REPLACE INTO %s (nonce, expires) VALUES (?, ?)"; |
$this->sql['get_nonce'] = |
"SELECT * FROM %s WHERE nonce = ?"; |
$this->sql['remove_nonce'] = |
"DELETE FROM %s WHERE nonce = ?"; |
} |
/** |
* @access private |
*/ |
function blobEncode($blob) |
{ |
return "0x" . bin2hex($blob); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/DatabaseConnection.php |
---|
New file |
0,0 → 1,131 |
<?php |
/** |
* The Auth_OpenID_DatabaseConnection class, which is used to emulate |
* a PEAR database connection. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* An empty base class intended to emulate PEAR connection |
* functionality in applications that supply their own database |
* abstraction mechanisms. See {@link Auth_OpenID_SQLStore} for more |
* information. You should subclass this class if you need to create |
* an SQL store that needs to access its database using an |
* application's database abstraction layer instead of a PEAR database |
* connection. Any subclass of Auth_OpenID_DatabaseConnection MUST |
* adhere to the interface specified here. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_DatabaseConnection { |
/** |
* Sets auto-commit mode on this database connection. |
* |
* @param bool $mode True if auto-commit is to be used; false if |
* not. |
*/ |
function autoCommit($mode) |
{ |
} |
/** |
* Run an SQL query with the specified parameters, if any. |
* |
* @param string $sql An SQL string with placeholders. The |
* placeholders are assumed to be specific to the database engine |
* for this connection. |
* |
* @param array $params An array of parameters to insert into the |
* SQL string using this connection's escaping mechanism. |
* |
* @return mixed $result The result of calling this connection's |
* internal query function. The type of result depends on the |
* underlying database engine. This method is usually used when |
* the result of a query is not important, like a DDL query. |
*/ |
function query($sql, $params = array()) |
{ |
} |
/** |
* Starts a transaction on this connection, if supported. |
*/ |
function begin() |
{ |
} |
/** |
* Commits a transaction on this connection, if supported. |
*/ |
function commit() |
{ |
} |
/** |
* Performs a rollback on this connection, if supported. |
*/ |
function rollback() |
{ |
} |
/** |
* Run an SQL query and return the first column of the first row |
* of the result set, if any. |
* |
* @param string $sql An SQL string with placeholders. The |
* placeholders are assumed to be specific to the database engine |
* for this connection. |
* |
* @param array $params An array of parameters to insert into the |
* SQL string using this connection's escaping mechanism. |
* |
* @return mixed $result The value of the first column of the |
* first row of the result set. False if no such result was |
* found. |
*/ |
function getOne($sql, $params = array()) |
{ |
} |
/** |
* Run an SQL query and return the first row of the result set, if |
* any. |
* |
* @param string $sql An SQL string with placeholders. The |
* placeholders are assumed to be specific to the database engine |
* for this connection. |
* |
* @param array $params An array of parameters to insert into the |
* SQL string using this connection's escaping mechanism. |
* |
* @return array $result The first row of the result set, if any, |
* keyed on column name. False if no such result was found. |
*/ |
function getRow($sql, $params = array()) |
{ |
} |
/** |
* Run an SQL query with the specified parameters, if any. |
* |
* @param string $sql An SQL string with placeholders. The |
* placeholders are assumed to be specific to the database engine |
* for this connection. |
* |
* @param array $params An array of parameters to insert into the |
* SQL string using this connection's escaping mechanism. |
* |
* @return array $result An array of arrays representing the |
* result of the query; each array is keyed on column name. |
*/ |
function getAll($sql, $params = array()) |
{ |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/TrustRoot.php |
---|
New file |
0,0 → 1,243 |
<?php |
/** |
* Functions for dealing with OpenID trust roots |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* A regular expression that matches a domain ending in a top-level domains. |
* Used in checking trust roots for sanity. |
* |
* @access private |
*/ |
define('Auth_OpenID___TLDs', |
'/\.(com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|' . |
'ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|' . |
'bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|' . |
'cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|' . |
'fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|' . |
'gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|' . |
'ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|' . |
'ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|' . |
'nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|' . |
'ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|' . |
'so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|' . |
'ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$/'); |
/** |
* A wrapper for trust-root related functions |
*/ |
class Auth_OpenID_TrustRoot { |
/** |
* Parse a URL into its trust_root parts. |
* |
* @static |
* |
* @access private |
* |
* @param string $trust_root The url to parse |
* |
* @return mixed $parsed Either an associative array of trust root |
* parts or false if parsing failed. |
*/ |
function _parse($trust_root) |
{ |
$parts = @parse_url($trust_root); |
if ($parts === false) { |
return false; |
} |
$required_parts = array('scheme', 'host'); |
$forbidden_parts = array('user', 'pass', 'fragment'); |
$keys = array_keys($parts); |
if (array_intersect($keys, $required_parts) != $required_parts) { |
return false; |
} |
if (array_intersect($keys, $forbidden_parts) != array()) { |
return false; |
} |
// Return false if the original trust root value has more than |
// one port specification. |
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) { |
return false; |
} |
$scheme = strtolower($parts['scheme']); |
$allowed_schemes = array('http', 'https'); |
if (!in_array($scheme, $allowed_schemes)) { |
return false; |
} |
$parts['scheme'] = $scheme; |
$host = strtolower($parts['host']); |
$hostparts = explode('*', $host); |
switch (count($hostparts)) { |
case 1: |
$parts['wildcard'] = false; |
break; |
case 2: |
if ($hostparts[0] || |
($hostparts[1] && substr($hostparts[1], 0, 1) != '.')) { |
return false; |
} |
$host = $hostparts[1]; |
$parts['wildcard'] = true; |
break; |
default: |
return false; |
} |
if (strpos($host, ':') !== false) { |
return false; |
} |
$parts['host'] = $host; |
if (isset($parts['path'])) { |
$path = strtolower($parts['path']); |
if (substr($path, -1) != '/') { |
$path .= '/'; |
} |
} else { |
$path = '/'; |
} |
$parts['path'] = $path; |
if (!isset($parts['port'])) { |
$parts['port'] = false; |
} |
return $parts; |
} |
/** |
* Is this trust root sane? |
* |
* A trust root is sane if it is syntactically valid and it has a |
* reasonable domain name. Specifically, the domain name must be |
* more than one level below a standard TLD or more than two |
* levels below a two-letter tld. |
* |
* For example, '*.com' is not a sane trust root, but '*.foo.com' |
* is. '*.co.uk' is not sane, but '*.bbc.co.uk' is. |
* |
* This check is not always correct, but it attempts to err on the |
* side of marking sane trust roots insane instead of marking |
* insane trust roots sane. For example, 'kink.fm' is marked as |
* insane even though it "should" (for some meaning of should) be |
* marked sane. |
* |
* This function should be used when creating OpenID servers to |
* alert the users of the server when a consumer attempts to get |
* the user to accept a suspicious trust root. |
* |
* @static |
* @param string $trust_root The trust root to check |
* @return bool $sanity Whether the trust root looks OK |
*/ |
function isSane($trust_root) |
{ |
$parts = Auth_OpenID_TrustRoot::_parse($trust_root); |
if ($parts === false) { |
return false; |
} |
// Localhost is a special case |
if ($parts['host'] == 'localhost') { |
return true; |
} |
// Get the top-level domain of the host. If it is not a valid TLD, |
// it's not sane. |
preg_match(Auth_OpenID___TLDs, $parts['host'], $matches); |
if (!$matches) { |
return false; |
} |
$tld = $matches[1]; |
// Require at least two levels of specificity for non-country |
// tlds and three levels for country tlds. |
$elements = explode('.', $parts['host']); |
$n = count($elements); |
if ($parts['wildcard']) { |
$n -= 1; |
} |
if (strlen($tld) == 2) { |
$n -= 1; |
} |
if ($n <= 1) { |
return false; |
} |
return true; |
} |
/** |
* Does this URL match the given trust root? |
* |
* Return whether the URL falls under the given trust root. This |
* does not check whether the trust root is sane. If the URL or |
* trust root do not parse, this function will return false. |
* |
* @param string $trust_root The trust root to match against |
* |
* @param string $url The URL to check |
* |
* @return bool $matches Whether the URL matches against the |
* trust root |
*/ |
function match($trust_root, $url) |
{ |
$trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root); |
$url_parsed = Auth_OpenID_TrustRoot::_parse($url); |
if (!$trust_root_parsed || !$url_parsed) { |
return false; |
} |
// Check hosts matching |
if ($url_parsed['wildcard']) { |
return false; |
} |
if ($trust_root_parsed['wildcard']) { |
$host_tail = $trust_root_parsed['host']; |
$host = $url_parsed['host']; |
if ($host_tail && |
substr($host, -(strlen($host_tail))) != $host_tail && |
substr($host_tail, 1) != $host) { |
return false; |
} |
} else { |
if ($trust_root_parsed['host'] != $url_parsed['host']) { |
return false; |
} |
} |
// Check path and query matching |
$base_path = $trust_root_parsed['path']; |
$path = $url_parsed['path']; |
if (!isset($trust_root_parsed['query'])) { |
if (substr($path, 0, strlen($base_path)) != $base_path) { |
return false; |
} |
} else { |
$base_query = $trust_root_parsed['query']; |
$query = @$url_parsed['query']; |
$qplus = substr($query, 0, strlen($base_query) + 1); |
$bqplus = $base_query . '&'; |
if ($base_path != $path || |
($base_query != $query && $qplus != $bqplus)) { |
return false; |
} |
} |
// The port and scheme need to match exactly |
return ($trust_root_parsed['scheme'] == $url_parsed['scheme'] && |
$url_parsed['port'] === $trust_root_parsed['port']); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/HMACSHA1.php |
---|
New file |
0,0 → 1,72 |
<?php |
/** |
* This is the HMACSHA1 implementation for the OpenID library. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback |
* implementation. |
*/ |
define('Auth_OpenID_SHA1_BLOCKSIZE', 64); |
if (!function_exists('sha1')) { |
/** |
* Return a raw SHA1 hash of the given string |
* |
* XXX: include the SHA1 code from Dan Libby's OpenID library |
*/ |
function Auth_OpenID_SHA1($text) |
{ |
trigger_error('No SHA1 function found', E_USER_ERROR); |
} |
} else { |
/** |
* @ignore |
*/ |
function Auth_OpenID_SHA1($text) |
{ |
$hex = sha1($text); |
$raw = ''; |
for ($i = 0; $i < 40; $i += 2) { |
$hexcode = substr($hex, $i, 2); |
$charcode = (int)base_convert($hexcode, 16, 10); |
$raw .= chr($charcode); |
} |
return $raw; |
} |
} |
/** |
* Compute an HMAC/SHA1 hash. |
* |
* @access private |
* @param string $key The HMAC key |
* @param string $text The message text to hash |
* @return string $mac The MAC |
*/ |
function Auth_OpenID_HMACSHA1($key, $text) |
{ |
if (strlen($key) > Auth_OpenID_SHA1_BLOCKSIZE) { |
$key = Auth_OpenID_SHA1($key, true); |
} |
$key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00)); |
$ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE); |
$opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE); |
$hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true); |
$hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true); |
return $hmac; |
} |
?> |
/trunk/composants/openid/Auth/OpenID/DiffieHellman.php |
---|
New file |
0,0 → 1,181 |
<?php |
/** |
* The OpenID library's Diffie-Hellman implementation. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
require_once 'Auth/OpenID/BigMath.php'; |
require_once 'Auth/OpenID/HMACSHA1.php'; |
function Auth_OpenID_getDefaultMod() |
{ |
return '155172898181473697471232257763715539915724801'. |
'966915404479707795314057629378541917580651227423'. |
'698188993727816152646631438561595825688188889951'. |
'272158842675419950341258706556549803580104870537'. |
'681476726513255747040765857479291291572334510643'. |
'245094715007229621094194349783925984760375594985'. |
'848253359305585439638443'; |
} |
function Auth_OpenID_getDefaultGen() |
{ |
return '2'; |
} |
/** |
* The Diffie-Hellman key exchange class. This class relies on |
* {@link Auth_OpenID_MathLibrary} to perform large number operations. |
* |
* @access private |
* @package OpenID |
*/ |
class Auth_OpenID_DiffieHellman { |
var $mod; |
var $gen; |
var $private; |
var $lib = null; |
function Auth_OpenID_DiffieHellman($mod = null, $gen = null, |
$private = null, $lib = null) |
{ |
if ($lib === null) { |
$this->lib =& Auth_OpenID_getMathLib(); |
} else { |
$this->lib =& $lib; |
} |
if ($mod === null) { |
$this->mod = $this->lib->init(Auth_OpenID_getDefaultMod()); |
} else { |
$this->mod = $mod; |
} |
if ($gen === null) { |
$this->gen = $this->lib->init(Auth_OpenID_getDefaultGen()); |
} else { |
$this->gen = $gen; |
} |
if ($private === null) { |
$r = $this->lib->rand($this->mod); |
$this->private = $this->lib->add($r, 1); |
} else { |
$this->private = $private; |
} |
$this->public = $this->lib->powmod($this->gen, $this->private, |
$this->mod); |
} |
function getSharedSecret($composite) |
{ |
return $this->lib->powmod($composite, $this->private, $this->mod); |
} |
function getPublicKey() |
{ |
return $this->public; |
} |
/** |
* Generate the arguments for an OpenID Diffie-Hellman association |
* request |
*/ |
function getAssocArgs() |
{ |
$cpub = $this->lib->longToBase64($this->getPublicKey()); |
$args = array( |
'openid.dh_consumer_public' => $cpub, |
'openid.session_type' => 'DH-SHA1' |
); |
if ($this->lib->cmp($this->mod, Auth_OpenID_getDefaultMod()) || |
$this->lib->cmp($this->gen, Auth_OpenID_getDefaultGen())) { |
$args['openid.dh_modulus'] = $this->lib->longToBase64($this->mod); |
$args['openid.dh_gen'] = $this->lib->longToBase64($this->gen); |
} |
return $args; |
} |
function usingDefaultValues() |
{ |
return ($this->mod == Auth_OpenID_getDefaultMod() && |
$this->gen == Auth_OpenID_getDefaultGen()); |
} |
/** |
* Perform the server side of the OpenID Diffie-Hellman association |
*/ |
function serverAssociate($consumer_args, $assoc_secret) |
{ |
$lib =& Auth_OpenID_getMathLib(); |
if (isset($consumer_args['openid.dh_modulus'])) { |
$mod = $lib->base64ToLong($consumer_args['openid.dh_modulus']); |
} else { |
$mod = null; |
} |
if (isset($consumer_args['openid.dh_gen'])) { |
$gen = $lib->base64ToLong($consumer_args['openid.dh_gen']); |
} else { |
$gen = null; |
} |
$cpub64 = @$consumer_args['openid.dh_consumer_public']; |
if (!isset($cpub64)) { |
return false; |
} |
$dh = new Auth_OpenID_DiffieHellman($mod, $gen); |
$cpub = $lib->base64ToLong($cpub64); |
$mac_key = $dh->xorSecret($cpub, $assoc_secret); |
$enc_mac_key = base64_encode($mac_key); |
$spub64 = $lib->longToBase64($dh->getPublicKey()); |
$server_args = array( |
'session_type' => 'DH-SHA1', |
'dh_server_public' => $spub64, |
'enc_mac_key' => $enc_mac_key |
); |
return $server_args; |
} |
function consumerFinish($reply) |
{ |
$spub = $this->lib->base64ToLong($reply['dh_server_public']); |
if ($this->lib->cmp($spub, 0) <= 0) { |
return false; |
} |
$enc_mac_key = base64_decode($reply['enc_mac_key']); |
return $this->xorSecret($spub, $enc_mac_key); |
} |
function xorSecret($composite, $secret) |
{ |
$dh_shared = $this->getSharedSecret($composite); |
$dh_shared_str = $this->lib->longToBinary($dh_shared); |
$sha1_dh_shared = Auth_OpenID_SHA1($dh_shared_str); |
$xsecret = ""; |
for ($i = 0; $i < strlen($secret); $i++) { |
$xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i])); |
} |
return $xsecret; |
} |
} |
/trunk/composants/openid/Auth/OpenID/Consumer.php |
---|
New file |
0,0 → 1,1186 |
<?php |
/** |
* This module documents the main interface with the OpenID consumer |
* library. The only part of the library which has to be used and |
* isn't documented in full here is the store required to create an |
* Auth_OpenID_Consumer instance. More on the abstract store type and |
* concrete implementations of it that are provided in the |
* documentation for the Auth_OpenID_Consumer constructor. |
* |
* OVERVIEW |
* |
* The OpenID identity verification process most commonly uses the |
* following steps, as visible to the user of this library: |
* |
* 1. The user enters their OpenID into a field on the consumer's |
* site, and hits a login button. |
* 2. The consumer site discovers the user's OpenID server using the |
* YADIS protocol. |
* 3. The consumer site sends the browser a redirect to the identity |
* server. This is the authentication request as described in |
* the OpenID specification. |
* 4. The identity server's site sends the browser a redirect back |
* to the consumer site. This redirect contains the server's |
* response to the authentication request. |
* |
* The most important part of the flow to note is the consumer's site |
* must handle two separate HTTP requests in order to perform the full |
* identity check. |
* |
* LIBRARY DESIGN |
* |
* This consumer library is designed with that flow in mind. The goal |
* is to make it as easy as possible to perform the above steps |
* securely. |
* |
* At a high level, there are two important parts in the consumer |
* library. The first important part is this module, which contains |
* the interface to actually use this library. The second is the |
* Auth_OpenID_Interface class, which describes the interface to use |
* if you need to create a custom method for storing the state this |
* library needs to maintain between requests. |
* |
* In general, the second part is less important for users of the |
* library to know about, as several implementations are provided |
* which cover a wide variety of situations in which consumers may use |
* the library. |
* |
* This module contains a class, Auth_OpenID_Consumer, with methods |
* corresponding to the actions necessary in each of steps 2, 3, and 4 |
* described in the overview. Use of this library should be as easy |
* as creating an Auth_OpenID_Consumer instance and calling the |
* methods appropriate for the action the site wants to take. |
* |
* STORES AND DUMB MODE |
* |
* OpenID is a protocol that works best when the consumer site is able |
* to store some state. This is the normal mode of operation for the |
* protocol, and is sometimes referred to as smart mode. There is |
* also a fallback mode, known as dumb mode, which is available when |
* the consumer site is not able to store state. This mode should be |
* avoided when possible, as it leaves the implementation more |
* vulnerable to replay attacks. |
* |
* The mode the library works in for normal operation is determined by |
* the store that it is given. The store is an abstraction that |
* handles the data that the consumer needs to manage between http |
* requests in order to operate efficiently and securely. |
* |
* Several store implementation are provided, and the interface is |
* fully documented so that custom stores can be used as well. See |
* the documentation for the Auth_OpenID_Consumer class for more |
* information on the interface for stores. The implementations that |
* are provided allow the consumer site to store the necessary data in |
* several different ways, including several SQL databases and normal |
* files on disk. |
* |
* There is an additional concrete store provided that puts the system |
* in dumb mode. This is not recommended, as it removes the library's |
* ability to stop replay attacks reliably. It still uses time-based |
* checking to make replay attacks only possible within a small |
* window, but they remain possible within that window. This store |
* should only be used if the consumer site has no way to retain data |
* between requests at all. |
* |
* IMMEDIATE MODE |
* |
* In the flow described above, the user may need to confirm to the |
* lidentity server that it's ok to authorize his or her identity. |
* The server may draw pages asking for information from the user |
* before it redirects the browser back to the consumer's site. This |
* is generally transparent to the consumer site, so it is typically |
* ignored as an implementation detail. |
* |
* There can be times, however, where the consumer site wants to get a |
* response immediately. When this is the case, the consumer can put |
* the library in immediate mode. In immediate mode, there is an |
* extra response possible from the server, which is essentially the |
* server reporting that it doesn't have enough information to answer |
* the question yet. In addition to saying that, the identity server |
* provides a URL to which the user can be sent to provide the needed |
* information and let the server finish handling the original |
* request. |
* |
* USING THIS LIBRARY |
* |
* Integrating this library into an application is usually a |
* relatively straightforward process. The process should basically |
* follow this plan: |
* |
* Add an OpenID login field somewhere on your site. When an OpenID |
* is entered in that field and the form is submitted, it should make |
* a request to the your site which includes that OpenID URL. |
* |
* First, the application should instantiate the Auth_OpenID_Consumer |
* class using the store of choice (Auth_OpenID_FileStore or one of |
* the SQL-based stores). If the application has any sort of session |
* framework that provides per-client state management, a dict-like |
* object to access the session should be passed as the optional |
* second parameter. (The default behavior is to use PHP's standard |
* session machinery.) |
* |
* Next, the application should call the Auth_OpenID_Consumer object's |
* 'begin' method. This method takes the OpenID URL. The 'begin' |
* method returns an Auth_OpenID_AuthRequest object. |
* |
* Next, the application should call the 'redirectURL' method of the |
* Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is |
* the URL that the OpenID server will send the user back to after |
* attempting to verify his or her identity. The 'trust_root' is the |
* URL (or URL pattern) that identifies your web site to the user when |
* he or she is authorizing it. Send a redirect to the resulting URL |
* to the user's browser. |
* |
* That's the first half of the authentication process. The second |
* half of the process is done after the user's ID server sends the |
* user's browser a redirect back to your site to complete their |
* login. |
* |
* When that happens, the user will contact your site at the URL given |
* as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL |
* call made above. The request will have several query parameters |
* added to the URL by the identity server as the information |
* necessary to finish the request. |
* |
* Lastly, instantiate an Auth_OpenID_Consumer instance as above and |
* call its 'complete' method, passing in all the received query |
* arguments. |
* |
* There are multiple possible return types possible from that |
* method. These indicate the whether or not the login was successful, |
* and include any additional information appropriate for their type. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Require utility classes and functions for the consumer. |
*/ |
require_once "Auth/OpenID.php"; |
require_once "Auth/OpenID/HMACSHA1.php"; |
require_once "Auth/OpenID/Association.php"; |
require_once "Auth/OpenID/CryptUtil.php"; |
require_once "Auth/OpenID/DiffieHellman.php"; |
require_once "Auth/OpenID/KVForm.php"; |
require_once "Auth/OpenID/Discover.php"; |
require_once "Services/Yadis/Manager.php"; |
require_once "Services/Yadis/XRI.php"; |
/** |
* This is the status code returned when the complete method returns |
* successfully. |
*/ |
define('Auth_OpenID_SUCCESS', 'success'); |
/** |
* Status to indicate cancellation of OpenID authentication. |
*/ |
define('Auth_OpenID_CANCEL', 'cancel'); |
/** |
* This is the status code completeAuth returns when the value it |
* received indicated an invalid login. |
*/ |
define('Auth_OpenID_FAILURE', 'failure'); |
/** |
* This is the status code completeAuth returns when the |
* {@link Auth_OpenID_Consumer} instance is in immediate mode, and the |
* identity server sends back a URL to send the user to to complete his |
* or her login. |
*/ |
define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); |
/** |
* This is the status code beginAuth returns when the page fetched |
* from the entered OpenID URL doesn't contain the necessary link tags |
* to function as an identity page. |
*/ |
define('Auth_OpenID_PARSE_ERROR', 'parse error'); |
/** |
* This is the characters that the nonces are made from. |
*/ |
define('Auth_OpenID_DEFAULT_NONCE_CHRS',"abcdefghijklmnopqrstuvwxyz" . |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); |
/** |
* An OpenID consumer implementation that performs discovery and does |
* session management. See the Consumer.php file documentation for |
* more information. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_Consumer { |
/** |
* @access private |
*/ |
var $session_key_prefix = "_openid_consumer_"; |
/** |
* @access private |
*/ |
var $_token_suffix = "last_token"; |
/** |
* Initialize a Consumer instance. |
* |
* You should create a new instance of the Consumer object with |
* every HTTP request that handles OpenID transactions. |
* |
* @param Auth_OpenID_OpenIDStore $store This must be an object |
* that implements the interface in {@link |
* Auth_OpenID_OpenIDStore}. Several concrete implementations are |
* provided, to cover most common use cases. For stores backed by |
* MySQL, PostgreSQL, or SQLite, see the {@link |
* Auth_OpenID_SQLStore} class and its sublcasses. For a |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} |
* module. As a last resort, if it isn't possible for the server |
* to store state at all, an instance of {@link |
* Auth_OpenID_DumbStore} can be used. |
* |
* @param mixed session An object which implements the interface |
* of the Services_Yadis_Session class. Particularly, this object |
* is expected to have these methods: get($key), set($key, |
* $value), and del($key). This defaults to a session object |
* which wraps PHP's native session machinery. You should only |
* need to pass something here if you have your own sessioning |
* implementation. |
*/ |
function Auth_OpenID_Consumer(&$store, $session = null) |
{ |
if ($session === null) { |
$session = new Services_Yadis_PHPSession(); |
} |
$this->session =& $session; |
$this->consumer =& new Auth_OpenID_GenericConsumer($store); |
$this->_token_key = $this->session_key_prefix . $this->_token_suffix; |
} |
/** |
* Start the OpenID authentication process. See steps 1-2 in the |
* overview at the top of this file. |
* |
* @param User_url: Identity URL given by the user. This method |
* performs a textual transformation of the URL to try and make |
* sure it is normalized. For example, a user_url of example.com |
* will be normalized to http://example.com/ normalizing and |
* resolving any redirects the server might issue. |
* |
* @return Auth_OpenID_AuthRequest $auth_request An object |
* containing the discovered information will be returned, with a |
* method for building a redirect URL to the server, as described |
* in step 3 of the overview. This object may also be used to add |
* extension arguments to the request, using its 'addExtensionArg' |
* method. |
*/ |
function begin($user_url) |
{ |
$discoverMethod = '_Auth_OpenID_discoverServiceList'; |
$openid_url = $user_url; |
if (Services_Yadis_identifierScheme($user_url) == 'XRI') { |
$discoverMethod = '_Auth_OpenID_discoverXRIServiceList'; |
} else { |
$openid_url = Auth_OpenID::normalizeUrl($user_url); |
} |
$disco =& new Services_Yadis_Discovery($this->session, |
$openid_url, |
$this->session_key_prefix); |
// Set the 'stale' attribute of the manager. If discovery |
// fails in a fatal way, the stale flag will cause the manager |
// to be cleaned up next time discovery is attempted. |
$m = $disco->getManager(); |
$loader = new Services_Yadis_ManagerLoader(); |
if ($m) { |
if ($m->stale) { |
$disco->destroyManager(); |
} else { |
$m->stale = true; |
$disco->session->set($disco->session_key, |
serialize($loader->toSession($m))); |
} |
} |
$endpoint = $disco->getNextService($discoverMethod, |
$this->consumer->fetcher); |
// Reset the 'stale' attribute of the manager. |
$m =& $disco->getManager(); |
if ($m) { |
$m->stale = false; |
$disco->session->set($disco->session_key, |
serialize($loader->toSession($m))); |
} |
if ($endpoint === null) { |
return null; |
} else { |
return $this->beginWithoutDiscovery($endpoint); |
} |
} |
/** |
* Start OpenID verification without doing OpenID server |
* discovery. This method is used internally by Consumer.begin |
* after discovery is performed, and exists to provide an |
* interface for library users needing to perform their own |
* discovery. |
* |
* @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service |
* endpoint descriptor. |
* |
* @return Auth_OpenID_AuthRequest $auth_request An OpenID |
* authentication request object. |
*/ |
function &beginWithoutDiscovery($endpoint) |
{ |
$loader = new Auth_OpenID_ServiceEndpointLoader(); |
$auth_req = $this->consumer->begin($endpoint); |
$this->session->set($this->_token_key, |
$loader->toSession($auth_req->endpoint)); |
return $auth_req; |
} |
/** |
* Called to interpret the server's response to an OpenID |
* request. It is called in step 4 of the flow described in the |
* consumer overview. |
* |
* @param array $query An array of the query parameters (key => |
* value pairs) for this HTTP request. |
* |
* @return Auth_OpenID_ConsumerResponse $response A instance of an |
* Auth_OpenID_ConsumerResponse subclass. The type of response is |
* indicated by the status attribute, which will be one of |
* SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. |
*/ |
function complete($query) |
{ |
$query = Auth_OpenID::fixArgs($query); |
$loader = new Auth_OpenID_ServiceEndpointLoader(); |
$endpoint_data = $this->session->get($this->_token_key); |
$endpoint = |
$loader->fromSession($endpoint_data); |
if ($endpoint === null) { |
$response = new Auth_OpenID_FailureResponse(null, |
'No session state found'); |
} else { |
$response = $this->consumer->complete($query, $endpoint); |
$this->session->del($this->_token_key); |
} |
if (in_array($response->status, array(Auth_OpenID_SUCCESS, |
Auth_OpenID_CANCEL))) { |
if ($response->identity_url !== null) { |
$disco = new Services_Yadis_Discovery($this->session, |
$response->identity_url, |
$this->session_key_prefix); |
$disco->cleanup(); |
} |
} |
return $response; |
} |
} |
class Auth_OpenID_DiffieHellmanConsumerSession { |
var $session_type = 'DH-SHA1'; |
function Auth_OpenID_DiffieHellmanConsumerSession($dh = null) |
{ |
if ($dh === null) { |
$dh = new Auth_OpenID_DiffieHellman(); |
} |
$this->dh = $dh; |
} |
function getRequest() |
{ |
$math =& Auth_OpenID_getMathLib(); |
$cpub = $math->longToBase64($this->dh->public); |
$args = array('openid.dh_consumer_public' => $cpub); |
if (!$this->dh->usingDefaultValues()) { |
$args = array_merge($args, array( |
'openid.dh_modulus' => |
$math->longToBase64($this->dh->mod), |
'openid.dh_gen' => |
$math->longToBase64($this->dh->gen))); |
} |
return $args; |
} |
function extractSecret($response) |
{ |
if (!array_key_exists('dh_server_public', $response)) { |
return null; |
} |
if (!array_key_exists('enc_mac_key', $response)) { |
return null; |
} |
$math =& Auth_OpenID_getMathLib(); |
$spub = $math->base64ToLong($response['dh_server_public']); |
$enc_mac_key = base64_decode($response['enc_mac_key']); |
return $this->dh->xorSecret($spub, $enc_mac_key); |
} |
} |
class Auth_OpenID_PlainTextConsumerSession { |
var $session_type = null; |
function getRequest() |
{ |
return array(); |
} |
function extractSecret($response) |
{ |
if (!array_key_exists('mac_key', $response)) { |
return null; |
} |
return base64_decode($response['mac_key']); |
} |
} |
/** |
* This class is the interface to the OpenID consumer logic. |
* Instances of it maintain no per-request state, so they can be |
* reused (or even used by multiple threads concurrently) as needed. |
* |
* @package OpenID |
* @access private |
*/ |
class Auth_OpenID_GenericConsumer { |
/** |
* This consumer's store object. |
*/ |
var $store; |
/** |
* @access private |
*/ |
var $_use_assocs; |
/** |
* This is the number of characters in the generated nonce for |
* each transaction. |
*/ |
var $nonce_len = 8; |
/** |
* What characters are allowed in nonces |
*/ |
var $nonce_chrs = Auth_OpenID_DEFAULT_NONCE_CHRS; |
/** |
* This method initializes a new {@link Auth_OpenID_Consumer} |
* instance to access the library. |
* |
* @param Auth_OpenID_OpenIDStore $store This must be an object |
* that implements the interface in {@link Auth_OpenID_OpenIDStore}. |
* Several concrete implementations are provided, to cover most common use |
* cases. For stores backed by MySQL, PostgreSQL, or SQLite, see |
* the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} module. |
* As a last resort, if it isn't possible for the server to store |
* state at all, an instance of {@link Auth_OpenID_DumbStore} can be used. |
* |
* @param bool $immediate This is an optional boolean value. It |
* controls whether the library uses immediate mode, as explained |
* in the module description. The default value is False, which |
* disables immediate mode. |
*/ |
function Auth_OpenID_GenericConsumer(&$store) |
{ |
$this->store =& $store; |
$this->_use_assocs = |
!(defined('Auth_OpenID_NO_MATH_SUPPORT') || |
($this->store && $this->store->isDumb())); |
$this->fetcher = Services_Yadis_Yadis::getHTTPFetcher(); |
} |
function begin($service_endpoint) |
{ |
$nonce = $this->_createNonce(); |
$assoc = $this->_getAssociation($service_endpoint->server_url); |
$r = new Auth_OpenID_AuthRequest($assoc, $service_endpoint); |
$r->return_to_args['nonce'] = $nonce; |
return $r; |
} |
function complete($query, $endpoint) |
{ |
$mode = Auth_OpenID::arrayGet($query, 'openid.mode', |
'<no mode specified>'); |
if ($mode == Auth_OpenID_CANCEL) { |
return new Auth_OpenID_CancelResponse($endpoint); |
} else if ($mode == 'error') { |
$error = Auth_OpenID::arrayGet($query, 'openid.error'); |
return new Auth_OpenID_FailureResponse($endpoint, $error); |
} else if ($mode == 'id_res') { |
if ($endpoint->identity_url === null) { |
return new Auth_OpenID_FailureResponse($identity_url, |
"No session state found"); |
} |
$response = $this->_doIdRes($query, $endpoint); |
if ($response === null) { |
return new Auth_OpenID_FailureResponse($endpoint, |
"HTTP request failed"); |
} |
if ($response->status == Auth_OpenID_SUCCESS) { |
return $this->_checkNonce($response, |
Auth_OpenID::arrayGet($query, |
'nonce')); |
} else { |
return $response; |
} |
} else { |
return new Auth_OpenID_FailureResponse($endpoint, |
sprintf("Invalid openid.mode '%s'", |
$mode)); |
} |
} |
/** |
* @access private |
*/ |
function _doIdRes($query, $endpoint) |
{ |
$user_setup_url = Auth_OpenID::arrayGet($query, |
'openid.user_setup_url'); |
if ($user_setup_url !== null) { |
return new Auth_OpenID_SetupNeededResponse($endpoint, |
$user_setup_url); |
} |
$return_to = Auth_OpenID::arrayGet($query, 'openid.return_to', null); |
$server_id2 = Auth_OpenID::arrayGet($query, 'openid.identity', null); |
$assoc_handle = Auth_OpenID::arrayGet($query, |
'openid.assoc_handle', null); |
if (($return_to === null) || |
($server_id2 === null) || |
($assoc_handle === null)) { |
return new Auth_OpenID_FailureResponse($endpoint, |
"Missing required field"); |
} |
if ($endpoint->getServerID() != $server_id2) { |
return new Auth_OpenID_FailureResponse($endpoint, |
"Server ID (delegate) mismatch"); |
} |
$signed = Auth_OpenID::arrayGet($query, 'openid.signed'); |
$assoc = $this->store->getAssociation($endpoint->server_url, |
$assoc_handle); |
if ($assoc === null) { |
// It's not an association we know about. Dumb mode is |
// our only possible path for recovery. |
if ($this->_checkAuth($query, $endpoint->server_url)) { |
return new Auth_OpenID_SuccessResponse($endpoint, $query, |
$signed); |
} else { |
return new Auth_OpenID_FailureResponse($endpoint, |
"Server denied check_authentication"); |
} |
} |
if ($assoc->getExpiresIn() <= 0) { |
$msg = sprintf("Association with %s expired", |
$endpoint->server_url); |
return new Auth_OpenID_FailureResponse($endpoint, $msg); |
} |
// Check the signature |
$sig = Auth_OpenID::arrayGet($query, 'openid.sig', null); |
if (($sig === null) || |
($signed === null)) { |
return new Auth_OpenID_FailureResponse($endpoint, |
"Missing argument signature"); |
} |
$signed_list = explode(",", $signed); |
//Fail if the identity field is present but not signed |
if (($endpoint->identity_url !== null) && |
(!in_array('identity', $signed_list))) { |
$msg = '"openid.identity" not signed'; |
return new Auth_OpenID_FailureResponse($endpoint, $msg); |
} |
$v_sig = $assoc->signDict($signed_list, $query); |
if ($v_sig != $sig) { |
return new Auth_OpenID_FailureResponse($endpoint, |
"Bad signature"); |
} |
return Auth_OpenID_SuccessResponse::fromQuery($endpoint, |
$query, $signed); |
} |
/** |
* @access private |
*/ |
function _checkAuth($query, $server_url) |
{ |
$request = $this->_createCheckAuthRequest($query); |
if ($request === null) { |
return false; |
} |
$response = $this->_makeKVPost($request, $server_url); |
if ($response == null) { |
return false; |
} |
return $this->_processCheckAuthResponse($response, $server_url); |
} |
/** |
* @access private |
*/ |
function _createCheckAuthRequest($query) |
{ |
$signed = Auth_OpenID::arrayGet($query, 'openid.signed', null); |
if ($signed === null) { |
return null; |
} |
$whitelist = array('assoc_handle', 'sig', |
'signed', 'invalidate_handle'); |
$signed = array_merge(explode(",", $signed), $whitelist); |
$check_args = array(); |
foreach ($query as $key => $value) { |
if (in_array(substr($key, 7), $signed)) { |
$check_args[$key] = $value; |
} |
} |
$check_args['openid.mode'] = 'check_authentication'; |
return $check_args; |
} |
/** |
* @access private |
*/ |
function _processCheckAuthResponse($response, $server_url) |
{ |
$is_valid = Auth_OpenID::arrayGet($response, 'is_valid', 'false'); |
$invalidate_handle = Auth_OpenID::arrayGet($response, |
'invalidate_handle'); |
if ($invalidate_handle !== null) { |
$this->store->removeAssociation($server_url, |
$invalidate_handle); |
} |
if ($is_valid == 'true') { |
return true; |
} |
return false; |
} |
/** |
* @access private |
*/ |
function _makeKVPost($args, $server_url) |
{ |
$mode = $args['openid.mode']; |
$pairs = array(); |
foreach ($args as $k => $v) { |
$v = urlencode($v); |
$pairs[] = "$k=$v"; |
} |
$body = implode("&", $pairs); |
$resp = $this->fetcher->post($server_url, $body); |
if ($resp === null) { |
return null; |
} |
$response = Auth_OpenID_KVForm::toArray($resp->body); |
if ($resp->status == 400) { |
return null; |
} else if ($resp->status != 200) { |
return null; |
} |
return $response; |
} |
/** |
* @access private |
*/ |
function _checkNonce($response, $nonce) |
{ |
$parsed_url = parse_url($response->getReturnTo()); |
$query_str = @$parsed_url['query']; |
$query = array(); |
parse_str($query_str, $query); |
$found = false; |
foreach ($query as $k => $v) { |
if ($k == 'nonce') { |
if ($v != $nonce) { |
return new Auth_OpenID_FailureResponse($response, |
"Nonce mismatch"); |
} else { |
$found = true; |
break; |
} |
} |
} |
if (!$found) { |
return new Auth_OpenID_FailureResponse($response, |
sprintf("Nonce missing from return_to: %s", |
$response->getReturnTo())); |
} |
if (!$this->store->useNonce($nonce)) { |
return new Auth_OpenID_FailureResponse($response, |
"Nonce missing from store"); |
} |
return $response; |
} |
/** |
* @access private |
*/ |
function _createNonce() |
{ |
$nonce = Auth_OpenID_CryptUtil::randomString($this->nonce_len, |
$this->nonce_chrs); |
$this->store->storeNonce($nonce); |
return $nonce; |
} |
/** |
* @access protected |
*/ |
function _createDiffieHellman() |
{ |
return new Auth_OpenID_DiffieHellman(); |
} |
/** |
* @access private |
*/ |
function _getAssociation($server_url) |
{ |
if (!$this->_use_assocs) { |
return null; |
} |
$assoc = $this->store->getAssociation($server_url); |
if (($assoc === null) || |
($assoc->getExpiresIn() <= 0)) { |
$parts = $this->_createAssociateRequest($server_url); |
if ($parts === null) { |
return null; |
} |
list($assoc_session, $args) = $parts; |
$response = $this->_makeKVPost($args, $server_url); |
if ($response === null) { |
$assoc = null; |
} else { |
$assoc = $this->_parseAssociation($response, $assoc_session, |
$server_url); |
} |
} |
return $assoc; |
} |
function _createAssociateRequest($server_url) |
{ |
$parts = parse_url($server_url); |
if ($parts === false) { |
return null; |
} |
if (array_key_exists('scheme', $parts)) { |
$proto = $parts['scheme']; |
} else { |
$proto = 'http'; |
} |
if ($proto == 'https') { |
$assoc_session = new Auth_OpenID_PlainTextConsumerSession(); |
} else { |
$assoc_session = new Auth_OpenID_DiffieHellmanConsumerSession(); |
} |
$args = array( |
'openid.mode' => 'associate', |
'openid.assoc_type' => 'HMAC-SHA1'); |
if ($assoc_session->session_type !== null) { |
$args['openid.session_type'] = $assoc_session->session_type; |
} |
$args = array_merge($args, $assoc_session->getRequest()); |
return array($assoc_session, $args); |
} |
/** |
* @access private |
*/ |
function _parseAssociation($results, $assoc_session, $server_url) |
{ |
$required_keys = array('assoc_type', 'assoc_handle', |
'expires_in'); |
foreach ($required_keys as $key) { |
if (!array_key_exists($key, $results)) { |
return null; |
} |
} |
$assoc_type = $results['assoc_type']; |
$assoc_handle = $results['assoc_handle']; |
$expires_in_str = $results['expires_in']; |
if ($assoc_type != 'HMAC-SHA1') { |
return null; |
} |
$expires_in = intval($expires_in_str); |
if ($expires_in <= 0) { |
return null; |
} |
$session_type = Auth_OpenID::arrayGet($results, 'session_type'); |
if ($session_type != $assoc_session->session_type) { |
if ($session_type === null) { |
$assoc_session = new Auth_OpenID_PlainTextConsumerSession(); |
} else { |
return null; |
} |
} |
$secret = $assoc_session->extractSecret($results); |
if (!$secret) { |
return null; |
} |
$assoc = Auth_OpenID_Association::fromExpiresIn( |
$expires_in, $assoc_handle, $secret, $assoc_type); |
$this->store->storeAssociation($server_url, $assoc); |
return $assoc; |
} |
} |
/** |
* This class represents an authentication request from a consumer to |
* an OpenID server. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_AuthRequest { |
/** |
* Initialize an authentication request with the specified token, |
* association, and endpoint. |
* |
* Users of this library should not create instances of this |
* class. Instances of this class are created by the library when |
* needed. |
*/ |
function Auth_OpenID_AuthRequest($assoc, $endpoint) |
{ |
$this->assoc = $assoc; |
$this->endpoint = $endpoint; |
$this->extra_args = array(); |
$this->return_to_args = array(); |
} |
/** |
* Add an extension argument to this OpenID authentication |
* request. |
* |
* Use caution when adding arguments, because they will be |
* URL-escaped and appended to the redirect URL, which can easily |
* get quite long. |
* |
* @param string $namespace The namespace for the extension. For |
* example, the simple registration extension uses the namespace |
* 'sreg'. |
* |
* @param string $key The key within the extension namespace. For |
* example, the nickname field in the simple registration |
* extension's key is 'nickname'. |
* |
* @param string $value The value to provide to the server for |
* this argument. |
*/ |
function addExtensionArg($namespace, $key, $value) |
{ |
$arg_name = implode('.', array('openid', $namespace, $key)); |
$this->extra_args[$arg_name] = $value; |
} |
/** |
* Compute the appropriate redirection URL for this request based |
* on a specified trust root and return-to. |
* |
* @param string $trust_root The trust root URI for your |
* application. |
* |
* @param string$ $return_to The return-to URL to be used when the |
* OpenID server redirects the user back to your site. |
* |
* @return string $redirect_url The resulting redirect URL that |
* you should send to the user agent. |
*/ |
function redirectURL($trust_root, $return_to, $immediate=false) |
{ |
if ($immediate) { |
$mode = 'checkid_immediate'; |
} else { |
$mode = 'checkid_setup'; |
} |
$return_to = Auth_OpenID::appendArgs($return_to, $this->return_to_args); |
$redir_args = array( |
'openid.mode' => $mode, |
'openid.identity' => $this->endpoint->getServerID(), |
'openid.return_to' => $return_to, |
'openid.trust_root' => $trust_root); |
if ($this->assoc) { |
$redir_args['openid.assoc_handle'] = $this->assoc->handle; |
} |
$redir_args = array_merge($redir_args, $this->extra_args); |
return Auth_OpenID::appendArgs($this->endpoint->server_url, |
$redir_args); |
} |
} |
/** |
* The base class for responses from the Auth_OpenID_Consumer. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_ConsumerResponse { |
var $status = null; |
} |
/** |
* A response with a status of Auth_OpenID_SUCCESS. Indicates that |
* this request is a successful acknowledgement from the OpenID server |
* that the supplied URL is, indeed controlled by the requesting |
* agent. This has three relevant attributes: |
* |
* identity_url - The identity URL that has been authenticated |
* |
* signed_args - The arguments in the server's response that were |
* signed and verified. |
* |
* status - Auth_OpenID_SUCCESS. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse { |
var $status = Auth_OpenID_SUCCESS; |
/** |
* @access private |
*/ |
function Auth_OpenID_SuccessResponse($endpoint, $signed_args) |
{ |
$this->endpoint = $endpoint; |
$this->identity_url = $endpoint->identity_url; |
$this->signed_args = $signed_args; |
} |
/** |
* @access private |
*/ |
function fromQuery($endpoint, $query, $signed) |
{ |
$signed_args = array(); |
foreach (explode(",", $signed) as $field_name) { |
$field_name = 'openid.' . $field_name; |
$signed_args[$field_name] = Auth_OpenID::arrayGet($query, |
$field_name, ''); |
} |
return new Auth_OpenID_SuccessResponse($endpoint, $signed_args); |
} |
/** |
* Extract signed extension data from the server's response. |
* |
* @param string $prefix The extension namespace from which to |
* extract the extension data. |
*/ |
function extensionResponse($prefix) |
{ |
$response = array(); |
$prefix = sprintf('openid.%s.', $prefix); |
$prefix_len = strlen($prefix); |
foreach ($this->signed_args as $k => $v) { |
if (strpos($k, $prefix) === 0) { |
$response_key = substr($k, $prefix_len); |
$response[$response_key] = $v; |
} |
} |
return $response; |
} |
/** |
* Get the openid.return_to argument from this response. |
* |
* This is useful for verifying that this request was initiated by |
* this consumer. |
* |
* @return string $return_to The return_to URL supplied to the |
* server on the initial request, or null if the response did not |
* contain an 'openid.return_to' argument. |
*/ |
function getReturnTo() |
{ |
return Auth_OpenID::arrayGet($this->signed_args, 'openid.return_to'); |
} |
} |
/** |
* A response with a status of Auth_OpenID_FAILURE. Indicates that the |
* OpenID protocol has failed. This could be locally or remotely |
* triggered. This has three relevant attributes: |
* |
* identity_url - The identity URL for which authentication was |
* attempted, if it can be determined. Otherwise, null. |
* |
* message - A message indicating why the request failed, if one is |
* supplied. Otherwise, null. |
* |
* status - Auth_OpenID_FAILURE. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse { |
var $status = Auth_OpenID_FAILURE; |
function Auth_OpenID_FailureResponse($endpoint, $message = null) |
{ |
$this->endpoint = $endpoint; |
if ($endpoint !== null) { |
$this->identity_url = $endpoint->identity_url; |
} else { |
$this->identity_url = null; |
} |
$this->message = $message; |
} |
} |
/** |
* A response with a status of Auth_OpenID_CANCEL. Indicates that the |
* user cancelled the OpenID authentication request. This has two |
* relevant attributes: |
* |
* identity_url - The identity URL for which authentication was |
* attempted, if it can be determined. Otherwise, null. |
* |
* status - Auth_OpenID_SUCCESS. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse { |
var $status = Auth_OpenID_CANCEL; |
function Auth_OpenID_CancelResponse($endpoint) |
{ |
$this->endpoint = $endpoint; |
$this->identity_url = $endpoint->identity_url; |
} |
} |
/** |
* A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates |
* that the request was in immediate mode, and the server is unable to |
* authenticate the user without further interaction. |
* |
* identity_url - The identity URL for which authentication was |
* attempted. |
* |
* setup_url - A URL that can be used to send the user to the server |
* to set up for authentication. The user should be redirected in to |
* the setup_url, either in the current window or in a new browser |
* window. |
* |
* status - Auth_OpenID_SETUP_NEEDED. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse { |
var $status = Auth_OpenID_SETUP_NEEDED; |
function Auth_OpenID_SetupNeededResponse($endpoint, |
$setup_url = null) |
{ |
$this->endpoint = $endpoint; |
$this->identity_url = $endpoint->identity_url; |
$this->setup_url = $setup_url; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/URINorm.php |
---|
New file |
0,0 → 1,231 |
<?php |
/** |
* URI normalization routines. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
require_once 'Services/Yadis/Misc.php'; |
// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt) |
function Auth_OpenID_getURIPattern() |
{ |
return '&^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?&'; |
} |
function Auth_OpenID_getAuthorityPattern() |
{ |
return '/^([^@]*@)?([^:]*)(:.*)?/'; |
} |
function Auth_OpenID_getEncodedPattern() |
{ |
return '/%([0-9A-Fa-f]{2})/'; |
} |
function Auth_OpenID_getUnreserved() |
{ |
$_unreserved = array(); |
for ($i = 0; $i < 256; $i++) { |
$_unreserved[$i] = false; |
} |
for ($i = ord('A'); $i <= ord('Z'); $i++) { |
$_unreserved[$i] = true; |
} |
for ($i = ord('0'); $i <= ord('9'); $i++) { |
$_unreserved[$i] = true; |
} |
for ($i = ord('a'); $i <= ord('z'); $i++) { |
$_unreserved[$i] = true; |
} |
$_unreserved[ord('-')] = true; |
$_unreserved[ord('.')] = true; |
$_unreserved[ord('_')] = true; |
$_unreserved[ord('~')] = true; |
return $_unreserved; |
} |
function Auth_OpenID_getEscapeRE() |
{ |
$parts = array(); |
foreach (array_merge(Services_Yadis_getUCSChars(), |
Services_Yadis_getIPrivateChars()) as $pair) { |
list($m, $n) = $pair; |
$parts[] = sprintf("%s-%s", chr($m), chr($n)); |
} |
return sprintf('[%s]', implode('', $parts)); |
} |
function Auth_OpenID_pct_encoded_replace_unreserved($mo) |
{ |
$_unreserved = Auth_OpenID_getUnreserved(); |
$i = intval($mo[1], 16); |
if ($_unreserved[$i]) { |
return chr($i); |
} else { |
return strtoupper($mo[0]); |
} |
return $mo[0]; |
} |
function Auth_OpenID_pct_encoded_replace($mo) |
{ |
return chr(intval($mo[1], 16)); |
} |
function Auth_OpenID_remove_dot_segments($path) |
{ |
$result_segments = array(); |
while ($path) { |
if (Services_Yadis_startswith($path, '../')) { |
$path = substr($path, 3); |
} else if (Services_Yadis_startswith($path, './')) { |
$path = substr($path, 2); |
} else if (Services_Yadis_startswith($path, '/./')) { |
$path = substr($path, 2); |
} else if ($path == '/.') { |
$path = '/'; |
} else if (Services_Yadis_startswith($path, '/../')) { |
$path = substr($path, 3); |
if ($result_segments) { |
array_pop($result_segments); |
} |
} else if ($path == '/..') { |
$path = '/'; |
if ($result_segments) { |
array_pop($result_segments); |
} |
} else if (($path == '..') || |
($path == '.')) { |
$path = ''; |
} else { |
$i = 0; |
if ($path[0] == '/') { |
$i = 1; |
} |
$i = strpos($path, '/', $i); |
if ($i === false) { |
$i = strlen($path); |
} |
$result_segments[] = substr($path, 0, $i); |
$path = substr($path, $i); |
} |
} |
return implode('', $result_segments); |
} |
function Auth_OpenID_urinorm($uri) |
{ |
$uri_matches = array(); |
preg_match(Auth_OpenID_getURIPattern(), $uri, $uri_matches); |
if (count($uri_matches) < 9) { |
for ($i = count($uri_matches); $i <= 9; $i++) { |
$uri_matches[] = ''; |
} |
} |
$scheme = $uri_matches[2]; |
if ($scheme) { |
$scheme = strtolower($scheme); |
} |
$scheme = $uri_matches[2]; |
if ($scheme === '') { |
// No scheme specified |
return null; |
} |
$scheme = strtolower($scheme); |
if (!in_array($scheme, array('http', 'https'))) { |
// Not an absolute HTTP or HTTPS URI |
return null; |
} |
$authority = $uri_matches[4]; |
if ($authority === '') { |
// Not an absolute URI |
return null; |
} |
$authority_matches = array(); |
preg_match(Auth_OpenID_getAuthorityPattern(), |
$authority, $authority_matches); |
if (count($authority_matches) === 0) { |
// URI does not have a valid authority |
return null; |
} |
if (count($authority_matches) < 4) { |
for ($i = count($authority_matches); $i <= 4; $i++) { |
$authority_matches[] = ''; |
} |
} |
list($_whole, $userinfo, $host, $port) = $authority_matches; |
if ($userinfo === null) { |
$userinfo = ''; |
} |
if (strpos($host, '%') !== -1) { |
$host = strtolower($host); |
$host = preg_replace_callback( |
Auth_OpenID_getEncodedPattern(), |
'Auth_OpenID_pct_encoded_replace', $host); |
// NO IDNA. |
// $host = unicode($host, 'utf-8').encode('idna'); |
} else { |
$host = strtolower($host); |
} |
if ($port) { |
if (($port == ':') || |
($scheme == 'http' && $port == ':80') || |
($scheme == 'https' && $port == ':443')) { |
$port = ''; |
} |
} else { |
$port = ''; |
} |
$authority = $userinfo . $host . $port; |
$path = $uri_matches[5]; |
$path = preg_replace_callback( |
Auth_OpenID_getEncodedPattern(), |
'Auth_OpenID_pct_encoded_replace_unreserved', $path); |
$path = Auth_OpenID_remove_dot_segments($path); |
if (!$path) { |
$path = '/'; |
} |
$query = $uri_matches[6]; |
if ($query === null) { |
$query = ''; |
} |
$fragment = $uri_matches[8]; |
if ($fragment === null) { |
$fragment = ''; |
} |
return $scheme . '://' . $authority . $path . $query . $fragment; |
} |
?> |
/trunk/composants/openid/Auth/OpenID/FileStore.php |
---|
New file |
0,0 → 1,674 |
<?php |
/** |
* This file supplies a Memcached store backend for OpenID servers and |
* consumers. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
* |
*/ |
/** |
* Require base class for creating a new interface. |
*/ |
require_once 'Auth/OpenID.php'; |
require_once 'Auth/OpenID/Interface.php'; |
require_once 'Auth/OpenID/HMACSHA1.php'; |
/** |
* This is a filesystem-based store for OpenID associations and |
* nonces. This store should be safe for use in concurrent systems on |
* both windows and unix (excluding NFS filesystems). There are a |
* couple race conditions in the system, but those failure cases have |
* been set up in such a way that the worst-case behavior is someone |
* having to try to log in a second time. |
* |
* Most of the methods of this class are implementation details. |
* People wishing to just use this store need only pay attention to |
* the constructor. |
* |
* @package OpenID |
*/ |
class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore { |
/** |
* Initializes a new {@link Auth_OpenID_FileStore}. This |
* initializes the nonce and association directories, which are |
* subdirectories of the directory passed in. |
* |
* @param string $directory This is the directory to put the store |
* directories in. |
*/ |
function Auth_OpenID_FileStore($directory) |
{ |
if (!Auth_OpenID::ensureDir($directory)) { |
trigger_error('Not a directory and failed to create: ' |
. $directory, E_USER_ERROR); |
} |
$directory = realpath($directory); |
$this->directory = $directory; |
$this->active = true; |
$this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces'; |
$this->association_dir = $directory . DIRECTORY_SEPARATOR . |
'associations'; |
// Temp dir must be on the same filesystem as the assciations |
// $directory and the $directory containing the auth key file. |
$this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp'; |
$this->auth_key_name = $directory . DIRECTORY_SEPARATOR . 'auth_key'; |
$this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds |
if (!$this->_setup()) { |
trigger_error('Failed to initialize OpenID file store in ' . |
$directory, E_USER_ERROR); |
} |
} |
function destroy() |
{ |
Auth_OpenID_FileStore::_rmtree($this->directory); |
$this->active = false; |
} |
/** |
* Make sure that the directories in which we store our data |
* exist. |
* |
* @access private |
*/ |
function _setup() |
{ |
return (Auth_OpenID::ensureDir(dirname($this->auth_key_name)) && |
Auth_OpenID::ensureDir($this->nonce_dir) && |
Auth_OpenID::ensureDir($this->association_dir) && |
Auth_OpenID::ensureDir($this->temp_dir)); |
} |
/** |
* Create a temporary file on the same filesystem as |
* $this->auth_key_name and $this->association_dir. |
* |
* The temporary directory should not be cleaned if there are any |
* processes using the store. If there is no active process using |
* the store, it is safe to remove all of the files in the |
* temporary directory. |
* |
* @return array ($fd, $filename) |
* @access private |
*/ |
function _mktemp() |
{ |
$name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir); |
$file_obj = @fopen($name, 'wb'); |
if ($file_obj !== false) { |
return array($file_obj, $name); |
} else { |
Auth_OpenID_FileStore::_removeIfPresent($name); |
} |
} |
/** |
* Read the auth key from the auth key file. Will return None if |
* there is currently no key. |
* |
* @return mixed |
*/ |
function readAuthKey() |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$auth_key_file = @fopen($this->auth_key_name, 'rb'); |
if ($auth_key_file === false) { |
return null; |
} |
$key = fread($auth_key_file, filesize($this->auth_key_name)); |
fclose($auth_key_file); |
return $key; |
} |
/** |
* Generate a new random auth key and safely store it in the |
* location specified by $this->auth_key_name. |
* |
* @return string $key |
*/ |
function createAuthKey() |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$auth_key = Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN); |
list($file_obj, $tmp) = $this->_mktemp(); |
fwrite($file_obj, $auth_key); |
fflush($file_obj); |
fclose($file_obj); |
if (function_exists('link')) { |
// Posix filesystem |
$saved = link($tmp, $this->auth_key_name); |
Auth_OpenID_FileStore::_removeIfPresent($tmp); |
} else { |
// Windows filesystem |
$saved = rename($tmp, $this->auth_key_name); |
} |
if (!$saved) { |
// The link failed, either because we lack the permission, |
// or because the file already exists; try to read the key |
// in case the file already existed. |
$auth_key = $this->readAuthKey(); |
} |
return $auth_key; |
} |
/** |
* Retrieve the auth key from the file specified by |
* $this->auth_key_name, creating it if it does not exist. |
* |
* @return string $key |
*/ |
function getAuthKey() |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$auth_key = $this->readAuthKey(); |
if ($auth_key === null) { |
$auth_key = $this->createAuthKey(); |
if (strlen($auth_key) != $this->AUTH_KEY_LEN) { |
$fmt = 'Got an invalid auth key from %s. Expected '. |
'%d-byte string. Got: %s'; |
$msg = sprintf($fmt, $this->auth_key_name, $this->AUTH_KEY_LEN, |
$auth_key); |
trigger_error($msg, E_USER_WARNING); |
return null; |
} |
} |
return $auth_key; |
} |
/** |
* Create a unique filename for a given server url and |
* handle. This implementation does not assume anything about the |
* format of the handle. The filename that is returned will |
* contain the domain name from the server URL for ease of human |
* inspection of the data directory. |
* |
* @return string $filename |
*/ |
function getAssociationFilename($server_url, $handle) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
if (strpos($server_url, '://') === false) { |
trigger_error(sprintf("Bad server URL: %s", $server_url), |
E_USER_WARNING); |
return null; |
} |
list($proto, $rest) = explode('://', $server_url, 2); |
$parts = explode('/', $rest); |
$domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]); |
$url_hash = Auth_OpenID_FileStore::_safe64($server_url); |
if ($handle) { |
$handle_hash = Auth_OpenID_FileStore::_safe64($handle); |
} else { |
$handle_hash = ''; |
} |
$filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash, |
$handle_hash); |
return $this->association_dir. DIRECTORY_SEPARATOR . $filename; |
} |
/** |
* Store an association in the association directory. |
*/ |
function storeAssociation($server_url, $association) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return false; |
} |
$association_s = $association->serialize(); |
$filename = $this->getAssociationFilename($server_url, |
$association->handle); |
list($tmp_file, $tmp) = $this->_mktemp(); |
if (!$tmp_file) { |
trigger_error("_mktemp didn't return a valid file descriptor", |
E_USER_WARNING); |
return false; |
} |
fwrite($tmp_file, $association_s); |
fflush($tmp_file); |
fclose($tmp_file); |
if (@rename($tmp, $filename)) { |
return true; |
} else { |
// In case we are running on Windows, try unlinking the |
// file in case it exists. |
@unlink($filename); |
// Now the target should not exist. Try renaming again, |
// giving up if it fails. |
if (@rename($tmp, $filename)) { |
return true; |
} |
} |
// If there was an error, don't leave the temporary file |
// around. |
Auth_OpenID_FileStore::_removeIfPresent($tmp); |
return false; |
} |
/** |
* Retrieve an association. If no handle is specified, return the |
* association with the most recent issue time. |
* |
* @return mixed $association |
*/ |
function getAssociation($server_url, $handle = null) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
if ($handle === null) { |
$handle = ''; |
} |
// The filename with the empty handle is a prefix of all other |
// associations for the given server URL. |
$filename = $this->getAssociationFilename($server_url, $handle); |
if ($handle) { |
return $this->_getAssociation($filename); |
} else { |
$association_files = |
Auth_OpenID_FileStore::_listdir($this->association_dir); |
$matching_files = array(); |
// strip off the path to do the comparison |
$name = basename($filename); |
foreach ($association_files as $association_file) { |
if (strpos($association_file, $name) === 0) { |
$matching_files[] = $association_file; |
} |
} |
$matching_associations = array(); |
// read the matching files and sort by time issued |
foreach ($matching_files as $name) { |
$full_name = $this->association_dir . DIRECTORY_SEPARATOR . |
$name; |
$association = $this->_getAssociation($full_name); |
if ($association !== null) { |
$matching_associations[] = array($association->issued, |
$association); |
} |
} |
$issued = array(); |
$assocs = array(); |
foreach ($matching_associations as $key => $assoc) { |
$issued[$key] = $assoc[0]; |
$assocs[$key] = $assoc[1]; |
} |
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC, |
$matching_associations); |
// return the most recently issued one. |
if ($matching_associations) { |
list($issued, $assoc) = $matching_associations[0]; |
return $assoc; |
} else { |
return null; |
} |
} |
} |
/** |
* @access private |
*/ |
function _getAssociation($filename) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$assoc_file = @fopen($filename, 'rb'); |
if ($assoc_file === false) { |
return null; |
} |
$assoc_s = fread($assoc_file, filesize($filename)); |
fclose($assoc_file); |
if (!$assoc_s) { |
return null; |
} |
$association = |
Auth_OpenID_Association::deserialize('Auth_OpenID_Association', |
$assoc_s); |
if (!$association) { |
Auth_OpenID_FileStore::_removeIfPresent($filename); |
return null; |
} |
if ($association->getExpiresIn() == 0) { |
Auth_OpenID_FileStore::_removeIfPresent($filename); |
return null; |
} else { |
return $association; |
} |
} |
/** |
* Remove an association if it exists. Do nothing if it does not. |
* |
* @return bool $success |
*/ |
function removeAssociation($server_url, $handle) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$assoc = $this->getAssociation($server_url, $handle); |
if ($assoc === null) { |
return false; |
} else { |
$filename = $this->getAssociationFilename($server_url, $handle); |
return Auth_OpenID_FileStore::_removeIfPresent($filename); |
} |
} |
/** |
* Mark this nonce as present. |
*/ |
function storeNonce($nonce) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce; |
$nonce_file = fopen($filename, 'w'); |
if ($nonce_file === false) { |
return false; |
} |
fclose($nonce_file); |
return true; |
} |
/** |
* Return whether this nonce is present. As a side effect, mark it |
* as no longer present. |
* |
* @return bool $present |
*/ |
function useNonce($nonce) |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce; |
$st = @stat($filename); |
if ($st === false) { |
return false; |
} |
// Either it is too old or we are using it. Either way, we |
// must remove the file. |
if (!unlink($filename)) { |
return false; |
} |
$now = time(); |
$nonce_age = $now - $st[9]; |
// We can us it if the age of the file is less than the |
// expiration time. |
return $nonce_age <= $this->max_nonce_age; |
} |
/** |
* Remove expired entries from the database. This is potentially |
* expensive, so only run when it is acceptable to take time. |
*/ |
function clean() |
{ |
if (!$this->active) { |
trigger_error("FileStore no longer active", E_USER_ERROR); |
return null; |
} |
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir); |
$now = time(); |
// Check all nonces for expiry |
foreach ($nonces as $nonce) { |
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce; |
$st = @stat($filename); |
if ($st !== false) { |
// Remove the nonce if it has expired |
$nonce_age = $now - $st[9]; |
if ($nonce_age > $this->max_nonce_age) { |
Auth_OpenID_FileStore::_removeIfPresent($filename); |
} |
} |
} |
$association_filenames = |
Auth_OpenID_FileStore::_listdir($this->association_dir); |
foreach ($association_filenames as $association_filename) { |
$association_file = fopen($association_filename, 'rb'); |
if ($association_file !== false) { |
$assoc_s = fread($association_file, |
filesize($association_filename)); |
fclose($association_file); |
// Remove expired or corrupted associations |
$association = |
Auth_OpenID_Association::deserialize( |
'Auth_OpenID_Association', $assoc_s); |
if ($association === null) { |
Auth_OpenID_FileStore::_removeIfPresent( |
$association_filename); |
} else { |
if ($association->getExpiresIn() == 0) { |
Auth_OpenID_FileStore::_removeIfPresent( |
$association_filename); |
} |
} |
} |
} |
} |
/** |
* @access private |
*/ |
function _rmtree($dir) |
{ |
if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) { |
$dir .= DIRECTORY_SEPARATOR; |
} |
if ($handle = opendir($dir)) { |
while ($item = readdir($handle)) { |
if (!in_array($item, array('.', '..'))) { |
if (is_dir($dir . $item)) { |
if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) { |
return false; |
} |
} else if (is_file($dir . $item)) { |
if (!unlink($dir . $item)) { |
return false; |
} |
} |
} |
} |
closedir($handle); |
if (!@rmdir($dir)) { |
return false; |
} |
return true; |
} else { |
// Couldn't open directory. |
return false; |
} |
} |
/** |
* @access private |
*/ |
function _mkstemp($dir) |
{ |
foreach (range(0, 4) as $i) { |
$name = tempnam($dir, "php_openid_filestore_"); |
if ($name !== false) { |
return $name; |
} |
} |
return false; |
} |
/** |
* @access private |
*/ |
function _mkdtemp($dir) |
{ |
foreach (range(0, 4) as $i) { |
$name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) . |
"-" . strval(rand(1, time())); |
if (!mkdir($name, 0700)) { |
return false; |
} else { |
return $name; |
} |
} |
return false; |
} |
/** |
* @access private |
*/ |
function _listdir($dir) |
{ |
$handle = opendir($dir); |
$files = array(); |
while (false !== ($filename = readdir($handle))) { |
$files[] = $filename; |
} |
return $files; |
} |
/** |
* @access private |
*/ |
function _isFilenameSafe($char) |
{ |
$_Auth_OpenID_filename_allowed = Auth_OpenID_letters . |
Auth_OpenID_digits . "."; |
return (strpos($_Auth_OpenID_filename_allowed, $char) !== false); |
} |
/** |
* @access private |
*/ |
function _safe64($str) |
{ |
$h64 = base64_encode(Auth_OpenID_SHA1($str)); |
$h64 = str_replace('+', '_', $h64); |
$h64 = str_replace('/', '.', $h64); |
$h64 = str_replace('=', '', $h64); |
return $h64; |
} |
/** |
* @access private |
*/ |
function _filenameEscape($str) |
{ |
$filename = ""; |
for ($i = 0; $i < strlen($str); $i++) { |
$c = $str[$i]; |
if (Auth_OpenID_FileStore::_isFilenameSafe($c)) { |
$filename .= $c; |
} else { |
$filename .= sprintf("_%02X", ord($c)); |
} |
} |
return $filename; |
} |
/** |
* Attempt to remove a file, returning whether the file existed at |
* the time of the call. |
* |
* @access private |
* @return bool $result True if the file was present, false if not. |
*/ |
function _removeIfPresent($filename) |
{ |
return @unlink($filename); |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/CryptUtil.php |
---|
New file |
0,0 → 1,109 |
<?php |
/** |
* CryptUtil: A suite of wrapper utility functions for the OpenID |
* library. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @access private |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
if (!defined('Auth_OpenID_RAND_SOURCE')) { |
/** |
* The filename for a source of random bytes. Define this yourself |
* if you have a different source of randomness. |
*/ |
define('Auth_OpenID_RAND_SOURCE', '/dev/urandom'); |
} |
class Auth_OpenID_CryptUtil { |
/** |
* Get the specified number of random bytes. |
* |
* Attempts to use a cryptographically secure (not predictable) |
* source of randomness if available. If there is no high-entropy |
* randomness source available, it will fail. As a last resort, |
* for non-critical systems, define |
* <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and |
* the code will fall back on a pseudo-random number generator. |
* |
* @param int $num_bytes The length of the return value |
* @return string $bytes random bytes |
*/ |
function getBytes($num_bytes) |
{ |
static $f = null; |
$bytes = ''; |
if ($f === null) { |
if (Auth_OpenID_RAND_SOURCE === null) { |
$f = false; |
} else { |
$f = @fopen(Auth_OpenID_RAND_SOURCE, "r"); |
if ($f === false) { |
$msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' . |
' continue with an insecure random number generator.'; |
trigger_error($msg, E_USER_ERROR); |
} |
} |
} |
if ($f === false) { |
// pseudorandom used |
$bytes = ''; |
for ($i = 0; $i < $num_bytes; $i += 4) { |
$bytes .= pack('L', mt_rand()); |
} |
$bytes = substr($bytes, 0, $num_bytes); |
} else { |
$bytes = fread($f, $num_bytes); |
} |
return $bytes; |
} |
/** |
* Produce a string of length random bytes, chosen from chrs. If |
* $chrs is null, the resulting string may contain any characters. |
* |
* @param integer $length The length of the resulting |
* randomly-generated string |
* @param string $chrs A string of characters from which to choose |
* to build the new string |
* @return string $result A string of randomly-chosen characters |
* from $chrs |
*/ |
function randomString($length, $population = null) |
{ |
if ($population === null) { |
return Auth_OpenID_CryptUtil::getBytes($length); |
} |
$popsize = strlen($population); |
if ($popsize > 256) { |
$msg = 'More than 256 characters supplied to ' . __FUNCTION__; |
trigger_error($msg, E_USER_ERROR); |
} |
$duplicate = 256 % $popsize; |
$str = ""; |
for ($i = 0; $i < $length; $i++) { |
do { |
$n = ord(Auth_OpenID_CryptUtil::getBytes(1)); |
} while ($n < $duplicate); |
$n %= $popsize; |
$str .= $population[$n]; |
} |
return $str; |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID/Interface.php |
---|
New file |
0,0 → 1,188 |
<?php |
/** |
* This file specifies the interface for PHP OpenID store implementations. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* This is the interface for the store objects the OpenID library |
* uses. It is a single class that provides all of the persistence |
* mechanisms that the OpenID library needs, for both servers and |
* consumers. If you want to create an SQL-driven store, please see |
* then {@link Auth_OpenID_SQLStore} class. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
*/ |
class Auth_OpenID_OpenIDStore { |
/** |
* @var integer The length of the auth key that should be returned |
* by the getAuthKey method. |
*/ |
var $AUTH_KEY_LEN = 20; |
/** |
* This method puts an Association object into storage, |
* retrievable by server URL and handle. |
* |
* @param string $server_url The URL of the identity server that |
* this association is with. Because of the way the server portion |
* of the library uses this interface, don't assume there are any |
* limitations on the character set of the input string. In |
* particular, expect to see unescaped non-url-safe characters in |
* the server_url field. |
* |
* @param Association $association The Association to store. |
*/ |
function storeAssociation($server_url, $association) |
{ |
trigger_error("Auth_OpenID_OpenIDStore::storeAssociation ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* This method returns an Association object from storage that |
* matches the server URL and, if specified, handle. It returns |
* null if no such association is found or if the matching |
* association is expired. |
* |
* If no handle is specified, the store may return any association |
* which matches the server URL. If multiple associations are |
* valid, the recommended return value for this method is the one |
* that will remain valid for the longest duration. |
* |
* This method is allowed (and encouraged) to garbage collect |
* expired associations when found. This method must not return |
* expired associations. |
* |
* @param string $server_url The URL of the identity server to get |
* the association for. Because of the way the server portion of |
* the library uses this interface, don't assume there are any |
* limitations on the character set of the input string. In |
* particular, expect to see unescaped non-url-safe characters in |
* the server_url field. |
* |
* @param mixed $handle This optional parameter is the handle of |
* the specific association to get. If no specific handle is |
* provided, any valid association matching the server URL is |
* returned. |
* |
* @return Association The Association for the given identity |
* server. |
*/ |
function getAssociation($server_url, $handle = null) |
{ |
trigger_error("Auth_OpenID_OpenIDStore::getAssociation ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* This method removes the matching association if it's found, and |
* returns whether the association was removed or not. |
* |
* @param string $server_url The URL of the identity server the |
* association to remove belongs to. Because of the way the server |
* portion of the library uses this interface, don't assume there |
* are any limitations on the character set of the input |
* string. In particular, expect to see unescaped non-url-safe |
* characters in the server_url field. |
* |
* @param string $handle This is the handle of the association to |
* remove. If there isn't an association found that matches both |
* the given URL and handle, then there was no matching handle |
* found. |
* |
* @return mixed Returns whether or not the given association existed. |
*/ |
function removeAssociation($server_url, $handle) |
{ |
trigger_error("Auth_OpenID_OpenIDStore::removeAssociation ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* Stores a nonce. This is used by the consumer to prevent replay |
* attacks. |
* |
* @param string $nonce The nonce to store. |
* |
* @return null |
*/ |
function storeNonce($nonce) |
{ |
trigger_error("Auth_OpenID_OpenIDStore::storeNonce ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* This method is called when the library is attempting to use a |
* nonce. If the nonce is in the store, this method removes it and |
* returns a value which evaluates as true. Otherwise it returns a |
* value which evaluates as false. |
* |
* This method is allowed and encouraged to treat nonces older |
* than some period (a very conservative window would be 6 hours, |
* for example) as no longer existing, and return False and remove |
* them. |
* |
* @param string $nonce The nonce to use. |
* |
* @return bool Whether or not the nonce was valid. |
*/ |
function useNonce($nonce) |
{ |
trigger_error("Auth_OpenID_OpenIDStore::useNonce ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* This method returns a key used to sign the tokens, to ensure |
* that they haven't been tampered with in transit. It should |
* return the same key every time it is called. The key returned |
* should be {@link AUTH_KEY_LEN} bytes long. |
* |
* @return string The key. It should be {@link AUTH_KEY_LEN} bytes in |
* length, and use the full range of byte values. That is, it |
* should be treated as a lump of binary data stored in a string. |
*/ |
function getAuthKey() |
{ |
trigger_error("Auth_OpenID_OpenIDStore::getAuthKey ". |
"not implemented", E_USER_ERROR); |
} |
/** |
* This method must return true if the store is a dumb-mode-style |
* store. Unlike all other methods in this class, this one |
* provides a default implementation, which returns false. |
* |
* In general, any custom subclass of {@link Auth_OpenID_OpenIDStore} |
* won't override this method, as custom subclasses are only likely to |
* be created when the store is fully functional. |
* |
* @return bool true if the store works fully, false if the |
* consumer will have to use dumb mode to use this store. |
*/ |
function isDumb() |
{ |
return false; |
} |
/** |
* Removes all entries from the store; implementation is optional. |
*/ |
function reset() |
{ |
} |
} |
?> |
/trunk/composants/openid/Auth/OpenID.php |
---|
New file |
0,0 → 1,412 |
<?php |
/** |
* This is the PHP OpenID library by JanRain, Inc. |
* |
* This module contains core utility functionality used by the |
* library. See Consumer.php and Server.php for the consumer and |
* server implementations. |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: See the COPYING file included in this distribution. |
* |
* @package OpenID |
* @author JanRain, Inc. <openid@janrain.com> |
* @copyright 2005 Janrain, Inc. |
* @license http://www.gnu.org/copyleft/lesser.html LGPL |
*/ |
/** |
* Require the fetcher code. |
*/ |
require_once "Services/Yadis/PlainHTTPFetcher.php"; |
require_once "Services/Yadis/ParanoidHTTPFetcher.php"; |
require_once "Auth/OpenID/BigMath.php"; |
/** |
* Status code returned by the server when the only option is to show |
* an error page, since we do not have enough information to redirect |
* back to the consumer. The associated value is an error message that |
* should be displayed on an HTML error page. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_LOCAL_ERROR', 'local_error'); |
/** |
* Status code returned when there is an error to return in key-value |
* form to the consumer. The caller should return a 400 Bad Request |
* response with content-type text/plain and the value as the body. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_REMOTE_ERROR', 'remote_error'); |
/** |
* Status code returned when there is a key-value form OK response to |
* the consumer. The value associated with this code is the |
* response. The caller should return a 200 OK response with |
* content-type text/plain and the value as the body. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_REMOTE_OK', 'remote_ok'); |
/** |
* Status code returned when there is a redirect back to the |
* consumer. The value is the URL to redirect back to. The caller |
* should return a 302 Found redirect with a Location: header |
* containing the URL. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_REDIRECT', 'redirect'); |
/** |
* Status code returned when the caller needs to authenticate the |
* user. The associated value is a {@link Auth_OpenID_ServerRequest} |
* object that can be used to complete the authentication. If the user |
* has taken some authentication action, use the retry() method of the |
* {@link Auth_OpenID_ServerRequest} object to complete the request. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_DO_AUTH', 'do_auth'); |
/** |
* Status code returned when there were no OpenID arguments |
* passed. This code indicates that the caller should return a 200 OK |
* response and display an HTML page that says that this is an OpenID |
* server endpoint. |
* |
* @see Auth_OpenID_Server |
*/ |
define('Auth_OpenID_DO_ABOUT', 'do_about'); |
/** |
* Defines for regexes and format checking. |
*/ |
define('Auth_OpenID_letters', |
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
define('Auth_OpenID_digits', |
"0123456789"); |
define('Auth_OpenID_punct', |
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); |
if (Auth_OpenID_getMathLib() === null) { |
define('Auth_OpenID_NO_MATH_SUPPORT', true); |
} |
/** |
* The OpenID utility function class. |
* |
* @package OpenID |
* @access private |
*/ |
class Auth_OpenID { |
/** |
* These namespaces are automatically fixed in query arguments by |
* Auth_OpenID::fixArgs. |
*/ |
function getOpenIDNamespaces() |
{ |
return array('openid', |
'sreg'); |
} |
/** |
* Rename query arguments back to 'openid.' from 'openid_' |
* |
* @access private |
* @param array $args An associative array of URL query arguments |
*/ |
function fixArgs($args) |
{ |
foreach (array_keys($args) as $key) { |
$fixed = $key; |
if (preg_match('/^openid/', $key)) { |
foreach (Auth_OpenID::getOpenIDNamespaces() as $ns) { |
if (preg_match('/'.$ns.'_/', $key)) { |
$fixed = preg_replace('/'.$ns.'_/', $ns.'.', $fixed); |
} |
} |
if ($fixed != $key) { |
$val = $args[$key]; |
unset($args[$key]); |
$args[$fixed] = $val; |
} |
} |
} |
return $args; |
} |
/** |
* Create dir_name as a directory if it does not exist. If it |
* exists, make sure that it is, in fact, a directory. Returns |
* true if the operation succeeded; false if not. |
* |
* @access private |
*/ |
function ensureDir($dir_name) |
{ |
if (is_dir($dir_name) || @mkdir($dir_name)) { |
return true; |
} else { |
if (Auth_OpenID::ensureDir(dirname($dir_name))) { |
return is_dir($dir_name) || @mkdir($dir_name); |
} else { |
return false; |
} |
} |
} |
/** |
* Convenience function for getting array values. |
* |
* @access private |
*/ |
function arrayGet($arr, $key, $fallback = null) |
{ |
if (is_array($arr)) { |
if (array_key_exists($key, $arr)) { |
return $arr[$key]; |
} else { |
return $fallback; |
} |
} else { |
trigger_error("Auth_OpenID::arrayGet expected " . |
"array as first parameter", E_USER_WARNING); |
return false; |
} |
} |
/** |
* Implements the PHP 5 'http_build_query' functionality. |
* |
* @access private |
* @param array $data Either an array key/value pairs or an array |
* of arrays, each of which holding two values: a key and a value, |
* sequentially. |
* @return string $result The result of url-encoding the key/value |
* pairs from $data into a URL query string |
* (e.g. "username=bob&id=56"). |
*/ |
function httpBuildQuery($data) |
{ |
$pairs = array(); |
foreach ($data as $key => $value) { |
if (is_array($value)) { |
$pairs[] = urlencode($value[0])."=".urlencode($value[1]); |
} else { |
$pairs[] = urlencode($key)."=".urlencode($value); |
} |
} |
return implode("&", $pairs); |
} |
/** |
* "Appends" query arguments onto a URL. The URL may or may not |
* already have arguments (following a question mark). |
* |
* @param string $url A URL, which may or may not already have |
* arguments. |
* @param array $args Either an array key/value pairs or an array of |
* arrays, each of which holding two values: a key and a value, |
* sequentially. If $args is an ordinary key/value array, the |
* parameters will be added to the URL in sorted alphabetical order; |
* if $args is an array of arrays, their order will be preserved. |
* @return string $url The original URL with the new parameters added. |
* |
*/ |
function appendArgs($url, $args) |
{ |
if (count($args) == 0) { |
return $url; |
} |
// Non-empty array; if it is an array of arrays, use |
// multisort; otherwise use sort. |
if (array_key_exists(0, $args) && |
is_array($args[0])) { |
// Do nothing here. |
} else { |
$keys = array_keys($args); |
sort($keys); |
$new_args = array(); |
foreach ($keys as $key) { |
$new_args[] = array($key, $args[$key]); |
} |
$args = $new_args; |
} |
$sep = '?'; |
if (strpos($url, '?') !== false) { |
$sep = '&'; |
} |
return $url . $sep . Auth_OpenID::httpBuildQuery($args); |
} |
/** |
* Turn a string into an ASCII string. |
* |
* Replace non-ascii characters with a %-encoded, UTF-8 |
* encoding. This function will fail if the input is a string and |
* there are non-7-bit-safe characters. It is assumed that the |
* caller will have already translated the input into a Unicode |
* character sequence, according to the encoding of the HTTP POST |
* or GET. |
* |
* Do not escape anything that is already 7-bit safe, so we do the |
* minimal transform on the identity URL |
* |
* @access private |
*/ |
function quoteMinimal($s) |
{ |
$res = array(); |
for ($i = 0; $i < strlen($s); $i++) { |
$c = $s[$i]; |
if ($c >= "\x80") { |
for ($j = 0; $j < count(utf8_encode($c)); $j++) { |
array_push($res, sprintf("%02X", ord($c[$j]))); |
} |
} else { |
array_push($res, $c); |
} |
} |
return implode('', $res); |
} |
/** |
* Implements python's urlunparse, which is not available in PHP. |
* Given the specified components of a URL, this function rebuilds |
* and returns the URL. |
* |
* @access private |
* @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'. |
* @param string $host The host. Required. |
* @param string $port The port. |
* @param string $path The path. |
* @param string $query The query. |
* @param string $fragment The fragment. |
* @return string $url The URL resulting from assembling the |
* specified components. |
*/ |
function urlunparse($scheme, $host, $port = null, $path = '/', |
$query = '', $fragment = '') |
{ |
if (!$scheme) { |
$scheme = 'http'; |
} |
if (!$host) { |
return false; |
} |
if (!$path) { |
$path = '/'; |
} |
$result = $scheme . "://" . $host; |
if ($port) { |
$result .= ":" . $port; |
} |
$result .= $path; |
if ($query) { |
$result .= "?" . $query; |
} |
if ($fragment) { |
$result .= "#" . $fragment; |
} |
return $result; |
} |
/** |
* Given a URL, this "normalizes" it by adding a trailing slash |
* and / or a leading http:// scheme where necessary. Returns |
* null if the original URL is malformed and cannot be normalized. |
* |
* @access private |
* @param string $url The URL to be normalized. |
* @return mixed $new_url The URL after normalization, or null if |
* $url was malformed. |
*/ |
function normalizeUrl($url) |
{ |
if ($url === null) { |
return null; |
} |
assert(is_string($url)); |
$old_url = $url; |
$url = trim($url); |
if (strpos($url, "://") === false) { |
$url = "http://" . $url; |
} |
$parsed = @parse_url($url); |
if ($parsed === false) { |
return null; |
} |
$defaults = array( |
'scheme' => '', |
'host' => '', |
'path' => '', |
'query' => '', |
'fragment' => '', |
'port' => '' |
); |
$parsed = array_merge($defaults, $parsed); |
if (($parsed['scheme'] == '') || |
($parsed['host'] == '')) { |
if ($parsed['path'] == '' && |
$parsed['query'] == '' && |
$parsed['fragment'] == '') { |
return null; |
} |
$url = 'http://' + $url; |
$parsed = parse_url($url); |
$parsed = array_merge($defaults, $parsed); |
} |
$tail = array_map(array('Auth_OpenID', 'quoteMinimal'), |
array($parsed['path'], |
$parsed['query'], |
$parsed['fragment'])); |
if ($tail[0] == '') { |
$tail[0] = '/'; |
} |
$url = Auth_OpenID::urlunparse($parsed['scheme'], $parsed['host'], |
$parsed['port'], $tail[0], $tail[1], |
$tail[2]); |
assert(is_string($url)); |
return $url; |
} |
} |
?> |
/trunk/composants/cartographie/Cartographie.php |
---|
New file |
0,0 → 1,642 |
<?php |
// declare(encoding='UTF-8'); |
/** |
* Composant Cartographie gérant les images représentant le fond de carte à insérer dans un fichier html contenant une |
* image map. |
* Avantage : |
* - pas de base de données liée au composant (simplicité d'utilisation dans les applications) |
* - facilite l'utilisation du Javascript et CSS pour intéragir avec la carte (affichage du nom des zones au survol) |
* - l'application qui utilise le composant définie elle-même l'intéraction avec le clic sur une zone |
* Inconvénient : |
* - l'utilisation d'une balise map alourdie la page à renvoyer |
* |
* Il est possible de créer des fond de carte en vraies couleurs |
* (16 millions de zones maxi sur la carte) ou en couleurs indexées (255 zones maxi sur la carte). |
* Les couleurs réservées et a ne pas utiliser pour créer l'image png de fond sont : |
* - le blanc (rvb : 255-255-255) |
* - le noir (rvb : 0-0-0) |
* - le gris (rvb : 128-128-128) |
* - le rouge (rvb : 255-0-0) |
* Pour activer le cache indiquer la date de dernière mise à jour des données servant à créer la carte de cette façon : |
* $Carte->setCarteInfo(array('donnees_date_maj' => $date_maj_donnees)); |
* |
* @category PHP5 |
* @package Collection |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @copyright 2010 Tela-Botanica |
* @license GPL-v3 et CECILL-v2 |
* @version SVN:$Id$ |
*/ |
class Cartographie { |
/*** Constantes : ***/ |
const FORMULE_PROPORTIONNEL = 'proportionnel'; |
const FORMULE_LEGENDE = 'legende'; |
//+----------------------------------------------------------------------------------------------------------------+ |
/*** Attributs : ***/ |
/** |
* L'image de la carte. |
* @var string l'image de la carte. |
*/ |
private $carte; |
/** |
* Le nom du fichier contenant la carte sans l'extension. |
* @var string le nom du fichier de la carte. |
*/ |
private $carte_nom; |
/** |
* @var string le chemin et le nom du fichier de l'image de la carte générée. |
*/ |
private $carte_fichier; |
/** |
* Tableaux associatif contenant les informations sur la carte. |
* donnees_date_maj = date de dernière mise à jour des données servant à créer la carte, si plus récente que la carte |
* déjà créée getCarteCache renvoie false. |
* |
* @var array le tableau des infos sur la carte. |
*/ |
private $carte_info = array(); |
/** |
* Indique si la carte existe déjà et à besoin ou pas d'être créée. |
* @var bool true si la carte existe.. |
*/ |
private $carte_cache = false; |
/** |
* Le nom du fichier de la carte de fond. |
* @var string nom du fichier de la carte de fond. |
*/ |
private $carte_fond_fichier; |
/** |
* Le nom du dossier contenant les cartes de fond. |
* @var string nom du dossier contenant les cartes de fond. |
*/ |
private $carte_fond_dossier; |
/** |
* Le nom du dossier où stocker les cartes créer via la classe Cartographie. |
* @var string nom du dossier contenant les cartes créées. |
*/ |
private $carte_stockage_dossier; |
/** |
* L'url correspondant au dossier où sont stockées les cartes crées via la classe Cartographie. |
* L'url est passé à la fonction sprintf est doit donc contennir %s afin d'indiquer l'endroite où ajouter le nom du |
* fichier de la carte. |
* @var string url des cartes créées. |
*/ |
private $carte_stockage_url; |
/** |
* Format du tableau : |
* carte_zone est un tableau de tableaux associatifs. |
* Chaque zone de la carte doit avoir son entrée dans ce tableau. Le code de la zone sert de clé. |
* Chaque zone est représentée par : |
* - nom : (string) |
* le nom de la zone qui sera affiché dans l'attribut title de la balise map html. |
* - rvb_fond : (string) Exemple : 255-255-255. |
* les valeurs entre 0 et 255 séparées par des tirets (-) de la couleur de la zone sur la carte de fond |
* Ne pas utiliser le blanc (255-255-255) et utiliser le noir pour les contours (0-0-0). |
* - poly : (string) |
* les coordonnées pour la balise map html. Si une même zone à plusieurs polygones, les séparer par le |
* caractère pipe "|". |
* - info_nombre : (int) Exemple : nombre de personnes présentent dans un département. |
* nombre d'occurence dans cette zone. |
* - url : (string) l'url qui doit être appelée sur un clic dans la zone. |
* - rvb_carte : (string) Exemple : 255-255-255. |
* les valeurs entre 0 et 255 séparées par des tirets (-) de la couleur de remplacement dans le cas de la formule |
* de coloriage de type "légende". |
* @var array les informations sur les zones de la carte. |
*/ |
private $carte_zones = null; |
/** |
* Tableau contenant la valeur RVB de la zone du fond de carte en clé et la valeur RVB venant la remplacer en valeur. |
* @var array valeur RVB de la zone du fond de carte en clé et valeur RVB venant la remplacer en valeur. |
*/ |
private $carte_correspondance_couleurs = array(); |
/** |
* La valeur RVB, sous forme de chaine de nombres séparées par des tirets (-), de la zone géographique à mettre en |
* surbrillance. |
* @var string la valeur RVB de la zone à repérer. |
*/ |
private $zone_marker; |
/** |
* La formule de coloriage de la carte. Les formules disponibles sont : légende, proportionnel. |
* @var string la formule de coloriage. |
*/ |
private $formule_coloriage; |
/** |
* Les valeurs RVB séparés par des virgules pour la couleur la plus foncée utilisée, entre autre, par la formule de |
* coloriage "proportionnel". |
* @var string les valeurs RVB séparées par des virgules. |
*/ |
private $coloriage_couleur_max; |
/** |
* Les valeurs RVB séparés par des virgules pour la couleur la plus claire utilisée, entre autre, par la formule de |
* coloriage "proportionnel". |
* @var string les valeurs RVB séparées par des virgules. |
*/ |
private $coloriage_couleur_min; |
/** |
* Contient le nombre de couleurs différentes utilisées par le coloriage pour créer l'image finale. |
* @var int le nombre de couleurs. |
*/ |
private $coloriage_couleurs; |
/** |
* Contient le tableau des fréquences et des couleurs correspondantes. |
* @var array les frequences et leurs couleurs. |
*/ |
private $coloriage_tableau_frequence = array(); |
/** |
* Permet de savoir si la cartographie est en mode déboguage ou pas. |
* @var bool true si on est en mode débug, sinon false. |
*/ |
private $mode_debug; |
//+----------------------------------------------------------------------------------------------------------------+ |
/*** Constructeur : ***/ |
public function __construct($options = array()) { |
// Initialisation de l'objet Cartographie |
$this->setCarteNom(isset($options['carte_nom']) ? $options['carte_nom'] : ''); |
$this->setFormuleColoriage(isset($options['formule']) ? $options['formule'] : ''); |
$this->setColoriageCouleurClaire(isset($options['couleur_claire']) ? $options['couleur_claire'] : ''); |
$this->setColoriageCouleurFoncee(isset($options['couleur_foncee']) ? $options['couleur_foncee'] : ''); |
$this->setCarteFondFichier(isset($options['fond_fichier']) ? $options['fond_fichier'] : ''); |
$this->setCarteFondDossier(isset($options['fond_dossier']) ? $options['fond_dossier'] : ''); |
$this->setCarteStockageDossier(isset($options['stock_dossier']) ? $options['stock_dossier'] : ''); |
$this->setCarteStockageUrl(isset($options['stock_url']) ? $options['stock_url'] : ''); |
$this->setCarteZones(isset($options['zones']) ? $options['zones'] : null); |
$this->setZoneMarker(isset($options['zone_marker']) ? $options['zone_marker'] : ''); |
$this->setModeDebug(isset($options['debug']) ? $options['debug'] : false); |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
/*** Accesseur : ***/ |
public function getTableauFrequence() { |
ksort($this->coloriage_tableau_frequence); |
return $this->coloriage_tableau_frequence; |
} |
public function getCarteCache() { |
// Gestion du cache |
if ($this->getCarteNom() != '') { |
$fichier_carte = $this->carte_stockage_dossier.$this->getCarteNom().'.png'; |
if (file_exists($fichier_carte)) { |
//echo filemtime($fichier_carte).'-'.strtotime($this->carte_info['donnees_date_maj']); |
if (filemtime($fichier_carte) < strtotime($this->carte_info['donnees_date_maj'])) { |
$this->carte_cache = false; |
} else { |
$this->carte_cache = true; |
} |
} |
} |
return $this->carte_cache; |
} |
public function getCarteInfo() { |
return $this->carte_info; |
} |
public function setCarteInfo($ci) { |
$this->carte_info = $ci; |
} |
public function getColoriageCouleurClaire() { |
return $this->coloriage_couleur_min; |
} |
public function setColoriageCouleurClaire($ccmi) { |
$this->coloriage_couleur_min = $ccmi; |
} |
public function getColoriageCouleurFoncee() { |
return $this->coloriage_couleur_max; |
} |
public function setColoriageCouleurFoncee($ccma) { |
$this->coloriage_couleur_max = $ccma; |
} |
public function getFormuleColoriage() { |
return $this->formule_coloriage; |
} |
public function setFormuleColoriage($fc) { |
$this->formule_coloriage = $fc; |
} |
public function getCarteNom() { |
return $this->carte_nom; |
} |
public function setCarteNom($cn) { |
$this->carte_nom = $cn; |
} |
public function getCarteFichier() { |
return $this->carte_fichier; |
} |
public function setCarteFichier($cf) { |
$this->carte_fichier = $cf; |
} |
public function getCarteFondFichier() { |
return $this->carte_fond_fichier; |
} |
public function setCarteFondFichier($cff) { |
$this->carte_fond_fichier = $cff; |
} |
public function getCarteFondDossier() { |
return $this->carte_fond_dossier; |
} |
public function setCarteFondDossier($cfd) { |
$this->carte_fond_dossier = $cfd; |
} |
public function getCarteStockageDossier() { |
return $this->carte_stockage_dossier; |
} |
public function setCarteStockageDossier($csd) { |
$this->carte_stockage_dossier = $csd; |
} |
public function getCarteStockageUrl() { |
return $this->carte_stockage_url; |
} |
public function setCarteStockageUrl($csu) { |
$this->carte_stockage_url = $csu; |
} |
public function getCarteZones() { |
if (is_null($this->carte_zones)) { |
$this->chargerZones(); |
} |
return $this->carte_zones; |
} |
public function setCarteZones($cz) { |
$this->carte_zones = $cz; |
} |
public function getZoneMarker() { |
return $this->zone_marker; |
} |
public function setZoneMarker($zm) { |
$this->zone_marker = $zm; |
} |
public function getModeDebug() { |
return $this->mode_debug; |
} |
public function setModeDebug($md) { |
$this->mode_debug = $md; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
/*** Méthodes PUBLIQUES : ***/ |
public function creerCarte() { |
if(file_exists($this->getCarteFichier())) { |
//echo 'suppression du fichier de carte : '.$this->getCarteFichier(); |
//unlink($this->getCarteFichier()); |
} |
// Création de la carte car aucun cache ou cache à vider |
$carte_fond_fichier = $this->carte_fond_dossier.$this->getCarteFondFichier().'.png'; |
$this->carte = imagecreatefrompng($carte_fond_fichier); |
// Vérification que la création à fonctionnée |
if (!$this->carte) { |
// Une erreur est survenue : création d'une image blanche |
$this->carte = imagecreatetruecolor(520, 60); |
$bgc = imagecolorallocate($this->carte, 255, 255, 255); |
$tc = imagecolorallocate($this->carte, 0, 0, 0); |
imagefilledrectangle($this->carte, 0, 0, 520, 60, $bgc); |
// Affichage d'un message d'erreur |
imagestring($this->carte, 1, 5, 5, "Erreur de chargement de l'image :", $tc); |
imagestring($this->carte, 1, 5, 15, $carte_fond_fichier, $tc); |
} else { |
// Nous construisons le tableau de correspondance entre les couleurs présente sur l'image de fond |
// et les couleurs qui doivent les remplacer. |
$this->construireCorrespondanceCouleur(); |
// Nous lançons la création de la carte |
$this->colorierCarte(); |
} |
// Nous chercons à créer une image indéxées en sortie |
if (imageistruecolor(&$this->carte) && $this->formule_coloriage != 'legende') { |
if ($this->coloriage_couleurs <= 253) { |
//imagetruecolortopalette(&$this->carte, false, ($this->coloriage_couleurs + 2));// + 2 car noir et blanc réservés. |
} else { |
// On force la création d'une palette... si cela pose problème ajouter un attribut permettant de désactiver |
// ce fonctionnement. |
imagetruecolortopalette(&$this->carte, false, 255); |
} |
} |
// Nous écrivons le fichier de la carte. |
if ($this->getCarteNom() == '') { |
$this->setCarteNom(md5($this->carte)); |
} |
$this->setCarteFichier($this->carte_stockage_dossier.$this->getCarteNom().'.png'); |
imagepng(&$this->carte, $this->getCarteFichier()); |
return true; |
} |
public function getImageMap() { |
// Initialisation de variables |
$carte_map = ''; |
// Gestion de l'image map |
$chemin_carte_map_fond = $this->getCarteFondDossier().$this->getCarteFondFichier().'.tpl.html'; |
$chemin_carte_map = $this->getCarteStockageDossier().$this->getCarteNom().'.html'; |
if (file_exists($chemin_carte_map)) { |
$carte_map = file_get_contents($chemin_carte_map); |
} else { |
$nom_carte_png = $this->getCarteNom().'.png'; |
$chemin_carte_png = $this->getCarteStockageDossier().$nom_carte_png; |
$donnees['carte_url'] = sprintf($this->getCarteStockageUrl(), $nom_carte_png); |
$donnees['carte_alt'] = 'info'; |
$donnees['zones'] = $this->getCarteZones(); |
//Debug::printr($donnees); |
$carte_map = SquelettePhp::analyser($chemin_carte_map_fond, $donnees); |
if (!file_put_contents($chemin_carte_map, $carte_map)) { |
$e = "Écriture du fichier contenant le html de la carte impossible : $chemin_carte_map"; |
trigger_error($e, E_USER_WARNING); |
} |
} |
return $carte_map; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
/*** Méthodes PRIVÉES : ***/ |
/** |
* Charge en mémoire les données du fichier csv des zones géographique de la carte |
*/ |
private function chargerZones() { |
$fichier_csv = $this->getCarteFondDossier().$this->getCarteFondFichier().'.csv'; |
$zones = array(); |
if (($handle = fopen($fichier_csv, 'r')) !== false) { |
$ligne = 1; |
$cles = array(); |
while (($donnees = fgetcsv($handle, 1000, ',')) !== false) { |
$cle = array_shift($donnees); |
if ($ligne == 1) { |
// Ligne 1 : les noms des champs |
$cles = $donnees; |
} else { |
// Ligne > 1 : traitements des données |
$zones[$cle] = array_combine($cles, $donnees); |
} |
$ligne++; |
} |
fclose($handle); |
} |
$this->setCarteZones($zones); |
} |
private function construireCorrespondanceCouleur() { |
switch ($this->formule_coloriage) { |
case self::FORMULE_LEGENDE : |
$this->construireCorrespondanceCouleurLegende(); |
break; |
case self::FORMULE_PROPORTIONNEL : |
$this->construireCorrespondanceCouleurProportionnel(); |
break; |
default : |
$e = "Aucune formule de coloriage n'a été définie parmis : ". |
self::FORMULE_LEGENDE.' et '.self::FORMULE_PROPORTIONNEL.'. '. |
"Veuillez la définir avec la méthode setFormuleColoriage()."; |
trigger_error($e, E_USER_ERROR); |
} |
} |
private function construireCorrespondanceCouleurProportionnel() { |
// Création d'un tableau contenant seulement les nombres d'information pour chaque zone. |
$tab_valeurs = array(); |
foreach ($this->getCarteZones() as $cle => $valeur) { |
//Nous recherchons le minimum, le maximum et le la valeur médium juste au dessous du maximum. |
if (isset($valeur['info_nombre'])) { |
$tab_valeurs[] = $valeur['info_nombre']; |
if ($valeur['info_nombre'] == 0){ |
//trigger_error($valeur['nom'], E_USER_NOTICE); |
} |
} |
} |
//Nombre d'entrées dans le tableau de valeurs non nulles : |
$valeurs_nbre = count($tab_valeurs); |
$valeurs_somme = array_sum($tab_valeurs); |
// Tabeau des fréquences trié de la plus petite à la plus grande clé. |
$tab_frequences = array_count_values($tab_valeurs); |
krsort($tab_frequences); |
//trigger_error(print_r($tab_frequences, true), E_USER_NOTICE); |
$frequences_nbre = count($tab_frequences); |
if ($valeurs_nbre > 0){ |
// Nous trions le tableau dans l'ordre croissant : |
sort($tab_valeurs); |
// Nous récupérons la valeur la plus petite : |
$mini = $tab_valeurs[0]; |
$maxi = $tab_valeurs[$valeurs_nbre - 1]; |
$medium = isset($tab_valeurs[$valeurs_nbre - 2]) ? $tab_valeurs[$valeurs_nbre - 2] : 0; |
$moyenne = $valeurs_somme / $valeurs_nbre; |
$ecart_au_carre_moyen = 0; |
for ($i = 0; $i < $valeurs_nbre; $i++) { |
$ecart_au_carre_moyen += pow(($tab_valeurs[$i] - $moyenne), 2); |
} |
$variance = $ecart_au_carre_moyen / $valeurs_nbre; |
$ecart_type = round(sqrt($variance), 0); |
$moyenne = round($moyenne, 0); |
$variance = round($variance, 0); |
} |
// Calcul de l'écart moyen pour chaque élément R, V et B. |
list($r_min, $v_min, $b_min) = explode(',', $this->coloriage_couleur_max); |
list($r_max, $v_max, $b_max) = explode(',', $this->coloriage_couleur_min); |
$r_diff = $r_min - $r_max; |
$r_ecart_moyen = abs($r_diff / $frequences_nbre); |
$v_diff = $v_min - $v_max; |
$v_ecart_moyen = abs($v_diff / $frequences_nbre); |
$b_diff = $b_min - $b_max; |
$b_ecart_moyen = abs($b_diff / $frequences_nbre); |
// Pour chaque fréquence nous attribuons une couleur. |
$i = 1; |
foreach ($tab_frequences as $cle => $valeur){ |
if ($cle == 0) { |
$this->coloriage_tableau_frequence[$cle] = '255-255-255'; |
} else { |
$r = $r_min + round(($i * $r_ecart_moyen), 0); |
$v = $v_min + round(($i * $v_ecart_moyen), 0); |
$b = $b_min + round(($i * $b_ecart_moyen), 0); |
$this->coloriage_tableau_frequence[$cle] = $r.'-'.$v.'-'.$b; |
} |
$i++; |
} |
// Attribution du nombre de couleurs utilisé pour réaliser la carte |
$this->coloriage_couleurs = count(array_count_values($this->coloriage_tableau_frequence)); |
//trigger_error('<pre>'.print_r($this->coloriage_couleurs, true).'</pre>', E_USER_ERROR); |
// Nous attribuons les couleurs à chaque zone géographique |
foreach ($this->getCarteZones() as $cle => $zg) { |
if (isset($this->coloriage_tableau_frequence[$zg['info_nombre']])) { |
$this->carte_correspondance_couleurs[$zg['rvb_fond']] = $this->coloriage_tableau_frequence[$zg['info_nombre']]; |
} else { |
$this->carte_correspondance_couleurs[$zg['rvb_fond']] = '128-128-128'; |
if ($this->getModeDebug()) { |
$e = "La zone ".$zg['nom']." (".$zg['rvb_fond'].") ne possède pas de couleur RVB pour la remplir. ". |
"La valeur 128-128-128 lui a été attribué."; |
trigger_error($e, E_USER_WARNING); |
} |
} |
} |
} |
private function construireCorrespondanceCouleurLegende() { |
$tab_couleurs = array(); |
foreach ($this->getCarteZones() as $cle => $zg) { |
if ($zg['rvb_carte'] != '') { |
$this->carte_correspondance_couleurs[$zg['rvb_fond']] = $zg['rvb_carte']; |
} else { |
$this->carte_correspondance_couleurs[$zg['rvb_fond']] = '128-128-128'; |
if ($this->getModeDebug()) { |
$e = "La zone ".$zg['nom']." (".$zg['rvb_fond'].") ne possède pas d'information pour la légende dans le champ". |
" rvb_carte. La valeur 128-128-128 lui a été attribué."; |
trigger_error($e, E_USER_WARNING); |
} |
} |
if (!isset($tab_couleurs[$this->carte_correspondance_couleurs[$zg['rvb_fond']]])) { |
$tab_couleurs[$this->carte_correspondance_couleurs[$zg['rvb_fond']]] = 1; |
} |
} |
// Attribution du nombre de couleurs utilisé pour réaliser la carte |
$this->coloriage_couleurs = count($tab_couleurs); |
} |
private function colorierCarte() { |
if (imageistruecolor(&$this->carte)) { |
//+--------------------------------------------------------------------------------------------------------+ |
// Remplacement des couleurs sur la carte en mode vraies couleurs (RGB) |
$this->colorierCarteModeVraiCouleur(); |
} else { |
//+--------------------------------------------------------------------------------------------------------+ |
// Remplacement des couleurs sur la carte en mode couleurs indexées (palette de couleurs) |
$this->colorierCarteModeIndexe(); |
} |
} |
private function colorierCarteModeVraiCouleur() { |
// Nous commençons le rempalcement des couleurs sur la carte de fond. |
$hauteur = imagesy(&$this->carte); |
$largeur = imagesx(&$this->carte); |
// Tableau contenant les couleurs traitées, pour éviter de traiter plusieurs fois la même couleur |
$tab_rvb_ok = array(); |
for ($x = 0; $x < $largeur; $x++) { |
for ($y = 0; $y < $hauteur; $y++) { |
$rvb = ImageColorAt(&$this->carte, $x, $y); |
if (!isset($tab_rvb_ok[$rvb])) { |
// Récupération de la couleur rvb au format xxx-xxx-xxx |
$cle = (($rvb >> 16) & 0xFF).'-'.(($rvb >> 8) & 0xFF).'-'.($rvb & 0xFF); |
// Si nous n'avons pas à faire à la couleur noire (utilisé pour délimité les zones), nous continuons |
if ($cle != '255-255-255') { |
$rvb_final = null; |
if (isset($this->carte_correspondance_couleurs[$cle])) { |
if ($this->zone_marker != '' && $cle == $this->zone_marker) { |
$rvb_final = '255'<<16 | '0'<<8 | '0'; |
} else { |
list($rouge, $vert, $bleu) = explode('-', $this->carte_correspondance_couleurs[$cle]); |
$rvb_final = $rouge<<16 | $vert<<8 | $bleu; |
} |
// Si le nombre de couleurs sur la carte finale est infèrieur à 255 nous créons une image indexée |
imagefill(&$this->carte, $x, $y, $rvb_final); |
} else { |
$rvb_final = '128'<<16 | '128'<<8 | '128'; |
imagefill(&$this->carte, $x, $y, $rvb_final); |
} |
// Nous ajoutons la couleur ajoutée à la carte dans le tableau des couleurs traitées |
$tab_rvb_ok[$rvb_final] = true; |
} |
// Nous ajoutons la couleur trouvées sur la carte de fond dans le tableau des couleurs traitées |
$tab_rvb_ok[$rvb] = true; |
} |
} |
} |
} |
private function colorierCarteModeIndexe() { |
// Nous attribuons à chaque zone présente dans le tableau $this->getCarteZones() la valeur de l'index |
// de la couleur RVB représentant cette zone sur la carte de fond. |
$this->construireAssociationIndexZone(); |
foreach ($this->getCarteZones() as $zg) { |
if (isset($this->carte_correspondance_couleurs[$zg['rvb_fond']])) { |
//Dans le cas où nous voulons repérer une zone sur la carte : |
if ($this->getZoneMarker() != '' && $zg['rvb_fond'] == $this->getZoneMarker()) { |
$rouge = 255; |
$vert = 0; |
$bleu = 0; |
} else { |
list($rouge, $vert, $bleu) = explode('-', $this->carte_correspondance_couleurs[$zg['rvb_fond']]); |
} |
if (isset($zg['index'])) { |
imagecolorset(&$this->carte, $zg['index'], $rouge, $vert, $bleu); |
} else if ($this->getModeDebug()) { |
$e = "La zone '{$zg['nom']}' n'est pas présente sur la carte."; |
trigger_error($e, E_USER_WARNING); |
} |
} |
} |
} |
private function construireAssociationIndexZone() { |
// Nous récupérons le nombre de couleur différentes contenues dans l'image. |
$taille_palette = imagecolorstotal($this->carte); |
// Pour chaque couleur contenue dans l'image, nous cherchons l'objet correspondant |
// dans le tableau $this->getCarteZones(), qui contient des informations sur chaque zone de l'image, |
// et nous attribuons la valeur de l'index de sa couleur sur la carte de fond. |
for ($i = 0; $i < $taille_palette; $i++) { |
$rvb = array(); |
$rvb = imagecolorsforindex($this->carte, $i); |
$rvb_cle = $rvb['red'].'-'.$rvb['green'].'-'.$rvb['blue']; |
// La couleur ne doit pas correspondre au noir ou au blanc car ces couleurs ne sont pas traitées |
if ($rvb_cle != '255-255-255' && $rvb_cle != '0-0-0') { |
$index_ok = false; |
foreach($this->getCarteZones() as $cle => $zg) { |
if (isset($zg['rvb_fond']) && $zg['rvb_fond'] == $rvb_cle) { |
$this->carte_zones[$cle]['index'] = $i; |
$index_ok = true; |
break; |
} |
} |
if (!$index_ok && $rvb_cle != '0-0-0' && $this->getModeDebug()) { |
$e = "Aucune information n'est fournie pour la zone sur la carte d'index $i : $rvb_cle"; |
trigger_error($e, E_USER_WARNING); |
//$this->carte_zones[] = array('rvb_fond' => $rvb_cle, 'rvb_carte' => '128-128-128', 'index' => $i); |
} |
} |
} |
} |
} |
?> |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_europe.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/pays_moyen_orient.csv |
---|
New file |
0,0 → 1,0 |
"code","rvb_fond","nom","poly" |
/trunk/composants/cartographie/squelettes/france.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_oceanie.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/pays_asie.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_asie.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_sud_amerique.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_sud_amerique.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_moyen_orient.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/continents.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/continents.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_asie.csv |
---|
New file |
0,0 → 1,0 |
"code","rvb_fond","nom","poly" |
/trunk/composants/cartographie/squelettes/pays_sud_amerique.csv |
---|
New file |
0,0 → 1,0 |
"code","rvb_fond","nom","poly" |
/trunk/composants/cartographie/squelettes/pays_nord_amerique.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_nord_amerique.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_afrique.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_afrique.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_europe.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_europe.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_asie.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/continents.csv |
---|
New file |
0,0 → 1,9 |
"code","rvb_fond","nom","poly" |
"1","60-174-15","Afrique","383,202,349,217,334,194,334,183,320,176,293,168,271,174,213,174,249,245,249,353,383,353" |
"2","128-218-141","Amérique du Nord","0,220,116,220,116,225,153,224,153,211,200,211,200,199,206,199,206,193,220,193,212,174,296,0,0,0" |
"3","189-179-25","Asie","579,20,579,150,501,225,501,252,494,252,485,249,473,254,384,256,383,193,392,193,392,182,398,182,396,181,396,171,401,172,401,166,409,166,409,156,355,156,357,154,358,150,354,146,360,142,363,143,377,144,378,132,381,131,379,127,382,125,381,122,378,117,384,110,384,87,379,79,379,20,431,20" |
"4","0-128-218","Europe","287,19,214,173,269,173,286,168,298,168,319,175,319,166,323,165,327,157,336,158,344,163,355,164,353,156,356,152,352,145,360,140,364,143,377,143,376,131,380,130,377,127,381,123,376,117,384,107,384,98,384,94,382,86,378,80,378,20" |
"5","206-0-0","Océanie","578,151,502,226,502,253,495,253,486,250,473,255,384,256,384,353,579,353" |
"6","255-125-0","Amérique du Sud","222,194,207,194,207,200,201,200,201,212,154,212,154,225,114,225,114,221,0,221,0,353,248,353,248,246" |
"7","0-0-255","Moyen-Orient","408,157,354,157,357,164,344,165,335,160,328,159,324,166,320,168,321,175,335,182,335,194,350,216,382,203,382,192,391,192,391,181,396,181,395,171,400,171,400,165,408,165,408,162" |
"0","255-255-255",, |
/trunk/composants/cartographie/squelettes/pays_nord_amerique.csv |
---|
New file |
0,0 → 1,24 |
"code","rvb_fond","nom","poly" |
"ag","200-100-0","Antigua et Barbuda","99,370,99,388,106,389,109,388,110,385,107,371" |
"bs","200-100-50","Bahamas","400,354,407,377,432,392,436,404,445,401,453,394,432,374,411,351" |
"bb","200-100-75","Barbade","128,442,137,443,137,450,133,453,128,449" |
"bz","200-100-100","Bélize","348,417,345,419,345,421,341,424,341,438,344,438,348,431,348,422,351,423,350,419" |
"ca","200-100-125","Canada","173,194,312,193,338,201,352,197,356,201,366,212,380,224,377,235,373,242,374,244,378,243,395,238,395,234,409,228,413,222,432,221,440,206,449,207,456,223,467,234,493,215,527,210,526,202,496,162,442,120,440,82,367,36,375,20,391,6,366,3,329,8,265,27,238,40,204,63,168,67,126,119,126,121,133,121,132,125,127,124,127,127,134,140,135,148,138,149,138,156,135,161,138,175,152,172,153,179,149,183,162,198,164,199,170,200" |
"cr","200-100-150","Costa Rica","363,477,377,479,382,487,382,498,377,497,374,491,365,487,363,486" |
"cu","200-100-175","Cuba","368,388,384,380,397,382,407,388,426,397,436,406,428,410,411,410,411,404,390,393,380,398,372,397,369,395,366,393" |
"dm","200-100-250","Dominique","449,408,452,415,450,416,451,425,453,426,457,421,459,422,462,420,466,419,467,421,470,420,472,419,472,417,465,412,460,409,457,407" |
"sv","200-200-0","Salvador","336,456,338,454,341,451,345,454,350,454,352,460,347,461,342,461" |
"us","200-200-100","Etats-Unis","173,194,312,194,335,201,345,205,365,211,377,233,375,240,373,244,384,243,395,239,395,234,408,233,409,226,413,222,434,221,440,205,449,206,451,216,457,224,440,233,442,246,432,250,424,253,423,260,418,274,418,289,414,296,396,311,386,323,392,342,396,353,396,368,390,374,384,374,381,369,387,365,382,352,378,345,378,338,373,332,370,333,359,332,356,328,342,330,340,337,331,339,322,335,309,335,307,342,299,342,296,348,294,363,280,357,278,348,274,343,273,338,270,334,262,332,258,339,249,331,249,326,239,317,230,318,230,321,214,321,193,311,179,311,177,305,163,298,53,309,25,288,34,284,158,280,150,251,153,233,163,199,172,200 6,151,26,111,50,87,70,82,72,72,130,56,173,63,120,126,6,157" |
"gd","200-200-125","Grenade","113,457,118,458,116,465,110,465,107,462" |
"gl","200-200-250","Groenland","113,457,118,458,116,465,110,465,107,462" |
"gt","0-100-200","Guatémala","327,427,331,427,332,424,341,424,341,438,346,441,346,443,343,443,341,451,339,453,336,456,330,455,326,454,325,453,322,450,323,447,323,444,328,438,333,437,333,433" |
"ht","50-100-200","Haïti","449,409,451,414,450,415,450,423,446,421,444,422,439,422,438,423,434,419,434,417,440,418,441,417,445,417,443,415,442,411,438,410,442,406" |
"hn","75-100-200","Honduras","346,440,346,443,342,447,342,451,345,454,350,454,352,460,356,461,357,455,360,455,362,453,364,453,368,450,368,448,370,447,372,449,379,445,377,443,369,439,347,440" |
"jm","100-100-200","Jamaïque","408,418,416,417,425,422,424,426,411,426" |
"mx","125-100-200","Mexique","179,311,185,333,194,344,188,347,190,352,195,356,199,357,203,372,214,386,219,384,232,377,242,401,239,409,273,435,301,444,306,439,311,441,323,448,323,444,327,437,333,437,333,433,327,427,331,427,332,424,341,424,348,416,350,418,356,399,355,395,348,394,335,397,326,417,302,416,294,362,281,357,274,343,268,332,261,332,258,339,248,331,248,325,239,316,230,317,229,321,214,322,193,312,179,311" |
"ni","150-100-200","Nicaragua","352,461,356,461,357,456,360,456,362,454,364,454,368,451,368,449,370,448,372,449,380,446,379,450,378,456,377,464,377,479,362,477" |
"pa","175-100-200","Panama","382,500,382,487,393,495,402,488,413,491,419,497,419,502,418,506,412,508,410,503,411,499,405,495,403,499,398,502,402,506,398,510,394,509,392,508,393,506" |
"vc","200-100-200","Saint Vincent et les Grenadines","114,444,120,445,118,452,112,451" |
"lc","0-100-100","Sainte Lucie","118,434,115,439,117,443,120,444,123,443,124,438,122,435" |
"tt","75-100-100","Trinité et Tobago","108,479,113,476,120,477,122,471,131,472,127,494,109,494,106,489" |
"0","255-255-255","","" |
/trunk/composants/cartographie/squelettes/pays_sud_amerique.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/france.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/france.png |
---|
New file |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_afrique.csv |
---|
New file |
0,0 → 1,57 |
"code","rvb_fond","nom","poly" |
"km","255-150-175","Comores","471,401,469,401,470,403 480,401,480,399,477,400 463,392,460,389,463,385,463,383,461,383,459,386,459,389,464,395" |
"ci","255-150-225","Côte d'Ivoire","99,263,110,263,113,264,113,261,111,259,110,249,112,247,112,244,115,240,114,232,110,229,104,229,98,230,94,226,91,225,88,226,87,223,85,223,85,225,75,226,74,230,76,232,77,239,75,240,75,245,73,247,73,253,76,255,77,257,80,259,80,263,79,269,88,265,90,265" |
"rw","175-255-150","Rwanda","359,319,364,319,364,315,363,312,361,312,357,313,354,318,354,320,358,321" |
"mw","25-250-255","Malawi","396,427,399,423,402,421,402,413,394,404,390,397,388,395,388,393,390,390,390,381,388,379,387,375,385,375,387,382,385,385,385,396,382,399,382,403,381,405,386,410,392,410,394,412,394,419,393,424" |
"bf","255-150-25","Burkina Faso","113,225,112,220,140,219,144,215,149,215,150,212,149,208,144,208,141,204,140,201,135,196,135,194,134,191,129,191,127,193,114,202,112,202,108,203,106,204,103,211,97,217,97,219,96,224,99,228,102,228,111,227,113,228" |
"sl","125-255-250","Sierra Leone","48,248,50,246,54,243,55,240,54,235,51,230,42,229,36,236,37,240,39,242,43,247,45,247" |
"eg","255-250-75","Egypte","389,85,391,84,391,81,393,76,393,71,390,64,390,62,389,59,387,59,377,60,377,63,379,64,379,68,380,72,382,74,385,81 384,86,379,81,377,76,377,74,375,71,375,58,372,56,361,56,359,58,354,60,352,62,349,62,346,60,344,60,337,57,335,57,329,56,327,55,323,56,321,55,320,59,321,66,319,68,319,70,320,74,322,75,322,134,384,134,388,135,391,131,393,131,394,128,399,126,399,118,396,115,387,96,387,94,384,89" |
"ga","25-50-255","Gabon","229,316,235,317,237,319,239,314,239,306,238,299,240,296,240,293,236,292,234,294,232,294,231,287,228,287,222,286,219,287,219,294,206,295,206,301,204,302,204,306,202,308,202,311,204,316,207,319,216,329,220,328,220,321" |
"bw","255-25-225","Botswana","320,499,324,498,326,493,328,491,334,486,338,481,340,478,347,473,350,472,350,470,345,469,342,467,342,465,341,460,338,457,331,453,327,448,325,443,323,441,322,438,320,438,315,440,311,444,309,443,308,440,303,440,294,442,291,443,291,469,284,470,284,492,289,497,291,500,291,504,290,507,297,508,305,501,308,497,315,497,318,499" |
"ug","25-255-150","Ouganda","361,309,371,309,371,307,372,304,374,302,377,300,382,300,385,299,389,300,390,297,392,295,396,290,396,284,391,278,391,274,388,273,386,275,379,275,373,276,368,275,366,278,366,282,369,285,369,287,366,290,364,291,364,293,360,298,358,307,358,311" |
"sz","151-151-75","Swaziland","371,511,373,509,373,503,369,502,366,507,367,511" |
"ls","75-150-255","Lesotho","350,536,352,534,353,530,351,528,349,525,346,525,340,530,339,535,343,540,347,537" |
"et","255-250-175","Ethiopie","448,272,450,270,452,270,455,269,458,266,463,264,471,264,482,253,491,245,491,243,484,243,475,239,473,239,466,236,464,236,456,228,454,225,454,221,449,222,447,220,447,217,449,212,449,209,446,206,443,202,441,201,437,196,431,195,429,194,421,195,418,193,416,196,408,196,408,201,406,206,401,210,397,217,397,220,391,225,391,238,389,240,384,241,384,243,387,244,390,247,392,250,396,253,396,255,399,260,402,261,404,263,404,267,408,267,421,274,423,274,430,275,434,271,436,271,442,270,445,272" |
"cg","175-151-151","Congo","219,332,223,336,224,338,229,334,233,337,235,337,238,334,241,334,242,337,244,337,253,329,254,321,256,316,258,314,260,311,266,306,266,300,267,292,268,287,272,280,272,277,266,277,261,276,255,282,255,289,252,290,249,288,246,288,234,287,233,291,239,290,242,293,242,297,240,300,240,304,241,317,240,320,236,321,234,319,230,319,228,318,226,320,222,323,222,329,219,330" |
"mr","25-255-50","Mauritanie","44,189,46,187,47,183,51,182,54,186,56,186,58,184,92,184,93,180,90,178,90,169,87,141,87,139,85,122,85,119,84,110,93,109,92,107,85,103,83,101,76,97,74,95,71,94,71,101,47,102,47,122,44,124,41,124,38,126,38,139,8,140,9,142,13,145,14,152,13,156,15,159,15,167,12,174,12,176,19,176,27,175,31,179,34,179,37,184,42,189" |
"sd","151-151-25","Soudan","377,274,385,273,392,267,402,267,402,264,397,261,394,256,394,254,392,252,389,250,386,246,384,246,381,244,381,242,383,239,386,239,389,237,389,224,391,222,392,220,395,219,395,216,398,211,400,209,405,203,405,200,406,189,409,179,409,177,410,173,413,172,415,170,420,168,421,165,414,160,412,157,412,142,410,140,409,135,404,130,401,128,399,128,396,129,395,132,392,133,390,135,389,137,384,137,322,136,322,150,315,151,315,183,313,185,307,185,307,187,303,192,300,205,303,207,303,211,307,218,307,221,311,226,312,233,311,235,314,236,317,239,320,239,324,242,324,245,331,250,333,252,333,254,334,256,339,259,342,264,344,266,345,268,354,268,357,267,360,270,365,274,373,273" |
"bi","255-150-75","Burundi","361,331,364,326,364,324,362,323,362,321,360,321,359,323,355,324,355,329,354,335,359,333" |
"zm","75-151-151","Zambie","335,437,340,430,342,428,345,426,348,426,348,422,355,419,359,419,359,415,362,413,364,413,371,410,373,410,378,408,380,399,383,395,383,384,385,382,385,380,378,372,376,372,373,371,370,368,368,368,366,369,361,365,359,365,354,366,352,369,352,381,350,388,350,391,352,393,357,393,358,401,351,402,345,397,344,395,342,395,338,392,329,392,324,389,323,387,317,387,316,385,315,399,300,400,300,426,302,429,308,434,320,433,323,436,326,436" |
"gm","75-50-255","Gambie","15,202,22,200,20,199,18,201,11,201" |
"dz","255-25-75","Algérie","149,147,151,147,153,149,155,149,158,150,159,157,161,157,171,154,173,154,178,152,181,149,194,139,201,135,203,133,212,128,215,125,218,124,220,122,220,120,216,115,210,114,208,112,208,110,207,107,205,105,203,102,203,99,206,96,206,81,205,74,203,72,202,66,203,60,202,54,201,51,196,48,195,44,192,41,190,40,189,32,191,30,195,27,195,25,196,7,191,7,188,6,185,7,182,6,179,8,177,8,173,9,170,7,164,7,158,8,155,10,147,10,138,14,135,17,131,17,125,22,122,23,122,29,123,39,124,42,127,45,126,49,114,50,112,53,110,53,107,54,107,57,108,60,99,64,94,69,90,69,86,70,85,72,81,72,79,74,77,74,71,79,71,91,78,95,80,97,87,101,90,104,97,108,99,110,106,114,109,117,116,121,118,123,143,140,144,143" |
"dj","255-250-25","Djibouti","456,217,454,216,454,214,457,212,457,209,455,208,451,212,451,214,449,217,449,220,452,220,455,219" |
"lr","125-150-255","Libéria","78,260,75,258,74,256,71,255,70,252,71,249,69,248,66,249,63,247,63,242,62,240,58,240,55,245,52,248,50,249,53,254,56,255,58,257,65,263,72,267,75,267,77,268,77,263" |
"tg","151-151-225","Togo","144,256,146,255,146,236,142,227,139,225,139,221,136,221,135,223,138,226,138,229,139,241,140,251" |
"cv","25-100-150","Cap Vert","58,349,58,346,56,345,56,348 66,335,65,335 46,328,45,328" |
"tz","151-151-125","Tanzanie","432,377,432,375,428,366,428,363,429,354,425,350,425,345,428,338,426,336,419,332,417,330,417,328,416,325,413,324,411,322,406,320,404,318,399,316,397,314,394,313,392,311,390,311,390,313,387,316,387,320,385,322,383,322,379,323,376,322,374,324,372,323,371,314,372,311,365,311,365,313,366,319,365,323,366,327,361,334,356,337,354,340,354,342,355,350,357,352,360,356,363,358,364,364,365,366,367,366,369,365,371,367,374,369,376,369,385,373,388,373,389,377,392,380,392,391,394,391,403,387,405,387,407,389,416,388,418,386,427,386,434,383,436,381" |
"gw","175-50-255","Guinée Bissau","23,219,23,217,25,215,30,214,30,210,31,208,20,208,14,209,13,211,16,213,20,213,21,215,20,218" |
"mz","75-255-50","Mozambique","372,472,372,479,374,488,374,500,375,507,379,508,379,506,377,504,377,501,379,500,381,497,390,493,392,493,398,488,398,485,397,483,399,476,399,473,397,471,397,469,395,462,393,460,393,454,398,449,402,446,403,444,405,444,407,442,407,440,409,439,411,436,420,431,423,431,428,427,430,426,436,419,436,415,437,410,436,395,435,390,438,385,438,383,437,383,433,386,431,386,428,388,419,388,417,390,410,390,406,391,401,390,392,394,391,396,394,399,397,404,400,407,404,412,404,422,401,425,399,426,399,429,396,430,391,425,390,421,392,418,392,413,385,412,382,409,363,415,361,416,361,418,362,422,370,422,379,426,381,428,381,430,382,442,380,447,381,456,378,461,378,463,376,466,376,468" |
"zw","225-151-151","Zimbabwe","349,467,352,469,356,469,361,471,367,471,370,472,373,468,379,455,379,451,378,445,380,440,380,433,379,429,377,427,373,426,370,424,362,424,359,421,355,421,352,423,350,423,350,427,346,428,344,430,340,433,337,437,336,439,326,439,324,438,327,442,330,449,333,452,336,453,339,456,343,459,343,463,344,466" |
"gq","225-50-255","Guinée équatoriale","217,293,217,287,208,287,206,292" |
"cd","125-255-150","République Démocratique du Congo","317,383,318,385,323,385,326,388,331,390,333,390,337,389,339,391,343,392,346,394,350,399,356,400,356,395,351,395,347,391,347,388,350,378,350,368,353,364,356,364,361,363,361,359,359,357,355,354,353,349,353,344,352,329,353,325,351,322,351,319,356,310,356,303,357,299,359,297,362,293,362,290,366,287,366,285,364,283,364,278,365,276,358,270,349,270,346,271,343,269,340,265,332,265,330,264,319,266,315,266,312,268,305,268,303,271,294,271,288,270,286,268,284,265,280,265,276,269,276,274,274,276,274,281,270,288,270,293,268,304,268,307,264,311,260,314,257,319,257,321,256,327,255,330,249,336,244,339,241,339,240,336,237,337,235,339,230,339,229,346,231,346,257,345,260,348,260,351,263,358,266,361,277,361,280,354,290,353,291,355,296,355,298,357,298,362,299,376,300,385,303,383" |
"so","175-255-250","Somalie","443,281,443,309,445,312,447,312,456,301,462,295,470,288,477,285,480,282,483,278,491,271,492,268,494,266,500,259,500,257,503,250,505,248,506,243,510,239,513,234,513,230,514,226,516,225,516,215,513,214,512,216,508,217,513,214,510,217,508,217,499,219,497,219,490,220,483,223,476,223,471,226,466,226,459,219,456,221,456,224,458,226,465,234,467,234,476,238,478,238,485,241,494,241,495,243,472,266,463,266,458,269,456,271,454,271,451,272,448,275" |
"td","151-151-175","Tchad","257,243,259,243,262,244,267,241,273,241,276,238,276,235,285,233,287,233,294,226,296,225,296,222,298,220,303,219,304,217,301,212,301,208,298,208,297,204,298,199,300,196,300,193,302,191,305,186,305,184,311,183,313,182,313,155,262,129,260,127,255,125,252,125,249,126,248,139,249,142,251,144,254,146,254,149,252,154,252,158,250,173,250,175,244,181,238,188,237,192,235,193,236,198,240,202,243,204,245,207,245,209,246,220,249,224,249,228,241,229,241,232,242,234,244,234,247,237,249,244,250,246,255,245" |
"ly","175-150-255","Libye","317,67,319,65,319,62,318,56,319,52,315,52,306,50,305,46,298,44,294,44,290,45,286,49,284,50,284,52,283,54,285,59,282,64,279,66,273,66,270,63,263,60,260,60,255,59,252,58,248,54,247,49,238,45,235,45,224,44,220,43,220,47,217,50,210,56,210,58,211,62,208,65,205,66,204,69,207,73,207,80,208,96,205,100,209,107,209,109,210,112,214,112,219,115,222,120,238,121,245,125,247,125,252,123,256,123,313,152,313,150,320,149,320,76,318,74,318,71" |
"er","255-250-125","Erythrée","451,208,454,207,453,205,450,203,447,199,445,198,441,193,439,193,437,191,435,191,433,189,433,187,431,187,428,185,426,180,426,178,424,171,423,169,416,172,415,174,422,169,413,174,411,179,411,181,408,191,408,194,411,194,413,193,415,194,416,191,419,190,422,193,424,193,430,192,436,193,438,195,442,198,443,200,448,204" |
"mg","225-150-255","Madagascar","462,479,462,481,463,484,462,486,463,490,468,495,471,497,474,497,477,495,480,495,485,493,489,484,489,481,493,468,493,466,501,441,501,436,504,429,504,421,506,420,508,421,508,405,504,398,502,397,501,399,497,401,498,406,494,409,492,409,492,412,491,416,487,417,487,419,482,422,482,425,479,426,476,425,474,427,468,427,467,431,464,436,465,443,464,445,467,449,468,453,465,456,465,459,464,463,460,468,459,475" |
"ma","125-250-255","Maroc","38,123,43,122,45,121,45,101,69,100,69,78,74,73,78,72,80,70,84,70,86,68,89,68,93,67,99,61,101,61,105,58,105,53,110,51,113,48,124,48,125,45,123,44,121,39,121,31,120,26,117,24,115,24,112,22,110,22,100,23,98,21,96,20,94,17,92,19,93,17,91,19,91,21,86,32,84,34,80,37,73,40,68,45,67,50,64,53,62,58,62,63,63,68,59,75,56,78,51,81,48,84,38,87,38,89,36,91,35,95,33,97,30,98,28,100,25,110,23,112,16,122,15,127,12,132,10,133,10,135,9,138,36,138,36,126" |
"st","25-255-250","Sao Tomé et Principe","182,299,182,296,181,296,180,300" |
"tn","25-151-151","Tunisie","214,49,218,46,218,41,214,38,210,37,207,35,207,32,209,31,213,26,215,25,216,19,213,17,212,11,215,8,211,8,210,5,204,4,198,9,198,25,197,28,195,30,191,33,191,37,192,39,196,42,197,46,202,49,204,56,204,59,205,64,208,62,208,55" |
"cf","75-255-150","République Centrafricaine","288,267,292,267,295,269,302,269,305,265,307,265,311,266,316,264,322,264,331,262,339,263,336,259,333,258,331,255,331,253,328,250,322,245,322,243,319,241,316,241,313,238,311,238,309,236,309,232,310,229,305,222,301,221,298,223,298,226,290,233,287,235,284,235,278,236,278,239,274,243,267,243,265,245,256,246,253,248,250,248,244,259,244,266,249,275,253,279,255,279,259,275,266,274,271,275,274,273,274,269,278,265,279,263,285,263" |
"ng","225-255-50","Nigéria","219,253,224,242,224,240,229,235,229,232,231,225,233,222,233,220,237,216,240,215,241,211,239,209,239,204,235,200,233,200,231,202,229,202,227,204,224,204,211,203,209,205,206,207,200,207,193,203,191,203,188,206,186,206,183,203,178,200,170,200,167,201,165,204,165,206,162,209,162,218,163,224,162,228,159,231,159,233,156,240,156,254,167,254,172,257,176,264,176,266,177,268,184,269,187,267,189,267,194,268,196,265,198,264,198,262,199,258,201,256,204,252,205,250,214,250,217,253" |
"ne","175-255-50","Niger","165,200,170,198,178,198,183,201,185,203,188,203,191,201,194,201,198,204,200,204,205,205,209,202,224,201,226,202,233,197,234,196,233,192,235,191,235,189,238,186,245,178,248,174,248,166,249,157,250,152,252,147,250,145,248,144,246,139,246,127,243,127,240,124,222,123,220,125,213,129,211,131,204,135,202,137,195,141,193,143,180,153,177,155,175,155,168,157,166,158,166,182,162,185,161,187,144,187,143,189,137,190,136,193,139,198,143,202,146,206,150,206,151,211,153,211,153,209,155,208,159,211,160,208,163,205,163,203" |
"mu","175-250-255","Maurice","557,460,557,456,556,456,555,460 594,455,593,455" |
"cm","255-150-125","Cameroun","229,284,246,285,250,286,252,288,253,282,247,276,246,273,244,271,242,266,242,257,244,255,248,246,246,241,246,239,245,237,242,236,239,233,238,229,240,227,247,227,247,225,244,221,244,211,243,208,241,207,243,210,243,215,236,220,232,229,232,232,231,236,226,241,226,243,220,254,216,255,213,252,206,252,202,257,201,263,199,265,199,267,201,268,202,271,206,272,207,275,209,277,209,282,208,285,219,285" |
"bj","255-25-175","Bénin","152,255,154,254,154,238,156,235,156,233,157,230,160,228,160,225,161,220,160,215,156,211,152,215,155,211,151,215,150,217,145,217,142,220,141,224,144,226,146,229,146,231,148,236,148,253,149,255" |
"sc","100-255-250","Seychelles","572,342,572,340,570,340,569,342" |
"ke","25-150-255","Kenya","426,334,430,335,434,328,434,326,437,321,440,319,442,316,444,315,443,312,441,310,441,281,444,278,446,275,441,272,438,272,435,273,432,276,423,277,419,276,406,269,393,269,392,272,394,279,398,282,398,290,394,295,390,302,392,304,392,307,391,309,398,312,400,314,403,315,405,317,410,319,412,321,415,322,418,325,419,329,421,331,424,332" |
"sn","75-255-250","Sénégal","10,204,10,208,12,208,25,206,36,206,41,208,48,208,48,204,44,201,44,196,43,191,41,191,34,184,33,181,30,181,26,177,21,177,11,178,11,185,7,190,7,193,9,195,10,198,17,199,19,197,22,197,31,199,33,200,33,202,25,203,20,202,15,204" |
"na","125-255-50","Namibie","282,470,284,468,289,468,289,442,292,440,296,440,305,438,309,438,310,440,312,440,314,438,318,437,319,435,312,435,307,436,302,438,300,438,289,439,276,438,271,434,237,434,234,431,229,431,224,432,222,434,222,436,223,440,230,450,239,469,241,471,243,482,243,488,245,493,245,498,249,515,251,517,256,523,258,523,260,521,262,521,266,524,267,527,276,527,278,525,281,524" |
"gn","125-151-151","Guinée","72,245,73,242,72,239,75,238,75,235,72,231,72,227,73,225,71,223,71,220,70,217,67,215,67,212,66,210,61,212,57,212,54,213,49,212,48,210,40,210,33,208,33,214,31,216,28,216,25,218,25,221,27,223,28,226,32,229,35,233,37,233,39,231,40,228,51,227,54,230,56,235,56,238,62,238,65,241,65,246,67,247,69,245" |
"ao","255-25-125","Angola","271,432,273,434,278,436,289,436,298,437,305,435,298,427,298,399,313,398,313,385,303,385,302,387,299,387,297,370,297,363,296,357,290,357,289,355,282,355,279,362,268,363,266,364,261,359,258,352,258,349,256,347,233,347,228,349,229,352,231,354,231,356,234,363,234,366,235,368,233,371,234,376,236,378,238,381,238,387,237,394,235,396,234,398,231,399,231,401,228,404,228,406,226,415,222,424,222,431,231,429,234,429,238,432" |
"ao","255-25-125","Angola","226,343,227,340,226,340" |
"ml","75-250-255","Mali","83,224,85,221,88,221,89,223,94,223,94,219,97,214,102,209,102,207,105,202,110,201,117,198,120,195,125,191,128,189,136,189,141,188,144,185,160,185,164,180,164,158,158,159,157,152,155,152,151,149,148,149,144,145,141,143,141,141,113,122,111,120,104,116,102,114,98,111,86,111,86,118,87,129,89,146,89,148,92,176,93,178,95,179,95,183,93,186,59,186,55,189,53,189,51,187,51,185,49,184,48,188,45,191,45,194,46,200,50,203,50,210,53,210,55,211,61,210,64,208,67,208,69,211,69,213,72,216,72,218,73,223,76,224,78,223" |
"gh","125-50-255","Ghana","120,266,125,263,128,263,135,259,141,259,142,257,139,254,137,231,136,227,133,224,133,221,114,221,114,224,116,235,116,237,117,241,115,243,114,248,112,250,112,256,115,260,115,265,117,265" |
"0","255-255-255,"0","0","0","287,577,285,579,279,579,276,576,273,574,271,574,270,568,268,566,266,563,270,558,270,556,269,552,267,550,259,533,259,531,255,524,249,519,246,514,246,510,244,503,244,500,243,493,241,488,241,479,240,474,235,465,233,463,232,459,225,446,223,444,220,439,220,423,222,420,222,418,224,413,224,411,225,406,229,399,231,397,233,396,235,393,235,388,236,382,232,377,232,374,231,370,232,363,225,348,225,346,224,342,220,335,218,333,215,331,211,326,206,322,202,317,202,315,200,312,200,310,199,308,202,305,202,301,204,296,204,294,203,292,205,289,205,286,206,281,207,278,205,276,205,274,203,274,200,272,199,270,196,269,190,270,187,269,185,271,178,271,174,267,174,265,173,262,170,259,169,257,167,257,153,256,147,257,144,258,141,261,136,261,129,265,127,265,120,268,117,268,110,265,97,265,88,267,81,271,78,271,73,269,71,269,67,266,64,265,56,257,54,257,48,251,41,248,40,246,36,243,34,240,34,234,30,231,30,229,27,228,24,223,20,220,18,219,18,215,15,215,8,210,8,204,7,202,9,200,5,193,3,193,2,191,4,189,8,186,9,177,13,164,13,159,11,157,10,154,12,152,12,148,11,146,9,145,8,143,6,142,6,140,7,135,11,130,13,125,13,123,15,121,16,118,18,116,23,109,23,107,27,98,31,95,33,94,36,87,39,84,42,84,49,81,54,76,58,73,59,70,61,68,61,65,60,56,63,51,65,49,65,46,67,44,72,38,81,34,84,31,87,26,87,24,90,17,92,15,95,15,97,17,99,20,107,21,113,20,116,22,120,22,123,21,128,16,134,15,137,12,144,9,146,9,154,8,157,6,162,6,172,5,174,7,178,6,181,4,185,4,187,5,191,4,199,5,202,2,1,2,1,580,599,580,599,2,211,2,212,6,217,4,218,8,216,11,214,12,214,14,215,16,218,18,218,23,217,26,211,31,209,34,216,36,217,39,219,39,224,42,235,42,244,46,246,46,249,49,249,51,253,56,255,56,264,58,269,60,273,64,279,64,281,62,282,56,281,51,288,44,293,42,301,42,306,44,307,48,310,48,316,49,320,50,323,54,329,53,335,54,342,57,344,57,347,59,349,59,351,60,355,57,362,54,373,54,375,56,378,58,385,58,388,56,391,58,391,60,394,67,394,69,396,74,393,84,390,88,383,82,382,78,380,77,382,82,386,86,386,89,388,91,389,96,391,101,393,103,398,114,402,118,401,125,404,128,411,134,411,137,414,141,414,156,415,159,418,160,421,163,423,164,424,167,426,169,426,171,428,180,431,185,434,185,435,188,438,189,440,191,442,191,446,195,447,197,452,201,454,204,459,208,460,212,457,215,463,220,465,223,470,224,477,220,484,221,486,219,496,218,509,215,513,212,517,213,518,224,519,226,516,227,516,230,515,235,513,237,510,242,508,244,508,247,506,249,502,258,502,260,495,267,493,272,489,276,481,283,478,287,471,290,467,294,453,307,447,314,444,318,442,319,441,321,438,323,433,334,430,337,430,339,427,346,427,349,429,351,432,355,431,363,430,365,432,368,432,370,435,377,439,380,440,385,438,390,438,408,440,413,438,416,438,420,433,425,430,429,426,432,421,433,412,438,405,447,403,447,395,455,395,459,397,461,397,463,399,470,401,472,401,478,400,488,395,494,388,496,381,500,379,502,381,504,381,511,379,518,379,520,378,523,369,532,362,545,353,554,344,564,338,569,331,571,327,571,327,573,325,574,322,573,320,575,314,574,310,573,305,574,301,573,297,576" |
"0","255-255-255,"0","0","0","88,391,88,323,25,323,25,391" |
"za","255-25-25","Afrique du Sud","284,577,286,575,295,575,300,571,305,571,308,572,314,571,320,572,325,571,326,569,332,569,337,567,341,563,346,559,348,558,355,549,360,544,361,541,363,539,364,536,366,534,367,531,376,522,376,519,378,512,378,510,375,510,371,514,367,513,365,511,364,507,366,502,369,499,372,500,372,487,370,478,369,475,366,473,359,473,353,472,351,474,346,476,343,479,339,482,335,487,334,489,328,494,326,499,321,501,317,501,314,499,309,499,305,503,300,509,289,510,288,504,289,500,285,496,284,524,277,529,267,529,264,526,262,523,259,525,259,527,265,540,265,542,268,546,271,553,271,555,272,559,269,563,273,570,273,572,275,572,276,574,278,574,280,577" |
/trunk/composants/cartographie/squelettes/.directory |
---|
New file |
0,0 → 1,2 |
[Dolphin] |
Timestamp=2010,5,12,18,42,59 |
/trunk/composants/cartographie/squelettes/pays_oceanie.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_oceanie.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_europe.csv |
---|
New file |
0,0 → 1,0 |
"code","rvb_fond","nom","poly" |
/trunk/composants/cartographie/squelettes/continents.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/pays_moyen_orient.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = image/png |
/trunk/composants/cartographie/squelettes/pays_moyen_orient.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+image/png |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_nord_amerique.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/cartographie/squelettes/france.csv |
---|
New file |
0,0 → 1,108 |
"code","rvb_fond","nom","poly" |
"01","0-204-51","Ain","483,314, 486,313, 489,308, 489,305, 491,303, 493,302, 494,298, 495,296, 492,293, 487,299, 484,303, 478,303, 477,301, 474,301, 472,303, 468,304, 467,301, 463,298, 462,296, 460,295, 459,293, 456,291, 451,292, 448,291, 447,296, 445,299, 445,302, 443,309, 443,311, 444,313, 443,322, 446,323, 449,326, 450,329, 457,329, 459,331, 462,331, 465,326, 467,326, 468,329, 475,336, 476,339, 478,339, 482,332, 482,330, 484,321, 484,319" |
"02","240-240-255","Aisne","389,160, 394,155, 395,150, 393,147, 393,145, 396,142, 395,135, 400,132, 402,132, 404,130, 406,130, 408,131, 409,127, 408,125, 409,117, 415,112, 415,102, 414,98, 408,98, 406,95, 402,95, 395,94, 393,95, 391,94, 388,96, 384,95, 391,94, 388,96, 384,95, 379,96, 379,99, 377,102, 377,104, 375,105, 375,109, 376,113, 378,115, 378,117, 377,120, 378,124, 377,126, 379,129, 375,136, 373,136, 375,138, 375,140, 377,143, 377,147, 379,149, 379,152, 386,159" |
"03","255-125-125","Allier","393,320, 397,320, 400,323, 402,321, 404,321, 405,314, 404,305, 409,299, 409,294, 406,294, 403,292, 401,292, 399,290, 399,287, 398,284, 395,283, 392,285, 390,285, 388,283, 382,283, 380,284, 377,281, 373,278, 370,278, 367,282, 362,282, 359,285, 360,289, 356,293, 349,293, 347,296, 352,302, 355,304, 358,311, 358,314, 361,314, 364,310, 366,310, 368,307, 370,307, 372,312, 375,315, 377,315, 379,317, 387,317, 389,318, 391,317" |
"04","51-51-153","Alpes-de-Haute-Provence","491,443, 494,442, 496,444, 499,444, 500,442, 502,442, 505,438, 509,441, 512,441, 512,439, 519,438, 522,437, 523,434, 527,433, 528,431, 530,431, 528,428, 524,425, 524,422, 522,420, 522,415, 525,411, 525,409, 527,407, 530,406, 530,403, 531,401, 528,398, 528,396, 531,394, 531,390, 530,390, 526,394, 521,398, 520,402, 514,403, 512,401, 509,399, 503,404, 501,404, 500,402, 498,402, 492,408, 491,412, 488,413, 488,415, 489,417, 482,418, 482,420, 477,421, 476,425, 478,426, 478,429, 477,431, 479,433, 481,434, 480,438, 482,438, 484,440, 487,442, 489,442" |
"05","51-51-204","Hautes-Alpes","478,409, 480,412, 482,413, 482,417, 488,417, 487,413, 490,412, 490,410, 493,405, 498,401, 500,401, 501,403, 503,403, 504,401, 508,398, 515,402, 519,402, 519,400, 520,397, 529,390, 531,388, 535,387, 533,380, 527,379, 525,377, 523,376, 522,370, 520,370, 518,368, 518,365, 517,365, 512,367, 511,369, 506,369, 505,366, 502,367, 501,371, 505,371, 507,376, 507,380, 505,381, 503,380, 498,381, 495,383, 493,383, 492,386, 487,387, 487,390, 485,393, 483,393, 480,394, 479,398, 480,401, 478,403, 475,403, 473,402, 474,405, 472,406, 473,408, 475,408" |
"06","51-51-102","Alpes-Maritimes","550,442, 551,440, 553,440, 555,437, 557,436, 556,431, 558,430, 559,427, 562,425, 562,422, 564,420, 562,417, 562,414, 557,417, 554,417, 550,418, 546,417, 539,413, 535,413, 534,410, 532,409, 531,407, 529,407, 526,409, 526,411, 524,413, 523,420, 525,422, 525,425, 531,430, 530,432, 528,432, 527,434, 524,434, 523,440, 525,440, 528,442, 528,445, 529,447, 533,447, 534,450, 539,450, 542,449, 542,445, 547,441" |
"07","0-102-51","Ardèche","422,413, 424,413, 427,411, 431,411, 434,410, 438,413, 440,413, 441,407, 443,405, 443,399, 445,396, 445,388, 448,384, 448,381, 449,377, 447,374, 448,371, 446,369, 446,364, 445,362, 446,358, 443,356, 440,357, 439,361, 437,363, 434,363, 432,365, 432,368, 429,370, 429,372, 426,375, 424,378, 422,379, 421,382, 415,383, 413,386, 411,387, 410,395, 411,398, 413,399, 414,404, 417,406, 417,411, 420,411" |
"08","0-255-0","Ardennes","446,132, 448,131, 448,126, 447,123, 448,120, 451,118, 454,119, 456,117, 456,114, 451,113, 447,109, 444,107, 440,108, 439,103, 440,101, 439,99, 437,98, 437,95, 439,90, 439,88, 437,88, 433,92, 433,96, 428,98, 426,100, 423,100, 416,99, 416,112, 410,118, 410,124, 409,126, 410,129, 412,129, 416,130, 421,135, 426,137, 431,136, 432,138, 440,138, 445,137" |
"09","255-102-102","Ariège","311,508, 313,508, 314,506, 317,506, 322,507, 324,509, 329,510, 334,506, 336,506, 341,505, 340,503, 337,501, 332,501, 329,497, 330,495, 333,494, 333,484, 332,480, 327,478, 325,478, 323,476, 323,474, 319,474, 315,473, 315,475, 313,477, 310,475, 308,478, 306,478, 306,482, 304,484, 302,483, 301,481, 297,481, 295,482, 294,486, 295,488, 293,490, 291,490, 288,493, 289,496, 294,498, 297,498, 299,500, 299,502, 307,502, 310,504" |
"10","50-255-50","Aube","405,209, 407,211, 414,211, 418,210, 420,211, 422,209, 429,209, 432,205, 437,206, 437,203, 441,202, 442,197, 441,195, 442,192, 440,190, 434,183, 434,178, 431,178, 428,179, 423,176, 422,170, 419,169, 412,170, 411,172, 409,173, 408,175, 405,177, 405,179, 397,180, 395,178, 394,176, 391,176, 391,178, 388,181, 388,187, 390,187, 395,192, 395,197, 397,199, 399,199, 403,202, 403,204, 405,207" |
"11","102-51-0","Aude","371,493, 373,495, 375,495, 375,493, 377,492, 377,487, 378,485, 376,485, 374,483, 374,481, 376,480, 380,481, 383,477, 383,475, 380,475, 378,473, 376,473, 375,471, 369,471, 367,474, 364,474, 362,472, 359,472, 358,470, 356,469, 356,466, 357,464, 348,464, 344,463, 344,465, 337,466, 330,465, 328,464, 327,468, 324,469, 323,473, 324,476, 328,477, 333,480, 333,483, 334,494, 330,497, 333,500, 337,500, 339,502, 342,504, 343,506, 345,506, 346,504, 348,504, 349,497, 351,496, 356,497, 358,496, 365,497, 368,493" |
"12","255-153-0","Aveyron","358,442, 362,446, 366,447, 371,446, 372,448, 377,448, 377,442, 384,441, 384,439, 386,437, 388,437, 389,434, 392,431, 392,429, 389,428, 387,426, 387,423, 389,421, 384,420, 382,417, 380,416, 380,412, 379,405, 377,403, 377,399, 374,396, 372,396, 370,393, 370,390, 367,385, 364,383, 364,381, 361,382, 359,385, 359,387, 357,389, 357,392, 355,394, 354,396, 347,396, 345,395, 341,400, 339,400, 335,401, 331,405, 329,405, 329,408, 331,411, 331,413, 330,416, 333,416, 334,418, 332,419, 332,423, 331,425, 336,424, 338,422, 341,422, 342,424, 348,425, 352,429, 354,430, 358,439" |
"13","0-0-153","Bouches-du-Rhône","479,472, 480,474, 484,473, 488,469, 485,466, 485,463, 488,462, 485,458, 485,454, 484,450, 488,447, 488,445, 486,445, 483,446, 482,448, 475,448, 470,445, 468,445, 465,444, 462,445, 457,441, 454,437, 452,437, 450,435, 448,435, 446,433, 444,433, 441,437, 441,442, 438,447, 434,447, 433,452, 430,455, 426,458, 424,458, 424,462, 426,462, 427,460, 441,460, 443,462, 446,462, 447,464, 450,465, 450,463, 453,461, 455,461, 457,463, 458,466, 466,466, 468,464, 470,464, 472,467, 472,472" |
"14","150-150-255","Calvados","266,155, 271,155, 273,154, 277,155, 278,151, 277,146, 278,144, 276,139, 276,136, 274,135, 274,130, 271,129, 268,130, 264,134, 259,136, 253,136, 251,134, 245,133, 243,132, 241,133, 234,132, 230,129, 225,129, 223,136, 225,137, 229,142, 232,147, 232,151, 230,152, 228,155, 225,155, 225,158, 223,160, 223,162, 229,163, 232,164, 235,163, 237,161, 240,161, 245,159, 248,159, 255,160, 257,162, 262,158, 264,158" |
"15","255-175-175","Cantal","367,383, 371,390, 371,393, 372,395, 374,394, 374,392, 376,389, 376,386, 378,385, 378,383, 383,382, 385,379, 388,378, 386,375, 386,370, 384,368, 384,365, 380,361, 378,361, 374,360, 372,358, 371,356, 369,356, 364,355, 361,352, 357,352, 357,354, 355,356, 351,356, 348,359, 344,364, 345,367, 341,374, 342,376, 338,380, 338,383, 340,386, 340,393, 341,396, 343,397, 345,394, 354,395, 356,392, 356,389, 358,387, 358,385, 360,383, 361,381, 364,380, 365,383" |
"16","175-255-175","Charente","250,358, 253,361, 262,362, 264,360, 266,357, 266,353, 268,350, 271,348, 273,348, 276,344, 276,341, 280,338, 283,333, 285,331, 287,331, 290,324, 293,323, 293,319, 289,316, 289,314, 285,313, 283,315, 279,315, 278,313, 277,313, 276,316, 274,317, 272,316, 269,317, 268,315, 265,315, 261,314, 259,316, 257,317, 256,321, 254,322, 253,327, 252,330, 249,331, 246,330, 242,331, 241,340, 245,343, 245,345, 247,347, 247,353, 246,358" |
"17","150-255-150","Charente-Maritime","210,326, 211,322, 210,318, 208,318, 205,316, 205,320, 207,322, 209,323|204,306, 203,304, 200,304, 207,308, 208,307|254,367, 256,366, 256,363, 252,362, 250,359, 246,359, 245,354, 246,347, 244,345, 244,343, 242,341, 240,340, 240,332, 242,330, 245,330, 248,329, 251,330, 251,328, 253,321, 253,319, 242,313, 240,313, 238,311, 236,311, 233,309, 230,305, 230,302, 221,301, 220,299, 217,299, 214,302, 214,304, 212,307, 215,310, 217,317, 216,323, 213,326, 213,328, 210,329, 210,333, 212,335, 215,336, 219,340, 220,342, 227,348, 229,353, 229,355, 228,357, 233,356, 237,357, 239,359, 239,362, 240,365, 242,365, 246,368, 250,369, 252,368, 254,369" |
"18","125-255-255","Cher","343,297, 346,297, 349,292, 356,292, 359,289, 359,287, 358,285, 362,281, 367,281, 374,273, 374,262, 373,257, 371,252, 371,250, 369,248, 367,247, 367,244, 369,241, 368,238, 364,238, 362,239, 358,235, 354,235, 353,233, 351,233, 349,234, 347,232, 344,232, 342,233, 345,236, 346,242, 342,243, 341,249, 338,251, 333,251, 333,253, 329,257, 335,258, 338,260, 338,263, 340,265, 340,267, 339,269, 342,271, 341,273, 339,274, 339,276, 340,280, 342,282, 342,284, 344,286, 344,288, 343,291, 344,295" |
"19","255-255-150","Corrèze","330,378, 333,378, 336,377, 337,379, 339,377, 341,376, 340,373, 342,371, 344,366, 343,364, 347,360, 351,355, 355,355, 356,352, 355,348, 356,345, 354,343, 356,340, 356,336, 354,336, 350,337, 348,339, 341,336, 339,336, 336,335, 331,337, 331,339, 328,340, 326,342, 324,342, 321,345, 319,345, 315,346, 314,348, 310,351, 307,351, 305,352, 306,356, 304,357, 304,364, 305,367, 309,368, 311,371, 311,375, 313,375, 320,374, 324,378, 325,380" |
"2A","200-150-90","Corse-du-Sud","549,543, 555,548, 558,549, 558,545, 560,542, 560,539, 562,537, 562,534, 564,529, 564,526, 563,523, 558,524, 555,522, 555,516, 554,514, 552,514, 551,511, 547,507, 547,505, 544,504, 542,502, 538,499, 536,499, 531,497, 529,497, 530,499, 532,500, 531,502, 528,503, 528,505, 529,508, 533,509, 535,512, 535,514, 533,515, 533,517, 531,518, 531,520, 535,520, 537,519, 538,526, 536,527, 536,530, 538,530, 543,533, 540,537, 540,540, 542,540, 544,542" |
"2B","200-150-60","Haute-Corse","548,507, 552,511, 552,513, 554,513, 556,516, 556,522, 561,523, 563,522, 563,514, 564,510, 567,509, 568,506, 567,504, 568,502, 566,489, 566,487, 565,482, 563,482, 561,477, 561,470, 562,467, 560,460, 559,458, 556,458, 556,460, 557,462, 556,468, 557,473, 555,476, 552,474, 548,474, 546,476, 546,478, 543,480, 540,480, 538,482, 536,482, 536,484, 533,485, 531,487, 531,492, 528,493, 527,496, 532,496, 539,499, 541,501, 548,505" |
"21","50-151-255","Côte-d'Or","459,260, 461,258, 464,256, 466,251, 466,248, 467,246, 466,242, 464,240, 464,238, 462,236, 463,234, 466,232, 466,229, 465,227, 462,229, 457,229, 454,226, 450,226, 448,224, 446,223, 446,216, 444,214, 443,212, 439,209, 438,207, 434,207, 432,206, 430,208, 429,210, 422,210, 421,217, 423,218, 423,223, 421,225, 417,234, 415,236, 416,240, 415,245, 417,246, 417,250, 419,250, 421,252, 421,255, 423,258, 432,263, 435,266, 438,268, 443,267, 448,265, 450,265, 452,264, 455,265, 459,262" |
"22","75-75-255","Côtes-d'Armor","176,196, 178,195, 178,193, 182,189, 184,189, 188,188, 188,186, 190,177, 188,175, 186,175, 182,172, 180,171, 179,169, 176,167, 172,168, 170,170, 165,174, 162,174, 158,169, 158,166, 156,165, 154,162, 154,160, 152,159, 152,157, 150,157, 149,154, 146,155, 144,154, 137,157, 135,155, 133,155, 144,154, 137,157, 135,155, 133,155, 132,158, 133,160, 131,161, 131,163, 128,165, 128,168, 130,170, 130,173, 129,180, 130,185, 131,187, 130,189, 134,190, 136,192, 138,192, 142,193, 146,190, 148,190, 152,193, 154,193, 156,195, 158,195, 163,196, 164,199, 167,197, 169,194, 172,194, 173,196" |
"23","255-255-125","Creuse","338,334, 342,335, 347,338, 350,336, 353,336, 354,333, 352,331, 353,329, 355,328, 359,321, 359,318, 357,316, 357,310, 353,303, 347,298, 341,298, 336,299, 327,298, 325,300, 315,300, 312,307, 312,310, 316,314, 316,316, 317,320, 318,322, 317,324, 320,325, 321,329, 324,330, 326,329, 328,331, 331,333, 331,336" |
"24","102-153-102","Dordogne","295,395, 297,397, 299,397, 301,395, 302,393, 307,390, 307,387, 309,386, 312,382, 312,378, 310,375, 310,371, 309,369, 307,369, 304,367, 304,365, 303,357, 305,356, 305,354, 303,351, 300,350, 299,347, 297,346, 296,343, 292,342, 289,344, 285,341, 285,338, 283,338, 281,337, 277,342, 277,344, 274,347, 273,349, 271,349, 267,353, 267,358, 265,359, 262,363, 258,363, 256,368, 259,368, 260,373, 258,378, 258,383, 263,384, 266,382, 268,382, 269,385, 268,388, 270,389, 271,394, 273,394, 278,393, 281,392, 287,393, 289,392, 290,396" |
"25","255-255-50","Doubs","493,279, 494,282, 501,276, 505,273, 505,263, 510,260, 512,260, 514,257, 514,255, 517,252, 519,251, 523,246, 523,244, 525,242, 526,239, 524,239, 522,240, 521,237, 523,235, 523,232, 521,232, 517,231, 513,228, 512,228, 510,231, 508,231, 508,233, 504,234, 499,233, 495,238, 490,241, 488,243, 484,243, 480,246, 478,247, 478,250, 481,253, 481,257, 480,259, 482,261, 486,262, 488,264, 488,267, 491,270, 496,273, 497,275" |
"26","1-51-51","Drôme","481,420, 481,413, 477,410, 473,409, 471,407, 473,404, 472,402, 474,401, 478,402, 479,399, 478,395, 480,393, 482,393, 485,392, 482,390, 480,390, 476,386, 473,386, 471,383, 471,381, 472,378, 471,376, 472,373, 463,372, 460,371, 459,369, 460,367, 459,362, 453,357, 448,359, 452,357, 449,359, 447,359, 447,361, 446,363, 447,369, 449,371, 448,374, 450,377, 450,380, 449,384, 447,386, 446,396, 444,399, 444,405, 442,407, 442,412, 445,412, 446,416, 449,416, 454,413, 457,413, 461,417, 466,417, 469,420, 471,420, 472,422, 475,422, 477,420" |
"27","204-255-0","Eure","318,149, 321,149, 323,146, 323,143, 326,142, 326,140, 328,139, 328,137, 327,133, 323,132, 321,130, 318,130, 312,129, 309,134, 307,134, 305,135, 304,137, 302,137, 301,139, 299,139, 298,137, 296,136, 296,132, 287,131, 282,127, 281,127, 276,129, 274,128, 275,135, 277,136, 277,140, 279,145, 278,150, 279,154, 277,156, 279,158, 284,158, 287,163, 291,166, 291,170, 293,170, 306,166, 311,166, 312,163, 316,159, 315,157, 317,156, 317,154, 316,151" |
"28","0-255-255","Eure-et-Loir","308,211, 312,211, 314,210, 315,208, 321,208, 323,206, 327,206, 331,205, 332,201, 334,199, 334,193, 333,187, 332,185, 330,185, 326,181, 326,178, 321,174, 321,172, 319,161, 316,157, 317,160, 313,163, 313,165, 311,167, 304,167, 297,170, 294,170, 292,171, 292,173, 296,178, 296,186, 293,189, 291,189, 290,194, 291,198, 294,199, 299,204, 303,204, 304,207, 307,209" |
"29","25-25-255","Finistère","103,210, 105,209, 105,207, 109,206, 112,207, 113,205, 116,207, 116,209, 117,211, 122,211, 127,213, 129,213, 132,208, 134,206, 131,205, 129,203, 126,203, 124,201, 124,199, 122,197, 122,194, 124,192, 126,192, 129,190, 129,188, 130,186, 128,177, 128,174, 129,170, 127,168, 127,165, 128,163, 125,161, 122,161, 120,164, 115,160, 109,161, 107,163, 105,163, 103,161, 100,162, 98,164, 95,163, 94,165, 89,166, 86,169, 86,178, 89,177, 92,178, 97,176, 98,179, 102,179, 103,181, 101,183, 99,183, 93,182, 93,186, 95,184, 99,185, 102,187, 102,189, 103,192, 93,193, 87,194, 90,196, 92,196, 97,200, 99,205, 99,207, 98,209, 101,209" |
"30","255-204-0","Gard","423,452, 421,454, 419,454, 422,459, 424,457, 426,457, 429,454, 432,452, 432,449, 434,446, 438,446, 440,441, 440,437, 443,434, 444,432, 446,431, 446,427, 443,426, 442,420, 440,418, 440,415, 436,412, 432,411, 427,412, 424,414, 422,414, 420,412, 417,412, 416,409, 414,408, 411,409, 411,411, 413,420, 410,424, 407,424, 404,422, 401,422, 399,425, 393,425, 392,423, 389,422, 388,426, 393,429, 393,431, 390,434, 390,436, 393,439, 395,439, 398,440, 401,436, 404,434, 406,434, 408,436, 408,439, 411,438, 415,442, 420,446, 422,447, 422,449" |
"31","204-102-102","Haute-Garonne","280,495, 283,494, 286,495, 287,492, 291,489, 293,489, 294,487, 293,484, 295,481, 301,480, 304,483, 305,478, 308,477, 310,474, 313,476, 314,473, 318,472, 322,473, 322,470, 324,468, 326,468, 326,466, 328,463, 334,464, 334,462, 329,459, 327,456, 322,454, 321,452, 322,448, 320,446, 317,442, 316,439, 314,439, 309,440, 309,443, 306,445, 303,445, 301,443, 298,443, 294,444, 301,453, 303,454, 303,456, 297,461, 297,463, 295,465, 294,467, 292,467, 289,465, 287,465, 278,473, 278,475, 274,478, 274,480, 277,483, 277,485, 279,486, 279,491, 276,495, 273,495, 273,502, 277,502, 280,503, 280,501, 279,498" |
"32","204-153-51","Gers","269,467, 270,469, 275,468, 278,470, 280,470, 282,468, 283,466, 288,464, 293,466, 296,463, 296,461, 298,458, 302,455, 297,450, 292,444, 290,443, 289,437, 285,436, 284,434, 287,431, 282,429, 280,430, 278,428, 273,428, 272,430, 270,430, 264,431, 262,430, 260,432, 257,432, 258,435, 254,436, 252,434, 247,435, 247,440, 246,445, 244,448, 244,451, 246,451, 251,453, 253,453, 256,457, 259,459, 259,463, 262,466, 266,466" |
"33","153-204-153","Gironde","256,405, 255,403, 257,400, 259,400, 261,397, 261,391, 263,389, 267,389, 267,386, 268,383, 266,383, 263,385, 259,385, 257,383, 257,377, 259,372, 259,369, 255,369, 253,370, 251,369, 248,370, 244,367, 240,366, 238,361, 238,359, 231,357, 230,362, 231,367, 232,369, 230,370, 227,363, 227,358, 224,353, 214,343, 212,352, 212,357, 209,376, 209,378, 208,384, 207,388, 209,388, 210,386, 215,390, 214,392, 209,392, 206,399, 206,402, 215,400, 217,401, 217,404, 219,404, 223,405, 230,404, 231,407, 236,411, 238,411, 240,414, 240,418, 243,418, 245,419, 245,416, 248,415, 249,417, 253,417, 254,414, 253,412, 256,409" |
"34","204-153-0","Hérault","369,470, 375,470, 376,472, 378,472, 380,474, 383,474, 385,475, 389,471, 396,470, 398,468, 398,464, 401,461, 403,461, 407,462, 410,457, 412,455, 414,455, 415,453, 422,452, 422,450, 421,447, 416,443, 414,443, 411,439, 408,440, 407,436, 404,435, 402,437, 399,439, 398,441, 396,441, 392,440, 390,437, 386,438, 385,441, 378,442, 378,448, 372,449, 371,451, 364,453, 361,452, 360,456, 362,458, 362,461, 359,463, 359,465, 357,466, 357,469, 360,471, 362,471, 364,473, 367,473" |
"35","100-100-255","Ille-et-Vilaine","197,219, 199,216, 203,215, 204,213, 208,213, 210,215, 213,215, 213,212, 215,210, 215,208, 218,205, 220,205, 220,202, 218,193, 218,190, 220,188, 220,184, 219,178, 215,177, 213,179, 210,181, 204,176, 204,171, 199,171, 195,170, 194,166, 191,166, 188,169, 183,169, 183,171, 184,173, 188,174, 191,177, 191,180, 190,185, 189,188, 182,190, 179,193, 179,195, 177,196, 177,198, 178,200, 176,203, 176,205, 179,205, 183,208, 183,210, 182,214, 183,216, 181,221, 182,222, 185,220, 188,220" |
"36","100-255-255","Indre","327,297, 335,297, 340,298, 342,297, 343,292, 342,289, 343,286, 341,284, 341,282, 339,280, 339,277, 338,274, 341,271, 338,269, 339,265, 337,263, 337,260, 329,259, 328,257, 330,255, 330,253, 326,252, 323,250, 321,250, 318,252, 314,252, 311,255, 313,258, 307,265, 302,264, 300,269, 300,272, 297,279, 292,280, 292,283, 291,285, 297,291, 299,291, 300,294, 304,298, 304,300, 308,300, 310,299, 312,301, 315,299, 325,299" |
"37","75-255-255","Indre-et-Loire","288,274, 288,276, 291,279, 296,279, 299,272, 299,268, 300,265, 302,263, 305,263, 307,264, 312,259, 309,252, 306,249, 303,249, 302,246, 303,243, 300,233, 297,230, 294,230, 292,227, 290,227, 283,225, 282,227, 279,227, 277,229, 275,230, 274,232, 271,230, 268,230, 266,231, 268,234, 265,244, 262,247, 262,253, 261,257, 263,257, 264,260, 268,261, 269,267, 276,268, 281,267, 283,266, 285,271" |
"38","51-102-102","Isère","483,389, 487,386, 491,386, 492,383, 499,380, 502,380, 504,379, 506,380, 506,375, 505,372, 501,372, 500,369, 502,364, 500,364, 497,360, 497,357, 499,355, 499,351, 496,348, 492,348, 491,346, 489,347, 487,350, 485,350, 482,349, 480,344, 475,339, 474,336, 467,329, 467,327, 465,327, 464,330, 462,332, 459,332, 457,331, 457,333, 458,336, 455,339, 454,341, 449,342, 447,344, 447,347, 444,350, 444,354, 445,357, 449,357, 451,355, 453,355, 457,358, 459,359, 461,362, 461,366, 462,369, 471,370, 474,373, 474,375, 473,377, 474,381, 473,383, 476,384, 478,386" |
"39","255-255-75","Jura","477,300, 478,302, 484,302, 486,300, 490,295, 492,292, 492,290, 494,287, 493,281, 492,279, 496,275, 495,273, 492,271, 490,271, 487,267, 487,264, 482,262, 479,259, 480,253, 477,250, 476,248, 473,249, 467,248, 467,252, 464,257, 462,259, 460,260, 460,262, 459,265, 466,270, 463,274, 466,281, 466,284, 464,286, 464,288, 466,290, 466,292, 462,293, 462,295, 464,297, 466,300, 468,301, 468,303, 470,303, 474,300" |
"40","153-255-153","Landes","246,435, 250,434, 252,433, 254,435, 257,435, 256,432, 260,425, 257,424, 255,422, 251,422, 250,418, 247,416, 246,419, 244,420, 240,419, 239,414, 238,412, 236,412, 233,409, 230,407, 230,405, 224,405, 220,406, 217,405, 216,401, 212,401, 208,402, 206,403, 206,405, 203,421, 200,431, 200,433, 197,443, 197,445, 194,452, 197,455, 203,455, 205,453, 207,453, 209,455, 212,455, 219,453, 221,453, 223,452, 232,454, 239,453, 241,454, 243,452, 243,448, 245,445, 245,441" |
"41","50-255-255","Loir-et-Cher","340,244, 342,242, 345,242, 345,238, 343,235, 341,234, 342,232, 344,231, 344,228, 336,228, 334,229, 331,228, 327,229, 326,226, 324,224, 321,224, 319,222, 318,219, 320,218, 320,215, 318,213, 318,209, 315,209, 314,211, 308,212, 306,210, 305,208, 303,207, 303,205, 299,205, 295,201, 293,201, 291,204, 291,206, 292,208, 291,213, 289,215, 289,218, 286,221, 283,223, 286,225, 289,225, 292,226, 295,229, 297,229, 301,233, 301,235, 303,240, 303,242, 304,245, 303,248, 306,248, 311,253, 314,251, 318,251, 321,249, 323,249, 326,251, 329,251, 331,253, 333,250, 338,250, 340,249" |
"42","0-153-51","Loire","443,352, 442,350, 440,350, 438,348, 438,346, 435,344, 432,344, 427,339, 427,335, 426,332, 427,328, 425,326, 425,324, 424,320, 422,318, 424,315, 424,313, 427,311, 425,310, 421,311, 414,310, 411,311, 410,309, 408,309, 405,306, 405,313, 406,320, 404,322, 402,322, 403,329, 402,331, 405,335, 405,338, 412,344, 412,346, 413,350, 410,353, 412,353, 416,354, 418,352, 422,352, 426,353, 428,355, 428,358, 430,358, 434,362, 437,362, 439,357, 442,356" |
"43","255-200-200","Haute-Loire","399,384, 403,385, 408,390, 410,388, 412,385, 414,384, 415,382, 420,382, 421,379, 423,377, 425,376, 425,374, 428,372, 428,370, 431,367, 431,365, 432,361, 430,359, 428,359, 427,355, 420,353, 418,353, 416,355, 413,355, 408,354, 403,356, 401,355, 398,356, 397,354, 384,354, 381,357, 378,357, 378,360, 380,360, 383,362, 385,365, 385,368, 387,370, 387,375, 389,377, 390,381, 393,385, 397,385" |
"44","255-0-0","Loire-Atlantique","173,260, 174,258, 173,258|201,261, 203,260, 204,265, 207,265, 207,263, 209,260, 211,260, 213,259, 215,260, 215,258, 213,258, 212,254, 215,253, 215,250, 213,250, 211,248, 211,246, 210,244, 212,242, 216,242, 224,241, 223,237, 218,236, 216,234, 216,231, 219,230, 216,227, 216,224, 213,221, 213,216, 210,216, 208,214, 204,214, 203,216, 201,216, 198,218, 197,220, 189,220, 185,221, 180,224, 180,228, 178,230, 173,230, 169,234, 167,235, 166,243, 171,243, 173,246, 176,245, 178,242, 182,242, 183,244, 179,245, 178,247, 179,249, 176,252, 180,253, 183,255, 184,259, 190,265, 192,265, 194,268, 198,268, 201,269, 202,265" |
"45","25-255-255","Loiret","353,232, 354,234, 358,234, 362,238, 367,237, 367,234, 369,232, 369,228, 368,226, 366,225, 366,222, 370,221, 372,220, 372,214, 376,210, 375,207, 373,205, 373,203, 371,201, 363,201, 361,203, 355,202, 353,200, 353,196, 350,194, 350,192, 345,192, 342,191, 340,193, 337,193, 335,194, 335,199, 333,201, 333,204, 331,206, 328,206, 323,207, 321,209, 319,209, 319,213, 321,215, 321,218, 319,219, 320,222, 324,223, 327,226, 327,228, 330,228, 333,227, 335,228, 344,227, 345,231, 347,231, 349,233" |
"46","204-102-0","Lot","328,413, 330,413, 330,411, 328,408, 328,405, 331,404, 335,400, 338,400, 341,399, 341,397, 339,392, 339,386, 337,383, 337,380, 336,378, 334,378, 329,379, 326,381, 323,379, 320,375, 314,375, 312,376, 313,382, 308,388, 308,390, 306,392, 300,397, 298,400, 296,401, 296,405, 298,407, 298,410, 299,413, 302,416, 305,417, 306,419, 311,419, 314,417, 317,419, 324,415, 326,415" |
"47","204-255-204","Lot-et-Garonne","280,429, 284,426, 285,424, 290,423, 290,420, 292,415, 290,413, 291,411, 293,411, 297,410, 297,407, 295,405, 295,401, 298,398, 295,396, 292,396, 290,397, 289,393, 282,394, 279,393, 274,394, 271,395, 270,391, 269,389, 263,390, 262,398, 260,399, 259,401, 257,401, 256,404, 257,410, 254,412, 255,416, 253,418, 251,419, 251,421, 255,421, 257,423, 260,423, 261,425, 258,430, 260,431, 262,429, 269,430, 272,429, 273,427, 278,427" |
"48","153-102-0","Lozère","407,423, 410,423, 412,420, 412,417, 411,412, 410,409, 415,407, 416,406, 413,404, 413,401, 412,399, 410,398, 410,396, 409,392, 403,386, 400,386, 398,385, 392,386, 389,381, 389,379, 387,379, 384,381, 383,383, 379,383, 379,385, 377,386, 377,389, 375,392, 375,396, 378,399, 378,403, 380,405, 380,411, 381,416, 385,419, 389,419, 391,422, 394,424, 399,424, 401,421, 404,421" |
"49","255-100-100","Maine-et-Loire","244,259, 249,257, 252,259, 255,259, 258,258, 258,255, 261,253, 261,247, 264,244, 265,239, 267,234, 263,230, 258,227, 254,228, 253,225, 248,224, 247,221, 244,221, 241,220, 238,222, 234,222, 231,220, 228,220, 225,219, 221,220, 214,219, 214,221, 217,224, 217,227, 220,229, 219,231, 217,231, 217,234, 222,235, 224,237, 224,239, 225,241, 217,242, 212,243, 211,245, 212,248, 215,249, 216,253, 213,254, 213,257, 215,257, 216,261, 231,263, 235,263, 238,261, 239,259" |
"50","125-125-255","Manche","224,164, 222,162, 222,160, 224,158, 224,155, 228,154, 231,150, 231,147, 226,140, 222,136, 222,134, 223,131, 221,130, 221,127, 219,125, 217,122, 217,118, 219,117, 219,114, 218,112, 213,111, 209,114, 205,114, 202,112, 200,112, 195,109, 194,109, 198,114, 198,117, 196,119, 198,124, 198,128, 201,130, 205,139, 205,149, 206,156, 205,164, 207,166, 207,168, 204,169, 205,176, 209,180, 216,176, 222,176, 231,178, 235,174, 234,171, 235,169, 233,168, 232,165, 230,165" |
"51","25-255-25","Marne","423,175, 428,178, 430,178, 434,177, 438,178, 437,175, 440,173, 443,169, 446,170, 446,166, 442,162, 442,157, 446,154, 446,150, 445,146, 443,143, 443,141, 444,139, 441,138, 432,139, 431,137, 427,137, 425,138, 420,135, 416,131, 413,131, 410,130, 408,132, 404,131, 402,133, 400,133, 396,136, 396,140, 397,143, 394,145, 394,147, 396,150, 396,153, 390,160, 390,162, 388,164, 388,167, 390,169, 389,172, 391,172, 396,177, 397,179, 404,179, 404,177, 412,169, 418,169, 421,168, 423,170" |
"52","75-255-75","Haute-Marne","472,223, 472,221, 473,219, 472,217, 476,213, 479,211, 479,208, 476,206, 476,204, 472,201, 472,197, 474,196, 472,193, 470,192, 469,190, 465,187, 463,187, 462,182, 461,180, 458,178, 455,178, 452,175, 449,174, 448,172, 446,172, 443,170, 441,172, 438,176, 439,178, 435,179, 435,183, 441,189, 443,192, 443,194, 442,196, 443,201, 441,203, 438,203, 438,206, 440,208, 442,211, 445,213, 447,216, 447,223, 450,225, 454,225, 457,228, 462,228, 463,225, 468,223" |
"53","255-75-75","Mayenne","224,219, 227,218, 231,219, 234,221, 238,221, 241,219, 243,219, 246,220, 246,217, 247,213, 250,211, 250,206, 253,205, 253,199, 256,197, 256,190, 259,187, 259,182, 257,182, 255,180, 255,177, 252,176, 251,178, 249,178, 246,179, 244,178, 241,180, 234,180, 232,181, 230,179, 228,179, 220,178, 220,183, 221,188, 219,190, 219,196, 221,205, 218,206, 216,208, 216,210, 214,212, 214,217, 220,218" |
"54","102-0-102","Meurthe-et-Moselle","508,179, 509,181, 512,181, 514,182, 517,181, 523,175, 521,172, 514,169, 512,169, 509,168, 507,166, 504,164, 502,164, 498,163, 494,159, 494,155, 492,155, 488,154, 484,151, 483,149, 480,147, 482,144, 482,140, 483,137, 479,130, 479,125, 480,123, 477,123, 473,120, 468,121, 463,123, 462,127, 469,128, 471,130, 472,139, 473,144, 476,146, 476,153, 474,156, 474,159, 475,163, 473,165, 473,167, 474,173, 473,176, 476,178, 480,178, 481,183, 484,186, 486,186, 489,185, 490,183, 493,183, 496,182, 499,183, 504,181, 506,179" |
"55","153-0-153","Meuse","465,185, 465,183, 469,182, 471,180, 474,180, 474,177, 472,176, 472,174, 473,168, 472,165, 474,163, 474,160, 473,156, 475,153, 475,146, 472,144, 472,140, 471,132, 469,129, 464,129, 461,127, 461,120, 460,117, 459,117, 458,119, 454,122, 449,121, 448,125, 449,131, 447,132, 447,136, 445,138, 445,140, 444,143, 446,146, 446,149, 447,155, 443,158, 443,162, 447,166, 447,171, 454,176, 458,177, 460,179, 462,180, 463,186" |
"56","50-50-255","Morbihan","144,241, 140,237, 138,237, 138,240, 141,240|173,229, 178,229, 180,220, 182,215, 181,211, 182,208, 179,206, 176,206, 175,203, 177,200, 176,197, 173,197, 172,195, 169,195, 165,200, 163,199, 163,197, 159,197, 156,196, 154,194, 151,194, 150,192, 146,191, 144,193, 139,194, 136,193, 134,191, 132,191, 130,190, 128,192, 124,193, 123,197, 125,199, 125,201, 129,202, 131,204, 134,204, 135,206, 131,211, 130,216, 135,217, 144,225, 148,225, 150,226, 154,223, 156,223, 158,225, 158,227, 154,228, 154,230, 156,231, 160,230, 162,229, 164,230, 166,229, 169,231, 171,231" |
"57","204-0-204","Moselle","519,155, 518,151, 520,150, 520,147, 522,146, 523,148, 527,148, 529,150, 533,150, 535,149, 539,150, 541,143, 535,138, 532,138, 531,140, 524,141, 520,140, 518,137, 514,137, 512,140, 509,140, 507,138, 505,133, 502,131, 503,128, 498,124, 494,124, 492,122, 487,122, 485,124, 480,125, 480,131, 482,133, 484,138, 483,145, 481,146, 482,148, 485,150, 486,152, 491,153, 494,154, 495,159, 498,162, 501,162, 504,163, 506,165, 509,167, 511,167, 522,172, 525,175, 529,171, 529,169, 530,166, 529,163, 530,160, 528,157, 522,156" |
"58","100-151-255","Nièvre","414,273, 415,271, 413,269, 414,266, 413,264, 414,259, 417,256, 420,255, 420,252, 417,251, 416,246, 411,246, 410,244, 405,244, 401,243, 398,240, 396,240, 395,237, 392,236, 385,238, 382,238, 377,233, 373,234, 369,233, 368,237, 370,240, 368,245, 368,247, 372,250, 372,253, 374,258, 374,261, 375,274, 373,275, 373,277, 376,280, 380,283, 388,282, 390,284, 392,284, 395,282, 396,280, 399,280, 403,279, 404,281, 406,281, 413,277" |
"59","153-153-51","Nord","413,89, 413,85, 415,83, 414,81, 411,80, 409,78, 406,78, 404,79, 399,78, 398,80, 396,79, 395,76, 396,73, 393,70, 391,70, 390,68, 387,70, 383,69, 382,65, 380,62, 381,59, 378,57, 378,55, 376,54, 372,55, 370,56, 369,58, 366,58, 363,55, 361,52, 359,52, 358,44, 356,42, 356,39, 353,38, 347,39, 342,41, 341,43, 344,48, 344,51, 347,54, 350,54, 351,60, 354,62, 356,62, 364,63, 366,65, 367,68, 371,69, 374,71, 375,75, 374,78, 375,82, 378,83, 379,88, 376,95, 383,95, 386,94, 388,95, 391,93, 394,94, 401,93, 406,94, 409,97, 413,97, 413,95, 416,93, 415,90" |
"60","225-225-255","Oise","372,137, 373,135, 375,135, 376,131, 378,130, 376,125, 377,121, 376,118, 377,115, 371,115, 369,117, 365,117, 365,119, 360,121, 358,121, 356,119, 354,119, 351,117, 349,117, 346,115, 342,115, 339,116, 336,114, 332,114, 330,112, 327,112, 327,115, 326,121, 327,124, 329,125, 327,128, 327,132, 329,139, 327,140, 327,142, 333,143, 338,141, 341,143, 345,143, 348,145, 350,145, 354,146, 356,148, 359,147, 361,149, 363,149, 371,148, 376,146, 376,143, 374,140, 374,138" |
"61","175-175-255","Orne","259,181, 260,184, 264,184, 266,181, 268,181, 272,180, 275,182, 276,189, 279,190, 282,193, 286,193, 289,196, 290,195, 289,190, 291,188, 293,188, 295,186, 295,178, 292,175, 290,170, 290,166, 288,164, 285,162, 284,159, 279,159, 275,156, 272,155, 266,156, 264,159, 262,159, 258,163, 255,161, 249,161, 244,160, 239,162, 237,162, 235,164, 233,164, 233,166, 234,168, 236,169, 235,172, 236,174, 234,177, 232,178, 232,180, 241,179, 244,177, 248,178, 251,177, 252,175, 254,175, 256,177, 256,180" |
"62","102-102-51","Pas-de-Calais","361,89, 368,94, 371,94, 376,93, 378,86, 378,84, 375,83, 373,76, 374,73, 373,71, 369,70, 366,68, 365,65, 357,64, 352,62, 350,60, 350,55, 346,55, 343,51, 343,48, 340,43, 334,42, 332,44, 330,44, 324,49, 322,49, 323,56, 322,73, 321,77, 326,77, 329,76, 331,78, 334,78, 335,81, 338,82, 339,85, 344,85, 347,83, 350,83, 354,86, 353,89, 356,89, 359,91" |
"63","255-150-150","Puy-de-Dôme","397,353, 398,355, 400,355, 402,354, 404,355, 407,354, 410,351, 412,350, 412,347, 411,344, 407,340, 404,338, 404,335, 402,333, 401,330, 402,324, 399,324, 397,321, 393,321, 390,318, 388,319, 379,318, 377,316, 375,316, 371,312, 370,308, 368,308, 366,311, 364,311, 362,313, 361,315, 358,315, 360,318, 360,322, 358,324, 355,329, 353,330, 357,337, 357,340, 355,342, 357,345, 357,347, 356,350, 362,351, 364,354, 368,354, 371,355, 373,357, 374,359, 376,359, 378,356, 381,356, 384,353" |
"64","102-255-102","Pyrénées-Atlantiques","198,481, 199,479, 203,480, 206,482, 208,482, 210,484, 212,484, 213,486, 222,486, 223,490, 230,496, 237,495, 239,493, 239,490, 240,486, 243,483, 245,482, 245,479, 247,478, 251,473, 252,470, 251,468, 254,467, 253,461, 251,458, 251,456, 248,453, 244,452, 241,455, 233,454, 229,455, 224,454, 222,453, 209,456, 207,454, 205,454, 203,456, 197,456, 195,454, 192,455, 190,457, 186,462, 182,462, 183,465, 187,465, 188,468, 190,468, 191,466, 194,468, 196,468, 198,470, 198,472, 194,479, 196,481" |
"65","153-102-51","Hautes-Pyrénées","265,503, 267,501, 270,503, 272,503, 272,495, 276,494, 278,491, 278,486, 276,485, 276,483, 273,480, 273,478, 277,474, 278,471, 275,469, 272,469, 270,470, 269,468, 267,468, 261,467, 258,463, 258,459, 252,454, 251,454, 252,458, 254,461, 254,465, 255,467, 252,468, 253,471, 250,476, 248,477, 246,480, 246,482, 244,484, 241,486, 241,489, 240,493, 239,495, 243,495, 246,496, 251,502, 255,502, 259,501, 261,500" |
"66","51-51-0","Pyrénées-Orientales","338,522, 340,519, 343,519, 347,518, 350,520, 352,520, 355,523, 357,523, 359,522, 362,523, 362,520, 367,519, 370,516, 377,516, 379,518, 382,518, 382,516, 381,514, 378,513, 377,497, 373,496, 371,494, 368,494, 365,498, 359,498, 357,497, 352,498, 350,497, 350,503, 348,505, 346,505, 345,507, 343,507, 337,506, 334,507, 333,509, 328,511, 326,511, 324,512, 324,514, 328,515, 330,517, 332,517, 334,522" |
"67","204-204-51","Bas-Rhin","532,186, 536,190, 538,190, 540,192, 540,194, 544,195, 544,193, 547,188, 547,185, 548,178, 550,173, 550,168, 555,162, 555,160, 557,159, 558,157, 560,156, 560,152, 563,148, 560,147, 558,145, 556,145, 542,144, 541,149, 539,151, 536,151, 534,150, 529,151, 527,149, 523,149, 522,147, 521,150, 519,151, 519,154, 521,154, 526,155, 530,158, 531,162, 530,165, 531,168, 530,171, 526,175, 525,184, 527,184, 530,186" |
"68","153-153-0","Haut-Rhin","544,229, 544,227, 545,223, 542,220, 543,209, 545,204, 545,202, 543,200, 543,196, 540,195, 539,192, 536,191, 532,187, 528,187, 525,194, 525,198, 523,200, 523,202, 520,205, 520,209, 518,216, 521,217, 528,230, 528,233, 530,233, 539,234" |
"69","0-255-51","Rhône","446,344, 449,341, 452,341, 454,340, 457,336, 457,334, 456,330, 450,330, 447,325, 444,324, 442,322, 442,314, 443,312, 442,310, 440,310, 438,308, 438,306, 431,305, 428,304, 428,307, 427,309, 428,311, 425,314, 423,317, 425,320, 425,323, 426,326, 428,328, 428,331, 427,334, 428,339, 432,343, 435,343, 439,346, 439,348, 444,349, 446,347" |
"70","255-255-0","Haute-Saône","488,242, 491,239, 496,236, 499,232, 503,232, 507,233, 507,231, 510,230, 512,227, 514,227, 515,222, 514,217, 515,215, 511,212, 508,211, 507,209, 505,211, 501,211, 499,208, 492,210, 489,208, 489,206, 486,206, 486,208, 483,210, 480,210, 477,214, 473,217, 474,220, 473,223, 467,224, 464,225, 466,227, 467,232, 463,236, 465,238, 465,240, 467,242, 467,245, 468,247, 472,247, 474,248, 477,247, 479,245, 484,242" |
"71","150-151-255","Saône-et-Loire","437,304, 439,306, 439,308, 442,309, 442,307, 447,291, 450,290, 454,291, 457,290, 458,292, 461,293, 465,292, 465,290, 463,288, 463,286, 465,284, 465,281, 462,274, 463,272, 465,271, 464,269, 462,269, 460,266, 456,265, 453,266, 451,265, 438,269, 436,267, 429,262, 426,261, 424,259, 420,256, 419,256, 415,259, 415,263, 414,265, 415,267, 414,269, 416,271, 415,277, 412,278, 410,280, 405,282, 402,280, 400,280, 397,281, 399,284, 399,286, 400,290, 407,293, 409,293, 410,299, 406,303, 406,305, 408,308, 410,308, 411,310, 413,310, 420,309, 424,310, 426,309, 427,304, 430,303" |
"72","255-25-25","Sarthe","271,229, 273,231, 276,228, 279,226, 282,226, 282,223, 285,220, 288,218, 288,215, 290,213, 290,209, 291,207, 290,204, 292,201, 290,198, 287,196, 286,194, 281,194, 279,191, 276,190, 275,184, 274,182, 269,181, 266,182, 264,185, 260,186, 257,190, 257,197, 254,200, 254,205, 251,206, 251,211, 248,214, 248,216, 247,220, 248,223, 252,223, 254,225, 254,227, 256,227, 258,226, 261,227, 262,229, 267,230" |
"73","51-153-153","Savoie","500,355, 498,357, 498,360, 501,363, 506,366, 506,368, 511,368, 512,366, 525,362, 527,362, 530,360, 531,358, 534,358, 536,356, 536,351, 538,348, 534,344, 531,343, 529,334, 527,334, 521,329, 518,329, 514,328, 512,326, 512,322, 510,322, 508,324, 508,327, 505,330, 505,332, 502,335, 499,333, 498,331, 496,331, 492,332, 490,330, 489,328, 485,325, 483,332, 483,334, 481,335, 480,338, 478,340, 481,344, 482,348, 484,348, 487,349, 490,345, 493,347, 496,347, 500,351" |
"74","0-204-204","Haute-Savoie","504,330, 507,327, 507,324, 510,321, 512,321, 513,326, 520,328, 522,327, 522,325, 525,323, 527,323, 529,321, 530,316, 526,312, 524,311, 524,308, 521,308, 520,306, 522,299, 519,296, 520,293, 508,293, 506,296, 501,296, 500,302, 501,304, 495,309, 489,309, 489,311, 487,312, 486,314, 484,314, 484,318, 485,323, 487,325, 488,327, 491,329, 492,331, 495,331, 498,330, 500,332, 501,334, 504,332" |
"75","199-255-175","Paris","350,164, 351,161, 348,160, 346,161, 346,163|503,55, 507,52, 509,52, 512,54, 515,54, 516,50, 512,49, 510,46, 510,43, 507,39, 507,36, 495,36, 493,38, 489,41, 487,41, 484,42, 482,44, 482,47, 485,48, 487,51, 490,51, 497,55" |
"76","204-204-0","Seine-Maritime","300,138, 302,136, 304,136, 305,134, 309,133, 310,130, 312,128, 317,128, 321,129, 323,131, 326,131, 326,128, 328,126, 326,124, 326,122, 325,116, 326,112, 327,110, 325,103, 323,101, 318,97, 315,95, 313,95, 307,100, 302,102, 299,102, 292,105, 286,105, 282,108, 279,109, 277,111, 273,112, 270,114, 270,117, 267,122, 267,124, 270,124, 273,126, 275,126, 276,128, 283,126, 285,129, 294,130, 297,132, 297,136" |
"77","199-255-75","Seine-et-Marne","371,198, 373,196, 373,190, 376,188, 385,188, 387,187, 387,181, 390,178, 390,176, 392,175, 391,173, 389,173, 388,171, 389,169, 387,167, 387,164, 388,161, 378,152, 378,149, 376,147, 375,147, 370,149, 364,149, 361,150, 359,148, 357,153, 359,158, 359,160, 358,162, 359,169, 357,172, 357,178, 356,188, 353,190, 351,193, 352,195, 354,196, 354,200, 359,201, 361,202, 363,200, 371,200" |
"78","199-255-25","Yvelines","337,178, 337,172, 340,170, 341,168, 343,168, 343,166, 341,164, 341,161, 342,155, 337,154, 332,152, 330,150, 326,151, 318,150, 317,153, 318,158, 320,161, 320,165, 321,171, 322,174, 324,175, 325,177, 327,178, 327,181, 330,184, 332,182, 334,182" |
"79","100-255-100","Deux-Sèvres","254,319, 254,321, 257,316, 261,313, 264,313, 266,314, 266,311, 263,309, 265,304, 265,301, 263,300, 261,301, 258,297, 258,294, 257,292, 259,290, 259,288, 260,285, 258,283, 258,281, 259,278, 258,273, 259,267, 258,264, 253,260, 250,258, 248,258, 243,260, 239,260, 237,263, 228,264, 229,270, 232,272, 232,275, 234,278, 234,280, 236,287, 236,289, 235,294, 237,296, 237,298, 233,301, 231,301, 231,305, 236,310, 238,310, 240,312, 242,312, 245,314, 247,314, 249,316, 251,316, 252,318" |
"80","200-200-255","Somme","375,114, 375,110, 374,105, 376,104, 376,102, 378,99, 378,97, 374,94, 372,94, 367,95, 366,93, 364,93, 362,90, 360,92, 355,90, 353,90, 352,88, 353,86, 350,84, 347,84, 344,86, 339,86, 338,83, 335,82, 334,79, 331,79, 329,77, 327,77, 321,78, 321,80, 320,83, 321,85, 319,86, 318,90, 315,94, 317,96, 326,103, 326,105, 327,109, 332,113, 336,113, 339,115, 341,115, 346,114, 349,116, 351,116, 354,118, 356,118, 358,120, 361,120, 364,119, 364,117, 369,116, 371,114" |
"81","153-102-102","Tarn","343,463, 347,462, 358,463, 361,460, 361,458, 359,456, 359,453, 361,451, 363,451, 366,452, 371,450, 371,447, 367,447, 364,448, 361,447, 357,442, 357,439, 353,430, 351,430, 348,426, 344,426, 340,423, 338,423, 336,425, 333,425, 331,426, 329,425, 327,428, 322,427, 324,429, 324,431, 321,433, 320,435, 318,435, 318,437, 317,440, 321,445, 323,448, 323,451, 322,453, 327,455, 332,460, 335,462, 337,465, 343,465" |
"82","153-51-0","Tarn-et-Garonne","320,434, 323,430, 321,428, 322,426, 325,426, 327,427, 329,424, 331,423, 331,419, 333,418, 330,417, 329,414, 326,416, 323,416, 321,418, 318,420, 316,420, 315,418, 310,420, 306,420, 305,418, 302,417, 298,413, 298,411, 294,411, 291,412, 293,415, 291,422, 290,424, 287,424, 285,425, 285,427, 284,429, 287,429, 288,431, 285,434, 290,437, 290,441, 291,443, 297,443, 301,442, 303,444, 306,444, 308,443, 308,440, 313,439, 317,436, 318,434" |
"83","0-0-204","Var","490,478, 491,480, 495,479, 496,477, 498,477, 504,478, 506,476, 510,476, 512,477, 512,475, 521,471, 524,471, 524,469, 525,467, 522,468, 521,466, 526,461, 526,459, 531,458, 534,456, 534,451, 533,448, 529,448, 527,445, 527,442, 523,441, 521,438, 513,439, 513,441, 509,442, 506,439, 504,440, 502,443, 500,443, 499,445, 496,445, 494,443, 492,443, 490,444, 487,443, 489,445, 489,448, 485,451, 485,453, 486,458, 489,461, 488,463, 486,463, 486,466, 489,469, 488,471, 485,473, 485,475, 488,476" |
"84","0-0-102","Vaucluse","482,439, 480,439, 479,436, 480,434, 476,431, 477,426, 475,425, 475,423, 472,423, 471,421, 469,421, 466,418, 461,418, 457,414, 454,414, 449,417, 446,417, 445,413, 441,413, 441,418, 443,420, 443,425, 445,425, 447,427, 447,433, 450,434, 452,436, 454,436, 458,440, 460,443, 464,444, 467,443, 476,447, 482,447, 483,445, 485,445, 485,441|454,408, 452,407, 451,411, 453,411" |
"85","255-50-50","Vendée","170,275, 169,275|234,294, 234,290, 235,285, 233,278, 231,275, 231,272, 228,270, 228,266, 227,264, 225,264, 217,263, 213,260, 209,261, 208,265, 204,266, 203,261, 202,264, 203,268, 201,270, 199,270, 194,269, 191,266, 186,263, 184,260, 182,260, 182,262, 178,266, 178,269, 179,271, 183,274, 187,279, 189,284, 189,286, 194,291, 197,293, 200,293, 202,295, 202,297, 206,297, 209,300, 212,301, 215,300, 217,298, 220,298, 221,300, 228,300, 230,301, 233,300, 236,297" |
"86","125-255-125","Vienne","283,314, 285,312, 287,312, 289,313, 290,310, 289,308, 293,304, 296,304, 297,301, 301,300, 303,299, 299,294, 299,292, 297,292, 290,285, 291,280, 287,276, 287,274, 284,271, 283,267, 272,269, 270,269, 268,267, 268,262, 265,262, 263,260, 263,258, 261,258, 260,256, 259,256, 259,258, 256,259, 255,261, 259,264, 259,266, 260,272, 259,277, 260,280, 259,283, 261,285, 261,287, 260,290, 258,292, 259,297, 262,300, 264,299, 266,301, 266,305, 264,308, 265,310, 267,311, 267,314, 270,316, 273,315, 275,316, 275,314, 277,312, 280,314" |
"87","255-255-100","Haute-Vienne","315,345, 318,345, 321,344, 324,341, 326,341, 328,339, 330,339, 330,333, 326,330, 323,331, 320,329, 320,326, 318,326, 316,324, 317,321, 315,314, 311,310, 311,307, 313,302, 310,300, 304,301, 302,300, 298,301, 297,304, 293,305, 290,308, 291,312, 290,316, 294,319, 294,323, 290,326, 288,331, 285,332, 283,335, 283,337, 285,337, 286,341, 288,343, 293,341, 296,342, 300,347, 300,349, 305,351, 310,350" |
"88","255-0-255","Vosges","475,203, 477,204, 477,206, 481,209, 483,209, 485,208, 485,206, 489,205, 490,208, 494,209, 499,207, 502,210, 505,210, 506,208, 510,211, 513,212, 514,214, 517,215, 519,206, 522,202, 522,200, 524,198, 524,194, 527,187, 527,185, 525,185, 524,176, 523,176, 517,182, 515,182, 513,183, 509,182, 508,180, 506,180, 504,182, 497,184, 494,183, 490,184, 489,186, 487,186, 484,187, 480,183, 480,179, 477,179, 474,181, 471,181, 469,183, 466,183, 466,186, 468,189, 475,195, 473,198, 473,201" |
"89","0-151-255","Yonne","401,242, 404,242, 410,243, 411,245, 414,245, 414,241, 415,238, 414,236, 417,233, 417,231, 422,222, 422,218, 420,217, 420,212, 415,211, 407,212, 404,209, 404,207, 402,204, 402,202, 399,200, 397,200, 394,197, 394,192, 390,188, 386,188, 376,189, 374,190, 374,196, 372,198, 372,201, 374,203, 374,205, 376,207, 377,210, 373,214, 373,220, 370,222, 367,222, 367,225, 369,226, 370,231, 373,233, 375,233, 378,232, 382,237, 387,237, 394,235, 396,237, 396,239, 398,239" |
"90","255-255-25","Territoire de Belfort","527,231, 527,229, 525,227, 521,218, 519,218, 516,216, 515,221, 516,226, 515,229, 524,231" |
"91","199-255-50","Essonne","340,192, 342,190, 344,190, 352,191, 355,187, 355,179, 356,171, 350,171, 345,169, 344,168, 341,169, 338,173, 338,179, 335,181, 333,184, 333,186, 334,192, 336,193" |
"92","199-255-100","Hauts-de-Seine","345,163, 345,161, 347,160, 347,154, 344,154, 343,160, 342,164, 347,169, 348,165|495,60, 495,55, 490,52, 487,52, 483,48, 481,47, 481,44, 484,41, 486,41, 489,40, 492,37, 495,35, 495,33, 498,30, 498,27, 494,26, 492,25, 485,29, 475,39, 492,25, 485,29, 479,35, 474,39, 473,48, 472,54, 475,54, 477,56, 477,58, 479,58, 483,62, 484,65, 489,66, 491,68, 491,71, 494,71, 495,66, 497,64, 497,62" |
"93","199-255-125","Seine-Saint-Denis","350,159, 352,161, 357,161, 358,157, 357,154, 356,154, 354,156, 348,156, 348,159|519,45, 522,46, 525,49, 529,52, 530,55, 534,56, 535,52, 532,50, 532,45, 530,43, 530,39, 533,36, 533,31, 535,30, 535,28, 531,23, 530,19, 531,15, 527,14, 524,19, 520,23, 518,23, 517,25, 507,25, 504,22, 502,22, 498,25, 493,24, 498,26, 499,30, 496,33, 496,35, 507,35, 508,39, 510,41, 511,46, 514,47" |
"94","199-255-150","Val-de-Marne","357,170, 358,163, 352,162, 349,166, 349,169|517,74, 524,73, 525,78, 527,80, 531,81, 531,78, 533,73, 533,71, 535,69, 535,66, 533,64, 533,62, 534,57, 532,57, 529,55, 528,52, 526,50, 522,47, 518,46, 515,47, 517,50, 517,53, 515,55, 512,55, 509,53, 507,53, 505,55, 496,56, 496,60, 498,62, 498,64, 496,66, 496,69, 498,69, 503,70, 504,75, 507,75" |
"95","199-255-0","Val-d'Oise","347,153, 349,155, 354,155, 356,153, 357,149, 354,147, 351,147, 344,144, 341,144, 338,142, 337,142, 332,144, 329,144, 324,143, 324,146, 323,149, 325,149, 328,150, 330,149, 332,151, 342,154" |
"971","161-161-25","Guadeloupe","80,269, 80,172, 1,172, 1,269" |
"972","161-161-125","Martinique","79,362, 79,271, 1,271, 1,362" |
"973","161-161-200","Guyane","78,449, 78,365, 3,365, 3,449" |
"974","161-161-225","Réunion","78,524, 78,453, 2,453, 2,524" |
"975","25-161-161","Saint-Pierre-et-Miquelon","158,130, 158,4, 88,4, 88,130" |
"976","125-161-161","Mayotte","81,101, 81,4, 3,4, 3,101" |
"977","0-0-0","Saint-Barthélémy","" |
"978","0-0-0","Saint-Martin","" |
"986","200-161-161","Wallis-et-Futuna","161,527, 161,465, 160,405, 93,405, 93,464, 81,465, 81,527" |
"987","225-161-161","Polynésie française","162,401, 162,306, 83,306, 83,401" |
"988","225-225-161","Nouvelle-Calédonie","281,82, 281,7, 197,7, 197,82" |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/trunk/composants/cartographie/squelettes/pays_oceanie.csv |
---|
New file |
0,0 → 1,16 |
"code","rvb_fond","nom","poly" |
"au","255-25-25","Australie","14,246,47,233,49,220,59,220,71,203,89,211,92,187,119,195,135,193,129,216,149,225,159,188,176,211,213,227,231,255,220,279,228,299,200,359,192,394,181,397,164,377,162,364,143,355,125,344,110,321,88,319,63,329,60,336,37,331-32,339,15,338,6,334,5,321,11,318,8,310,5,292,0,281,3,252" |
"fj","255-25-75","Fidji","321,180,342,181,382,222,379,250,365,269,348,271,325,258,324,246,331,222,320,203,322,180" |
"ki","255-25-125","Kiribati","311,120,330,106,348,112,373,132,398,129,478,144,482,129,461,113,483,92,507,107,530,143,530,199,505,200,508,186,500,181,493,174,369,168,360,155,336,154,321,145,312,120" |
"mh","255-25-175","Marshall","244,64,262,26,317,28,346,48,345,110,329,106,310,119,292,111,293,106" |
"fm","255-25-225","Micronésie","128,72,193,59,244,65,291,105,290,117,286,119,170,118,152,103,128,103" |
"nr","255-75-25","Nauru","297,112,312,120,319,144,292,141,292,129,298,121" |
"nz","255-75-75","Nouvelle-Zélande","325,327,348,350,364,356,338,388,301,437,293,441,279,419,318,364,330,354,318,332" |
"pw","255-75-125","Palau","127,73,92,93,92,116,111,123,128,104" |
"pg","255-75-175","Papouasie - Nouvelle Guinée","152,131,152,184,158,187,171,186,179,201,228,200,233,193,231,178,225,179,225,174,234,170,249,159,281,158,281,131" |
"sb","255-75-225","Salomon","281,158,249,158,226,176,226,178,232,178,232,194,242,194,251,203,321,204,321,180,303,162" |
"ws","255-125-25","Samoa","394,205,405,203,411,193,423,214,414,223" |
"to","25-125-225","Tonga","382,220,375,214,376,209,383,202,393,204,416,226,401,262,388,256,371,258,380,249" |
"tv","25-175-25","Tuvalu","325,155,359,155,374,175,361,198,341,180,336,180" |
"vu","255-125-175","Vanuatu","287,203,307,247,325,246,331,222,321,204" |
"0","0-0-0","","" |
/trunk/composants/cartographie/squelettes/pays_afrique.tpl.html |
---|
New file |
0,0 → 1,12 |
<div id="cartographie"> |
<img id="carte-img" src="<?=$carte_url;?>" alt="<?=$carte_alt;?>" usemap="#carte-map" /> |
<map name="carte-map"> |
<?php foreach ($zones as $code => $zone) : ?> |
<?php if (!empty($zone['poly'])) : ?> |
<?php foreach (explode('|', $zone['poly']) as $coords) : ?> |
<area shape="poly" title="<?=$zone['nom']?> (<? echo $zone['info_nombre']; if($zone['info_nombre'] > 1) {echo ' inscrits';} else { echo ' inscrit';} ?>)" class="zone-<?=$code?>" href="<?=$zone['url']?>" coords="<?=$coords?>" /> |
<?php endforeach; ?> |
<?php endif; ?> |
<?php endforeach; ?> |
</map> |
</div> |
/trunk/composants/Composant.php |
---|
New file |
0,0 → 1,13 |
<?php |
class Composant { |
public static function fabrique($classe, $options = array()) { |
$classe_nom = implode('', array_map('ucfirst', explode('_', $classe))); |
require_once dirname(__FILE__).DS.$classe.DS.$classe_nom.'.php'; |
$Composant = new $classe_nom($options); |
return $Composant; |
} |
} |
?> |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |