import v1.1.0_beta1 | 2009-08-21
This commit is contained in:
147
libs/Auth/Yadis/HTTPFetcher.php
Normal file
147
libs/Auth/Yadis/HTTPFetcher.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This module contains the HTTP fetcher interface
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* LICENSE: See the COPYING file included in this distribution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Require logging functionality
|
||||
*/
|
||||
require_once "Auth/OpenID.php";
|
||||
|
||||
define('Auth_OpenID_FETCHER_MAX_RESPONSE_KB', 1024);
|
||||
define('Auth_OpenID_USER_AGENT',
|
||||
'php-openid/'.Auth_OpenID_VERSION.' (php/'.phpversion().')');
|
||||
|
||||
class Auth_Yadis_HTTPResponse {
|
||||
function Auth_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 OpenID
|
||||
*/
|
||||
class Auth_Yadis_HTTPFetcher {
|
||||
|
||||
var $timeout = 20; // timeout in seconds.
|
||||
|
||||
/**
|
||||
* Return whether a URL can be fetched. Returns false if the URL
|
||||
* scheme is not allowed or is not supported by this fetcher
|
||||
* implementation; returns true otherwise.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function canFetchURL($url)
|
||||
{
|
||||
if ($this->isHTTPS($url) && !$this->supportsSSL()) {
|
||||
Auth_OpenID::log("HTTPS URL unsupported fetching %s",
|
||||
$url);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->allowedURL($url)) {
|
||||
Auth_OpenID::log("URL fetching not allowed for '%s'",
|
||||
$url);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this fetcher implementation (and runtime) support fetching
|
||||
* HTTPS URLs? May inspect the runtime environment.
|
||||
*
|
||||
* @return bool $support True if this fetcher supports HTTPS
|
||||
* fetching; false if not.
|
||||
*/
|
||||
function supportsSSL()
|
||||
{
|
||||
trigger_error("not implemented", E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an https URL?
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function isHTTPS($url)
|
||||
{
|
||||
return (bool)preg_match('/^https:\/\//i', $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(strtolower($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 = null)
|
||||
{
|
||||
trigger_error("not implemented", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
529
libs/Auth/Yadis/Manager.php
Normal file
529
libs/Auth/Yadis/Manager.php
Normal file
@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Yadis service manager to be used during yadis-driven authentication
|
||||
* attempts.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
|
||||
/**
|
||||
* The base session class used by the Auth_Yadis_Manager. This
|
||||
* class wraps the default PHP session machinery and should be
|
||||
* subclassed if your application doesn't use PHP sessioning.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_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.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_SessionLoader {
|
||||
/**
|
||||
* Override this.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
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).
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function prepareForSave($obj)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A concrete loader implementation for Auth_OpenID_ServiceEndpoints.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_OpenID_ServiceEndpointLoader extends Auth_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']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A concrete loader implementation for Auth_Yadis_Managers.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader {
|
||||
function requiredKeys()
|
||||
{
|
||||
return array('starting_url',
|
||||
'yadis_url',
|
||||
'services',
|
||||
'session_key',
|
||||
'_current',
|
||||
'stale');
|
||||
}
|
||||
|
||||
function newObject($data)
|
||||
{
|
||||
return new Auth_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 OpenID
|
||||
*/
|
||||
class Auth_Yadis_Manager {
|
||||
|
||||
/**
|
||||
* Intialize a new yadis service manager.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function Auth_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 .cleanup() to clean up the
|
||||
* session state.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_Discovery {
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
var $DEFAULT_SUFFIX = 'auth';
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
var $PREFIX = '_yadis_services_';
|
||||
|
||||
/**
|
||||
* Initialize a discovery object.
|
||||
*
|
||||
* @param Auth_Yadis_PHPSession $session An object which
|
||||
* implements the Auth_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 Auth_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();
|
||||
|
||||
list($yadis_url, $services) = call_user_func($discover_cb,
|
||||
$this->url,
|
||||
$fetcher);
|
||||
|
||||
$manager = $this->createManager($services, $yadis_url);
|
||||
}
|
||||
|
||||
if ($manager) {
|
||||
$loader = new Auth_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.
|
||||
*
|
||||
* @param $force True if the manager should be deleted regardless
|
||||
* of whether it's a manager for $this->url.
|
||||
*/
|
||||
function cleanup($force=false)
|
||||
{
|
||||
$manager = $this->getManager($force);
|
||||
if ($manager) {
|
||||
$service = $manager->current();
|
||||
$this->destroyManager($force);
|
||||
} 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
|
||||
*
|
||||
* @param $force True if the manager should be returned regardless
|
||||
* of whether it's a manager for $this->url.
|
||||
*/
|
||||
function &getManager($force=false)
|
||||
{
|
||||
// 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 Auth_Yadis_ManagerLoader();
|
||||
$manager = $loader->fromSession(unserialize($manager_str));
|
||||
}
|
||||
|
||||
if ($manager && ($manager->forURL($this->url) || $force)) {
|
||||
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 Auth_Yadis_ManagerLoader();
|
||||
$manager = new Auth_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
|
||||
*
|
||||
* @param $force True if the manager should be deleted regardless
|
||||
* of whether it's a manager for $this->url.
|
||||
*/
|
||||
function destroyManager($force=false)
|
||||
{
|
||||
if ($this->getManager($force) !== null) {
|
||||
$key = $this->getSessionKey();
|
||||
$this->session->del($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
59
libs/Auth/Yadis/Misc.php
Normal file
59
libs/Auth/Yadis/Misc.php
Normal 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-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
function Auth_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 Auth_Yadis_getIPrivateChars()
|
||||
{
|
||||
return array(
|
||||
array(0xE000, 0xF8FF),
|
||||
array(0xF0000, 0xFFFFD),
|
||||
array(0x100000, 0x10FFFD)
|
||||
);
|
||||
}
|
||||
|
||||
function Auth_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 Auth_Yadis_startswith($s, $stuff)
|
||||
{
|
||||
return strpos($s, $stuff) === 0;
|
||||
}
|
||||
|
||||
?>
|
226
libs/Auth/Yadis/ParanoidHTTPFetcher.php
Normal file
226
libs/Auth/Yadis/ParanoidHTTPFetcher.php
Normal file
@ -0,0 +1,226 @@
|
||||
<?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 OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface import
|
||||
*/
|
||||
require_once "Auth/Yadis/HTTPFetcher.php";
|
||||
|
||||
require_once "Auth/OpenID.php";
|
||||
|
||||
/**
|
||||
* A paranoid {@link Auth_Yadis_HTTPFetcher} class which uses CURL
|
||||
* for fetching.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
||||
function Auth_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)
|
||||
{
|
||||
if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) {
|
||||
return 0;
|
||||
} else {
|
||||
$this->data .= $data;
|
||||
return strlen($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this fetcher support SSL URLs?
|
||||
*/
|
||||
function supportsSSL()
|
||||
{
|
||||
$v = curl_version();
|
||||
if(is_array($v)) {
|
||||
return in_array('https', $v['protocols']);
|
||||
} elseif (is_string($v)) {
|
||||
return preg_match('/OpenSSL/i', $v);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function get($url, $extra_headers = null)
|
||||
{
|
||||
if (!$this->canFetchURL($url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stop = time() + $this->timeout;
|
||||
$off = $this->timeout;
|
||||
|
||||
$redir = true;
|
||||
|
||||
while ($redir && ($off > 0)) {
|
||||
$this->reset();
|
||||
|
||||
$c = curl_init();
|
||||
|
||||
if ($c === false) {
|
||||
Auth_OpenID::log(
|
||||
"curl_init returned false; could not " .
|
||||
"initialize for URL '%s'", $url);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (defined('CURLOPT_NOSIGNAL')) {
|
||||
curl_setopt($c, CURLOPT_NOSIGNAL, true);
|
||||
}
|
||||
|
||||
if (!$this->allowedURL($url)) {
|
||||
Auth_OpenID::log("Fetching URL not allowed: %s",
|
||||
$url);
|
||||
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);
|
||||
}
|
||||
|
||||
$cv = curl_version();
|
||||
if(is_array($cv)) {
|
||||
$curl_user_agent = 'curl/'.$cv['version'];
|
||||
} else {
|
||||
$curl_user_agent = $cv;
|
||||
}
|
||||
curl_setopt($c, CURLOPT_USERAGENT,
|
||||
Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
|
||||
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) {
|
||||
Auth_OpenID::log("Got no response code when fetching %s", $url);
|
||||
Auth_OpenID::log("CURL error (%s): %s",
|
||||
curl_errno($c), curl_error($c));
|
||||
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 (strpos($header, ': ')) {
|
||||
list($name, $value) = explode(': ', $header, 2);
|
||||
$new_headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
Auth_OpenID::log(
|
||||
"Successfully fetched '%s': GET response code %s",
|
||||
$url, $code);
|
||||
|
||||
return new Auth_Yadis_HTTPResponse($url, $code,
|
||||
$new_headers, $body);
|
||||
}
|
||||
|
||||
$off = $stop - time();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function post($url, $body, $extra_headers = null)
|
||||
{
|
||||
if (!$this->canFetchURL($url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
|
||||
$c = curl_init();
|
||||
|
||||
if (defined('CURLOPT_NOSIGNAL')) {
|
||||
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) {
|
||||
Auth_OpenID::log("Got no response code when fetching %s", $url);
|
||||
return null;
|
||||
}
|
||||
|
||||
$body = $this->data;
|
||||
|
||||
curl_close($c);
|
||||
|
||||
$new_headers = $extra_headers;
|
||||
|
||||
foreach ($this->headers as $header) {
|
||||
if (strpos($header, ': ')) {
|
||||
list($name, $value) = explode(': ', $header, 2);
|
||||
$new_headers[$name] = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
|
||||
$url, $code);
|
||||
|
||||
return new Auth_Yadis_HTTPResponse($url, $code,
|
||||
$new_headers, $body);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
259
libs/Auth/Yadis/ParseHTML.php
Normal file
259
libs/Auth/Yadis/ParseHTML.php
Normal file
@ -0,0 +1,259 @@
|
||||
<?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 OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 OpenID
|
||||
*/
|
||||
class Auth_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 Auth_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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
249
libs/Auth/Yadis/PlainHTTPFetcher.php
Normal file
249
libs/Auth/Yadis/PlainHTTPFetcher.php
Normal file
@ -0,0 +1,249 @@
|
||||
<?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 OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface import
|
||||
*/
|
||||
require_once "Auth/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 OpenID
|
||||
*/
|
||||
class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
||||
/**
|
||||
* Does this fetcher support SSL URLs?
|
||||
*/
|
||||
function supportsSSL()
|
||||
{
|
||||
return function_exists('openssl_open');
|
||||
}
|
||||
|
||||
function get($url, $extra_headers = null)
|
||||
{
|
||||
if (!$this->canFetchURL($url)) {
|
||||
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 {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists('path', $parts)) {
|
||||
$parts['path'] = '/';
|
||||
}
|
||||
|
||||
$host = $parts['host'];
|
||||
|
||||
if ($parts['scheme'] == 'https') {
|
||||
$host = 'ssl://' . $host;
|
||||
}
|
||||
|
||||
$user_agent = Auth_OpenID_USER_AGENT;
|
||||
|
||||
$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 = "";
|
||||
$kilobytes = 0;
|
||||
while (!feof($sock) &&
|
||||
$kilobytes < Auth_OpenID_FETCHER_MAX_RESPONSE_KB ) {
|
||||
$data .= fgets($sock, 1024);
|
||||
$kilobytes += 1;
|
||||
}
|
||||
|
||||
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)) {
|
||||
$parts = explode(": ", $header, 2);
|
||||
|
||||
if (count($parts) == 2) {
|
||||
list($name, $value) = $parts;
|
||||
$new_headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Auth_Yadis_HTTPResponse($url, $code, $new_headers, $body);
|
||||
}
|
||||
|
||||
function post($url, $body, $extra_headers = null)
|
||||
{
|
||||
if (!$this->canFetchURL($url)) {
|
||||
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 {
|
||||
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) {
|
||||
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 Auth_Yadis_HTTPResponse($url, $code,
|
||||
$new_headers, $response_body);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
374
libs/Auth/Yadis/XML.php
Normal file
374
libs/Auth/Yadis/XML.php
Normal file
@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* XML-parsing classes to wrap the domxml and DOM extensions for PHP 4
|
||||
* and 5, respectively.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 Auth_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 OpenID
|
||||
*/
|
||||
class Auth_Yadis_XMLParser {
|
||||
/**
|
||||
* Initialize an instance of Auth_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 Auth_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 Auth_Yadis_XMLParser class for
|
||||
* details on this class's methods.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_domxml extends Auth_Yadis_XMLParser {
|
||||
function Auth_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) {
|
||||
$n = array();
|
||||
return $n;
|
||||
}
|
||||
|
||||
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 Auth_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 Auth_Yadis_XMLParser class for
|
||||
* details on this class's methods.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
|
||||
function Auth_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();
|
||||
|
||||
if (!$result) {
|
||||
return $n;
|
||||
}
|
||||
|
||||
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 $__Auth_Yadis_defaultParser;
|
||||
$__Auth_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 Auth_Yadis_XMLParser $parser An instance of a
|
||||
* Auth_Yadis_XMLParser subclass.
|
||||
*/
|
||||
function Auth_Yadis_setDefaultParser(&$parser)
|
||||
{
|
||||
global $__Auth_Yadis_defaultParser;
|
||||
$__Auth_Yadis_defaultParser =& $parser;
|
||||
}
|
||||
|
||||
function Auth_Yadis_getSupportedExtensions()
|
||||
{
|
||||
return array(
|
||||
'dom' => array('classname' => 'Auth_Yadis_dom',
|
||||
'libname' => array('dom.so', 'dom.dll')),
|
||||
'domxml' => array('classname' => 'Auth_Yadis_domxml',
|
||||
'libname' => array('domxml.so', 'php_domxml.dll')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of a Auth_Yadis_XMLParser subclass based on
|
||||
* the availability of PHP extensions for XML parsing. If
|
||||
* Auth_Yadis_setDefaultParser has been called, the parser used in
|
||||
* that call will be returned instead.
|
||||
*/
|
||||
function &Auth_Yadis_getXMLParser()
|
||||
{
|
||||
global $__Auth_Yadis_defaultParser;
|
||||
|
||||
if (isset($__Auth_Yadis_defaultParser)) {
|
||||
return $__Auth_Yadis_defaultParser;
|
||||
}
|
||||
|
||||
$p = null;
|
||||
$classname = null;
|
||||
|
||||
$extensions = Auth_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 {
|
||||
Auth_Yadis_setDefaultParser($p);
|
||||
}
|
||||
|
||||
return $p;
|
||||
}
|
||||
|
||||
?>
|
478
libs/Auth/Yadis/XRDS.php
Normal file
478
libs/Auth/Yadis/XRDS.php
Normal file
@ -0,0 +1,478 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This module contains the XRDS parsing code.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* LICENSE: See the COPYING file included in this distribution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Require the XPath implementation.
|
||||
*/
|
||||
require_once 'Auth/Yadis/XML.php';
|
||||
|
||||
/**
|
||||
* This match mode means a given service must match ALL filters passed
|
||||
* to the Auth_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 Auth_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));
|
||||
|
||||
/**
|
||||
* XRD XML namespace
|
||||
*/
|
||||
define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)');
|
||||
|
||||
/**
|
||||
* XRDS XML namespace
|
||||
*/
|
||||
define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds');
|
||||
|
||||
function Auth_Yadis_getNSMap()
|
||||
{
|
||||
return array('xrds' => Auth_Yadis_XMLNS_XRDS,
|
||||
'xrd' => Auth_Yadis_XMLNS_XRD_2_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function Auth_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
|
||||
* Auth_Yadis_XRDS::services() and
|
||||
* Auth_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 Auth_Yadis_Yadis} for more
|
||||
* information on the role this class plays in Yadis discovery.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_Service {
|
||||
|
||||
/**
|
||||
* Creates an empty service object.
|
||||
*/
|
||||
function Auth_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;
|
||||
}
|
||||
|
||||
function matchTypes($type_uris)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($this->getTypes() as $typ) {
|
||||
if (in_array($typ, $type_uris)) {
|
||||
$result[] = $typ;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = Auth_Yadis_array_scramble($uris[$k]);
|
||||
$result = array_merge($result, $new_uris);
|
||||
}
|
||||
|
||||
$result = array_merge($result,
|
||||
Auth_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 Auth_Yadis_XMLParser}).
|
||||
*/
|
||||
function getElements($name)
|
||||
{
|
||||
return $this->parser->evalXPath($name, $this->element);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the expiration date of this XRD element, or None if no
|
||||
* expiration was specified.
|
||||
*
|
||||
* @param $default The value to use as the expiration if no expiration
|
||||
* was specified in the XRD.
|
||||
*/
|
||||
function Auth_Yadis_getXRDExpiration($xrd_element, $default=null)
|
||||
{
|
||||
$expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires');
|
||||
if ($expires_element === null) {
|
||||
return $default;
|
||||
} else {
|
||||
$expires_string = $expires_element->text;
|
||||
|
||||
// Will raise ValueError if the string is not the expected
|
||||
// format
|
||||
$t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ");
|
||||
|
||||
if ($t === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// [int $hour [, int $minute [, int $second [,
|
||||
// int $month [, int $day [, int $year ]]]]]]
|
||||
return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'],
|
||||
$t['tm_mon'], $t['tm_day'], $t['tm_year']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class performs parsing of XRDS documents.
|
||||
*
|
||||
* You should not instantiate this class directly; rather, call
|
||||
* parseXRDS statically:
|
||||
*
|
||||
* <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre>
|
||||
*
|
||||
* If the XRDS can be parsed and is valid, an instance of
|
||||
* Auth_Yadis_XRDS will be returned. Otherwise, null will be
|
||||
* returned. This class is used by the Auth_Yadis_Yadis::discover
|
||||
* method.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_XRDS {
|
||||
|
||||
/**
|
||||
* Instantiate a Auth_Yadis_XRDS object. Requires an XPath
|
||||
* instance which has been used to parse a valid XRDS document.
|
||||
*/
|
||||
function Auth_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
|
||||
* Auth_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 Auth_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 = Auth_Yadis_getXMLParser();
|
||||
|
||||
$ns_map = Auth_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'] != Auth_Yadis_XMLNS_XRDS) {
|
||||
return $_null;
|
||||
} else if (array_key_exists('xmlns', $attrs) &&
|
||||
preg_match('/xri/', $attrs['xmlns']) &&
|
||||
$attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_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 Auth_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 Auth_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
|
||||
* Auth_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
|
||||
* Auth_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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
234
libs/Auth/Yadis/XRI.php
Normal file
234
libs/Auth/Yadis/XRI.php
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Routines for XRI resolution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
require_once 'Auth/Yadis/Misc.php';
|
||||
require_once 'Auth/Yadis/Yadis.php';
|
||||
require_once 'Auth/OpenID.php';
|
||||
|
||||
function Auth_Yadis_getDefaultProxy()
|
||||
{
|
||||
return 'http://xri.net/';
|
||||
}
|
||||
|
||||
function Auth_Yadis_getXRIAuthorities()
|
||||
{
|
||||
return array('!', '=', '@', '+', '$', '(');
|
||||
}
|
||||
|
||||
function Auth_Yadis_getEscapeRE()
|
||||
{
|
||||
$parts = array();
|
||||
foreach (array_merge(Auth_Yadis_getUCSChars(),
|
||||
Auth_Yadis_getIPrivateChars()) as $pair) {
|
||||
list($m, $n) = $pair;
|
||||
$parts[] = sprintf("%s-%s", chr($m), chr($n));
|
||||
}
|
||||
|
||||
return sprintf('/[%s]/', implode('', $parts));
|
||||
}
|
||||
|
||||
function Auth_Yadis_getXrefRE()
|
||||
{
|
||||
return '/\((.*?)\)/';
|
||||
}
|
||||
|
||||
function Auth_Yadis_identifierScheme($identifier)
|
||||
{
|
||||
if (Auth_Yadis_startswith($identifier, 'xri://') ||
|
||||
($identifier &&
|
||||
in_array($identifier[0], Auth_Yadis_getXRIAuthorities()))) {
|
||||
return "XRI";
|
||||
} else {
|
||||
return "URI";
|
||||
}
|
||||
}
|
||||
|
||||
function Auth_Yadis_toIRINormal($xri)
|
||||
{
|
||||
if (!Auth_Yadis_startswith($xri, 'xri://')) {
|
||||
$xri = 'xri://' . $xri;
|
||||
}
|
||||
|
||||
return Auth_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 Auth_Yadis_escapeForIRI($xri)
|
||||
{
|
||||
$xri = str_replace('%', '%25', $xri);
|
||||
$xri = preg_replace_callback(Auth_Yadis_getXrefRE(),
|
||||
'_escape_xref', $xri);
|
||||
return $xri;
|
||||
}
|
||||
|
||||
function Auth_Yadis_toURINormal($xri)
|
||||
{
|
||||
return Auth_Yadis_iriToURI(Auth_Yadis_toIRINormal($xri));
|
||||
}
|
||||
|
||||
function Auth_Yadis_iriToURI($iri)
|
||||
{
|
||||
if (1) {
|
||||
return $iri;
|
||||
} else {
|
||||
// According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
|
||||
return preg_replace_callback(Auth_Yadis_getEscapeRE(),
|
||||
'Auth_Yadis_pct_escape_unicode', $iri);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Auth_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 Auth_Yadis_providerIsAuthoritative($providerID, $canonicalID)
|
||||
{
|
||||
$lastbang = strrpos($canonicalID, '!');
|
||||
$p = substr($canonicalID, 0, $lastbang);
|
||||
return $p == $providerID;
|
||||
}
|
||||
|
||||
function Auth_Yadis_rootAuthority($xri)
|
||||
{
|
||||
// Return the root authority for an XRI.
|
||||
|
||||
$root = null;
|
||||
|
||||
if (Auth_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], Auth_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 Auth_Yadis_XRI($root);
|
||||
}
|
||||
|
||||
function Auth_Yadis_XRI($xri)
|
||||
{
|
||||
if (!Auth_Yadis_startswith($xri, 'xri://')) {
|
||||
$xri = 'xri://' . $xri;
|
||||
}
|
||||
return $xri;
|
||||
}
|
||||
|
||||
function Auth_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[0];
|
||||
$canonicalID = Auth_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, '!'));
|
||||
$parentCID = $parser->evalXPath('xrd:CanonicalID', $xrd);
|
||||
if (!$parentCID) {
|
||||
return false;
|
||||
}
|
||||
$parentCID = Auth_Yadis_XRI($parser->content($parentCID[0]));
|
||||
|
||||
if (strcasecmp($parent_sought, $parentCID)) {
|
||||
// raise XRDSFraud.
|
||||
return false;
|
||||
}
|
||||
|
||||
$childID = $parent_sought;
|
||||
}
|
||||
|
||||
$root = Auth_Yadis_rootAuthority($iname);
|
||||
if (!Auth_Yadis_providerIsAuthoritative($root, $childID)) {
|
||||
// raise XRDSFraud.
|
||||
return false;
|
||||
}
|
||||
|
||||
return $canonicalID;
|
||||
}
|
||||
|
||||
?>
|
72
libs/Auth/Yadis/XRIRes.php
Normal file
72
libs/Auth/Yadis/XRIRes.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Code for using a proxy XRI resolver.
|
||||
*/
|
||||
|
||||
require_once 'Auth/Yadis/XRDS.php';
|
||||
require_once 'Auth/Yadis/XRI.php';
|
||||
|
||||
class Auth_Yadis_ProxyResolver {
|
||||
function Auth_Yadis_ProxyResolver(&$fetcher, $proxy_url = null)
|
||||
{
|
||||
$this->fetcher =& $fetcher;
|
||||
$this->proxy_url = $proxy_url;
|
||||
if (!$this->proxy_url) {
|
||||
$this->proxy_url = Auth_Yadis_getDefaultProxy();
|
||||
}
|
||||
}
|
||||
|
||||
function queryURL($xri, $service_type = null)
|
||||
{
|
||||
// trim off the xri:// prefix
|
||||
$qxri = substr(Auth_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 = Auth_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 and $response->status != 206) {
|
||||
continue;
|
||||
}
|
||||
$xrds = Auth_Yadis_XRDS::parseXRDS($response->body);
|
||||
if (!$xrds) {
|
||||
continue;
|
||||
}
|
||||
$canonicalID = Auth_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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
382
libs/Auth/Yadis/Yadis.php
Normal file
382
libs/Auth/Yadis/Yadis.php
Normal file
@ -0,0 +1,382 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The core PHP Yadis implementation.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* LICENSE: See the COPYING file included in this distribution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005-2008 Janrain, Inc.
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Need both fetcher types so we can use the right one based on the
|
||||
* presence or absence of CURL.
|
||||
*/
|
||||
require_once "Auth/Yadis/PlainHTTPFetcher.php";
|
||||
require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
|
||||
|
||||
/**
|
||||
* Need this for parsing HTML (looking for META tags).
|
||||
*/
|
||||
require_once "Auth/Yadis/ParseHTML.php";
|
||||
|
||||
/**
|
||||
* Need this to parse the XRDS document during Yadis discovery.
|
||||
*/
|
||||
require_once "Auth/Yadis/XRDS.php";
|
||||
|
||||
/**
|
||||
* XRDS (yadis) content type
|
||||
*/
|
||||
define('Auth_Yadis_CONTENT_TYPE', 'application/xrds+xml');
|
||||
|
||||
/**
|
||||
* Yadis header
|
||||
*/
|
||||
define('Auth_Yadis_HEADER_NAME', 'X-XRDS-Location');
|
||||
|
||||
/**
|
||||
* Contains the result of performing Yadis discovery on a URI.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_DiscoveryResult {
|
||||
|
||||
// The URI that was passed to the fetcher
|
||||
var $request_uri = null;
|
||||
|
||||
// The result of following redirects from the request_uri
|
||||
var $normalized_uri = null;
|
||||
|
||||
// The URI from which the response text was returned (set to
|
||||
// None if there was no XRDS document found)
|
||||
var $xrds_uri = null;
|
||||
|
||||
var $xrds = null;
|
||||
|
||||
// The content-type returned with the response_text
|
||||
var $content_type = null;
|
||||
|
||||
// The document returned from the xrds_uri
|
||||
var $response_text = null;
|
||||
|
||||
// Did the discovery fail miserably?
|
||||
var $failed = false;
|
||||
|
||||
function Auth_Yadis_DiscoveryResult($request_uri)
|
||||
{
|
||||
// Initialize the state of the object
|
||||
// sets all attributes to None except the request_uri
|
||||
$this->request_uri = $request_uri;
|
||||
}
|
||||
|
||||
function fail()
|
||||
{
|
||||
$this->failed = true;
|
||||
}
|
||||
|
||||
function isFailure()
|
||||
{
|
||||
return $this->failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Auth_Yadis_Service}
|
||||
* objects
|
||||
*/
|
||||
function services()
|
||||
{
|
||||
if ($this->xrds) {
|
||||
return $this->xrds->services();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function usedYadisLocation()
|
||||
{
|
||||
// Was the Yadis protocol's indirection used?
|
||||
return $this->normalized_uri != $this->xrds_uri;
|
||||
}
|
||||
|
||||
function isXRDS()
|
||||
{
|
||||
// Is the response text supposed to be an XRDS document?
|
||||
return ($this->usedYadisLocation() ||
|
||||
$this->content_type == Auth_Yadis_CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Perform the Yadis protocol on the input URL and return an iterable
|
||||
* of resulting endpoint objects.
|
||||
*
|
||||
* input_url: The URL on which to perform the Yadis protocol
|
||||
*
|
||||
* @return: The normalized identity URL and an iterable of endpoint
|
||||
* objects generated by the filter function.
|
||||
*
|
||||
* xrds_parse_func: a callback which will take (uri, xrds_text) and
|
||||
* return an array of service endpoint objects or null. Usually
|
||||
* array('Auth_OpenID_ServiceEndpoint', 'fromXRDS').
|
||||
*
|
||||
* discover_func: if not null, a callback which should take (uri) and
|
||||
* return an Auth_Yadis_Yadis object or null.
|
||||
*/
|
||||
function Auth_Yadis_getServiceEndpoints($input_url, $xrds_parse_func,
|
||||
$discover_func=null, $fetcher=null)
|
||||
{
|
||||
if ($discover_func === null) {
|
||||
$discover_function = array('Auth_Yadis_Yadis', 'discover');
|
||||
}
|
||||
|
||||
$yadis_result = call_user_func_array($discover_func,
|
||||
array($input_url, $fetcher));
|
||||
|
||||
if ($yadis_result === null) {
|
||||
return array($input_url, array());
|
||||
}
|
||||
|
||||
$endpoints = call_user_func_array($xrds_parse_func,
|
||||
array($yadis_result->normalized_uri,
|
||||
$yadis_result->response_text));
|
||||
|
||||
if ($endpoints === null) {
|
||||
$endpoints = array();
|
||||
}
|
||||
|
||||
return array($yadis_result->normalized_uri, $endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 "Auth/Yadis/Yadis.php";</pre>
|
||||
*
|
||||
* To perform Yadis discovery, first call the "discover" method
|
||||
* statically with a URI parameter:
|
||||
*
|
||||
* <pre> $http_response = array();
|
||||
* $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
* $yadis_object = Auth_Yadis_Yadis::discover($uri,
|
||||
* $http_response, $fetcher);</pre>
|
||||
*
|
||||
* If the discovery succeeds, $yadis_object will be an instance of
|
||||
* {@link Auth_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 Auth_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 Auth_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
|
||||
* Auth_Yadis_Service}.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_Yadis {
|
||||
|
||||
/**
|
||||
* Returns an HTTP fetcher object. If the CURL extension is
|
||||
* present, an instance of {@link Auth_Yadis_ParanoidHTTPFetcher}
|
||||
* is returned. If not, an instance of
|
||||
* {@link Auth_Yadis_PlainHTTPFetcher} is returned.
|
||||
*
|
||||
* If Auth_Yadis_CURL_OVERRIDE is defined, this method will always
|
||||
* return a {@link Auth_Yadis_PlainHTTPFetcher}.
|
||||
*/
|
||||
function getHTTPFetcher($timeout = 20)
|
||||
{
|
||||
if (Auth_Yadis_Yadis::curlPresent() &&
|
||||
(!defined('Auth_Yadis_CURL_OVERRIDE'))) {
|
||||
$fetcher = new Auth_Yadis_ParanoidHTTPFetcher($timeout);
|
||||
} else {
|
||||
$fetcher = new Auth_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
|
||||
* Auth_Yadis_HTTPResponse}.
|
||||
*
|
||||
* @param Auth_Yadis_HTTPFetcher $fetcher An instance of a
|
||||
* Auth_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
|
||||
* Auth_Yadis_Yadis, depending on whether the discovery
|
||||
* succeeded.
|
||||
*/
|
||||
function discover($uri, &$fetcher,
|
||||
$extra_ns_map = null, $timeout = 20)
|
||||
{
|
||||
$result = new Auth_Yadis_DiscoveryResult($uri);
|
||||
|
||||
$request_uri = $uri;
|
||||
$headers = array("Accept: " . Auth_Yadis_CONTENT_TYPE .
|
||||
', text/html; q=0.3, application/xhtml+xml; q=0.5');
|
||||
|
||||
if ($fetcher === null) {
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher($timeout);
|
||||
}
|
||||
|
||||
$response = $fetcher->get($uri, $headers);
|
||||
|
||||
if (!$response || ($response->status != 200 and
|
||||
$response->status != 206)) {
|
||||
$result->fail();
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result->normalized_uri = $response->final_url;
|
||||
$result->content_type = Auth_Yadis_Yadis::_getHeader(
|
||||
$response->headers,
|
||||
array('content-type'));
|
||||
|
||||
if ($result->content_type &&
|
||||
(Auth_Yadis_Yadis::_getContentType($result->content_type) ==
|
||||
Auth_Yadis_CONTENT_TYPE)) {
|
||||
$result->xrds_uri = $result->normalized_uri;
|
||||
} else {
|
||||
$yadis_location = Auth_Yadis_Yadis::_getHeader(
|
||||
$response->headers,
|
||||
array(Auth_Yadis_HEADER_NAME));
|
||||
|
||||
if (!$yadis_location) {
|
||||
$parser = new Auth_Yadis_ParseHTML();
|
||||
$yadis_location = $parser->getHTTPEquiv($response->body);
|
||||
}
|
||||
|
||||
if ($yadis_location) {
|
||||
$result->xrds_uri = $yadis_location;
|
||||
|
||||
$response = $fetcher->get($yadis_location);
|
||||
|
||||
if ((!$response) || ($response->status != 200 and
|
||||
$response->status != 206)) {
|
||||
$result->fail();
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result->content_type = Auth_Yadis_Yadis::_getHeader(
|
||||
$response->headers,
|
||||
array('content-type'));
|
||||
}
|
||||
}
|
||||
|
||||
$result->response_text = $response->body;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Reference in New Issue
Block a user