/tags/v1.0-aigle/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; |
} |
} |
?> |
/tags/v1.0-aigle/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); |
} |
} |
} |
?> |
/tags/v1.0-aigle/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; |
} |
?> |
/tags/v1.0-aigle/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); |
} |
} |
?> |
/tags/v1.0-aigle/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); |
} |
} |
?> |
/tags/v1.0-aigle/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; |
} |
} |
?> |
/tags/v1.0-aigle/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); |
} |
} |
?> |
/tags/v1.0-aigle/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; |
} |
?> |
/tags/v1.0-aigle/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; |
} |
} |
?> |
/tags/v1.0-aigle/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; |
} |
?> |
/tags/v1.0-aigle/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); |
} |
} |
?> |