17515512: JanRain php-openid library included, normalization fix

git-svn-id: http://xe-core.googlecode.com/svn/sandbox@5137 201d5d3c-b55e-5fd7-737f-ddc643e51545
This commit is contained in:
araste 2008-12-19 11:00:19 +00:00
parent f541dd59b9
commit 658c0e6dbc
219 changed files with 41415 additions and 10 deletions

View file

@ -86,37 +86,112 @@
ob_clean();
}
function getLegacyUserIDsFromOpenID($openid_identity) {
// Issue 17515512:
// OpenID Provider가 openid.identity를
// http://araste.myid.net/ 으로 넘겨주든
// http://araste.myid.net 으로 넘겨주든,
// 동일한 제로보드 사용자 이름에 매핑되어야 한다.
// 가입되지 않은 경우엔 / 를 뗀 것이 우선이 된다.
// OpenID Provider의 정책의 일시적 변경 때문에
// / 가 붙은 꼴의 유저아이디로 (예: jangxyz.myid.net/ )
// 가입된 사례가 있다. 이 때는
// jangxyz.myid.net 과 jangxyz.myid.net/ 둘 모두로 시도해보아야 한다.
// 이를 위한 workaround이다.
$result = array();
$uri_matches = array();
preg_match(Auth_OpenID_getURIPattern(), $openid_identity, $uri_matches);
if (count($uri_matches) < 9) {
for ($i = count($uri_matches); $i <= 9; $i++) {
$uri_matches[] = '';
}
}
$scheme = $uri_matches[2];
$authority = $uri_matches[4];
$path = $uri_matches[5];
$query = $uri_matches[6];
$fragment = $uri_matches[8];
if ($scheme === null) $scheme = '';
if ($authority === null) $authority = '';
if ($path === null) $path = '';
if ($query === null) $query = '';
if ($fragment === null) $fragment = '';
if ($scheme == 'http' or $scheme == '')
$scheme_part = '';
else
$scheme_part = $scheme."://";
if ($path == '' || $path == '/') {
$result[] = $scheme_part.$authority.''.$query.$fragment;
$result[] = $scheme_part.$authority.'/'.$query.$fragment;
}
else {
$result[] = $scheme_part.$authority.$path.$query.$fragment;
}
return $result;
}
/**
* @brief openid 인증 체크
**/
function procMemberOpenIDValidate() {
// use the JanRain php-openid library
ini_set('include_path', ini_get('include_path').':./modules/member/php-openid-1.2.3');
require_once('Auth/OpenID/URINorm.php');
$oModuleModel = &getModel('module');
$config = $oModuleModel->getModuleConfig('member');
if($config->enable_openid != 'Y') $this->stop('msg_invalid_request');
$openid_identity = Auth_OpenID_urinorm($_GET['openid_identity']);
ob_start();
require('./modules/member/openid_lib/class.openid.php');
require_once('./modules/member/openid_lib/libcurlemu.inc.php');
$openid = new SimpleOpenID;
$openid->SetIdentity($_GET['openid_identity']);
$openid->SetIdentity($openid_identity);
$openid_validation_result = $openid->ValidateWithServer();
ob_clean();
// 인증 성공
if ($openid_validation_result == true) {
// 기본 정보들을 받음
$args->user_id = $args->nick_name = preg_replace('/^http:\/\//i','',Context::get('openid_identity'));
$args->email_address = Context::get('openid_sreg_email');
$args->user_name = Context::get('openid_sreg_fullname');
if(!$args->user_name) list($args->user_name) = explode('@', $args->email_address);
$args->birthday = str_replace('-','',Context::get('openid_sreg_dob'));
// 자체 인증 시도
$output = $this->doLogin($args->user_id);
// 이 오픈아이디와 연결된 (또는 연결되어 있을 가능성이 있는) 제로보드 아이디들을 받아온다.
$login_success = false;
$user_id_candidates = $this->getLegacyUserIDsFromOpenID($openid_identity);
$default_user_id = $user_id_candidates[0];
foreach($user_id_candidates as $user_id) {
$args->user_id = $args->nick_name = $user_id;
// 기본 정보들을 받음
$args->email_address = Context::get('openid_sreg_email');
$args->user_name = Context::get('openid_sreg_fullname');
if(!$args->user_name) list($args->user_name) = explode('@', $args->email_address);
$args->birthday = str_replace('-','',Context::get('openid_sreg_dob'));
// 자체 인증 시도
$output = $this->doLogin($args->user_id);
if ($output->toBool()) {
$login_success = true;
break;
}
}
// 자체 인증 실패시 회원 가입시킴
if(!$output->toBool()) {
if(!$login_success) {
$args->user_id = $args->nick_name = $default_user_id;
$args->password = md5(getmicrotime());
$output = $this->insertMember($args);
if(!$output->toBool()) return $this->stop($output->getMessage());

View file

@ -0,0 +1,412 @@
<?php
/**
* This is the PHP OpenID library by JanRain, Inc.
*
* This module contains core utility functionality used by the
* library. See Consumer.php and Server.php for the consumer and
* server implementations.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Require the fetcher code.
*/
require_once "Services/Yadis/PlainHTTPFetcher.php";
require_once "Services/Yadis/ParanoidHTTPFetcher.php";
require_once "Auth/OpenID/BigMath.php";
/**
* Status code returned by the server when the only option is to show
* an error page, since we do not have enough information to redirect
* back to the consumer. The associated value is an error message that
* should be displayed on an HTML error page.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_LOCAL_ERROR', 'local_error');
/**
* Status code returned when there is an error to return in key-value
* form to the consumer. The caller should return a 400 Bad Request
* response with content-type text/plain and the value as the body.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REMOTE_ERROR', 'remote_error');
/**
* Status code returned when there is a key-value form OK response to
* the consumer. The value associated with this code is the
* response. The caller should return a 200 OK response with
* content-type text/plain and the value as the body.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REMOTE_OK', 'remote_ok');
/**
* Status code returned when there is a redirect back to the
* consumer. The value is the URL to redirect back to. The caller
* should return a 302 Found redirect with a Location: header
* containing the URL.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_REDIRECT', 'redirect');
/**
* Status code returned when the caller needs to authenticate the
* user. The associated value is a {@link Auth_OpenID_ServerRequest}
* object that can be used to complete the authentication. If the user
* has taken some authentication action, use the retry() method of the
* {@link Auth_OpenID_ServerRequest} object to complete the request.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_DO_AUTH', 'do_auth');
/**
* Status code returned when there were no OpenID arguments
* passed. This code indicates that the caller should return a 200 OK
* response and display an HTML page that says that this is an OpenID
* server endpoint.
*
* @see Auth_OpenID_Server
*/
define('Auth_OpenID_DO_ABOUT', 'do_about');
/**
* Defines for regexes and format checking.
*/
define('Auth_OpenID_letters',
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
define('Auth_OpenID_digits',
"0123456789");
define('Auth_OpenID_punct',
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
if (Auth_OpenID_getMathLib() === null) {
define('Auth_OpenID_NO_MATH_SUPPORT', true);
}
/**
* The OpenID utility function class.
*
* @package OpenID
* @access private
*/
class Auth_OpenID {
/**
* These namespaces are automatically fixed in query arguments by
* Auth_OpenID::fixArgs.
*/
function getOpenIDNamespaces()
{
return array('openid',
'sreg');
}
/**
* Rename query arguments back to 'openid.' from 'openid_'
*
* @access private
* @param array $args An associative array of URL query arguments
*/
function fixArgs($args)
{
foreach (array_keys($args) as $key) {
$fixed = $key;
if (preg_match('/^openid/', $key)) {
foreach (Auth_OpenID::getOpenIDNamespaces() as $ns) {
if (preg_match('/'.$ns.'_/', $key)) {
$fixed = preg_replace('/'.$ns.'_/', $ns.'.', $fixed);
}
}
if ($fixed != $key) {
$val = $args[$key];
unset($args[$key]);
$args[$fixed] = $val;
}
}
}
return $args;
}
/**
* Create dir_name as a directory if it does not exist. If it
* exists, make sure that it is, in fact, a directory. Returns
* true if the operation succeeded; false if not.
*
* @access private
*/
function ensureDir($dir_name)
{
if (is_dir($dir_name) || @mkdir($dir_name)) {
return true;
} else {
if (Auth_OpenID::ensureDir(dirname($dir_name))) {
return is_dir($dir_name) || @mkdir($dir_name);
} else {
return false;
}
}
}
/**
* Convenience function for getting array values.
*
* @access private
*/
function arrayGet($arr, $key, $fallback = null)
{
if (is_array($arr)) {
if (array_key_exists($key, $arr)) {
return $arr[$key];
} else {
return $fallback;
}
} else {
trigger_error("Auth_OpenID::arrayGet expected " .
"array as first parameter", E_USER_WARNING);
return false;
}
}
/**
* Implements the PHP 5 'http_build_query' functionality.
*
* @access private
* @param array $data Either an array key/value pairs or an array
* of arrays, each of which holding two values: a key and a value,
* sequentially.
* @return string $result The result of url-encoding the key/value
* pairs from $data into a URL query string
* (e.g. "username=bob&id=56").
*/
function httpBuildQuery($data)
{
$pairs = array();
foreach ($data as $key => $value) {
if (is_array($value)) {
$pairs[] = urlencode($value[0])."=".urlencode($value[1]);
} else {
$pairs[] = urlencode($key)."=".urlencode($value);
}
}
return implode("&", $pairs);
}
/**
* "Appends" query arguments onto a URL. The URL may or may not
* already have arguments (following a question mark).
*
* @param string $url A URL, which may or may not already have
* arguments.
* @param array $args Either an array key/value pairs or an array of
* arrays, each of which holding two values: a key and a value,
* sequentially. If $args is an ordinary key/value array, the
* parameters will be added to the URL in sorted alphabetical order;
* if $args is an array of arrays, their order will be preserved.
* @return string $url The original URL with the new parameters added.
*
*/
function appendArgs($url, $args)
{
if (count($args) == 0) {
return $url;
}
// Non-empty array; if it is an array of arrays, use
// multisort; otherwise use sort.
if (array_key_exists(0, $args) &&
is_array($args[0])) {
// Do nothing here.
} else {
$keys = array_keys($args);
sort($keys);
$new_args = array();
foreach ($keys as $key) {
$new_args[] = array($key, $args[$key]);
}
$args = $new_args;
}
$sep = '?';
if (strpos($url, '?') !== false) {
$sep = '&';
}
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
/**
* Turn a string into an ASCII string.
*
* Replace non-ascii characters with a %-encoded, UTF-8
* encoding. This function will fail if the input is a string and
* there are non-7-bit-safe characters. It is assumed that the
* caller will have already translated the input into a Unicode
* character sequence, according to the encoding of the HTTP POST
* or GET.
*
* Do not escape anything that is already 7-bit safe, so we do the
* minimal transform on the identity URL
*
* @access private
*/
function quoteMinimal($s)
{
$res = array();
for ($i = 0; $i < strlen($s); $i++) {
$c = $s[$i];
if ($c >= "\x80") {
for ($j = 0; $j < count(utf8_encode($c)); $j++) {
array_push($res, sprintf("%02X", ord($c[$j])));
}
} else {
array_push($res, $c);
}
}
return implode('', $res);
}
/**
* Implements python's urlunparse, which is not available in PHP.
* Given the specified components of a URL, this function rebuilds
* and returns the URL.
*
* @access private
* @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'.
* @param string $host The host. Required.
* @param string $port The port.
* @param string $path The path.
* @param string $query The query.
* @param string $fragment The fragment.
* @return string $url The URL resulting from assembling the
* specified components.
*/
function urlunparse($scheme, $host, $port = null, $path = '/',
$query = '', $fragment = '')
{
if (!$scheme) {
$scheme = 'http';
}
if (!$host) {
return false;
}
if (!$path) {
$path = '/';
}
$result = $scheme . "://" . $host;
if ($port) {
$result .= ":" . $port;
}
$result .= $path;
if ($query) {
$result .= "?" . $query;
}
if ($fragment) {
$result .= "#" . $fragment;
}
return $result;
}
/**
* Given a URL, this "normalizes" it by adding a trailing slash
* and / or a leading http:// scheme where necessary. Returns
* null if the original URL is malformed and cannot be normalized.
*
* @access private
* @param string $url The URL to be normalized.
* @return mixed $new_url The URL after normalization, or null if
* $url was malformed.
*/
function normalizeUrl($url)
{
if ($url === null) {
return null;
}
assert(is_string($url));
$old_url = $url;
$url = trim($url);
if (strpos($url, "://") === false) {
$url = "http://" . $url;
}
$parsed = @parse_url($url);
if ($parsed === false) {
return null;
}
$defaults = array(
'scheme' => '',
'host' => '',
'path' => '',
'query' => '',
'fragment' => '',
'port' => ''
);
$parsed = array_merge($defaults, $parsed);
if (($parsed['scheme'] == '') ||
($parsed['host'] == '')) {
if ($parsed['path'] == '' &&
$parsed['query'] == '' &&
$parsed['fragment'] == '') {
return null;
}
$url = 'http://' + $url;
$parsed = parse_url($url);
$parsed = array_merge($defaults, $parsed);
}
$tail = array_map(array('Auth_OpenID', 'quoteMinimal'),
array($parsed['path'],
$parsed['query'],
$parsed['fragment']));
if ($tail[0] == '') {
$tail[0] = '/';
}
$url = Auth_OpenID::urlunparse($parsed['scheme'], $parsed['host'],
$parsed['port'], $tail[0], $tail[1],
$tail[2]);
assert(is_string($url));
return $url;
}
}
?>

View file

@ -0,0 +1,308 @@
<?php
/**
* This module contains code for dealing with associations between
* consumers and servers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* @access private
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* @access private
*/
require_once 'Auth/OpenID/KVForm.php';
/**
* This class represents an association between a server and a
* consumer. In general, users of this library will never see
* instances of this object. The only exception is if you implement a
* custom {@link Auth_OpenID_OpenIDStore}.
*
* If you do implement such a store, it will need to store the values
* of the handle, secret, issued, lifetime, and assoc_type instance
* variables.
*
* @package OpenID
*/
class Auth_OpenID_Association {
/**
* This is a HMAC-SHA1 specific value.
*
* @access private
*/
var $SIG_LENGTH = 20;
/**
* The ordering and name of keys as stored by serialize.
*
* @access private
*/
var $assoc_keys = array(
'version',
'handle',
'secret',
'issued',
'lifetime',
'assoc_type'
);
/**
* This is an alternate constructor (factory method) used by the
* OpenID consumer library to create associations. OpenID store
* implementations shouldn't use this constructor.
*
* @access private
*
* @param integer $expires_in This is the amount of time this
* association is good for, measured in seconds since the
* association was issued.
*
* @param string $handle This is the handle the server gave this
* association.
*
* @param string secret This is the shared secret the server
* generated for this association.
*
* @param assoc_type This is the type of association this
* instance represents. The only valid value of this field at
* this time is 'HMAC-SHA1', but new types may be defined in the
* future.
*
* @return association An {@link Auth_OpenID_Association}
* instance.
*/
function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
{
$issued = time();
$lifetime = $expires_in;
return new Auth_OpenID_Association($handle, $secret,
$issued, $lifetime, $assoc_type);
}
/**
* This is the standard constructor for creating an association.
* The library should create all of the necessary associations, so
* this constructor is not part of the external API.
*
* @access private
*
* @param string $handle This is the handle the server gave this
* association.
*
* @param string $secret This is the shared secret the server
* generated for this association.
*
* @param integer $issued This is the time this association was
* issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a
* unix timestamp)
*
* @param integer $lifetime This is the amount of time this
* association is good for, measured in seconds since the
* association was issued.
*
* @param string $assoc_type This is the type of association this
* instance represents. The only valid value of this field at
* this time is 'HMAC-SHA1', but new types may be defined in the
* future.
*/
function Auth_OpenID_Association(
$handle, $secret, $issued, $lifetime, $assoc_type)
{
if ($assoc_type != 'HMAC-SHA1') {
$fmt = 'HMAC-SHA1 is the only supported association type (got %s)';
trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
}
$this->handle = $handle;
$this->secret = $secret;
$this->issued = $issued;
$this->lifetime = $lifetime;
$this->assoc_type = $assoc_type;
}
/**
* This returns the number of seconds this association is still
* valid for, or 0 if the association is no longer valid.
*
* @return integer $seconds The number of seconds this association
* is still valid for, or 0 if the association is no longer valid.
*/
function getExpiresIn($now = null)
{
if ($now == null) {
$now = time();
}
return max(0, $this->issued + $this->lifetime - $now);
}
/**
* This checks to see if two {@link Auth_OpenID_Association}
* instances represent the same association.
*
* @return bool $result true if the two instances represent the
* same association, false otherwise.
*/
function equal($other)
{
return ((gettype($this) == gettype($other))
&& ($this->handle == $other->handle)
&& ($this->secret == $other->secret)
&& ($this->issued == $other->issued)
&& ($this->lifetime == $other->lifetime)
&& ($this->assoc_type == $other->assoc_type));
}
/**
* Convert an association to KV form.
*
* @return string $result String in KV form suitable for
* deserialization by deserialize.
*/
function serialize()
{
$data = array(
'version' => '2',
'handle' => $this->handle,
'secret' => base64_encode($this->secret),
'issued' => strval(intval($this->issued)),
'lifetime' => strval(intval($this->lifetime)),
'assoc_type' => $this->assoc_type
);
assert(array_keys($data) == $this->assoc_keys);
return Auth_OpenID_KVForm::fromArray($data, $strict = true);
}
/**
* Parse an association as stored by serialize(). This is the
* inverse of serialize.
*
* @param string $assoc_s Association as serialized by serialize()
* @return Auth_OpenID_Association $result instance of this class
*/
function deserialize($class_name, $assoc_s)
{
$pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
$keys = array();
$values = array();
foreach ($pairs as $key => $value) {
if (is_array($value)) {
list($key, $value) = $value;
}
$keys[] = $key;
$values[] = $value;
}
$class_vars = get_class_vars($class_name);
$class_assoc_keys = $class_vars['assoc_keys'];
sort($keys);
sort($class_assoc_keys);
if ($keys != $class_assoc_keys) {
trigger_error('Unexpected key values: ' . strval($keys),
E_USER_WARNING);
return null;
}
$version = $pairs['version'];
$handle = $pairs['handle'];
$secret = $pairs['secret'];
$issued = $pairs['issued'];
$lifetime = $pairs['lifetime'];
$assoc_type = $pairs['assoc_type'];
if ($version != '2') {
trigger_error('Unknown version: ' . $version, E_USER_WARNING);
return null;
}
$issued = intval($issued);
$lifetime = intval($lifetime);
$secret = base64_decode($secret);
return new $class_name(
$handle, $secret, $issued, $lifetime, $assoc_type);
}
/**
* Generate a signature for a sequence of (key, value) pairs
*
* @access private
* @param array $pairs The pairs to sign, in order. This is an
* array of two-tuples.
* @return string $signature The binary signature of this sequence
* of pairs
*/
function sign($pairs)
{
$kv = Auth_OpenID_KVForm::fromArray($pairs);
return Auth_OpenID_HMACSHA1($this->secret, $kv);
}
/**
* Generate a signature for some fields in a dictionary
*
* @access private
* @param array $fields The fields to sign, in order; this is an
* array of strings.
* @param array $data Dictionary of values to sign (an array of
* string => string pairs).
* @return string $signature The signature, base64 encoded
*/
function signDict($fields, $data, $prefix = 'openid.')
{
$pairs = array();
foreach ($fields as $field) {
$pairs[] = array($field, $data[$prefix . $field]);
}
return base64_encode($this->sign($pairs));
}
/**
* Add a signature to an array of fields
*
* @access private
*/
function addSignature($fields, &$data, $prefix = 'openid.')
{
$sig = $this->signDict($fields, $data, $prefix);
$signed = implode(",", $fields);
$data[$prefix . 'sig'] = $sig;
$data[$prefix . 'signed'] = $signed;
}
/**
* Confirm that the signature of these fields matches the
* signature contained in the data
*
* @access private
*/
function checkSignature($data, $prefix = 'openid.')
{
$signed = $data[$prefix . 'signed'];
$fields = explode(",", $signed);
$expected_sig = $this->signDict($fields, $data, $prefix);
$request_sig = $data[$prefix . 'sig'];
return ($request_sig == $expected_sig);
}
}
?>

View file

@ -0,0 +1,444 @@
<?php
/**
* BigMath: A math library wrapper that abstracts out the underlying
* long integer library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Needed for random number generation
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* The superclass of all big-integer math implementations
* @access private
* @package OpenID
*/
class Auth_OpenID_MathLibrary {
/**
* Given a long integer, returns the number converted to a binary
* string. This function accepts long integer values of arbitrary
* magnitude and uses the local large-number math library when
* available.
*
* @param integer $long The long number (can be a normal PHP
* integer or a number created by one of the available long number
* libraries)
* @return string $binary The binary version of $long
*/
function longToBinary($long)
{
$cmp = $this->cmp($long, 0);
if ($cmp < 0) {
$msg = __FUNCTION__ . " takes only positive integers.";
trigger_error($msg, E_USER_ERROR);
return null;
}
if ($cmp == 0) {
return "\x00";
}
$bytes = array();
while ($this->cmp($long, 0) > 0) {
array_unshift($bytes, $this->mod($long, 256));
$long = $this->div($long, pow(2, 8));
}
if ($bytes && ($bytes[0] > 127)) {
array_unshift($bytes, 0);
}
$string = '';
foreach ($bytes as $byte) {
$string .= pack('C', $byte);
}
return $string;
}
/**
* Given a binary string, returns the binary string converted to a
* long number.
*
* @param string $binary The binary version of a long number,
* probably as a result of calling longToBinary
* @return integer $long The long number equivalent of the binary
* string $str
*/
function binaryToLong($str)
{
if ($str === null) {
return null;
}
// Use array_merge to return a zero-indexed array instead of a
// one-indexed array.
$bytes = array_merge(unpack('C*', $str));
$n = $this->init(0);
if ($bytes && ($bytes[0] > 127)) {
trigger_error("bytesToNum works only for positive integers.",
E_USER_WARNING);
return null;
}
foreach ($bytes as $byte) {
$n = $this->mul($n, pow(2, 8));
$n = $this->add($n, $byte);
}
return $n;
}
function base64ToLong($str)
{
$b64 = base64_decode($str);
if ($b64 === false) {
return false;
}
return $this->binaryToLong($b64);
}
function longToBase64($str)
{
return base64_encode($this->longToBinary($str));
}
/**
* Returns a random number in the specified range. This function
* accepts $start, $stop, and $step values of arbitrary magnitude
* and will utilize the local large-number math library when
* available.
*
* @param integer $start The start of the range, or the minimum
* random number to return
* @param integer $stop The end of the range, or the maximum
* random number to return
* @param integer $step The step size, such that $result - ($step
* * N) = $start for some N
* @return integer $result The resulting randomly-generated number
*/
function rand($stop)
{
static $duplicate_cache = array();
// Used as the key for the duplicate cache
$rbytes = $this->longToBinary($stop);
if (array_key_exists($rbytes, $duplicate_cache)) {
list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
} else {
if ($rbytes[0] == "\x00") {
$nbytes = strlen($rbytes) - 1;
} else {
$nbytes = strlen($rbytes);
}
$mxrand = $this->pow(256, $nbytes);
// If we get a number less than this, then it is in the
// duplicated range.
$duplicate = $this->mod($mxrand, $stop);
if (count($duplicate_cache) > 10) {
$duplicate_cache = array();
}
$duplicate_cache[$rbytes] = array($duplicate, $nbytes);
}
do {
$bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes);
$n = $this->binaryToLong($bytes);
// Keep looping if this value is in the low duplicated range
} while ($this->cmp($n, $duplicate) < 0);
return $this->mod($n, $stop);
}
}
/**
* Exposes BCmath math library functionality.
*
* {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided
* by the BCMath extension.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{
var $type = 'bcmath';
function add($x, $y)
{
return bcadd($x, $y);
}
function sub($x, $y)
{
return bcsub($x, $y);
}
function pow($base, $exponent)
{
return bcpow($base, $exponent);
}
function cmp($x, $y)
{
return bccomp($x, $y);
}
function init($number, $base = 10)
{
return $number;
}
function mod($base, $modulus)
{
return bcmod($base, $modulus);
}
function mul($x, $y)
{
return bcmul($x, $y);
}
function div($x, $y)
{
return bcdiv($x, $y);
}
/**
* Same as bcpowmod when bcpowmod is missing
*
* @access private
*/
function _powmod($base, $exponent, $modulus)
{
$square = $this->mod($base, $modulus);
$result = 1;
while($this->cmp($exponent, 0) > 0) {
if ($this->mod($exponent, 2)) {
$result = $this->mod($this->mul($result, $square), $modulus);
}
$square = $this->mod($this->mul($square, $square), $modulus);
$exponent = $this->div($exponent, 2);
}
return $result;
}
function powmod($base, $exponent, $modulus)
{
if (function_exists('bcpowmod')) {
return bcpowmod($base, $exponent, $modulus);
} else {
return $this->_powmod($base, $exponent, $modulus);
}
}
function toString($num)
{
return $num;
}
}
/**
* Exposes GMP math library functionality.
*
* {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided
* by the GMP extension.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
var $type = 'gmp';
function add($x, $y)
{
return gmp_add($x, $y);
}
function sub($x, $y)
{
return gmp_sub($x, $y);
}
function pow($base, $exponent)
{
return gmp_pow($base, $exponent);
}
function cmp($x, $y)
{
return gmp_cmp($x, $y);
}
function init($number, $base = 10)
{
return gmp_init($number, $base);
}
function mod($base, $modulus)
{
return gmp_mod($base, $modulus);
}
function mul($x, $y)
{
return gmp_mul($x, $y);
}
function div($x, $y)
{
return gmp_div_q($x, $y);
}
function powmod($base, $exponent, $modulus)
{
return gmp_powm($base, $exponent, $modulus);
}
function toString($num)
{
return gmp_strval($num);
}
}
/**
* Define the supported extensions. An extension array has keys
* 'modules', 'extension', and 'class'. 'modules' is an array of PHP
* module names which the loading code will attempt to load. These
* values will be suffixed with a library file extension (e.g. ".so").
* 'extension' is the name of a PHP extension which will be tested
* before 'modules' are loaded. 'class' is the string name of a
* {@link Auth_OpenID_MathWrapper} subclass which should be
* instantiated if a given extension is present.
*
* You can define new math library implementations and add them to
* this array.
*/
global $_Auth_OpenID_math_extensions;
$_Auth_OpenID_math_extensions = array(
array('modules' => array('gmp', 'php_gmp'),
'extension' => 'gmp',
'class' => 'Auth_OpenID_GmpMathWrapper'),
array('modules' => array('bcmath', 'php_bcmath'),
'extension' => 'bcmath',
'class' => 'Auth_OpenID_BcMathWrapper')
);
/**
* Detect which (if any) math library is available
*/
function Auth_OpenID_detectMathLibrary($exts)
{
$loaded = false;
foreach ($exts as $extension) {
// See if the extension specified is already loaded.
if ($extension['extension'] &&
extension_loaded($extension['extension'])) {
$loaded = true;
}
// Try to load dynamic modules.
if (!$loaded) {
foreach ($extension['modules'] as $module) {
if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
$loaded = true;
break;
}
}
}
// If the load succeeded, supply an instance of
// Auth_OpenID_MathWrapper which wraps the specified
// module's functionality.
if ($loaded) {
return $extension;
}
}
return false;
}
/**
* {@link Auth_OpenID_getMathLib} checks for the presence of long
* number extension modules and returns an instance of
* {@link Auth_OpenID_MathWrapper} which exposes the module's
* functionality.
*
* Checks for the existence of an extension module described by the
* local {@link Auth_OpenID_math_extensions} array and returns an
* instance of a wrapper for that extension module. If no extension
* module is found, an instance of {@link Auth_OpenID_MathWrapper} is
* returned, which wraps the native PHP integer implementation. The
* proper calling convention for this method is $lib =&
* Auth_OpenID_getMathLib().
*
* This function checks for the existence of specific long number
* implementations in the following order: GMP followed by BCmath.
*
* @return Auth_OpenID_MathWrapper $instance An instance of
* {@link Auth_OpenID_MathWrapper} or one of its subclasses
*
* @package OpenID
*/
function &Auth_OpenID_getMathLib()
{
// The instance of Auth_OpenID_MathWrapper that we choose to
// supply will be stored here, so that subseqent calls to this
// method will return a reference to the same object.
static $lib = null;
if (isset($lib)) {
return $lib;
}
if (defined('Auth_OpenID_NO_MATH_SUPPORT')) {
$null = null;
return $null;
}
// If this method has not been called before, look at
// $Auth_OpenID_math_extensions and try to find an extension that
// works.
global $_Auth_OpenID_math_extensions;
$ext = Auth_OpenID_detectMathLibrary($_Auth_OpenID_math_extensions);
if ($ext === false) {
$tried = array();
foreach ($_Auth_OpenID_math_extensions as $extinfo) {
$tried[] = $extinfo['extension'];
}
$triedstr = implode(", ", $tried);
define('Auth_OpenID_NO_MATH_SUPPORT', true);
return null;
}
// Instantiate a new wrapper
$class = $ext['class'];
$lib = new $class();
return $lib;
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
<?php
/**
* CryptUtil: A suite of wrapper utility functions for the OpenID
* library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
if (!defined('Auth_OpenID_RAND_SOURCE')) {
/**
* The filename for a source of random bytes. Define this yourself
* if you have a different source of randomness.
*/
define('Auth_OpenID_RAND_SOURCE', '/dev/urandom');
}
class Auth_OpenID_CryptUtil {
/**
* Get the specified number of random bytes.
*
* Attempts to use a cryptographically secure (not predictable)
* source of randomness if available. If there is no high-entropy
* randomness source available, it will fail. As a last resort,
* for non-critical systems, define
* <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and
* the code will fall back on a pseudo-random number generator.
*
* @param int $num_bytes The length of the return value
* @return string $bytes random bytes
*/
function getBytes($num_bytes)
{
static $f = null;
$bytes = '';
if ($f === null) {
if (Auth_OpenID_RAND_SOURCE === null) {
$f = false;
} else {
$f = @fopen(Auth_OpenID_RAND_SOURCE, "r");
if ($f === false) {
$msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' .
' continue with an insecure random number generator.';
trigger_error($msg, E_USER_ERROR);
}
}
}
if ($f === false) {
// pseudorandom used
$bytes = '';
for ($i = 0; $i < $num_bytes; $i += 4) {
$bytes .= pack('L', mt_rand());
}
$bytes = substr($bytes, 0, $num_bytes);
} else {
$bytes = fread($f, $num_bytes);
}
return $bytes;
}
/**
* Produce a string of length random bytes, chosen from chrs. If
* $chrs is null, the resulting string may contain any characters.
*
* @param integer $length The length of the resulting
* randomly-generated string
* @param string $chrs A string of characters from which to choose
* to build the new string
* @return string $result A string of randomly-chosen characters
* from $chrs
*/
function randomString($length, $population = null)
{
if ($population === null) {
return Auth_OpenID_CryptUtil::getBytes($length);
}
$popsize = strlen($population);
if ($popsize > 256) {
$msg = 'More than 256 characters supplied to ' . __FUNCTION__;
trigger_error($msg, E_USER_ERROR);
}
$duplicate = 256 % $popsize;
$str = "";
for ($i = 0; $i < $length; $i++) {
do {
$n = ord(Auth_OpenID_CryptUtil::getBytes(1));
} while ($n < $duplicate);
$n %= $popsize;
$str .= $population[$n];
}
return $str;
}
}
?>

View file

@ -0,0 +1,131 @@
<?php
/**
* The Auth_OpenID_DatabaseConnection class, which is used to emulate
* a PEAR database connection.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* An empty base class intended to emulate PEAR connection
* functionality in applications that supply their own database
* abstraction mechanisms. See {@link Auth_OpenID_SQLStore} for more
* information. You should subclass this class if you need to create
* an SQL store that needs to access its database using an
* application's database abstraction layer instead of a PEAR database
* connection. Any subclass of Auth_OpenID_DatabaseConnection MUST
* adhere to the interface specified here.
*
* @package OpenID
*/
class Auth_OpenID_DatabaseConnection {
/**
* Sets auto-commit mode on this database connection.
*
* @param bool $mode True if auto-commit is to be used; false if
* not.
*/
function autoCommit($mode)
{
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return mixed $result The result of calling this connection's
* internal query function. The type of result depends on the
* underlying database engine. This method is usually used when
* the result of a query is not important, like a DDL query.
*/
function query($sql, $params = array())
{
}
/**
* Starts a transaction on this connection, if supported.
*/
function begin()
{
}
/**
* Commits a transaction on this connection, if supported.
*/
function commit()
{
}
/**
* Performs a rollback on this connection, if supported.
*/
function rollback()
{
}
/**
* Run an SQL query and return the first column of the first row
* of the result set, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return mixed $result The value of the first column of the
* first row of the result set. False if no such result was
* found.
*/
function getOne($sql, $params = array())
{
}
/**
* Run an SQL query and return the first row of the result set, if
* any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return array $result The first row of the result set, if any,
* keyed on column name. False if no such result was found.
*/
function getRow($sql, $params = array())
{
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql An SQL string with placeholders. The
* placeholders are assumed to be specific to the database engine
* for this connection.
*
* @param array $params An array of parameters to insert into the
* SQL string using this connection's escaping mechanism.
*
* @return array $result An array of arrays representing the
* result of the query; each array is keyed on column name.
*/
function getAll($sql, $params = array())
{
}
}
?>

View file

@ -0,0 +1,181 @@
<?php
/**
* The OpenID library's Diffie-Hellman implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'Auth/OpenID/BigMath.php';
require_once 'Auth/OpenID/HMACSHA1.php';
function Auth_OpenID_getDefaultMod()
{
return '155172898181473697471232257763715539915724801'.
'966915404479707795314057629378541917580651227423'.
'698188993727816152646631438561595825688188889951'.
'272158842675419950341258706556549803580104870537'.
'681476726513255747040765857479291291572334510643'.
'245094715007229621094194349783925984760375594985'.
'848253359305585439638443';
}
function Auth_OpenID_getDefaultGen()
{
return '2';
}
/**
* The Diffie-Hellman key exchange class. This class relies on
* {@link Auth_OpenID_MathLibrary} to perform large number operations.
*
* @access private
* @package OpenID
*/
class Auth_OpenID_DiffieHellman {
var $mod;
var $gen;
var $private;
var $lib = null;
function Auth_OpenID_DiffieHellman($mod = null, $gen = null,
$private = null, $lib = null)
{
if ($lib === null) {
$this->lib =& Auth_OpenID_getMathLib();
} else {
$this->lib =& $lib;
}
if ($mod === null) {
$this->mod = $this->lib->init(Auth_OpenID_getDefaultMod());
} else {
$this->mod = $mod;
}
if ($gen === null) {
$this->gen = $this->lib->init(Auth_OpenID_getDefaultGen());
} else {
$this->gen = $gen;
}
if ($private === null) {
$r = $this->lib->rand($this->mod);
$this->private = $this->lib->add($r, 1);
} else {
$this->private = $private;
}
$this->public = $this->lib->powmod($this->gen, $this->private,
$this->mod);
}
function getSharedSecret($composite)
{
return $this->lib->powmod($composite, $this->private, $this->mod);
}
function getPublicKey()
{
return $this->public;
}
/**
* Generate the arguments for an OpenID Diffie-Hellman association
* request
*/
function getAssocArgs()
{
$cpub = $this->lib->longToBase64($this->getPublicKey());
$args = array(
'openid.dh_consumer_public' => $cpub,
'openid.session_type' => 'DH-SHA1'
);
if ($this->lib->cmp($this->mod, Auth_OpenID_getDefaultMod()) ||
$this->lib->cmp($this->gen, Auth_OpenID_getDefaultGen())) {
$args['openid.dh_modulus'] = $this->lib->longToBase64($this->mod);
$args['openid.dh_gen'] = $this->lib->longToBase64($this->gen);
}
return $args;
}
function usingDefaultValues()
{
return ($this->mod == Auth_OpenID_getDefaultMod() &&
$this->gen == Auth_OpenID_getDefaultGen());
}
/**
* Perform the server side of the OpenID Diffie-Hellman association
*/
function serverAssociate($consumer_args, $assoc_secret)
{
$lib =& Auth_OpenID_getMathLib();
if (isset($consumer_args['openid.dh_modulus'])) {
$mod = $lib->base64ToLong($consumer_args['openid.dh_modulus']);
} else {
$mod = null;
}
if (isset($consumer_args['openid.dh_gen'])) {
$gen = $lib->base64ToLong($consumer_args['openid.dh_gen']);
} else {
$gen = null;
}
$cpub64 = @$consumer_args['openid.dh_consumer_public'];
if (!isset($cpub64)) {
return false;
}
$dh = new Auth_OpenID_DiffieHellman($mod, $gen);
$cpub = $lib->base64ToLong($cpub64);
$mac_key = $dh->xorSecret($cpub, $assoc_secret);
$enc_mac_key = base64_encode($mac_key);
$spub64 = $lib->longToBase64($dh->getPublicKey());
$server_args = array(
'session_type' => 'DH-SHA1',
'dh_server_public' => $spub64,
'enc_mac_key' => $enc_mac_key
);
return $server_args;
}
function consumerFinish($reply)
{
$spub = $this->lib->base64ToLong($reply['dh_server_public']);
if ($this->lib->cmp($spub, 0) <= 0) {
return false;
}
$enc_mac_key = base64_decode($reply['enc_mac_key']);
return $this->xorSecret($spub, $enc_mac_key);
}
function xorSecret($composite, $secret)
{
$dh_shared = $this->getSharedSecret($composite);
$dh_shared_str = $this->lib->longToBinary($dh_shared);
$sha1_dh_shared = Auth_OpenID_SHA1($dh_shared_str);
$xsecret = "";
for ($i = 0; $i < strlen($secret); $i++) {
$xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i]));
}
return $xsecret;
}
}

View file

@ -0,0 +1,258 @@
<?php
/**
* The OpenID and Yadis discovery implementation for OpenID 1.2.
*/
require_once "Auth/OpenID.php";
require_once "Auth/OpenID/Parse.php";
require_once "Services/Yadis/XRIRes.php";
require_once "Services/Yadis/Yadis.php";
define('_OPENID_1_0_NS', 'http://openid.net/xmlns/1.0');
define('_OPENID_1_2_TYPE', 'http://openid.net/signon/1.2');
define('_OPENID_1_1_TYPE', 'http://openid.net/signon/1.1');
define('_OPENID_1_0_TYPE', 'http://openid.net/signon/1.0');
/**
* Object representing an OpenID service endpoint.
*/
class Auth_OpenID_ServiceEndpoint {
function Auth_OpenID_ServiceEndpoint()
{
$this->identity_url = null;
$this->server_url = null;
$this->type_uris = array();
$this->delegate = null;
$this->canonicalID = null;
$this->used_yadis = false; // whether this came from an XRDS
}
function usesExtension($extension_uri)
{
return in_array($extension_uri, $this->type_uris);
}
function parseService($yadis_url, $uri, $type_uris, $service_element)
{
// Set the state of this object based on the contents of the
// service element.
$this->type_uris = $type_uris;
$this->identity_url = $yadis_url;
$this->server_url = $uri;
$this->delegate = Auth_OpenID_ServiceEndpoint::findDelegate(
$service_element);
$this->used_yadis = true;
}
function findDelegate($service)
{
// Extract a openid:Delegate value from a Yadis Service
// element. If no delegate is found, returns null.
// Try to register new namespace.
$service->parser->registerNamespace('openid',
'http://openid.net/xmlns/1.0');
// XXX: should this die if there is more than one delegate
// element?
$delegates = $service->getElements("openid:Delegate");
if ($delegates) {
return $service->parser->content($delegates[0]);
} else {
return null;
}
}
function getServerID()
{
// Return the identifier that should be sent as the
// openid.identity_url parameter to the server.
if ($this->delegate === null) {
if ($this->canonicalID) {
return $this->canonicalID;
} else {
return $this->identity_url;
}
} else {
return $this->delegate;
}
}
function fromHTML($uri, $html)
{
// Parse the given document as HTML looking for an OpenID <link
// rel=...>
$urls = Auth_OpenID_legacy_discover($html);
if ($urls === false) {
return null;
}
list($delegate_url, $server_url) = $urls;
$service = new Auth_OpenID_ServiceEndpoint();
$service->identity_url = $uri;
$service->delegate = $delegate_url;
$service->server_url = $server_url;
$service->type_uris = array(_OPENID_1_0_TYPE);
return $service;
}
}
function filter_MatchesAnyOpenIDType(&$service)
{
$uris = $service->getTypes();
foreach ($uris as $uri) {
if (in_array($uri,
array(_OPENID_1_0_TYPE,
_OPENID_1_1_TYPE,
_OPENID_1_2_TYPE))) {
return true;
}
}
return false;
}
function Auth_OpenID_makeOpenIDEndpoints($uri, $endpoints)
{
$s = array();
if (!$endpoints) {
return $s;
}
foreach ($endpoints as $service) {
$type_uris = $service->getTypes();
$uris = $service->getURIs();
// If any Type URIs match and there is an endpoint URI
// specified, then this is an OpenID endpoint
if ($type_uris &&
$uris) {
foreach ($uris as $service_uri) {
$openid_endpoint = new Auth_OpenID_ServiceEndpoint();
$openid_endpoint->parseService($uri,
$service_uri,
$type_uris,
$service);
$s[] = $openid_endpoint;
}
}
}
return $s;
}
function Auth_OpenID_discoverWithYadis($uri, &$fetcher)
{
// Discover OpenID services for a URI. Tries Yadis and falls back
// on old-style <link rel='...'> discovery if Yadis fails.
// Might raise a yadis.discover.DiscoveryFailure if no document
// came back for that URI at all. I don't think falling back to
// OpenID 1.0 discovery on the same URL will help, so don't bother
// to catch it.
$openid_services = array();
$http_response = null;
$response = Services_Yadis_Yadis::discover($uri, $http_response,
$fetcher);
if ($response) {
$identity_url = $response->uri;
$openid_services =
$response->xrds->services(array('filter_MatchesAnyOpenIDType'));
}
if (!$openid_services) {
return @Auth_OpenID_discoverWithoutYadis($uri,
$fetcher);
}
if (!$openid_services) {
$body = $response->body;
// Try to parse the response as HTML to get OpenID 1.0/1.1
// <link rel="...">
$service = Auth_OpenID_ServiceEndpoint::fromHTML($identity_url,
$body);
if ($service !== null) {
$openid_services = array($service);
}
} else {
$openid_services = Auth_OpenID_makeOpenIDEndpoints($response->uri,
$openid_services);
}
return array($identity_url, $openid_services, $http_response);
}
function _Auth_OpenID_discoverServiceList($uri, &$fetcher)
{
list($url, $services, $resp) = Auth_OpenID_discoverWithYadis($uri,
$fetcher);
return $services;
}
function _Auth_OpenID_discoverXRIServiceList($uri, &$fetcher)
{
list($url, $services, $resp) = _Auth_OpenID_discoverXRI($uri,
$fetcher);
return $services;
}
function Auth_OpenID_discoverWithoutYadis($uri, &$fetcher)
{
$http_resp = @$fetcher->get($uri);
if ($http_resp->status != 200) {
return array(null, array(), $http_resp);
}
$identity_url = $http_resp->final_url;
// Try to parse the response as HTML to get OpenID 1.0/1.1 <link
// rel="...">
$endpoint =& new Auth_OpenID_ServiceEndpoint();
$service = $endpoint->fromHTML($identity_url, $http_resp->body);
if ($service === null) {
$openid_services = array();
} else {
$openid_services = array($service);
}
return array($identity_url, $openid_services, $http_resp);
}
function _Auth_OpenID_discoverXRI($iname, &$fetcher)
{
$services = new Services_Yadis_ProxyResolver($fetcher);
list($canonicalID, $service_list) = $services->query($iname,
array(_OPENID_1_0_TYPE,
_OPENID_1_1_TYPE,
_OPENID_1_2_TYPE),
array('filter_MatchesAnyOpenIDType'));
$endpoints = Auth_OpenID_makeOpenIDEndpoints($iname, $service_list);
for ($i = 0; $i < count($endpoints); $i++) {
$endpoints[$i]->canonicalID = $canonicalID;
}
// FIXME: returned xri should probably be in some normal form
return array($iname, $endpoints, null);
}
function Auth_OpenID_discover($uri, &$fetcher)
{
return @Auth_OpenID_discoverWithYadis($uri, $fetcher);
}
?>

View file

@ -0,0 +1,116 @@
<?php
/**
* This file supplies a dumb store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMACSHA1.php';
/**
* This is a store for use in the worst case, when you have no way of
* saving state on the consumer site. Using this store makes the
* consumer vulnerable to replay attacks, as it's unable to use
* nonces. Avoid using this store if it is at all possible.
*
* Most of the methods of this class are implementation details.
* Users of this class need to worry only about the constructor.
*
* @package OpenID
*/
class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore {
/**
* Creates a new {@link Auth_OpenID_DumbStore} instance. For the security
* of the tokens generated by the library, this class attempts to
* at least have a secure implementation of getAuthKey.
*
* When you create an instance of this class, pass in a secret
* phrase. The phrase is hashed with sha1 to make it the correct
* length and form for an auth key. That allows you to use a long
* string as the secret phrase, which means you can make it very
* difficult to guess.
*
* Each {@link Auth_OpenID_DumbStore} instance that is created for use by
* your consumer site needs to use the same $secret_phrase.
*
* @param string secret_phrase The phrase used to create the auth
* key returned by getAuthKey
*/
function Auth_OpenID_DumbStore($secret_phrase)
{
$this->auth_key = Auth_OpenID_SHA1($secret_phrase);
}
/**
* This implementation does nothing.
*/
function storeAssociation($server_url, $association)
{
}
/**
* This implementation always returns null.
*/
function getAssociation($server_url, $handle = null)
{
return null;
}
/**
* This implementation always returns false.
*/
function removeAssociation($server_url, $handle)
{
return false;
}
/**
* This implementation does nothing.
*/
function storeNonce($nonce)
{
}
/**
* In a system truly limited to dumb mode, nonces must all be
* accepted. This therefore always returns true, which makes
* replay attacks feasible.
*/
function useNonce($nonce)
{
return true;
}
/**
* This method returns the auth key generated by the constructor.
*/
function getAuthKey()
{
return $this->auth_key;
}
/**
* This store is a dumb mode store, so this method is overridden
* to return true.
*/
function isDumb()
{
return true;
}
}
?>

View file

@ -0,0 +1,674 @@
<?php
/**
* This file supplies a Memcached store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*
*/
/**
* Require base class for creating a new interface.
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMACSHA1.php';
/**
* This is a filesystem-based store for OpenID associations and
* nonces. This store should be safe for use in concurrent systems on
* both windows and unix (excluding NFS filesystems). There are a
* couple race conditions in the system, but those failure cases have
* been set up in such a way that the worst-case behavior is someone
* having to try to log in a second time.
*
* Most of the methods of this class are implementation details.
* People wishing to just use this store need only pay attention to
* the constructor.
*
* @package OpenID
*/
class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
/**
* Initializes a new {@link Auth_OpenID_FileStore}. This
* initializes the nonce and association directories, which are
* subdirectories of the directory passed in.
*
* @param string $directory This is the directory to put the store
* directories in.
*/
function Auth_OpenID_FileStore($directory)
{
if (!Auth_OpenID::ensureDir($directory)) {
trigger_error('Not a directory and failed to create: '
. $directory, E_USER_ERROR);
}
$directory = realpath($directory);
$this->directory = $directory;
$this->active = true;
$this->nonce_dir = $directory . DIRECTORY_SEPARATOR . 'nonces';
$this->association_dir = $directory . DIRECTORY_SEPARATOR .
'associations';
// Temp dir must be on the same filesystem as the assciations
// $directory and the $directory containing the auth key file.
$this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
$this->auth_key_name = $directory . DIRECTORY_SEPARATOR . 'auth_key';
$this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
if (!$this->_setup()) {
trigger_error('Failed to initialize OpenID file store in ' .
$directory, E_USER_ERROR);
}
}
function destroy()
{
Auth_OpenID_FileStore::_rmtree($this->directory);
$this->active = false;
}
/**
* Make sure that the directories in which we store our data
* exist.
*
* @access private
*/
function _setup()
{
return (Auth_OpenID::ensureDir(dirname($this->auth_key_name)) &&
Auth_OpenID::ensureDir($this->nonce_dir) &&
Auth_OpenID::ensureDir($this->association_dir) &&
Auth_OpenID::ensureDir($this->temp_dir));
}
/**
* Create a temporary file on the same filesystem as
* $this->auth_key_name and $this->association_dir.
*
* The temporary directory should not be cleaned if there are any
* processes using the store. If there is no active process using
* the store, it is safe to remove all of the files in the
* temporary directory.
*
* @return array ($fd, $filename)
* @access private
*/
function _mktemp()
{
$name = Auth_OpenID_FileStore::_mkstemp($dir = $this->temp_dir);
$file_obj = @fopen($name, 'wb');
if ($file_obj !== false) {
return array($file_obj, $name);
} else {
Auth_OpenID_FileStore::_removeIfPresent($name);
}
}
/**
* Read the auth key from the auth key file. Will return None if
* there is currently no key.
*
* @return mixed
*/
function readAuthKey()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$auth_key_file = @fopen($this->auth_key_name, 'rb');
if ($auth_key_file === false) {
return null;
}
$key = fread($auth_key_file, filesize($this->auth_key_name));
fclose($auth_key_file);
return $key;
}
/**
* Generate a new random auth key and safely store it in the
* location specified by $this->auth_key_name.
*
* @return string $key
*/
function createAuthKey()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$auth_key = Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN);
list($file_obj, $tmp) = $this->_mktemp();
fwrite($file_obj, $auth_key);
fflush($file_obj);
fclose($file_obj);
if (function_exists('link')) {
// Posix filesystem
$saved = link($tmp, $this->auth_key_name);
Auth_OpenID_FileStore::_removeIfPresent($tmp);
} else {
// Windows filesystem
$saved = rename($tmp, $this->auth_key_name);
}
if (!$saved) {
// The link failed, either because we lack the permission,
// or because the file already exists; try to read the key
// in case the file already existed.
$auth_key = $this->readAuthKey();
}
return $auth_key;
}
/**
* Retrieve the auth key from the file specified by
* $this->auth_key_name, creating it if it does not exist.
*
* @return string $key
*/
function getAuthKey()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$auth_key = $this->readAuthKey();
if ($auth_key === null) {
$auth_key = $this->createAuthKey();
if (strlen($auth_key) != $this->AUTH_KEY_LEN) {
$fmt = 'Got an invalid auth key from %s. Expected '.
'%d-byte string. Got: %s';
$msg = sprintf($fmt, $this->auth_key_name, $this->AUTH_KEY_LEN,
$auth_key);
trigger_error($msg, E_USER_WARNING);
return null;
}
}
return $auth_key;
}
/**
* Create a unique filename for a given server url and
* handle. This implementation does not assume anything about the
* format of the handle. The filename that is returned will
* contain the domain name from the server URL for ease of human
* inspection of the data directory.
*
* @return string $filename
*/
function getAssociationFilename($server_url, $handle)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if (strpos($server_url, '://') === false) {
trigger_error(sprintf("Bad server URL: %s", $server_url),
E_USER_WARNING);
return null;
}
list($proto, $rest) = explode('://', $server_url, 2);
$parts = explode('/', $rest);
$domain = Auth_OpenID_FileStore::_filenameEscape($parts[0]);
$url_hash = Auth_OpenID_FileStore::_safe64($server_url);
if ($handle) {
$handle_hash = Auth_OpenID_FileStore::_safe64($handle);
} else {
$handle_hash = '';
}
$filename = sprintf('%s-%s-%s-%s', $proto, $domain, $url_hash,
$handle_hash);
return $this->association_dir. DIRECTORY_SEPARATOR . $filename;
}
/**
* Store an association in the association directory.
*/
function storeAssociation($server_url, $association)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return false;
}
$association_s = $association->serialize();
$filename = $this->getAssociationFilename($server_url,
$association->handle);
list($tmp_file, $tmp) = $this->_mktemp();
if (!$tmp_file) {
trigger_error("_mktemp didn't return a valid file descriptor",
E_USER_WARNING);
return false;
}
fwrite($tmp_file, $association_s);
fflush($tmp_file);
fclose($tmp_file);
if (@rename($tmp, $filename)) {
return true;
} else {
// In case we are running on Windows, try unlinking the
// file in case it exists.
@unlink($filename);
// Now the target should not exist. Try renaming again,
// giving up if it fails.
if (@rename($tmp, $filename)) {
return true;
}
}
// If there was an error, don't leave the temporary file
// around.
Auth_OpenID_FileStore::_removeIfPresent($tmp);
return false;
}
/**
* Retrieve an association. If no handle is specified, return the
* association with the most recent issue time.
*
* @return mixed $association
*/
function getAssociation($server_url, $handle = null)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
if ($handle === null) {
$handle = '';
}
// The filename with the empty handle is a prefix of all other
// associations for the given server URL.
$filename = $this->getAssociationFilename($server_url, $handle);
if ($handle) {
return $this->_getAssociation($filename);
} else {
$association_files =
Auth_OpenID_FileStore::_listdir($this->association_dir);
$matching_files = array();
// strip off the path to do the comparison
$name = basename($filename);
foreach ($association_files as $association_file) {
if (strpos($association_file, $name) === 0) {
$matching_files[] = $association_file;
}
}
$matching_associations = array();
// read the matching files and sort by time issued
foreach ($matching_files as $name) {
$full_name = $this->association_dir . DIRECTORY_SEPARATOR .
$name;
$association = $this->_getAssociation($full_name);
if ($association !== null) {
$matching_associations[] = array($association->issued,
$association);
}
}
$issued = array();
$assocs = array();
foreach ($matching_associations as $key => $assoc) {
$issued[$key] = $assoc[0];
$assocs[$key] = $assoc[1];
}
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
$matching_associations);
// return the most recently issued one.
if ($matching_associations) {
list($issued, $assoc) = $matching_associations[0];
return $assoc;
} else {
return null;
}
}
}
/**
* @access private
*/
function _getAssociation($filename)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$assoc_file = @fopen($filename, 'rb');
if ($assoc_file === false) {
return null;
}
$assoc_s = fread($assoc_file, filesize($filename));
fclose($assoc_file);
if (!$assoc_s) {
return null;
}
$association =
Auth_OpenID_Association::deserialize('Auth_OpenID_Association',
$assoc_s);
if (!$association) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
return null;
}
if ($association->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
return null;
} else {
return $association;
}
}
/**
* Remove an association if it exists. Do nothing if it does not.
*
* @return bool $success
*/
function removeAssociation($server_url, $handle)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$assoc = $this->getAssociation($server_url, $handle);
if ($assoc === null) {
return false;
} else {
$filename = $this->getAssociationFilename($server_url, $handle);
return Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
/**
* Mark this nonce as present.
*/
function storeNonce($nonce)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$nonce_file = fopen($filename, 'w');
if ($nonce_file === false) {
return false;
}
fclose($nonce_file);
return true;
}
/**
* Return whether this nonce is present. As a side effect, mark it
* as no longer present.
*
* @return bool $present
*/
function useNonce($nonce)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$st = @stat($filename);
if ($st === false) {
return false;
}
// Either it is too old or we are using it. Either way, we
// must remove the file.
if (!unlink($filename)) {
return false;
}
$now = time();
$nonce_age = $now - $st[9];
// We can us it if the age of the file is less than the
// expiration time.
return $nonce_age <= $this->max_nonce_age;
}
/**
* Remove expired entries from the database. This is potentially
* expensive, so only run when it is acceptable to take time.
*/
function clean()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
// Check all nonces for expiry
foreach ($nonces as $nonce) {
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$st = @stat($filename);
if ($st !== false) {
// Remove the nonce if it has expired
$nonce_age = $now - $st[9];
if ($nonce_age > $this->max_nonce_age) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
}
$association_filenames =
Auth_OpenID_FileStore::_listdir($this->association_dir);
foreach ($association_filenames as $association_filename) {
$association_file = fopen($association_filename, 'rb');
if ($association_file !== false) {
$assoc_s = fread($association_file,
filesize($association_filename));
fclose($association_file);
// Remove expired or corrupted associations
$association =
Auth_OpenID_Association::deserialize(
'Auth_OpenID_Association', $assoc_s);
if ($association === null) {
Auth_OpenID_FileStore::_removeIfPresent(
$association_filename);
} else {
if ($association->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent(
$association_filename);
}
}
}
}
}
/**
* @access private
*/
function _rmtree($dir)
{
if ($dir[strlen($dir) - 1] != DIRECTORY_SEPARATOR) {
$dir .= DIRECTORY_SEPARATOR;
}
if ($handle = opendir($dir)) {
while ($item = readdir($handle)) {
if (!in_array($item, array('.', '..'))) {
if (is_dir($dir . $item)) {
if (!Auth_OpenID_FileStore::_rmtree($dir . $item)) {
return false;
}
} else if (is_file($dir . $item)) {
if (!unlink($dir . $item)) {
return false;
}
}
}
}
closedir($handle);
if (!@rmdir($dir)) {
return false;
}
return true;
} else {
// Couldn't open directory.
return false;
}
}
/**
* @access private
*/
function _mkstemp($dir)
{
foreach (range(0, 4) as $i) {
$name = tempnam($dir, "php_openid_filestore_");
if ($name !== false) {
return $name;
}
}
return false;
}
/**
* @access private
*/
function _mkdtemp($dir)
{
foreach (range(0, 4) as $i) {
$name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
"-" . strval(rand(1, time()));
if (!mkdir($name, 0700)) {
return false;
} else {
return $name;
}
}
return false;
}
/**
* @access private
*/
function _listdir($dir)
{
$handle = opendir($dir);
$files = array();
while (false !== ($filename = readdir($handle))) {
$files[] = $filename;
}
return $files;
}
/**
* @access private
*/
function _isFilenameSafe($char)
{
$_Auth_OpenID_filename_allowed = Auth_OpenID_letters .
Auth_OpenID_digits . ".";
return (strpos($_Auth_OpenID_filename_allowed, $char) !== false);
}
/**
* @access private
*/
function _safe64($str)
{
$h64 = base64_encode(Auth_OpenID_SHA1($str));
$h64 = str_replace('+', '_', $h64);
$h64 = str_replace('/', '.', $h64);
$h64 = str_replace('=', '', $h64);
return $h64;
}
/**
* @access private
*/
function _filenameEscape($str)
{
$filename = "";
for ($i = 0; $i < strlen($str); $i++) {
$c = $str[$i];
if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
$filename .= $c;
} else {
$filename .= sprintf("_%02X", ord($c));
}
}
return $filename;
}
/**
* Attempt to remove a file, returning whether the file existed at
* the time of the call.
*
* @access private
* @return bool $result True if the file was present, false if not.
*/
function _removeIfPresent($filename)
{
return @unlink($filename);
}
}
?>

View file

@ -0,0 +1,72 @@
<?php
/**
* This is the HMACSHA1 implementation for the OpenID library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback
* implementation.
*/
define('Auth_OpenID_SHA1_BLOCKSIZE', 64);
if (!function_exists('sha1')) {
/**
* Return a raw SHA1 hash of the given string
*
* XXX: include the SHA1 code from Dan Libby's OpenID library
*/
function Auth_OpenID_SHA1($text)
{
trigger_error('No SHA1 function found', E_USER_ERROR);
}
} else {
/**
* @ignore
*/
function Auth_OpenID_SHA1($text)
{
$hex = sha1($text);
$raw = '';
for ($i = 0; $i < 40; $i += 2) {
$hexcode = substr($hex, $i, 2);
$charcode = (int)base_convert($hexcode, 16, 10);
$raw .= chr($charcode);
}
return $raw;
}
}
/**
* Compute an HMAC/SHA1 hash.
*
* @access private
* @param string $key The HMAC key
* @param string $text The message text to hash
* @return string $mac The MAC
*/
function Auth_OpenID_HMACSHA1($key, $text)
{
if (strlen($key) > Auth_OpenID_SHA1_BLOCKSIZE) {
$key = Auth_OpenID_SHA1($key, true);
}
$key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00));
$ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE);
$opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE);
$hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true);
$hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true);
return $hmac;
}
?>

View file

@ -0,0 +1,188 @@
<?php
/**
* This file specifies the interface for PHP OpenID store implementations.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* This is the interface for the store objects the OpenID library
* uses. It is a single class that provides all of the persistence
* mechanisms that the OpenID library needs, for both servers and
* consumers. If you want to create an SQL-driven store, please see
* then {@link Auth_OpenID_SQLStore} class.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
*/
class Auth_OpenID_OpenIDStore {
/**
* @var integer The length of the auth key that should be returned
* by the getAuthKey method.
*/
var $AUTH_KEY_LEN = 20;
/**
* This method puts an Association object into storage,
* retrievable by server URL and handle.
*
* @param string $server_url The URL of the identity server that
* this association is with. Because of the way the server portion
* of the library uses this interface, don't assume there are any
* limitations on the character set of the input string. In
* particular, expect to see unescaped non-url-safe characters in
* the server_url field.
*
* @param Association $association The Association to store.
*/
function storeAssociation($server_url, $association)
{
trigger_error("Auth_OpenID_OpenIDStore::storeAssociation ".
"not implemented", E_USER_ERROR);
}
/**
* This method returns an Association object from storage that
* matches the server URL and, if specified, handle. It returns
* null if no such association is found or if the matching
* association is expired.
*
* If no handle is specified, the store may return any association
* which matches the server URL. If multiple associations are
* valid, the recommended return value for this method is the one
* that will remain valid for the longest duration.
*
* This method is allowed (and encouraged) to garbage collect
* expired associations when found. This method must not return
* expired associations.
*
* @param string $server_url The URL of the identity server to get
* the association for. Because of the way the server portion of
* the library uses this interface, don't assume there are any
* limitations on the character set of the input string. In
* particular, expect to see unescaped non-url-safe characters in
* the server_url field.
*
* @param mixed $handle This optional parameter is the handle of
* the specific association to get. If no specific handle is
* provided, any valid association matching the server URL is
* returned.
*
* @return Association The Association for the given identity
* server.
*/
function getAssociation($server_url, $handle = null)
{
trigger_error("Auth_OpenID_OpenIDStore::getAssociation ".
"not implemented", E_USER_ERROR);
}
/**
* This method removes the matching association if it's found, and
* returns whether the association was removed or not.
*
* @param string $server_url The URL of the identity server the
* association to remove belongs to. Because of the way the server
* portion of the library uses this interface, don't assume there
* are any limitations on the character set of the input
* string. In particular, expect to see unescaped non-url-safe
* characters in the server_url field.
*
* @param string $handle This is the handle of the association to
* remove. If there isn't an association found that matches both
* the given URL and handle, then there was no matching handle
* found.
*
* @return mixed Returns whether or not the given association existed.
*/
function removeAssociation($server_url, $handle)
{
trigger_error("Auth_OpenID_OpenIDStore::removeAssociation ".
"not implemented", E_USER_ERROR);
}
/**
* Stores a nonce. This is used by the consumer to prevent replay
* attacks.
*
* @param string $nonce The nonce to store.
*
* @return null
*/
function storeNonce($nonce)
{
trigger_error("Auth_OpenID_OpenIDStore::storeNonce ".
"not implemented", E_USER_ERROR);
}
/**
* This method is called when the library is attempting to use a
* nonce. If the nonce is in the store, this method removes it and
* returns a value which evaluates as true. Otherwise it returns a
* value which evaluates as false.
*
* This method is allowed and encouraged to treat nonces older
* than some period (a very conservative window would be 6 hours,
* for example) as no longer existing, and return False and remove
* them.
*
* @param string $nonce The nonce to use.
*
* @return bool Whether or not the nonce was valid.
*/
function useNonce($nonce)
{
trigger_error("Auth_OpenID_OpenIDStore::useNonce ".
"not implemented", E_USER_ERROR);
}
/**
* This method returns a key used to sign the tokens, to ensure
* that they haven't been tampered with in transit. It should
* return the same key every time it is called. The key returned
* should be {@link AUTH_KEY_LEN} bytes long.
*
* @return string The key. It should be {@link AUTH_KEY_LEN} bytes in
* length, and use the full range of byte values. That is, it
* should be treated as a lump of binary data stored in a string.
*/
function getAuthKey()
{
trigger_error("Auth_OpenID_OpenIDStore::getAuthKey ".
"not implemented", E_USER_ERROR);
}
/**
* This method must return true if the store is a dumb-mode-style
* store. Unlike all other methods in this class, this one
* provides a default implementation, which returns false.
*
* In general, any custom subclass of {@link Auth_OpenID_OpenIDStore}
* won't override this method, as custom subclasses are only likely to
* be created when the store is fully functional.
*
* @return bool true if the store works fully, false if the
* consumer will have to use dumb mode to use this store.
*/
function isDumb()
{
return false;
}
/**
* Removes all entries from the store; implementation is optional.
*/
function reset()
{
}
}
?>

View file

@ -0,0 +1,112 @@
<?php
/**
* OpenID protocol key-value/comma-newline format parsing and
* serialization
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Container for key-value/comma-newline OpenID format and parsing
*/
class Auth_OpenID_KVForm {
/**
* Convert an OpenID colon/newline separated string into an
* associative array
*
* @static
* @access private
*/
function toArray($kvs, $strict=false)
{
$lines = explode("\n", $kvs);
$last = array_pop($lines);
if ($last !== '') {
array_push($lines, $last);
if ($strict) {
return false;
}
}
$values = array();
for ($lineno = 0; $lineno < count($lines); $lineno++) {
$line = $lines[$lineno];
$kv = explode(':', $line, 2);
if (count($kv) != 2) {
if ($strict) {
return false;
}
continue;
}
$key = $kv[0];
$tkey = trim($key);
if ($tkey != $key) {
if ($strict) {
return false;
}
}
$value = $kv[1];
$tval = trim($value);
if ($tval != $value) {
if ($strict) {
return false;
}
}
$values[$tkey] = $tval;
}
return $values;
}
/**
* Convert an array into an OpenID colon/newline separated string
*
* @static
* @access private
*/
function fromArray($values)
{
if ($values === null) {
return null;
}
ksort($values);
$serialized = '';
foreach ($values as $key => $value) {
if (is_array($value)) {
list($key, $value) = array($value[0], $value[1]);
}
if (strpos($key, ':') !== false) {
return null;
}
if (strpos($key, "\n") !== false) {
return null;
}
if (strpos($value, "\n") !== false) {
return null;
}
$serialized .= "$key:$value\n";
}
return $serialized;
}
}
?>

View file

@ -0,0 +1,78 @@
<?php
/**
* A MySQL store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses MySQL as its backend.
*
* @package OpenID
*/
class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
/**
* @access private
*/
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER) TYPE=InnoDB";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url BLOB, handle VARCHAR(255), ".
"secret BLOB, issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url(255), handle)) ".
"TYPE=InnoDB";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BLOB) TYPE=InnoDB";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', !)";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
$this->sql['set_assoc'] =
"REPLACE INTO %s VALUES (?, ?, !, ?, ?, ?)";
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"REPLACE INTO %s (nonce, expires) VALUES (?, ?)";
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
}
/**
* @access private
*/
function blobEncode($blob)
{
return "0x" . bin2hex($blob);
}
}
?>

View file

@ -0,0 +1,308 @@
<?php
/**
* This module implements a VERY limited parser that finds <link> tags
* in the head of HTML or XHTML documents and parses out their
* attributes according to the OpenID spec. It is a liberal parser,
* but it requires these things from the data in order to work:
*
* - There must be an open <html> tag
*
* - There must be an open <head> tag inside of the <html> tag
*
* - Only <link>s that are found inside of the <head> tag are parsed
* (this is by design)
*
* - The parser follows the OpenID specification in resolving the
* attributes of the link tags. This means that the attributes DO
* NOT get resolved as they would by an XML or HTML parser. In
* particular, only certain entities get replaced, and href
* attributes do not get resolved relative to a base URL.
*
* From http://openid.net/specs.bml:
*
* - The openid.server URL MUST be an absolute URL. OpenID consumers
* MUST NOT attempt to resolve relative URLs.
*
* - The openid.server URL MUST NOT include entities other than &amp;,
* &lt;, &gt;, and &quot;.
*
* The parser ignores SGML comments and <![CDATA[blocks]]>. Both kinds
* of quoting are allowed for attributes.
*
* The parser deals with invalid markup in these ways:
*
* - Tag names are not case-sensitive
*
* - The <html> tag is accepted even when it is not at the top level
*
* - The <head> tag is accepted even when it is not a direct child of
* the <html> tag, but a <html> tag must be an ancestor of the
* <head> tag
*
* - <link> tags are accepted even when they are not direct children
* of the <head> tag, but a <head> tag must be an ancestor of the
* <link> tag
*
* - If there is no closing tag for an open <html> or <head> tag, the
* remainder of the document is viewed as being inside of the
* tag. If there is no closing tag for a <link> tag, the link tag is
* treated as a short tag. Exceptions to this rule are that <html>
* closes <html> and <body> or <head> closes <head>
*
* - Attributes of the <link> tag are not required to be quoted.
*
* - In the case of duplicated attribute names, the attribute coming
* last in the tag will be the value returned.
*
* - Any text that does not parse as an attribute within a link tag
* will be ignored. (e.g. <link pumpkin rel='openid.server' /> will
* ignore pumpkin)
*
* - If there are more than one <html> or <head> tag, the parser only
* looks inside of the first one.
*
* - The contents of <script> tags are ignored entirely, except
* unclosed <script> tags. Unclosed <script> tags are ignored.
*
* - Any other invalid markup is ignored, including unclosed SGML
* comments and unclosed <![CDATA[blocks.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Require Auth_OpenID::arrayGet().
*/
require_once "Auth/OpenID.php";
class Auth_OpenID_Parse {
/**
* Specify some flags for use with regex matching.
*/
var $_re_flags = "si";
/**
* Stuff to remove before we start looking for tags
*/
var $_removed_re =
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
/**
* Starts with the tag name at a word boundary, where the tag name
* is not a namespace
*/
var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*?)(?:<\/?%s\s*>|\Z))";
var $_attr_find = '\b(\w+)=("[^"]*"|\'[^\']*\'|[^\'"\s\/<>]+)';
function Auth_OpenID_Parse()
{
$this->_link_find = sprintf("/<link\b(?!:)([^>]*)(?!<)>/%s",
$this->_re_flags);
$this->_entity_replacements = array(
'amp' => '&',
'lt' => '<',
'gt' => '>',
'quot' => '"'
);
$this->_attr_find = sprintf("/%s/%s",
$this->_attr_find,
$this->_re_flags);
$this->_removed_re = sprintf("/%s/%s",
$this->_removed_re,
$this->_re_flags);
$this->_ent_replace =
sprintf("&(%s);", implode("|",
$this->_entity_replacements));
}
/**
* Returns a regular expression that will match a given tag in an
* SGML string.
*/
function tagMatcher($tag_name, $close_tags = null)
{
if ($close_tags) {
$options = implode("|", array_merge(array($tag_name), $close_tags));
$closer = sprintf("(?:%s)", $options);
} else {
$closer = $tag_name;
}
$expr = sprintf($this->_tag_expr, $tag_name, $closer);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function htmlFind()
{
return $this->tagMatcher('html');
}
function headFind()
{
return $this->tagMatcher('head', array('body'));
}
function replaceEntities($str)
{
foreach ($this->_entity_replacements as $old => $new) {
$str = preg_replace(sprintf("/&%s;/", $old), $new, $str);
}
return $str;
}
function removeQuotes($str)
{
$matches = array();
$double = '/^"(.*)"$/';
$single = "/^\'(.*)\'$/";
if (preg_match($double, $str, $matches)) {
return $matches[1];
} else if (preg_match($single, $str, $matches)) {
return $matches[1];
} else {
return $str;
}
}
/**
* Find all link tags in a string representing a HTML document and
* return a list of their attributes.
*
* @param string $html The text to parse
* @return array $list An array of arrays of attributes, one for each
* link tag
*/
function parseLinkAttrs($html)
{
$stripped = preg_replace($this->_removed_re,
"",
$html);
// Try to find the <HTML> tag.
$html_re = $this->htmlFind();
$html_matches = array();
if (!preg_match($html_re, $stripped, $html_matches)) {
return array();
}
// Try to find the <HEAD> tag.
$head_re = $this->headFind();
$head_matches = array();
if (!preg_match($head_re, $html_matches[0], $head_matches)) {
return array();
}
$link_data = array();
$link_matches = array();
if (!preg_match_all($this->_link_find, $head_matches[0],
$link_matches)) {
return array();
}
foreach ($link_matches[0] as $link) {
$attr_matches = array();
preg_match_all($this->_attr_find, $link, $attr_matches);
$link_attrs = array();
foreach ($attr_matches[0] as $index => $full_match) {
$name = $attr_matches[1][$index];
$value = $this->replaceEntities(
$this->removeQuotes($attr_matches[2][$index]));
$link_attrs[strtolower($name)] = $value;
}
$link_data[] = $link_attrs;
}
return $link_data;
}
function relMatches($rel_attr, $target_rel)
{
// Does this target_rel appear in the rel_str?
// XXX: TESTME
$rels = preg_split("/\s+/", trim($rel_attr));
foreach ($rels as $rel) {
$rel = strtolower($rel);
if ($rel == $target_rel) {
return 1;
}
}
return 0;
}
function linkHasRel($link_attrs, $target_rel)
{
// Does this link have target_rel as a relationship?
// XXX: TESTME
$rel_attr = Auth_OpeniD::arrayGet($link_attrs, 'rel', null);
return ($rel_attr && $this->relMatches($rel_attr,
$target_rel));
}
function findLinksRel($link_attrs_list, $target_rel)
{
// Filter the list of link attributes on whether it has
// target_rel as a relationship.
// XXX: TESTME
$result = array();
foreach ($link_attrs_list as $attr) {
if ($this->linkHasRel($attr, $target_rel)) {
$result[] = $attr;
}
}
return $result;
}
function findFirstHref($link_attrs_list, $target_rel)
{
// Return the value of the href attribute for the first link
// tag in the list that has target_rel as a relationship.
// XXX: TESTME
$matches = $this->findLinksRel($link_attrs_list,
$target_rel);
if (!$matches) {
return null;
}
$first = $matches[0];
return Auth_OpenID::arrayGet($first, 'href', null);
}
}
function Auth_OpenID_legacy_discover($html_text)
{
$p = new Auth_OpenID_Parse();
$link_attrs = $p->parseLinkAttrs($html_text);
$server_url = $p->findFirstHref($link_attrs,
'openid.server');
if ($server_url === null) {
return false;
} else {
$delegate_url = $p->findFirstHref($link_attrs,
'openid.delegate');
return array($delegate_url, $server_url);
}
}
?>

View file

@ -0,0 +1,136 @@
<?php
/**
* A PostgreSQL store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses PostgreSQL as its backend.
*
* @package OpenID
*/
class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
/**
* @access private
*/
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER)";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
"secret BYTEA, issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle), ".
"CONSTRAINT secret_length_constraint CHECK ".
"(LENGTH(secret) <= 128))";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BYTEA, ".
"CONSTRAINT value_length_constraint CHECK (LENGTH(value) <= 20))";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', '!')";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
$this->sql['set_assoc'] =
array(
'insert_assoc' => "INSERT INTO %s (server_url, handle, ".
"secret, issued, lifetime, assoc_type) VALUES ".
"(?, ?, '!', ?, ?, ?)",
'update_assoc' => "UPDATE %s SET secret = '!', issued = ?, ".
"lifetime = ?, assoc_type = ? WHERE server_url = ? AND ".
"handle = ?"
);
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
array(
'insert_nonce' => "INSERT INTO %s (nonce, expires) VALUES ".
"(?, ?)",
'update_nonce' => "UPDATE %s SET expires = ? WHERE nonce = ?"
);
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
}
/**
* @access private
*/
function _set_assoc($server_url, $handle, $secret, $issued, $lifetime,
$assoc_type)
{
$result = $this->_get_assoc($server_url, $handle);
if ($result) {
// Update the table since this associations already exists.
$this->connection->query($this->sql['set_assoc']['update_assoc'],
array($secret, $issued, $lifetime,
$assoc_type, $server_url, $handle));
} else {
// Insert a new record because this association wasn't
// found.
$this->connection->query($this->sql['set_assoc']['insert_assoc'],
array($server_url, $handle, $secret,
$issued, $lifetime, $assoc_type));
}
}
/**
* @access private
*/
function _add_nonce($nonce, $expires)
{
if ($this->_get_nonce($nonce)) {
return $this->resultToBool($this->connection->query(
$this->sql['add_nonce']['update_nonce'],
array($expires, $nonce)));
} else {
return $this->resultToBool($this->connection->query(
$this->sql['add_nonce']['insert_nonce'],
array($nonce, $expires)));
}
}
/**
* @access private
*/
function blobEncode($blob)
{
return $this->_octify($blob);
}
/**
* @access private
*/
function blobDecode($blob)
{
return $this->_unoctify($blob);
}
}
?>

View file

@ -0,0 +1,658 @@
<?php
/**
* SQL-backed OpenID stores.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Require the PEAR DB module because we'll need it for the SQL-based
* stores implemented here. We silence any errors from the inclusion
* because it might not be present, and a user of the SQL stores may
* supply an Auth_OpenID_DatabaseConnection instance that implements
* its own storage.
*/
global $__Auth_OpenID_PEAR_AVAILABLE;
$__Auth_OpenID_PEAR_AVAILABLE = @include_once 'DB.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Interface.php';
/**
* This is the parent class for the SQL stores, which contains the
* logic common to all of the SQL stores.
*
* The table names used are determined by the class variables
* settings_table_name, associations_table_name, and
* nonces_table_name. To change the name of the tables used, pass new
* table names into the constructor.
*
* To create the tables with the proper schema, see the createTables
* method.
*
* This class shouldn't be used directly. Use one of its subclasses
* instead, as those contain the code necessary to use a specific
* database. If you're an OpenID integrator and you'd like to create
* an SQL-driven store that wraps an application's database
* abstraction, be sure to create a subclass of
* {@link Auth_OpenID_DatabaseConnection} that calls the application's
* database abstraction calls. Then, pass an instance of your new
* database connection class to your SQLStore subclass constructor.
*
* All methods other than the constructor and createTables should be
* considered implementation details.
*
* @package OpenID
*/
class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
/**
* This creates a new SQLStore instance. It requires an
* established database connection be given to it, and it allows
* overriding the default table names.
*
* @param connection $connection This must be an established
* connection to a database of the correct type for the SQLStore
* subclass you're using. This must either be an PEAR DB
* connection handle or an instance of a subclass of
* Auth_OpenID_DatabaseConnection.
*
* @param string $settings_table This is an optional parameter to
* specify the name of the table used for this store's settings.
* The default value is 'oid_settings'.
*
* @param associations_table: This is an optional parameter to
* specify the name of the table used for storing associations.
* The default value is 'oid_associations'.
*
* @param nonces_table: This is an optional parameter to specify
* the name of the table used for storing nonces. The default
* value is 'oid_nonces'.
*/
function Auth_OpenID_SQLStore($connection, $settings_table = null,
$associations_table = null,
$nonces_table = null)
{
global $__Auth_OpenID_PEAR_AVAILABLE;
$this->settings_table_name = "oid_settings";
$this->associations_table_name = "oid_associations";
$this->nonces_table_name = "oid_nonces";
// Check the connection object type to be sure it's a PEAR
// database connection.
if (!(is_object($connection) &&
(is_subclass_of($connection, 'db_common') ||
is_subclass_of($connection,
'auth_openid_databaseconnection')))) {
trigger_error("Auth_OpenID_SQLStore expected PEAR connection " .
"object (got ".get_class($connection).")",
E_USER_ERROR);
return;
}
$this->connection = $connection;
// Be sure to set the fetch mode so the results are keyed on
// column name instead of column index. This is a PEAR
// constant, so only try to use it if PEAR is present. Note
// that Auth_Openid_Databaseconnection instances need not
// implement ::setFetchMode for this reason.
if ($__Auth_OpenID_PEAR_AVAILABLE) {
$this->connection->setFetchMode(DB_FETCHMODE_ASSOC);
}
if ($settings_table) {
$this->settings_table_name = $settings_table;
}
if ($associations_table) {
$this->associations_table_name = $associations_table;
}
if ($nonces_table) {
$this->nonces_table_name = $nonces_table;
}
$this->max_nonce_age = 6 * 60 * 60;
// Be sure to run the database queries with auto-commit mode
// turned OFF, because we want every function to run in a
// transaction, implicitly. As a rule, methods named with a
// leading underscore will NOT control transaction behavior.
// Callers of these methods will worry about transactions.
$this->connection->autoCommit(false);
// Create an empty SQL strings array.
$this->sql = array();
// Call this method (which should be overridden by subclasses)
// to populate the $this->sql array with SQL strings.
$this->setSQL();
// Verify that all required SQL statements have been set, and
// raise an error if any expected SQL strings were either
// absent or empty.
list($missing, $empty) = $this->_verifySQL();
if ($missing) {
trigger_error("Expected keys in SQL query list: " .
implode(", ", $missing),
E_USER_ERROR);
return;
}
if ($empty) {
trigger_error("SQL list keys have no SQL strings: " .
implode(", ", $empty),
E_USER_ERROR);
return;
}
// Add table names to queries.
$this->_fixSQL();
}
function tableExists($table_name)
{
return !$this->isError(
$this->connection->query("SELECT * FROM %s LIMIT 0",
$table_name));
}
/**
* Returns true if $value constitutes a database error; returns
* false otherwise.
*/
function isError($value)
{
return PEAR::isError($value);
}
/**
* Converts a query result to a boolean. If the result is a
* database error according to $this->isError(), this returns
* false; otherwise, this returns true.
*/
function resultToBool($obj)
{
if ($this->isError($obj)) {
return false;
} else {
return true;
}
}
/**
* This method should be overridden by subclasses. This method is
* called by the constructor to set values in $this->sql, which is
* an array keyed on sql name.
*/
function setSQL()
{
}
/**
* Resets the store by removing all records from the store's
* tables.
*/
function reset()
{
$this->connection->query(sprintf("DELETE FROM %s",
$this->associations_table_name));
$this->connection->query(sprintf("DELETE FROM %s",
$this->nonces_table_name));
$this->connection->query(sprintf("DELETE FROM %s",
$this->settings_table_name));
}
/**
* @access private
*/
function _verifySQL()
{
$missing = array();
$empty = array();
$required_sql_keys = array(
'nonce_table',
'assoc_table',
'settings_table',
'get_auth',
'create_auth',
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc',
'add_nonce',
'get_nonce',
'remove_nonce'
);
foreach ($required_sql_keys as $key) {
if (!array_key_exists($key, $this->sql)) {
$missing[] = $key;
} else if (!$this->sql[$key]) {
$empty[] = $key;
}
}
return array($missing, $empty);
}
/**
* @access private
*/
function _fixSQL()
{
$replacements = array(
array(
'value' => $this->nonces_table_name,
'keys' => array('nonce_table',
'add_nonce',
'get_nonce',
'remove_nonce')
),
array(
'value' => $this->associations_table_name,
'keys' => array('assoc_table',
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc')
),
array(
'value' => $this->settings_table_name,
'keys' => array('settings_table',
'get_auth',
'create_auth')
)
);
foreach ($replacements as $item) {
$value = $item['value'];
$keys = $item['keys'];
foreach ($keys as $k) {
if (is_array($this->sql[$k])) {
foreach ($this->sql[$k] as $part_key => $part_value) {
$this->sql[$k][$part_key] = sprintf($part_value,
$value);
}
} else {
$this->sql[$k] = sprintf($this->sql[$k], $value);
}
}
}
}
function blobDecode($blob)
{
return $blob;
}
function blobEncode($str)
{
return $str;
}
function createTables()
{
$this->connection->autoCommit(true);
$n = $this->create_nonce_table();
$a = $this->create_assoc_table();
$s = $this->create_settings_table();
$this->connection->autoCommit(false);
if ($n && $a && $s) {
return true;
} else {
return false;
}
}
function create_nonce_table()
{
if (!$this->tableExists($this->nonces_table_name)) {
$r = $this->connection->query($this->sql['nonce_table']);
return $this->resultToBool($r);
}
return true;
}
function create_assoc_table()
{
if (!$this->tableExists($this->associations_table_name)) {
$r = $this->connection->query($this->sql['assoc_table']);
return $this->resultToBool($r);
}
return true;
}
function create_settings_table()
{
if (!$this->tableExists($this->settings_table_name)) {
$r = $this->connection->query($this->sql['settings_table']);
return $this->resultToBool($r);
}
return true;
}
/**
* @access private
*/
function _get_auth()
{
return $this->connection->getOne($this->sql['get_auth']);
}
/**
* @access private
*/
function _create_auth($str)
{
return $this->connection->query($this->sql['create_auth'],
array($str));
}
function getAuthKey()
{
$value = $this->_get_auth();
if (!$value) {
$auth_key =
Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN);
$auth_key_s = $this->blobEncode($auth_key);
$this->_create_auth($auth_key_s);
} else {
$auth_key_s = $value;
$auth_key = $this->blobDecode($auth_key_s);
}
$this->connection->commit();
if (strlen($auth_key) != $this->AUTH_KEY_LEN) {
$fmt = "Expected %d-byte string for auth key. Got key of length %d";
trigger_error(sprintf($fmt, $this->AUTH_KEY_LEN, strlen($auth_key)),
E_USER_WARNING);
return null;
}
return $auth_key;
}
/**
* @access private
*/
function _set_assoc($server_url, $handle, $secret, $issued,
$lifetime, $assoc_type)
{
return $this->connection->query($this->sql['set_assoc'],
array(
$server_url,
$handle,
$secret,
$issued,
$lifetime,
$assoc_type));
}
function storeAssociation($server_url, $association)
{
if ($this->resultToBool($this->_set_assoc(
$server_url,
$association->handle,
$this->blobEncode(
$association->secret),
$association->issued,
$association->lifetime,
$association->assoc_type
))) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
}
/**
* @access private
*/
function _get_assoc($server_url, $handle)
{
$result = $this->connection->getRow($this->sql['get_assoc'],
array($server_url, $handle));
if ($this->isError($result)) {
return null;
} else {
return $result;
}
}
/**
* @access private
*/
function _get_assocs($server_url)
{
$result = $this->connection->getAll($this->sql['get_assocs'],
array($server_url));
if ($this->isError($result)) {
return array();
} else {
return $result;
}
}
function removeAssociation($server_url, $handle)
{
if ($this->_get_assoc($server_url, $handle) == null) {
return false;
}
if ($this->resultToBool($this->connection->query(
$this->sql['remove_assoc'],
array($server_url, $handle)))) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
return true;
}
function getAssociation($server_url, $handle = null)
{
if ($handle !== null) {
$assoc = $this->_get_assoc($server_url, $handle);
$assocs = array();
if ($assoc) {
$assocs[] = $assoc;
}
} else {
$assocs = $this->_get_assocs($server_url);
}
if (!$assocs || (count($assocs) == 0)) {
return null;
} else {
$associations = array();
foreach ($assocs as $assoc_row) {
$assoc = new Auth_OpenID_Association($assoc_row['handle'],
$assoc_row['secret'],
$assoc_row['issued'],
$assoc_row['lifetime'],
$assoc_row['assoc_type']);
$assoc->secret = $this->blobDecode($assoc->secret);
if ($assoc->getExpiresIn() == 0) {
$this->removeAssociation($server_url, $assoc->handle);
} else {
$associations[] = array($assoc->issued, $assoc);
}
}
if ($associations) {
$issued = array();
$assocs = array();
foreach ($associations as $key => $assoc) {
$issued[$key] = $assoc[0];
$assocs[$key] = $assoc[1];
}
array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
$associations);
// return the most recently issued one.
list($issued, $assoc) = $associations[0];
return $assoc;
} else {
return null;
}
}
}
/**
* @access private
*/
function _add_nonce($nonce, $expires)
{
$sql = $this->sql['add_nonce'];
$result = $this->connection->query($sql, array($nonce, $expires));
return $this->resultToBool($result);
}
/**
* @access private
*/
function storeNonce($nonce)
{
if ($this->_add_nonce($nonce, time())) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
}
/**
* @access private
*/
function _get_nonce($nonce)
{
$result = $this->connection->getRow($this->sql['get_nonce'],
array($nonce));
if ($this->isError($result)) {
return null;
} else {
return $result;
}
}
/**
* @access private
*/
function _remove_nonce($nonce)
{
$this->connection->query($this->sql['remove_nonce'],
array($nonce));
}
function useNonce($nonce)
{
$row = $this->_get_nonce($nonce);
if ($row !== null) {
$nonce = $row['nonce'];
$timestamp = $row['expires'];
$nonce_age = time() - $timestamp;
if ($nonce_age > $this->max_nonce_age) {
$present = 0;
} else {
$present = 1;
}
$this->_remove_nonce($nonce);
} else {
$present = 0;
}
$this->connection->commit();
return $present;
}
/**
* "Octifies" a binary string by returning a string with escaped
* octal bytes. This is used for preparing binary data for
* PostgreSQL BYTEA fields.
*
* @access private
*/
function _octify($str)
{
$result = "";
for ($i = 0; $i < strlen($str); $i++) {
$ch = substr($str, $i, 1);
if ($ch == "\\") {
$result .= "\\\\\\\\";
} else if (ord($ch) == 0) {
$result .= "\\\\000";
} else {
$result .= "\\" . strval(decoct(ord($ch)));
}
}
return $result;
}
/**
* "Unoctifies" octal-escaped data from PostgreSQL and returns the
* resulting ASCII (possibly binary) string.
*
* @access private
*/
function _unoctify($str)
{
$result = "";
$i = 0;
while ($i < strlen($str)) {
$char = $str[$i];
if ($char == "\\") {
// Look to see if the next char is a backslash and
// append it.
if ($str[$i + 1] != "\\") {
$octal_digits = substr($str, $i + 1, 3);
$dec = octdec($octal_digits);
$char = chr($dec);
$i += 4;
} else {
$char = "\\";
$i += 2;
}
} else {
$i += 1;
}
$result .= $char;
}
return $result;
}
}
?>

View file

@ -0,0 +1,66 @@
<?php
/**
* An SQLite store.
*
* @package OpenID
*/
/**
* Require the base class file.
*/
require_once "Auth/OpenID/SQLStore.php";
/**
* An SQL store that uses SQLite as its backend.
*
* @package OpenID
*/
class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore {
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER)";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
"secret BLOB(128), issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle))";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BLOB(20))";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', ?)";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
$this->sql['set_assoc'] =
"INSERT OR REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ?";
$this->sql['get_assoc'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
"WHERE server_url = ? AND handle = ?";
$this->sql['remove_assoc'] =
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"INSERT OR REPLACE INTO %s (nonce, expires) VALUES (?, ?)";
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
<?php
/**
* OpenID Server Request
*
* @see Auth_OpenID_Server
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Imports
*/
require_once "Auth/OpenID.php";
/**
* Object that holds the state of a request to the OpenID server
*
* With accessor functions to get at the internal request data.
*
* @see Auth_OpenID_Server
* @package OpenID
*/
class Auth_OpenID_ServerRequest {
function Auth_OpenID_ServerRequest()
{
$this->mode = null;
}
}
?>

View file

@ -0,0 +1,243 @@
<?php
/**
* Functions for dealing with OpenID trust roots
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* A regular expression that matches a domain ending in a top-level domains.
* Used in checking trust roots for sanity.
*
* @access private
*/
define('Auth_OpenID___TLDs',
'/\.(com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|' .
'ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|' .
'bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|' .
'cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|' .
'fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|' .
'gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|' .
'ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|' .
'ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|' .
'nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|' .
'ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|' .
'so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|' .
'ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$/');
/**
* A wrapper for trust-root related functions
*/
class Auth_OpenID_TrustRoot {
/**
* Parse a URL into its trust_root parts.
*
* @static
*
* @access private
*
* @param string $trust_root The url to parse
*
* @return mixed $parsed Either an associative array of trust root
* parts or false if parsing failed.
*/
function _parse($trust_root)
{
$parts = @parse_url($trust_root);
if ($parts === false) {
return false;
}
$required_parts = array('scheme', 'host');
$forbidden_parts = array('user', 'pass', 'fragment');
$keys = array_keys($parts);
if (array_intersect($keys, $required_parts) != $required_parts) {
return false;
}
if (array_intersect($keys, $forbidden_parts) != array()) {
return false;
}
// Return false if the original trust root value has more than
// one port specification.
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) {
return false;
}
$scheme = strtolower($parts['scheme']);
$allowed_schemes = array('http', 'https');
if (!in_array($scheme, $allowed_schemes)) {
return false;
}
$parts['scheme'] = $scheme;
$host = strtolower($parts['host']);
$hostparts = explode('*', $host);
switch (count($hostparts)) {
case 1:
$parts['wildcard'] = false;
break;
case 2:
if ($hostparts[0] ||
($hostparts[1] && substr($hostparts[1], 0, 1) != '.')) {
return false;
}
$host = $hostparts[1];
$parts['wildcard'] = true;
break;
default:
return false;
}
if (strpos($host, ':') !== false) {
return false;
}
$parts['host'] = $host;
if (isset($parts['path'])) {
$path = strtolower($parts['path']);
if (substr($path, -1) != '/') {
$path .= '/';
}
} else {
$path = '/';
}
$parts['path'] = $path;
if (!isset($parts['port'])) {
$parts['port'] = false;
}
return $parts;
}
/**
* Is this trust root sane?
*
* A trust root is sane if it is syntactically valid and it has a
* reasonable domain name. Specifically, the domain name must be
* more than one level below a standard TLD or more than two
* levels below a two-letter tld.
*
* For example, '*.com' is not a sane trust root, but '*.foo.com'
* is. '*.co.uk' is not sane, but '*.bbc.co.uk' is.
*
* This check is not always correct, but it attempts to err on the
* side of marking sane trust roots insane instead of marking
* insane trust roots sane. For example, 'kink.fm' is marked as
* insane even though it "should" (for some meaning of should) be
* marked sane.
*
* This function should be used when creating OpenID servers to
* alert the users of the server when a consumer attempts to get
* the user to accept a suspicious trust root.
*
* @static
* @param string $trust_root The trust root to check
* @return bool $sanity Whether the trust root looks OK
*/
function isSane($trust_root)
{
$parts = Auth_OpenID_TrustRoot::_parse($trust_root);
if ($parts === false) {
return false;
}
// Localhost is a special case
if ($parts['host'] == 'localhost') {
return true;
}
// Get the top-level domain of the host. If it is not a valid TLD,
// it's not sane.
preg_match(Auth_OpenID___TLDs, $parts['host'], $matches);
if (!$matches) {
return false;
}
$tld = $matches[1];
// Require at least two levels of specificity for non-country
// tlds and three levels for country tlds.
$elements = explode('.', $parts['host']);
$n = count($elements);
if ($parts['wildcard']) {
$n -= 1;
}
if (strlen($tld) == 2) {
$n -= 1;
}
if ($n <= 1) {
return false;
}
return true;
}
/**
* Does this URL match the given trust root?
*
* Return whether the URL falls under the given trust root. This
* does not check whether the trust root is sane. If the URL or
* trust root do not parse, this function will return false.
*
* @param string $trust_root The trust root to match against
*
* @param string $url The URL to check
*
* @return bool $matches Whether the URL matches against the
* trust root
*/
function match($trust_root, $url)
{
$trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root);
$url_parsed = Auth_OpenID_TrustRoot::_parse($url);
if (!$trust_root_parsed || !$url_parsed) {
return false;
}
// Check hosts matching
if ($url_parsed['wildcard']) {
return false;
}
if ($trust_root_parsed['wildcard']) {
$host_tail = $trust_root_parsed['host'];
$host = $url_parsed['host'];
if ($host_tail &&
substr($host, -(strlen($host_tail))) != $host_tail &&
substr($host_tail, 1) != $host) {
return false;
}
} else {
if ($trust_root_parsed['host'] != $url_parsed['host']) {
return false;
}
}
// Check path and query matching
$base_path = $trust_root_parsed['path'];
$path = $url_parsed['path'];
if (!isset($trust_root_parsed['query'])) {
if (substr($path, 0, strlen($base_path)) != $base_path) {
return false;
}
} else {
$base_query = $trust_root_parsed['query'];
$query = @$url_parsed['query'];
$qplus = substr($query, 0, strlen($base_query) + 1);
$bqplus = $base_query . '&';
if ($base_path != $path ||
($base_query != $query && $qplus != $bqplus)) {
return false;
}
}
// The port and scheme need to match exactly
return ($trust_root_parsed['scheme'] == $url_parsed['scheme'] &&
$url_parsed['port'] === $trust_root_parsed['port']);
}
}
?>

View file

@ -0,0 +1,231 @@
<?php
/**
* URI normalization routines.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'Services/Yadis/Misc.php';
// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt)
function Auth_OpenID_getURIPattern()
{
return '&^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?&';
}
function Auth_OpenID_getAuthorityPattern()
{
return '/^([^@]*@)?([^:]*)(:.*)?/';
}
function Auth_OpenID_getEncodedPattern()
{
return '/%([0-9A-Fa-f]{2})/';
}
function Auth_OpenID_getUnreserved()
{
$_unreserved = array();
for ($i = 0; $i < 256; $i++) {
$_unreserved[$i] = false;
}
for ($i = ord('A'); $i <= ord('Z'); $i++) {
$_unreserved[$i] = true;
}
for ($i = ord('0'); $i <= ord('9'); $i++) {
$_unreserved[$i] = true;
}
for ($i = ord('a'); $i <= ord('z'); $i++) {
$_unreserved[$i] = true;
}
$_unreserved[ord('-')] = true;
$_unreserved[ord('.')] = true;
$_unreserved[ord('_')] = true;
$_unreserved[ord('~')] = true;
return $_unreserved;
}
function Auth_OpenID_getEscapeRE()
{
$parts = array();
foreach (array_merge(Services_Yadis_getUCSChars(),
Services_Yadis_getIPrivateChars()) as $pair) {
list($m, $n) = $pair;
$parts[] = sprintf("%s-%s", chr($m), chr($n));
}
return sprintf('[%s]', implode('', $parts));
}
function Auth_OpenID_pct_encoded_replace_unreserved($mo)
{
$_unreserved = Auth_OpenID_getUnreserved();
$i = intval($mo[1], 16);
if ($_unreserved[$i]) {
return chr($i);
} else {
return strtoupper($mo[0]);
}
return $mo[0];
}
function Auth_OpenID_pct_encoded_replace($mo)
{
return chr(intval($mo[1], 16));
}
function Auth_OpenID_remove_dot_segments($path)
{
$result_segments = array();
while ($path) {
if (Services_Yadis_startswith($path, '../')) {
$path = substr($path, 3);
} else if (Services_Yadis_startswith($path, './')) {
$path = substr($path, 2);
} else if (Services_Yadis_startswith($path, '/./')) {
$path = substr($path, 2);
} else if ($path == '/.') {
$path = '/';
} else if (Services_Yadis_startswith($path, '/../')) {
$path = substr($path, 3);
if ($result_segments) {
array_pop($result_segments);
}
} else if ($path == '/..') {
$path = '/';
if ($result_segments) {
array_pop($result_segments);
}
} else if (($path == '..') ||
($path == '.')) {
$path = '';
} else {
$i = 0;
if ($path[0] == '/') {
$i = 1;
}
$i = strpos($path, '/', $i);
if ($i === false) {
$i = strlen($path);
}
$result_segments[] = substr($path, 0, $i);
$path = substr($path, $i);
}
}
return implode('', $result_segments);
}
function Auth_OpenID_urinorm($uri)
{
$uri_matches = array();
preg_match(Auth_OpenID_getURIPattern(), $uri, $uri_matches);
if (count($uri_matches) < 9) {
for ($i = count($uri_matches); $i <= 9; $i++) {
$uri_matches[] = '';
}
}
$scheme = $uri_matches[2];
if ($scheme) {
$scheme = strtolower($scheme);
}
$scheme = $uri_matches[2];
if ($scheme === '') {
// No scheme specified
return null;
}
$scheme = strtolower($scheme);
if (!in_array($scheme, array('http', 'https'))) {
// Not an absolute HTTP or HTTPS URI
return null;
}
$authority = $uri_matches[4];
if ($authority === '') {
// Not an absolute URI
return null;
}
$authority_matches = array();
preg_match(Auth_OpenID_getAuthorityPattern(),
$authority, $authority_matches);
if (count($authority_matches) === 0) {
// URI does not have a valid authority
return null;
}
if (count($authority_matches) < 4) {
for ($i = count($authority_matches); $i <= 4; $i++) {
$authority_matches[] = '';
}
}
list($_whole, $userinfo, $host, $port) = $authority_matches;
if ($userinfo === null) {
$userinfo = '';
}
if (strpos($host, '%') !== -1) {
$host = strtolower($host);
$host = preg_replace_callback(
Auth_OpenID_getEncodedPattern(),
'Auth_OpenID_pct_encoded_replace', $host);
// NO IDNA.
// $host = unicode($host, 'utf-8').encode('idna');
} else {
$host = strtolower($host);
}
if ($port) {
if (($port == ':') ||
($scheme == 'http' && $port == ':80') ||
($scheme == 'https' && $port == ':443')) {
$port = '';
}
} else {
$port = '';
}
$authority = $userinfo . $host . $port;
$path = $uri_matches[5];
$path = preg_replace_callback(
Auth_OpenID_getEncodedPattern(),
'Auth_OpenID_pct_encoded_replace_unreserved', $path);
$path = Auth_OpenID_remove_dot_segments($path);
if (!$path) {
$path = '/';
}
$query = $uri_matches[6];
if ($query === null) {
$query = '';
}
$fragment = $uri_matches[8];
if ($fragment === null) {
$fragment = '';
}
return $scheme . '://' . $authority . $path . $query . $fragment;
}
?>

View file

@ -0,0 +1,8 @@
Thu Apr 26 11:41:52 PDT 2007 cygnus@janrain.com
tagged 1.2.3
Thu Apr 26 11:19:11 PDT 2007 cygnus@janrain.com
* Make yadis META tag search ignore comments
Fri Mar 23 11:15:57 PDT 2007 cygnus@janrain.com
tagged 1.2.2

View file

@ -0,0 +1,24 @@
PHP OpenID - OpenID consumer and server library
Copyright (C) 2005 Janrain, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
More info about PHP OpenID:
eng+openid@janrain.com
http://www.openidenabled.com/openid/libraries/php/
More info about OpenID:
http://www.openid.net

View file

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -0,0 +1,88 @@
What's New in PHP OpenID 1.1.0
==============================
Version 1.1 of the PHP OpenID library implements recent changes to the
OpenID specification as well as making API changes that should make
integration with applications easier. The consumer and server
examples have been updated to use the new APIs. As usual, if you have
any questions or would like to provide any kind of feedback at all,
please subscribe to our development discussion list (see README) and
let us know!
Yadis Support
-------------
One of the major changes to OpenID since the last release has been the
approval of Yadis discovery as the preferred way to specify the OpenID
metadata for an identity URL instead of using <link> tags in
HTML. This library does Yadis discovery, and if that fails, it falls
back to old-style discovery.
Some advantages of Yadis support are:
* Support for fallback if your primary OpenID provider is not
available
* Support for load-balancing between OpenID servers
* Easy interoperability for different identity services
For more information about Yadis, see http://yadis.org/ or
http://www.openidenabled.com/yadis/.
Extension Support
-----------------
OpenID also has formalized support for extensions. Extensions are a
mechanism for transferring information from the consumer to the server
and from the server to the consumer in the process of performing
OpenID authentication. Extensions are implemented as additional
namespaced query arguments that go along with standard OpenID requests
and responses. This library provides a simple API for adding extension
arguments to requests and extracting extension responses from replies.
Consumer API
------------
The consumer API has been changed for more natural use as well as to
support extension arguments.
* Auth_OpenID_Consumer($store, [$fetcher], [$immediate]) is now
Auth_OpenID_Consumer($store, [$session]). The session object is an
instance of Services_Yadis_PHPSession or a subclass thereof. The
default implementation wraps PHP's session machinery.
* Auth_OpenID_Consumer::beginAuth($user_url) is now
Auth_OpenID_Consumer::begin($user_url) and either returns an
Auth_OpenID_AuthRequest object or returns null. There is no more
tuple unpacking or status codes.
* Auth_OpenID_Consumer::constructRedirect($auth_req, $return_to,
$trust_root) is now
Auth_OpenID_AuthRequest::redirectURL($trust_root, $return_to,
[$immediate]).
* Auth_OpenID_Consumer::completeAuth($token, $query) is now
Auth_OpenID_Consumer::complete($query). It no longer returns a
tuple. Instead it returns an object that has a status code and
additional information about the response. See the API
documentation and examples for more information.
Server API
----------
The server API has been changed for greater extensibility. Instead of
taking an "is_authorized" callback, processing happens in several
stages, allowing you to insert extension data into the response before
it is signed and returned. See the documentation for the Server.php
file. Particularly, see the server example code in the examples/
directory.
Upgrading
---------
The server changed the way it indexes associations in the store, so if
you're upgrading a server installation, we recommend you clear the old
records from your store when you do so. As a consequence, consumers
will re-establish associations with your server a little sooner than
they would have otherwise.

View file

@ -0,0 +1,122 @@
PHP OpenID
----------
This is the PHP OpenID library by JanRain, Inc. You can visit our
website for more information about this package and other OpenID
implementations and tools:
http://www.openidenabled.com/
GETTING STARTED
===============
First, run the 'examples/detect.php' script either from the command
line or via the web. It will generate a report of any system
configuration changes necessary to run the library.
INSTALLATION
============
You will need PHP 4.3.0 or greater to use this library. We have
tested the library Linux on PHP 4.3.0, 4.4.1, 5.0.5, and 5.1.1. We
have tested the library on Windows XP on PHP 4.4.4.
Follow these steps:
1. Install dependencies.
- Enable either the GMP extension or Bcmath extension. (GMP is
STRONGLY recommended because it's MUCH faster!) This is
required.
- Enable the CURL extension.
- If you plan to use SQLite, PostgreSQL, or MySQL to store OpenID
data, you'll need PEAR DB. You can install this by running this
as root:
# pear install DB
You'll also need to install and enable the appropriate PHP
database extension. Alternatively, you can store OpenID data on
the filesystem instead of using a relational database. Nothing
special is required for using the filesystem method.
- Install either the DOM or domxml PHP XML processing extension,
but not both (they are incompatible).
2. Use the PEAR installer to install the package. To use the PEAR
installer, run this command from the package directory:
# pear install package.xml
If your PEAR version is sufficiently new, you can run this instead:
# pear install package2.xml
If the PEAR installer isn't available, you can copy the Auth/ and
Services/ directories into your PHP include path.
TESTING YOUR SETUP
==================
You can use the example code to test your setup. To run the example
consumer or server, follow the instructions in the examples/README
file.
USING THE API
=============
The best way to get started using the API is to take a look at the
example consumer and server in the examples/ directory. See the
examples/README file for more details.
TROUBLESHOOTING
===============
On some systems, PHP basedir restrictions prevent web servers from
opening a source of randomness, such as /dev/urandom. If your PHP
OpenID library has trouble getting a satisfactory source of
randomness, check your Apache and PHP configurations to be sure that
the randomness source is in the list of allowed paths for the
"open_basedir" option.
GETTING HELP
============
If you have any questions, recommendations, or patches, please tell
us! Subscribe to our OpenID development discussion list at
http://lists.openidenabled.com/mailman/listinfo/dev
DOCUMENTATION
=============
You can view the HTML library documentation in the doc/ directory.
You can also find it on the web at
http://www.openidenabled.com/resources/docs/openid/php/
This package's documentation is in PhpDoc format. To generate the
documentation, install phpdoc and run the admin/makedoc.sh script.
Phpdoc lives at:
http://www.phpdoc.org/
CONTRIBUTING
============
If you have a bugfix or feature you'd like to contribute, don't
hesitate to send it to us. Post your patch to the development list at
http://lists.openidenabled.com/mailman/listinfo/dev
To run the test suite included with this package, install PHPUnit 1.x
and run
php admin/texttest.php
PHPUnit 1.x can be found at
http://pear.phpunit.de/get/

View file

@ -0,0 +1,8 @@
Development Environment Setup
=============================
Janrain note: You'll need to run these commands to generate
documentation for this project:
apt-get install php4-pear
pear install PhpDocumentor

View 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);
}
}
?>

View 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);
}
}
}
?>

View 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;
}
?>

View 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);
}
}
?>

View 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;
}
}
?>

View 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);
}
}
?>

View 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;
}
?>

View 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;
}
}
?>

View 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;
}
?>

View 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);
}
}
?>

View 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;
}
}
?>

View file

@ -0,0 +1,38 @@
<?php
/**
* Tests for the Association 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 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/Association.php';
class Tests_Auth_OpenID_Association extends PHPUnit_TestCase {
function test_me()
{
$issued = time();
$lifetime = 600;
$assoc = new Auth_OpenID_Association('handle', 'secret', $issued,
$lifetime, 'HMAC-SHA1');
$s = $assoc->serialize();
$assoc2 = Auth_OpenID_Association::deserialize(
'Auth_OpenID_Association', $s);
if ($assoc2 === null) {
$this->fail('deserialize returned null');
} else {
$this->assertTrue($assoc2->equal($assoc));
}
}
}
?>

View file

@ -0,0 +1,235 @@
<?php
/**
* Tests for the BigMath functions.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/BigMath.php';
require_once 'Tests/Auth/OpenID/TestUtil.php';
class Tests_Auth_OpenID_BinLongConvertRnd extends PHPUnit_TestCase {
var $lib;
var $max;
function Tests_Auth_OpenID_BinLongConvertRnd(&$lib, $max)
{
$this->lib =& $lib;
$this->max = $max;
}
function runTest()
{
$n = $this->lib->init(0);
foreach (range(0, 9) as $i) {
$rnd = $this->lib->rand($this->max);
$n = $this->lib->add($n, $rnd);
}
$s = $this->lib->longToBinary($n);
$this->assertTrue(is_string($s));
$n_prime = $this->lib->binaryToLong($s);
$this->assertEquals($this->lib->cmp($n, $n_prime), 0);
}
}
class Tests_Auth_OpenID_BinLongConvert extends PHPUnit_TestCase {
var $lib;
var $bin;
var $lng;
function Tests_Auth_OpenID_BinLongConvert(&$lib, $bin, $lng)
{
$this->lib =& $lib;
$this->bin = $bin;
$this->lng = $lng;
}
function runTest()
{
$n_prime = $this->lib->binaryToLong($this->bin);
$s_prime = $this->lib->longToBinary($this->lng);
$this->assertEquals($this->lib->cmp($this->lng, $n_prime), 0);
$this->assertTrue($this->bin == $s_prime);
}
}
class Tests_Auth_OpenID_Base64ToLong extends PHPUnit_TestCase {
var $num;
var $b64;
var $lib;
function Tests_Auth_OpenID_Base64ToLong(&$lib, $b64, $num)
{
$this->lib = $lib;
$this->b64 = $b64;
$this->num = $num;
}
function runTest()
{
$actual = $this->lib->base64ToLong($this->b64);
$this->assertTrue($this->lib->cmp($this->num, $actual) == 0);
}
}
class Tests_Auth_OpenID_LongToBase64 extends Tests_Auth_OpenID_Base64ToLong {
function Tests_Auth_OpenID_LongToBase64(&$lib, $b64, $num)
{
$this->lib = $lib;
$this->b64 = $b64;
$this->num = $num;
}
function runTest()
{
$actual = $this->lib->longToBase64($this->num);
$this->assertEquals($this->b64, $actual);
}
}
class Tests_Auth_OpenID_Rand extends PHPUnit_TestCase {
function Tests_Auth_OpenID_Rand(&$lib)
{
$this->lib =& $lib;
}
function runTest()
{
$stop = $this->lib->pow(2, 128);
$a = $this->lib->rand($stop);
$b = $this->lib->rand($stop);
$this->assertFalse($this->lib->cmp($b, $a) == 0, "Same: $a $b");
$n = $this->lib->init(Tests_Auth_OpenID_maxint());
$n = $this->lib->add($n, 1);
// Make sure that we can generate random numbers that are
// larger than platform int size
$result = $this->lib->rand($n);
// What can we say about the result?
}
}
/**
* Computes the maximum integer value for this PHP installation.
*
* @return int $max_int_value The maximum integer value for this
* PHP installation
*/
function Tests_Auth_OpenID_maxint()
{
/* assumes largest integer is of form 2^n - 1 */
$to_test = pow(2, 16);
while (1) {
$last = $to_test;
$to_test = 2 * $to_test;
if (($to_test < $last) || (!is_int($to_test))) {
return($last + ($last - 1));
}
}
}
class Tests_Auth_OpenID_BigMath extends PHPUnit_TestSuite {
function _parseBase64Data()
{
$lines = Tests_Auth_OpenID_readlines('n2b64');
$data = array();
foreach ($lines as $line) {
$line = trim($line);
if (!$line) {
continue;
}
list($b64, $ascii) = explode(' ', $line);
$data[$b64] = $ascii;
}
return $data;
}
function _addB64Tests()
{
$lib =& Auth_OpenID_getMathLib();
$count = defined('Tests_Auth_OpenID_thorough') ? -1 : 2;
$data = $this->_parseBase64Data();
foreach ($data as $b64 => $num_s) {
// Only test the first few unless thorough is defined
if (strlen($num_s) > 5) {
if ($count == 0) {
break;
} else {
$count -= 1;
}
}
$num = $lib->init($num_s);
$test = new Tests_Auth_OpenID_Base64ToLong($lib, $b64, $num);
$test->setName("B64->Long $num_s");
$this->addTest($test);
$test = new Tests_Auth_OpenID_LongToBase64($lib, $b64, $num);
$test->setName("Long->B64 $num_s");
$this->addTest($test);
}
}
function _addBinLongTests()
{
$lib =& Auth_OpenID_getMathLib();
$max = Tests_Auth_OpenID_maxint();
$upper = defined('Tests_Auth_OpenID_thorough') ? 499 : 3;
foreach (range(0, $upper) as $iteration) {
$test = new Tests_Auth_OpenID_BinLongConvertRnd($lib, $max);
$test->setName("BinLongConvertRnd " . strval($iteration));
$this->addTest($test);
}
$cases = array(
array("\x00", 0),
array("\x01", 1),
array("\x7F", 127),
array("\x00\x80", 128),
array("\x00\x81", 129),
array("\x00\xFF", 255),
array("\x00\x80\x00", 32768),
array("OpenID is cool",
"1611215304203901150134421257416556")
);
foreach ($cases as $case) {
list($bin, $lng_m) = $case;
$lng = $lib->init($lng_m);
$test = new Tests_Auth_OpenID_BinLongConvert($lib, $bin, $lng);
$test->setName('BinLongConvert ' . bin2hex($bin));
$this->addTest($test);
}
}
function Tests_Auth_OpenID_BigMath($name)
{
$this->setName($name);
if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
$this->addTestSuite('Tests_Auth_OpenID_BigInt');
$this->_addB64Tests();
$this->_addBinLongTests();
$test = new Tests_Auth_OpenID_Rand(Auth_OpenID_getMathLib());
$test->setName('Big number rand');
$this->addTest($test);
}
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
<?php
/**
* Tests for the CryptUtil functions.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/CryptUtil.php';
class Tests_Auth_OpenID_CryptUtil extends PHPUnit_TestCase {
function test_length()
{
$cases = array(1, 10, 255);
foreach ($cases as $length) {
$data = Auth_OpenID_CryptUtil::getBytes($length);
$this->assertEquals(strlen($data), $length);
}
}
function test_different()
{
$num_iterations = 100;
$data_length = 20;
$data = Auth_OpenID_CryptUtil::getBytes($num_iterations);
for ($i = 0; $i < $num_iterations; $i++) {
$last = $data;
$data = Auth_OpenID_CryptUtil::getBytes($data_length);
$this->assertFalse($data == $last);
}
}
function test_cryptrand()
{
// It's possible, but HIGHLY unlikely that a correct
// implementation will fail by returning the same number twice
$s = Auth_OpenID_CryptUtil::getBytes(32);
$t = Auth_OpenID_CryptUtil::getBytes(32);
$this->assertEquals(strlen($s), 32);
$this->assertEquals(strlen($t), 32);
$this->assertFalse($s == $t);
}
}
?>

View file

@ -0,0 +1,160 @@
<?php
/**
* Tests for the Diffie-Hellman key exchange implementation in the
* OpenID 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 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/DiffieHellman.php';
require_once 'Tests/Auth/OpenID/TestUtil.php';
class Tests_Auth_OpenID_DiffieHellman_CheckCases extends PHPUnit_TestCase {
function Tests_Auth_OpenID_DiffieHellman_CheckCases($cases, $n)
{
$this->cases = $cases;
$this->n = $n;
}
function runTest()
{
$this->assertEquals($this->n, count($this->cases));
}
}
class Tests_Auth_OpenID_DiffieHellman_Private extends PHPUnit_TestCase {
function Tests_Auth_OpenID_DiffieHellman_Private($name, $input, $expected)
{
$this->setName("$name");
$this->input = $input;
$this->expected = $expected;
}
function runTest()
{
$lib =& Auth_OpenID_getMathLib();
$dh = new Auth_OpenID_DiffieHellman(null, null, $this->input);
$this->assertEquals($lib->cmp($this->expected, $dh->getPublicKey()), 0);
}
}
class Tests_Auth_OpenID_DiffieHellman_Exch extends PHPUnit_TestCase {
function Tests_Auth_OpenID_DiffieHellman_Exch($name, $p1, $p2, $shared)
{
$this->setName("$name");
$this->p1 = $p1;
$this->p2 = $p2;
$this->shared = $shared;
}
function runTest()
{
$lib =& Auth_OpenID_getMathLib();
$shared = $lib->init($this->shared);
$dh1 = new Auth_OpenID_DiffieHellman(null, null, $this->p1);
$dh2 = new Auth_OpenID_DiffieHellman(null, null, $this->p2);
$sh1 = $dh1->getSharedSecret($dh2->getPublicKey());
$sh2 = $dh2->getSharedSecret($dh1->getPublicKey());
$this->assertEquals($lib->cmp($shared, $sh1), 0);
$this->assertEquals($lib->cmp($shared, $sh2), 0);
}
}
class Tests_Auth_OpenID_DiffieHellman extends PHPUnit_TestSuite {
function _readPrivateTestCases()
{
$lines = Tests_Auth_OpenID_readlines('dhpriv');
$cases = array();
foreach ($lines as $line) {
$case = array();
if (!preg_match('/^(\d+) (\d+)\n$/', $line, $case)) {
trigger_error("Bad test input: $line", E_USER_ERROR);
}
$c = count($case);
if ($c != 3) {
trigger_error("Wrong number of elements in parsed case: $c",
E_USER_ERROR);
}
array_shift($case);
$cases[] = $case;
}
return $cases;
}
function _readExchTestCases()
{
$lines = Tests_Auth_OpenID_readlines('dhexch');
$cases = array();
foreach ($lines as $line) {
$case = array();
if (!preg_match('/^(\d+) (\d+) (\d+)\n$/', $line, $case)) {
trigger_error("Bad test input: $line", E_USER_ERROR);
}
$c = count($case);
if ($c != 4) {
trigger_error("Wrong number of elements in parsed case: $c",
E_USER_ERROR);
}
array_shift($case);
$cases[] = $case;
}
return $cases;
}
function Tests_Auth_OpenID_DiffieHellman($name)
{
$this->setName($name);
$priv_cases = Tests_Auth_OpenID_DiffieHellman::_readPrivateTestCases();
$sanity = new Tests_Auth_OpenID_DiffieHellman_CheckCases(
$priv_cases, 29);
$sanity->setName('Check parsing of priv test data');
$this->addTest($sanity);
$exch_cases = Tests_Auth_OpenID_DiffieHellman::_readExchTestCases();
$sanity = new Tests_Auth_OpenID_DiffieHellman_CheckCases(
$exch_cases, 25);
$sanity->setName('Check parsing of exch test data');
$this->addTest($sanity);
if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
if (defined('Tests_Auth_OpenID_thorough')) {
$npriv = count($priv_cases);
$nexch = count($exch_cases);
} else {
$npriv = 1;
$nexch = 3;
}
for ($i = 0; $i < $npriv; $i++) {
list($input, $expected) = $priv_cases[$i];
$one = new Tests_Auth_OpenID_DiffieHellman_Private(
"DHPriv $i", $input, $expected);
$this->addTest($one);
}
for ($i = 0; $i < $nexch; $i++) {
$case = $exch_cases[$i];
$one = new Tests_Auth_OpenID_DiffieHellman_Exch(
$i, $case[0], $case[1], $case[2]);
$this->addTest($one);
}
}
}
}
?>

View file

@ -0,0 +1,670 @@
<?php
require_once 'PHPUnit.php';
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Discover.php';
require_once 'Services/Yadis/Manager.php';
require_once 'Services/Yadis/Misc.php';
require_once 'Services/Yadis/XRI.php';
/**
* Tests for the core of the PHP Yadis library discovery logic.
*/
class _SimpleMockFetcher {
function _SimpleMockFetcher($responses)
{
$this->responses = $responses;
}
function get($url)
{
$response = array_pop($this->responses);
assert($response[1] == $url);
return $response;
}
}
class Tests_Services_Yadis_DiscoveryFailure extends PHPUnit_TestCase {
function Tests_Services_Yadis_DiscoveryFailure($responses)
{
// Response is ($code, $url, $body).
$this->cases = array(
array(null, 'http://network.error/', ''),
array(404, 'http://not.found/', ''),
array(400, 'http://bad.request/', ''),
array(500, 'http://server.error/', ''),
array(200, 'http://header.found/', 200,
array('x-xrds-location' => 'http://xrds.missing/')),
array(404, 'http://xrds.missing/', ''));
$this->url = $responses[0]->final_url;
$this->responses = $responses;
$this->fetcher = new _SimpleMockFetcher($this->responses);
}
function runTest()
{
foreach ($this->cases as $case) {
list($status, $url, $body) = $case;
$expected_status = $status;
$result = Auth_OpenID_discover($this->url, $this->fetcher);
list($id_url, $svclist, $http_response) = $result;
$this->assertEquals($http_response->status, $expected_status);
}
}
}
### Tests for raising/catching exceptions from the fetcher through the
### discover function
class _ErrorRaisingFetcher {
// Just raise an exception when fetch is called
function _ErrorRaisingFetcher($thing_to_raise)
{
$this->thing_to_raise = $thing_to_raise;
}
function post($body = null)
{
__raiseError($this->thing_to_raise);
}
function get($url)
{
__raiseError($this->thing_to_raise);
}
}
define('E_AUTH_OPENID_EXCEPTION', 'e_exception');
define('E_AUTH_OPENID_DIDFETCH', 'e_didfetch');
define('E_AUTH_OPENID_VALUE_ERROR', 'e_valueerror');
define('E_AUTH_OPENID_RUNTIME_ERROR', 'e_runtimeerror');
define('E_AUTH_OPENID_OI', 'e_oi');
class Tests_Auth_OpenID_Discover_FetchException extends PHPUnit_TestCase {
// Make sure exceptions get passed through discover function from
// fetcher.
function Tests_Auth_OpenID_Discover_FetchException($exc)
{
$this->cases = array(E_AUTH_OPENID_EXCEPTION,
E_AUTH_OPENID_DIDFETCH,
E_AUTH_OPENID_VALUE_ERROR,
E_AUTH_OPENID_RUNTIME_ERROR,
E_AUTH_OPENID_OI);
}
function runTest()
{
foreach ($this->cases as $thing_to_raise) {
$fetcher = ErrorRaisingFetcher($thing_to_raise);
Auth_OpenID_discover('http://doesnt.matter/', $fetcher);
$exc = __getError();
if ($exc !== $thing_to_raise) {
$this->fail('FetchException expected %s to be raised',
$thing_to_raise);
}
}
}
}
// Tests for openid.consumer.discover.discover
class _DiscoveryMockFetcher {
function _DiscoveryMockFetcher(&$documents)
{
$this->redirect = null;
$this->documents = &$documents;
$this->fetchlog = array();
}
function post($url, $body = null, $headers = null)
{
return $this->get($url, $headers, $body);
}
function get($url, $headers = null, $body = null)
{
$this->fetchlog[] = array($url, $body, $headers);
if ($this->redirect) {
$final_url = $this->redirect;
} else {
$final_url = $url;
}
if (array_key_exists($url, $this->documents)) {
list($ctype, $body) = $this->documents[$url];
$status = 200;
} else {
$status = 404;
$ctype = 'text/plain';
$body = '';
}
return new Services_Yadis_HTTPResponse($final_url, $status,
array('content-type' => $ctype), $body);
}
}
define('DISCOVERYBASE_ID_URL', "http://someuser.unittest/");
class _DiscoveryBase extends PHPUnit_TestCase {
var $id_url = DISCOVERYBASE_ID_URL;
var $documents = array();
function setUp()
{
$this->fetcher = new _DiscoveryMockFetcher($this->documents);
}
}
$__yadis_2entries = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
xmlns:openid="http://openid.net/xmlns/1.0"
>
<XRD>
<CanonicalID>=!1000</CanonicalID>
<Service priority="10">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://smoker.myopenid.com/</openid:Delegate>
</Service>
<Service priority="20">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.livejournal.com/openid/server.bml</URI>
<openid:Delegate>http://frank.livejournal.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>
';
$__yadis_2entries_flipped_priority = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
xmlns:openid="http://openid.net/xmlns/1.0"
>
<XRD>
<Service priority="20">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://smoker.myopenid.com/</openid:Delegate>
</Service>
<Service priority="10">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.livejournal.com/openid/server.bml</URI>
<openid:Delegate>http://frank.livejournal.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>
';
$__yadis_another = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
xmlns:openid="http://openid.net/xmlns/1.0"
>
<XRD>
<Service priority="10">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://vroom.unittest/server</URI>
<openid:Delegate>http://smoker.myopenid.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>
';
$__yadis_0entries = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
xmlns:openid="http://openid.net/xmlns/1.0"
>
<XRD>
<Service >
<Type>http://is-not-openid.unittest/</Type>
<URI>http://noffing.unittest./</URI>
</Service>
</XRD>
</xrds:XRDS>
';
$__yadis_no_delegate = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
>
<XRD>
<Service priority="10">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
</Service>
</XRD>
</xrds:XRDS>
';
$__openid_html = '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Identity Page for Smoker</title>
<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid.delegate" href="http://smoker.myopenid.com/" />
</head><body><p>foo</p></body></html>
';
$__openid_html_no_delegate = '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Identity Page for Smoker</title>
<link rel="openid.server" href="http://www.myopenid.com/server" />
</head><body><p>foo</p></body></html>
';
$__openid_and_yadis_html = '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Identity Page for Smoker</title>
<meta http-equiv="X-XRDS-Location" content="http://someuser.unittest/xrds" />
<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid.delegate" href="http://smoker.myopenid.com/" />
</head><body><p>foo</p></body></html>
';
class _MockFetcherForXRIProxy {
function _MockFetcherForXRIProxy($documents)
{
$this->documents = $documents;
$this->fetchlog = array();
}
function get($url, $headers=null)
{
return $this->fetch($url, $headers);
}
function post($url, $body)
{
return $this->fetch($url, $body);
}
function fetch($url, $body=null, $headers=null)
{
$this->fetchlog[] = array($url, $body, $headers);
$u = parse_url($url);
$proxy_host = $u['host'];
$xri = $u['path'];
$query = $u['query'];
if ((!$headers) && (!$query)) {
trigger_error('Error in mock XRI fetcher: no headers or query');
}
if (Services_Yadis_startswith($xri, '/')) {
$xri = substr($xri, 1);
}
if (array_key_exists($xri, $this->documents)) {
list($ctype, $body) = $this->documents[$xri];
$status = 200;
} else {
$status = 404;
$ctype = 'text/plain';
$body = '';
}
return new Services_Yadis_HTTPResponse($url, $status,
array('content-type' => $ctype),
$body);
}
}
class Tests_Auth_OpenID_DiscoverSession {
function Tests_Auth_OpenID_DiscoverSession()
{
$this->data = array();
}
function set($name, $value)
{
$this->data[$name] = $value;
}
function get($name, $default=null)
{
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
} else {
return $default;
}
}
function del($name)
{
unset($this->data[$name]);
}
}
$__Tests_BOGUS_SERVICE = new Auth_OpenID_ServiceEndpoint();
$__Tests_BOGUS_SERVICE->identity_url = "=really.bogus.endpoint";
function __serviceCheck_discover_cb($url, $fetcher)
{
global $__Tests_BOGUS_SERVICE;
return array($__Tests_BOGUS_SERVICE);
}
class Tests_Auth_OpenID_Discover extends _DiscoveryBase {
function _usedYadis($service)
{
$this->assertTrue($service->used_yadis,
"Expected to use Yadis");
}
function _notUsedYadis($service)
{
$this->assertFalse($service->used_yadis,
"Expected to use old-style discovery");
}
function test_404()
{
$result = Auth_OpenID_discover($this->id_url . '/404',
$this->fetcher);
list($id_url, $svclist, $http_response) = $result;
$this->assertTrue($http_response->status == 404,
"Expected 404 status from /404 discovery");
}
function test_noYadis()
{
global $__openid_html;
$this->documents[$this->id_url] = array('text/html', $__openid_html);
list($id_url, $services, $http_response) =
Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertEquals(count($services), 1,
"More than one service");
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[0]->delegate,
"http://smoker.myopenid.com/");
$this->assertEquals($services[0]->identity_url, $this->id_url);
$this->_notUsedYadis($services[0]);
}
function test_managerServices()
{
global $__yadis_2entries_flipped_priority;
$url = "http://bogus.xxx/";
$sess = new Tests_Auth_OpenID_DiscoverSession();
$m = new Services_Yadis_Discovery($sess, $url);
$documents = array(
$url => array("application/xrds+xml",
$__yadis_2entries_flipped_priority)
);
$fetcher = new _DiscoveryMockFetcher($documents);
$expected = array("http://frank.livejournal.com/",
"http://smoker.myopenid.com/");
foreach ($expected as $openid) {
$s = $m->getNextService('_Auth_OpenID_discoverServiceList',
$fetcher);
$this->assertEquals($s->delegate, $openid);
}
}
function test_serviceCheck()
{
global $__Tests_BOGUS_SERVICE;
$url = "http://bogus.xxx/";
$sess =& new Tests_Auth_OpenID_DiscoverSession();
$disco =& new Services_Yadis_Discovery($sess, $url);
# Set an empty manager to be sure it gets blown away
$manager =& new Services_Yadis_Manager($url, null, array(),
$disco->getSessionKey());
$loader =& new Services_Yadis_ManagerLoader();
$disco->session->set($disco->session_key,
serialize($loader->toSession($manager)));
$docs = array();
$fetcher =& new _DiscoveryMockFetcher($docs);
$result = $disco->getNextService('__serviceCheck_discover_cb', $fetcher);
$newMan = $disco->getManager();
$currentService = $newMan->_current;
$this->assertEquals($currentService->identity_url,
$__Tests_BOGUS_SERVICE->identity_url);
}
function test_noOpenID()
{
$this->fetcher->documents = array(
$this->id_url => array('text/plain', "junk"));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertFalse(count($services) > 0);
}
function test_yadis()
{
global $__yadis_2entries;
$this->fetcher->documents = array(
DISCOVERYBASE_ID_URL => array('application/xrds+xml',
$__yadis_2entries));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertEquals(count($services), 2,
"Not 2 services");
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->_usedYadis($services[0]);
$this->assertEquals($services[1]->server_url,
"http://www.livejournal.com/openid/server.bml");
$this->_usedYadis($services[1]);
}
function test_redirect()
{
global $__openid_html;
$expected_final_url = "http://elsewhere.unittest/";
$this->fetcher->redirect = $expected_final_url;
$this->fetcher->documents = array(
$this->id_url => array('text/html', $__openid_html));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($expected_final_url, $id_url);
$this->assertEquals(count($services), 1,
"More than one service");
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[0]->delegate,
"http://smoker.myopenid.com/");
$this->assertEquals($services[0]->identity_url,
$expected_final_url);
$this->_notUsedYadis($services[0]);
}
function test_emptyList()
{
global $__yadis_0entries;
$this->fetcher->documents = array(
$this->id_url =>
array('application/xrds+xml', $__yadis_0entries));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertTrue(count($services) == 0);
}
function test_emptyListWithLegacy()
{
global $__openid_and_yadis_html,
$__yadis_0entries;
$this->fetcher->documents = array(
$this->id_url => array('text/html', $__openid_and_yadis_html),
$this->id_url . 'xrds' => array('application/xrds+xml', $__yadis_0entries));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertEquals(count($services), 1,
"Not one service");
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[0]->identity_url, $this->id_url);
$this->_notUsedYadis($services[0]);
}
function test_yadisNoDelegate()
{
global $__yadis_no_delegate;
$this->fetcher->documents = array(
$this->id_url => array('application/xrds+xml', $__yadis_no_delegate));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertEquals(count($services), 1,
"Not 1 service");
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[0]->delegate, null,
'Delegate should be null');
$this->_usedYadis($services[0]);
}
function test_openidNoDelegate()
{
global $__openid_html_no_delegate;
$this->fetcher->documents = array(
$this->id_url => array('text/html',
$__openid_html_no_delegate));
list($id_url, $services, $http) = Auth_OpenID_discover($this->id_url,
$this->fetcher);
$this->assertEquals($this->id_url, $id_url);
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[0]->identity_url, $this->id_url);
$this->assertEquals($services[0]->delegate, null,
'Delegate should be null');
$this->_notUsedYadis($services[0]);
}
function test_xriDiscovery()
{
global $__yadis_2entries;
$documents = array(
'=smoker' => array('application/xrds+xml',
$__yadis_2entries)
);
$fetcher = new _MockFetcherForXRIProxy($documents);
list($user_xri, $services) = _Auth_OpenID_discoverXRI('=smoker',
$fetcher);
$this->assertTrue($services);
$this->assertEquals($services[0]->server_url,
"http://www.myopenid.com/server");
$this->assertEquals($services[1]->server_url,
"http://www.livejournal.com/openid/server.bml");
$this->assertEquals($services[0]->canonicalID, Services_Yadis_XRI("=!1000"));
}
function test_useCanonicalID()
{
// When there is no delegate, the CanonicalID should be used
// with XRI.
$endpoint = new Auth_OpenID_ServiceEndpoint();
$endpoint->identity_url = "=example";
$endpoint->canonicalID = Services_Yadis_XRI("=!1000");
$this->assertEquals($endpoint->getServerID(), Services_Yadis_XRI("=!1000"));
}
}
?>

View file

@ -0,0 +1,148 @@
<?php
/**
* Tests for the HMAC-SHA1 utility functions used by the OpenID
* 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 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/HMACSHA1.php';
require_once 'Tests/Auth/OpenID/TestUtil.php';
class Tests_Auth_OpenID_HMACSHA1_TestCase extends PHPUnit_TestCase {
function Tests_Auth_OpenID_HMACSHA1_TestCase(
$name, $key, $data, $expected)
{
$this->setName($name);
$this->key = $key;
$this->data = $data;
$this->expected = $expected;
}
function runTest()
{
$actual = Auth_OpenID_HMACSHA1($this->key, $this->data);
$this->assertEquals($this->expected, $actual);
}
}
class Tests_Auth_OpenID_HMACSHA1 extends PHPUnit_TestSuite {
function _strConvert($s)
{
$repeat_pat = '/^0x([a-f0-9]{2}) repeated (\d+) times$/';
if (preg_match($repeat_pat, $s, $match)) {
$c = chr(hexdec($match[1]));
$n = $match[2];
$data = '';
for ($i = 0; $i < $n; $i++) {
$data .= $c;
}
} elseif (substr($s, 0, 2) == "0x") {
$data = pack('H*', substr($s, 2, strlen($s) - 1));
} elseif (preg_match('/^"(.*)"$/', $s, $match)) {
$data = $match[1];
} else {
trigger_error("Bad data format: $s", E_USER_ERROR);
}
return $data;
}
function _readTestCases()
{
$lines = Tests_Auth_OpenID_readlines('hmac.txt');
$cases = array();
$case = array();
foreach ($lines as $line) {
if ($line{0} == "#") {
continue;
}
// Blank line separates test cases
if ($line == "\n") {
$cases[] = $case;
$case = array();
} else {
$match = array();
$pat = '/^([a-z0-9_-]+) =\s+(.*?)\n$/';
if (!preg_match($pat, $line, $match)) {
trigger_error("Bad test input: $line", E_USER_ERROR);
}
$c = count($match);
if ($c != 3) {
trigger_error(
"Wrong number of elements in parsed case: $c",
E_USER_ERROR);
return false;
}
$key = $match[1];
$value = $match[2];
$case[$key] = $value;
}
}
if (count($case)) {
$cases[] = $case;
}
$final = array();
// Normalize strings and check data integrity
foreach ($cases as $case) {
$clean = array();
$clean["key"] =
Tests_Auth_OpenID_HMACSHA1::_strConvert($case["key"]);
if (strlen($clean["key"]) != $case["key_len"]) {
trigger_error("Bad key length", E_USER_ERROR);
}
$clean["data"] =
Tests_Auth_OpenID_HMACSHA1::_strConvert($case["data"]);
if (strlen($clean["data"]) != $case["data_len"]) {
trigger_error("Bad data length", E_USER_ERROR);
}
$clean["digest"] =
Tests_Auth_OpenID_HMACSHA1::_strConvert($case["digest"]);
if (strlen($clean["digest"]) != 20) {
$l = strlen($clean["digest"]);
trigger_error("Bad digest length: $l", E_USER_ERROR);
}
$clean['test_case'] = $case['test_case'];
$final[] = $clean;
}
return $final;
}
function Tests_Auth_OpenID_HMACSHA1($name)
{
$this->setName($name);
$cases = $this->_readTestCases();
foreach ($cases as $case) {
$test = new Tests_Auth_OpenID_HMACSHA1_TestCase(
$case['test_case'],
$case['key'],
$case['data'],
$case['digest']
);
$digest = $case['digest'];
$this->addTest($test);
}
}
}
?>

View file

@ -0,0 +1,259 @@
<?php
/**
* Tests for the KVForm module.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/KVForm.php';
$_Tests_Auth_OpenID_kverrors = null;
/**
* Keep a list of the logged errors
*/
function Tests_Auth_OpenID_kvHandleError($errno, $errmsg)
{
global $_Tests_Auth_OpenID_kverrors;
$_Tests_Auth_OpenID_kverrors[] = $errmsg;
}
class Tests_Auth_OpenID_KVForm_TestCase extends PHPUnit_TestCase {
var $errs;
function runTest()
{
// Re-set the number of logged errors
global $_Tests_Auth_OpenID_kverrors;
$_Tests_Auth_OpenID_kverrors = array();
set_error_handler("Tests_Auth_OpenID_kvHandleError");
$this->_runTest();
// Check to make sure we have the expected number of logged errors
//$this->assertEquals($this->errs, count($_Tests_Auth_OpenID_kverrors));
restore_error_handler();
}
function _runTest()
{
trigger_error('Must be overridden', E_USER_ERROR);
}
}
class Tests_Auth_OpenID_KVForm_TestCase_Parse
extends Tests_Auth_OpenID_KVForm_TestCase {
function Tests_Auth_OpenID_KVForm_TestCase_Parse(
$arr, $str, $lossy, $errs)
{
$this->arr = $arr;
$this->str = $str;
$this->lossy = $lossy;
$this->errs = $errs;
}
function _runTest()
{
// Do one parse, after which arrayToKV and kvToArray should be
// inverses.
$parsed1 = Auth_OpenID_KVForm::toArray($this->str);
$serial1 = Auth_OpenID_KVForm::fromArray($this->arr);
if ($this->lossy == "neither" || $this->lossy == "str") {
$this->assertEquals($this->arr, $parsed1, "str was lossy");
}
if ($this->lossy == "neither" || $this->lossy == "arr") {
$this->assertEquals($this->str, $serial1, "array was lossy");
}
$parsed2 = Auth_OpenID_KVForm::toArray($serial1);
$serial2 = Auth_OpenID_KVForm::fromArray($parsed1);
// Round-trip both
$parsed3 = Auth_OpenID_KVForm::toArray($serial2);
$serial3 = Auth_OpenID_KVForm::fromArray($parsed2);
$this->assertEquals($serial2, $serial3, "serialized forms differ");
// Check to make sure that they're inverses.
$this->assertEquals($parsed2, $parsed3, "parsed forms differ");
}
}
class Tests_Auth_OpenID_KVForm_TestCase_Null
extends Tests_Auth_OpenID_KVForm_TestCase {
function Tests_Auth_OpenID_KVForm_TestCase_Null($arr, $errs)
{
$this->arr = $arr;
$this->errs = $errs;
}
function _runTest()
{
$serialized = Auth_OpenID_KVForm::fromArray($this->arr);
$this->assertTrue($serialized === null,
'serialization unexpectedly succeeded');
}
}
class Tests_Auth_OpenID_KVForm extends PHPUnit_TestSuite {
function Tests_Auth_OpenID_KVForm($name)
{
$this->setName($name);
$testdata_list = array(
array("name" => "simple",
"str" => "college:harvey mudd\n",
"arr" => array("college" => "harvey mudd"),
),
array("name" => "empty",
"str" => "",
"arr" => array(),
),
array("name" => "empty (just newline)",
"str" => "\n",
"arr" => array(),
"lossy" => "str",
"errors" => 1,
),
array("name" => "empty (double newline)",
"str" => "\n\n",
"arr" => array(),
"lossy" => "str",
"errors" => 2,
),
array("name" => "empty (no colon)",
"str" => "East is least\n",
"arr" => array(),
"lossy" => "str",
"errors" => 1,
),
array("name" => "two keys",
"str" => "city:claremont\nstate:CA\n",
"arr" => array('city' => 'claremont',
'state' => 'CA'),
),
array("name" => "real life",
"str" => "is_valid:true\ninvalidate_handle:" .
"{HMAC-SHA1:2398410938412093}\n",
"arr" => array('is_valid' => 'true',
'invalidate_handle' =>
'{HMAC-SHA1:2398410938412093}'),
),
array("name" => "empty key and value",
"str" => ":\n",
"arr" => array(''=>''),
),
array("name" => "empty key, not value",
"str" => ":missing key\n",
"arr" => array(''=>'missing key'),
),
array("name" => "whitespace at front of key",
"str" => " street:foothill blvd\n",
"arr" => array('street'=>'foothill blvd'),
"lossy" => "str",
"errors" => 1,
),
array("name" => "whitespace at front of value",
"str" => "major: computer science\n",
"arr" => array('major'=>'computer science'),
"lossy" => "str",
"errors" => 1,
),
array("name" => "whitespace around key and value",
"str" => " dorm : east \n",
"arr" => array('dorm'=>'east'),
"lossy" => "str",
"errors" => 2,
),
array("name" => "missing trailing newline",
"str" => "e^(i*pi)+1:0",
"arr" => array('e^(i*pi)+1'=>'0'),
"lossy" => "str",
"errors" => 1,
),
array("name" => "missing trailing newline (two key)",
"str" => "east:west\nnorth:south",
"arr" => array('east'=>'west',
'north'=>'south'),
"lossy" => "str",
"errors" => 1,
),
array("name" => "colon in key",
"arr" => array("k:k" => 'v'),
"errors" => 1,
),
array("name" => "newline in key",
"arr" => array("k\nk" => 'v'),
"errors" => 1,
),
array("name" => "newline in value",
"arr" => array('k' => "v\nv"),
"errors" => 1,
),
array("name" => "array whitespace",
"arr" => array(" k " => "v"),
"lossy" => "both",
"str" => " k :v\n",
"errors" => 2,
),
array("name" => "array ordering 1",
"arr" => array("a" => "x",
"b" => "x",
"c" => "x"),
"str" => "a:x\nb:x\nc:x\n",
),
array("name" => "array ordering 2",
"arr" => array("a" => "x",
"c" => "x",
"b" => "x"),
"str" => "a:x\nc:x\nb:x\n",
),
);
foreach ($testdata_list as $testdata) {
if (isset($testdata['str'])) {
$str = $testdata['str'];
} else {
$str = null;
}
$arr = $testdata["arr"];
if (isset($testdata['errors'])) {
$errs = $testdata["errors"];
} else {
$errs = 0;
}
if (is_null($str)) {
$test = new Tests_Auth_OpenID_KVForm_TestCase_Null($arr, $errs);
} else {
if (isset($testdata['lossy'])) {
$lossy = $testdata["lossy"];
} else {
$lossy = 'neither';
}
$test = new Tests_Auth_OpenID_KVForm_TestCase(
$arr, $str, $lossy, $errs);
}
$test->setName($testdata["name"]);
$this->addTest($test);
}
}
}
?>

View file

@ -0,0 +1,110 @@
<?php
/**
* In-memory OpenID store implementation for testing only
*/
require_once "Auth/OpenID/Interface.php";
class Tests_Auth_OpenID_MemStore extends Auth_OpenID_OpenIDStore {
var $assocs = null;
var $nonces = null;
function Tests_Auth_OpenID_MemStore($auth_key=null)
{
$this->assocs = array();
$this->nonces = array();
$this->auth_key = $auth_key;
}
function getKey($server_url, $handle)
{
return serialize(array($server_url, $handle));
}
function getAssocPairs()
{
$pairs = array();
foreach ($this->assocs as $key => $assoc) {
list($assoc_url, $_) = unserialize($key);
$pairs[] = array($assoc_url, $assoc);
}
return $pairs;
}
function getServerAssocs($server_url)
{
$matches = array();
foreach ($this->getAssocPairs() as $pair) {
list($assoc_url, $assoc) = $pair;
if ($assoc_url == $server_url) {
$matches[] = $assoc;
}
}
return $matches;
}
function getAssociation($server_url, $handle=null)
{
$assocs = $this->getServerAssocs($server_url);
if ($handle === null) {
$best = null;
foreach ($assocs as $assoc) {
if (!isset($best) ||
$best->issued < $assoc->issued) {
$best = $assoc;
}
}
return $best;
} else {
foreach ($assocs as $assoc) {
if ($assoc->handle == $handle) {
return $assoc;
}
}
return null;
}
}
function storeAssociation($server_url, &$association)
{
$key = $this->getKey($server_url, $association->handle);
$this->assocs[$key] = $association;
}
function removeAssociation($server_url, $handle)
{
$key = $this->getKey($server_url, $handle);
$present = isset($this->assocs[$key]);
unset($this->assocs[$key]);
return $present;
}
function storeNonce($nonce)
{
if (!in_array($nonce, $this->nonces)) {
$this->nonces[] = $nonce;
}
}
function useNonce($nonce)
{
$index = array_search($nonce, $this->nonces);
$present = $index !== false;
if ($present) {
unset($this->nonces[$index]);
}
return $present;
}
function reset()
{
$this->assocs = array();
$this->nonces = array();
}
function getAuthKey()
{
return $this->auth_key;
}
}

View file

@ -0,0 +1,224 @@
<?php
/**
* Tests for the combination of Yadis discovery and the OpenID
* protocol.
*/
require_once "PHPUnit.php";
require_once "Services/Yadis/XRDS.php";
require_once "Auth/OpenID/Discover.php";
$__XRDS_BOILERPLATE = '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)"
xmlns:openid="http://openid.net/xmlns/1.0">
<XRD>
%s
</XRD>
</xrds:XRDS>
';
// Different sets of server URLs for use in the URI tag
$__server_url_options = array(
array(), // This case should not generate an endpoint object
array('http://server.url/'),
array('https://server.url/'),
array('https://server.url/', 'http://server.url/'),
array('https://server.url/',
'http://server.url/',
'http://example.server.url/'),
);
// A couple of example extension type URIs. These are not at all
// official, but are just here for testing.
$__ext_types = array(
'http://janrain.com/extension/blah',
'http://openid.net/sreg/1.0');
// All valid combinations of Type tags that should produce an OpenID
// endpoint
$__openid_types = array(
_OPENID_1_0_TYPE,
_OPENID_1_1_TYPE,
_OPENID_1_2_TYPE);
$temp = array();
foreach (__subsets($__ext_types) as $exts) {
foreach (__subsets($__openid_types) as $ts) {
if ($ts) {
$temp[] = array_merge($exts, $ts);
}
}
}
$__type_uri_options = $temp;
// Range of valid Delegate tag values for generating test data
$__delegate_options = array(
null,
'http://vanity.domain/',
'https://somewhere/yadis/');
$temp = array();
foreach ($__delegate_options as $delegate) {
foreach ($__type_uri_options as $type_uris) {
foreach ($__server_url_options as $uris) {
$temp[] = array($uris, $type_uris, $delegate);
}
}
}
// All combinations of valid URIs, Type URIs and Delegate tags
$__data = $temp;
function _mkXRDS($services_str)
{
global $__XRDS_BOILERPLATE;
return sprintf($__XRDS_BOILERPLATE, $services_str);
}
function _mkService($uris = null, $type_uris = null,
$delegate = null, $dent = ' ')
{
$chunks = array($dent, "<Service>\n");
$dent2 = $dent . ' ';
if ($type_uris) {
foreach ($type_uris as $type_uri) {
$chunks = array_merge($chunks,
array($dent2 . '<Type>',
$type_uri, "</Type>\n"));
}
}
if ($uris) {
foreach ($uris as $uri) {
if (is_array($uri)) {
list($uri, $prio) = $uri;
} else {
$prio = null;
}
$chunks = array_merge($chunks, array($dent2, '<URI'));
if ($prio !== null) {
$chunks = array_merge($chunks, array(' priority="', strval($prio), '"'));
}
$chunks = array_merge($chunks, array('>', $uri, "</URI>\n"));
}
}
if ($delegate) {
$chunks = array_merge($chunks,
array($dent2, '<openid:Delegate>',
$delegate, "</openid:Delegate>\n"));
}
$chunks = array_merge($chunks, array($dent, "</Service>\n"));
return implode("", $chunks);
}
// Used for generating test data
function __subsets($list)
{
// Generate all non-empty sublists of a list
$subsets_list = array(array());
foreach ($list as $elem) {
$temp = array();
foreach ($subsets_list as $t) {
$temp[] = array_merge(array($elem), $t);
}
$subsets_list = array_merge($subsets_list, $temp);
}
return $subsets_list;
}
class Tests_Auth_OpenID_Tester extends PHPUnit_TestCase {
function Tests_Auth_OpenID_Tester($uris, $type_uris, $delegate)
{
$this->uris = $uris;
$this->type_uris = $type_uris;
$this->delegate = $delegate;
parent::PHPUnit_TestCase();
}
function setUp()
{
$this->yadis_url = 'http://unit.test/';
// Create an XRDS document to parse
$services = _mkService($this->uris,
$this->type_uris,
$this->delegate);
$this->xrds = _mkXRDS($services);
}
function runTest()
{
// Parse into endpoint objects that we will check
$xrds_object = Services_Yadis_XRDS::parseXRDS($this->xrds);
$endpoints = array();
if ($xrds_object) {
$endpoints = $xrds_object->services(array('filter_MatchesAnyOpenIDType'));
$endpoints = Auth_OpenID_makeOpenIDEndpoints($this->yadis_url, $endpoints);
}
// make sure there are the same number of endpoints as
// URIs. This assumes that the type_uris contains at least one
// OpenID type.
$this->assertEquals(count($this->uris), count($endpoints),
"URI <-> Endpoint count");
// So that we can check equality on the endpoint types
$type_uris = $this->type_uris;
sort($type_uris);
$seen_uris = array();
foreach ($endpoints as $endpoint) {
$seen_uris[] = $endpoint->server_url;
// All endpoints will have same yadis_url
$this->assertEquals($this->yadis_url, $endpoint->identity_url);
// and delegate
$this->assertEquals($this->delegate, $endpoint->delegate);
// and types
$actual_types = $endpoint->type_uris;
sort($actual_types);
$this->assertEquals($actual_types, $type_uris);
}
// So that they will compare equal, because we don't care what
// order they are in
sort($seen_uris);
$uris = $this->uris;
sort($uris);
// Make sure we saw all URIs, and saw each one once
$this->assertEquals($uris, $seen_uris);
}
}
class Tests_Auth_OpenID_OpenID_Yadis extends PHPUnit_TestSuite {
function Tests_Auth_OpenID_OpenID_Yadis()
{
global $__data;
foreach ($__data as $case) {
$this->addTest(new Tests_Auth_OpenID_Tester($case[0], $case[1], $case[2]));
}
}
function getName()
{
return 'Tests_Auth_OpenID_OpenID_Yadis';
}
}
?>

View file

@ -0,0 +1,181 @@
<?php
/**
* Tests for the Consumer parsing functions.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'Tests/Auth/OpenID/TestUtil.php';
require_once 'Auth/OpenID/Parse.php';
require_once 'PHPUnit.php';
class Tests_Auth_OpenID_Link extends PHPUnit_TestCase {
function Tests_Auth_OpenID_Link($case)
{
list($desc, $markup, $links, $case_text) = $case;
$this->desc = $desc;
$this->markup = $markup;
$this->expected_links = $links;
$this->case_text = $case_text;
$this->parser = new Auth_OpenID_Parse();
}
function getName()
{
return $this->desc;
}
function runTest()
{
$parsed = $this->parser->parseLinkAttrs($this->markup);
$i = 0;
foreach ($this->expected_links as $expected) {
list($is_optional_link, $expected_link) = $expected;
if ($is_optional_link &&
($i >= count($parsed))) {
continue;
}
$act_link = $parsed[$i];
$increment = true;
foreach ($expected_link as $attr => $data) {
list($is_optional_attr, $value) = $data;
if ($is_optional_attr) {
$actual_value = null;
if (array_key_exists($attr, $act_link)) {
$actual_value = $act_link[$attr];
} else {
continue;
}
} else {
$actual_value = $act_link[$attr];
}
if ($is_optional_link &&
($value != $actual_value)) {
$increment = false;
break;
}
$this->assertEquals($value, $actual_value);
}
if ($increment) {
$i++;
}
}
$this->assertEquals($i, count($parsed));
}
}
class NumTestCases extends PHPUnit_TestCase {
function NumTestCases($test_cases, $num_tests)
{
$this->test_cases = $test_cases;
$this->num_tests = $num_tests;
}
function runTest()
{
$this->assertEquals(count($this->test_cases),
$this->num_tests);
}
}
class Tests_Auth_OpenID_Parse extends PHPUnit_TestSuite {
function getName()
{
return "Tests_Auth_OpenID_Parse";
}
function _parseCheck($cond, $where)
{
if (!$cond) {
trigger_error('Parse error in ' . $where, E_USER_ERROR);
}
}
function parseLink($line)
{
$parts = explode(" ", $line);
$optional = intval($parts[0] == 'Link*:');
$this->_parseCheck($optional || ($parts[0] == 'Link:'), __FUNCTION__);
$attrs = array();
foreach (array_slice($parts, 1) as $attr) {
list($k, $v) = explode("=", $attr, 2);
if ($k[strlen($k) - 1] == '*') {
$attr_optional = 1;
$k = substr($k, 0, strlen($k) - 1);
} else {
$attr_optional = 0;
}
$attrs[$k] = array($attr_optional, $v);
}
return array($optional, $attrs);
}
function parseCase($s)
{
list($header, $markup) = explode("\n\n", $s, 2);
$lines = explode("\n", $header);
$name = array_shift($lines);
$this->_parseCheck(strpos($name, 'Name: ') == 0, __FUNCTION__);
$desc = substr($name, 6);
$parsed = array();
foreach ($lines as $line) {
$parsed[] = $this->parseLink($line);
}
return array($desc, $markup, $parsed);
}
function parseTests($s)
{
$tests = array();
$cases = explode("\n\n\n", $s);
$header = array_shift($cases);
list($tests_line, $unused) = explode("\n", $header, 2);
list($k, $v) = explode(": ", $tests_line);
$this->_parseCheck(('Num Tests' == $k), __FUNCTION__);
$num_tests = intval($v);
foreach (array_slice($cases, 0, count($cases) - 1) as $case) {
list($desc, $markup, $links) = $this->parseCase($case);
$tests[] = array($desc, $markup, $links, $case);
}
return array($num_tests, $tests);
}
function Tests_Auth_OpenID_Parse()
{
$test_data = Tests_Auth_OpenID_readdata('linkparse.txt');
list($num_tests, $test_cases) = $this->parseTests($test_data);
$this->addTest(new NumTestCases($test_cases, $num_tests));
foreach ($test_cases as $case) {
$this->addTest(new Tests_Auth_OpenID_Link($case));
}
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,574 @@
<?php
/**
* A test script for the OpenIDStore classes.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Require classes and functions to run the Store tests.
*/
require_once 'Auth/OpenID/Association.php';
require_once 'Auth/OpenID/CryptUtil.php';
require_once 'Auth/OpenID.php';
require_once 'PHPUnit.php';
function _Auth_OpenID_mkdtemp()
{
if (strpos(PHP_OS, 'WIN') === 0) {
$dir = $_ENV['TMP'];
if (!isset($dir)) {
$dir = 'C:\Windows\Temp';
}
} else {
$dir = @$_ENV['TMPDIR'];
if (!isset($dir)) {
$dir = '/tmp';
}
}
return Auth_OpenID_FileStore::_mkdtemp($dir);
}
/**
* This is the host where the SQL stores' databases should be created
* and destroyed.
*/
$_Auth_OpenID_db_test_host = 'dbtest';
/**
* Generate a sufficently unique database name so many hosts can run
* SQL store tests on the server at the same time and not step on each
* other.
*/
function _Auth_OpenID_getTmpDbName()
{
$hostname = php_uname('n');
$hostname = strtolower(str_replace('.', '_', $hostname));
return sprintf("%s_%d_%s_openid_test",
$hostname,
getmypid(),
strval(rand(1, time())));
}
/**
* Tests the OpenID stores.
*
* @package OpenID
*/
class Tests_Auth_OpenID_StoreTest extends PHPUnit_TestCase {
/**
* Prepares for the SQL store tests.
*/
function setUp()
{
$this->letters = Auth_OpenID_letters;
$this->digits = Auth_OpenID_digits;
$this->punct = Auth_OpenID_punct;
$this->allowed_nonce = $this->letters . $this->digits;
$this->allowed_handle = $this->letters . $this->digits . $this->punct;
}
/**
* Generates a nonce value.
*/
function generateNonce()
{
return Auth_OpenID_CryptUtil::randomString(8, $this->allowed_nonce);
}
/**
* Generates an association with the specified parameters.
*/
function genAssoc($now, $issued = 0, $lifetime = 600)
{
$sec = Auth_OpenID_CryptUtil::randomString(20);
$hdl = Auth_OpenID_CryptUtil::randomString(128, $this->allowed_handle);
return new Auth_OpenID_Association($hdl, $sec, $now + $issued,
$lifetime, 'HMAC-SHA1');
}
/**
* @access private
*/
function _checkRetrieve(&$store, $url, $handle, $expected, $name = null)
{
$retrieved_assoc = $store->getAssociation($url, $handle);
if (($expected === null) || ($store->isDumb())) {
$this->assertNull($retrieved_assoc, "Retrieved association " .
"was non-null");
} else {
if ($retrieved_assoc === null) {
$this->fail("$name: Got null when expecting " .
$expected->serialize());
} else {
$this->assertEquals($expected->serialize(),
$retrieved_assoc->serialize(), $name);
}
}
}
function _checkRemove(&$store, $url, $handle, $expected, $name = null)
{
$present = $store->removeAssociation($url, $handle);
$expectedPresent = (!$store->isDumb() && $expected);
$this->assertTrue((!$expectedPresent && !$present) ||
($expectedPresent && $present),
$name);
}
/**
* Make sure a given store has a minimum of API compliance. Call
* this function with an empty store.
*
* Raises AssertionError if the store does not work as expected.
*
* OpenIDStore -> NoneType
*/
function _testStore($store)
{
// Association functions
$now = time();
$server_url = 'http://www.myopenid.com/openid';
$assoc = $this->genAssoc($now);
$this->_checkRetrieve($store, $server_url, null, null,
'Make sure that a missing association returns no result');
$store->storeAssociation($server_url, $assoc);
$this->_checkRetrieve($store, $server_url, null, $assoc,
'Check that after storage, getting returns the same result');
$this->_checkRetrieve($store, $server_url, null, $assoc,
'more than once');
$store->storeAssociation($server_url, $assoc);
$this->_checkRetrieve($store, $server_url, null, $assoc,
'Storing more than once has no ill effect');
// Removing an association that does not exist returns not present
$this->_checkRemove($store, $server_url, $assoc->handle . 'x', false,
"Remove nonexistent association (1)");
// Removing an association that does not exist returns not present
$this->_checkRemove($store, $server_url . 'x', $assoc->handle, false,
"Remove nonexistent association (2)");
// Removing an association that is present returns present
$this->_checkRemove($store, $server_url, $assoc->handle, true,
"Remove existent association");
// but not present on subsequent calls
$this->_checkRemove($store, $server_url, $assoc->handle, false,
"Remove nonexistent association after removal");
// Put assoc back in the store
$store->storeAssociation($server_url, $assoc);
// More recent and expires after assoc
$assoc2 = $this->genAssoc($now, $issued = 1);
$store->storeAssociation($server_url, $assoc2);
$this->_checkRetrieve($store, $server_url, null, $assoc2,
'After storing an association with a different handle, but the
same $server_url, the handle with the later expiration is
returned.');
$this->_checkRetrieve($store, $server_url, $assoc->handle, $assoc,
'We can still retrieve the older association');
$this->_checkRetrieve($store, $server_url, $assoc2->handle, $assoc2,
'Plus we can retrieve the association with the later expiration
explicitly');
$assoc3 = $this->genAssoc($now, $issued = 2, $lifetime = 100);
$store->storeAssociation($server_url, $assoc3);
// More recent issued time, so assoc3 is expected.
$this->_checkRetrieve($store, $server_url, null, $assoc3, "(1)");
$this->_checkRetrieve($store, $server_url, $assoc->handle,
$assoc, "(2)");
$this->_checkRetrieve($store, $server_url, $assoc2->handle,
$assoc2, "(3)");
$this->_checkRetrieve($store, $server_url, $assoc3->handle,
$assoc3, "(4)");
$this->_checkRemove($store, $server_url, $assoc2->handle, true, "(5)");
$this->_checkRetrieve($store, $server_url, null, $assoc3, "(6)");
$this->_checkRetrieve($store, $server_url, $assoc->handle,
$assoc, "(7)");
$this->_checkRetrieve($store, $server_url, $assoc2->handle,
null, "(8)");
$this->_checkRetrieve($store, $server_url, $assoc3->handle,
$assoc3, "(9)");
$this->_checkRemove($store, $server_url, $assoc2->handle,
false, "(10)");
$this->_checkRemove($store, $server_url, $assoc3->handle,
true, "(11)");
$this->_checkRetrieve($store, $server_url, null, $assoc, "(12)");
$this->_checkRetrieve($store, $server_url, $assoc->handle,
$assoc, "(13)");
$this->_checkRetrieve($store, $server_url, $assoc2->handle,
null, "(14)");
$this->_checkRetrieve($store, $server_url, $assoc3->handle,
null, "(15)");
$this->_checkRemove($store, $server_url, $assoc2->handle,
false, "(16)");
$this->_checkRemove($store, $server_url, $assoc->handle,
true, "(17)");
$this->_checkRemove($store, $server_url, $assoc3->handle,
false, "(18)");
$this->_checkRetrieve($store, $server_url, null, null, "(19)");
$this->_checkRetrieve($store, $server_url, $assoc->handle,
null, "(20)");
$this->_checkRetrieve($store, $server_url, $assoc2->handle,
null, "(21)");
$this->_checkRetrieve($store, $server_url,$assoc3->handle,
null, "(22)");
$this->_checkRemove($store, $server_url, $assoc2->handle,
false, "(23)");
$this->_checkRemove($store, $server_url, $assoc->handle,
false, "(24)");
$this->_checkRemove($store, $server_url, $assoc3->handle,
false, "(25)");
// Put associations into store, for two different server URLs
$assoc1 = $this->genAssoc($now);
$assoc2 = $this->genAssoc($now + 2);
$server_url1 = "http://one.example.com/one";
$server_url2 = "http://two.localhost.localdomain/two";
$store->storeAssociation($server_url1, $assoc1);
$store->storeAssociation($server_url2, $assoc2);
// Ask for each one, make sure we get it
$this->_checkRetrieve($store, $server_url1, $assoc1->handle,
$assoc1, "(26)");
$this->_checkRetrieve($store, $server_url2, $assoc2->handle,
$assoc2, "(27)");
$store->storeAssociation($server_url1, $assoc1);
$store->storeAssociation($server_url2, $assoc2);
// Ask for each one, make sure we get it
$this->_checkRetrieve($store, $server_url1, null,
$assoc1, "(28)");
$this->_checkRetrieve($store, $server_url2, null,
$assoc2, "(29)");
}
function _checkUseNonce(&$store, $nonce, $expected, $msg=null)
{
$actual = $store->useNonce($nonce);
$expected = $store->isDumb() || $expected;
$val = ($actual && $expected) || (!$actual && !$expected);
$this->assertTrue($val, "_checkUseNonce failed: $msg");
}
function _testNonce(&$store)
{
// Nonce functions
// Random nonce (not in store)
$nonce1 = $this->generateNonce();
// A nonce is not present by default
$this->_checkUseNonce($store, $nonce1, false, 1);
// Storing once causes useNonce to return true the first, and
// only the first, time it is called after the $store->
$store->storeNonce($nonce1);
$this->_checkUseNonce($store, $nonce1, true, 2);
$this->_checkUseNonce($store, $nonce1, false, 3);
$this->_checkUseNonce($store, $nonce1, false, 4);
// Storing twice has the same effect as storing once.
$store->storeNonce($nonce1);
$store->storeNonce($nonce1);
$this->_checkUseNonce($store, $nonce1, true, 5);
$this->_checkUseNonce($store, $nonce1, false, 6);
$this->_checkUseNonce($store, $nonce1, false, 7);
// Auth key functions
// There is no key to start with, so generate a new key and
// return it.
$key = $store->getAuthKey();
// The second time around should return the same as last time.
$key2 = $store->getAuthKey();
$this->assertEquals($key, $key2, "Auth keys differ");
$this->assertEquals(strlen($key), $store->AUTH_KEY_LEN,
"Key length not equals AUTH_KEY_LEN");
}
function test_memstore()
{
require_once 'Tests/Auth/OpenID/MemStore.php';
$store = new Tests_Auth_OpenID_MemStore('Bogus auth key ');
$this->_testStore(&$store);
$this->_testNonce(&$store);
}
function test_filestore()
{
require_once 'Auth/OpenID/FileStore.php';
$temp_dir = _Auth_OpenID_mkdtemp();
if (!$temp_dir) {
trigger_error('Could not create temporary directory ' .
'with Auth_OpenID_FileStore::_mkdtemp',
E_USER_WARNING);
return null;
}
$store = new Auth_OpenID_FileStore($temp_dir);
$this->_testStore($store);
$this->_testNonce($store);
$store->destroy();
}
function test_postgresqlstore()
{
// If the postgres extension isn't loaded or loadable, succeed
// because we can't run the test.
if (!(extension_loaded('pgsql') ||
@dl('pgsql.' . PHP_SHLIB_SUFFIX))) {
$this->pass();
return;
}
require_once 'Auth/OpenID/PostgreSQLStore.php';
require_once 'DB.php';
global $_Auth_OpenID_db_test_host;
$temp_db_name = _Auth_OpenID_getTmpDbName();
$connect_db_name = 'test_master';
$dsn = array(
'phptype' => 'pgsql',
'username' => 'openid_test',
'password' => '',
'hostspec' => $_Auth_OpenID_db_test_host,
'database' => $connect_db_name
);
$allowed_failures = 5;
$result = null;
$sleep_time = 1.0;
$sql = sprintf("CREATE DATABASE %s", $temp_db_name);
for ($failures = 0; $failures < $allowed_failures; $failures++) {
$template_db =& DB::connect($dsn);
if (PEAR::isError($template_db)) {
$result &= $template_db;
} else {
// Try to create the test database.
$result = $template_db->query($sql);
$template_db->disconnect();
unset($template_db);
if (!PEAR::isError($result)) {
break;
}
}
$sleep_time *= ((mt_rand(1, 100) / 100.0) + 1.5);
print "Failed to create database $temp_db_name.\n".
"Waiting $sleep_time before trying again\n";
$int_sleep = floor($sleep_time);
$frac_sleep = $sleep_time - $int_sleep;
sleep($int_sleep);
usleep($frac_sleep * 1000000.0);
}
if ($failures == $allowed_failures) {
$this->fail("Temporary database creation failed after $failures ".
" tries ('$temp_db_name'): " . $result->getMessage());
return;
}
// Disconnect from template1 and reconnect to the temporary
// testing database.
$dsn['database'] = $temp_db_name;
$db =& DB::connect($dsn);
if (PEAR::isError($db)) {
$this->fail("Temporary database connection failed " .
" ('$temp_db_name'): " . $db->getMessage());
return;
}
$store =& new Auth_OpenID_PostgreSQLStore($db);
$store->createTables();
$this->_testStore($store);
$this->_testNonce($store);
$db->disconnect();
unset($db);
// Connect to template1 again so we can drop the temporary
// database.
$dsn['database'] = $connect_db_name;
$template_db =& DB::connect($dsn);
if (PEAR::isError($template_db)) {
$this->fail("Template database connection (to drop " .
"temporary database) failed: " .
$template_db->getMessage());
return;
}
$result = $template_db->query(sprintf("DROP DATABASE %s",
$temp_db_name));
if (PEAR::isError($result)) {
$this->fail("Dropping temporary database failed: " .
$result->getMessage());
return;
}
$template_db->disconnect();
unset($template_db);
}
function test_sqlitestore()
{
// If the postgres extension isn't loaded or loadable, succeed
// because we can't run the test.
if (!(extension_loaded('sqlite') ||
@dl('sqlite.' . PHP_SHLIB_SUFFIX))) {
$this->pass();
return;
}
require_once 'Auth/OpenID/SQLiteStore.php';
require_once 'DB.php';
$temp_dir = _Auth_OpenID_mkdtemp();
if (!$temp_dir) {
trigger_error('Could not create temporary directory ' .
'with Auth_OpenID_FileStore::_mkdtemp',
E_USER_WARNING);
return null;
}
$dsn = sprintf("sqlite:///%s/file.db", $temp_dir);
$db =& DB::connect($dsn);
if (PEAR::isError($db)) {
$this->fail("SQLite database connection failed: " .
$db->getMessage());
} else {
$store =& new Auth_OpenID_SQLiteStore($db);
$this->assertTrue($store->createTables(), "Table creation failed");
$this->_testStore($store);
$this->_testNonce($store);
}
$db->disconnect();
unset($db);
unset($store);
unlink($temp_dir . '/file.db');
rmdir($temp_dir);
}
function test_mysqlstore()
{
// If the mysql extension isn't loaded or loadable, succeed
// because we can't run the test.
if (!(extension_loaded('mysql') ||
@dl('mysql.' . PHP_SHLIB_SUFFIX))) {
$this->pass();
return;
}
require_once 'Auth/OpenID/MySQLStore.php';
require_once 'DB.php';
global $_Auth_OpenID_db_test_host;
$dsn = array(
'phptype' => 'mysql',
'username' => 'openid_test',
'password' => '',
'hostspec' => $_Auth_OpenID_db_test_host
);
$db =& DB::connect($dsn);
if (PEAR::isError($db)) {
print "MySQL database connection failed: " .
$db->getMessage();
$this->pass();
return;
}
$temp_db_name = _Auth_OpenID_getTmpDbName();
$result = $db->query("CREATE DATABASE $temp_db_name");
if (PEAR::isError($result)) {
$this->fail("Error creating MySQL temporary database: " .
$result->getMessage());
return;
}
$db->query("USE $temp_db_name");
$store =& new Auth_OpenID_MySQLStore($db);
$store->createTables();
$this->_testStore($store);
$this->_testNonce($store);
$db->query("DROP DATABASE $temp_db_name");
}
}
?>

View file

@ -0,0 +1,28 @@
<?php
/**
* Utilites for test functions
*/
function Tests_Auth_OpenID_datafile($name, $reader)
{
$path = dirname(realpath(__FILE__));
$sep = DIRECTORY_SEPARATOR;
$filename = $path . $sep . 'data' . $sep . $name;
$data = $reader($filename);
if ($data === false) {
$msg = "Failed to open data file: $name";
trigger_error($msg, E_USER_ERROR);
}
return $data;
}
function Tests_Auth_OpenID_readdata($name)
{
return Tests_Auth_OpenID_datafile($name, 'file_get_contents');
}
function Tests_Auth_OpenID_readlines($name)
{
return Tests_Auth_OpenID_datafile($name, 'file');
}

View file

@ -0,0 +1,167 @@
<?php
/**
* Tests for the TrustRoot module
*/
require_once "Auth/OpenID/TrustRoot.php";
require_once "Tests/Auth/OpenID/TestUtil.php";
require_once "PHPUnit.php";
class Tests_Auth_OpenID_TRParseCase extends PHPUnit_TestCase {
function Tests_Auth_OpenID_TRParseCase($desc, $case, $expected)
{
$this->setName($desc);
$this->case = $case;
$this->expected = $expected;
}
function runTest()
{
$is_sane = Auth_OpenID_TrustRoot::isSane($this->case);
$parsed = (bool)Auth_OpenID_TrustRoot::_parse($this->case);
switch ($this->expected) {
case 'sane':
$this->assertTrue($is_sane);
$this->assertTrue($parsed);
break;
case 'insane':
$this->assertTrue($parsed);
$this->assertFalse($is_sane);
break;
default:
$this->assertFalse($parsed);
$this->assertFalse($is_sane);
}
}
}
class Tests_Auth_OpenID_TRMatchCase extends PHPUnit_TestCase {
function Tests_Auth_OpenID_TRMatchCase($desc, $tr, $rt, $matches)
{
$this->setName($desc);
$this->tr = $tr;
$this->rt = $rt;
$this->matches = $matches;
}
function runTest()
{
$matches = Auth_OpenID_TrustRoot::match($this->tr, $this->rt);
$this->assertEquals((bool)$this->matches, (bool)$matches);
}
}
function Tests_Auth_OpenID_parseHeadings($data, $c)
{
$heading_pat = '/(^|\n)' . $c . '{40}\n([^\n]+)\n' . $c . '{40}\n()/';
$offset = 0;
$headings = array();
while (true) {
preg_match($heading_pat, substr($data, $offset), $matches,
PREG_OFFSET_CAPTURE);
if (!$matches) {
break;
}
$start = $matches[0][1];
$heading = $matches[2][0];
$end = $matches[3][1];
$headings[] = array('heading' => $heading,
'start' => $offset + $start,
'end' => $offset + $end,
);
$offset += $end;
}
return $headings;
}
function Tests_Auth_OpenID_getSections($data)
{
$headings = Tests_Auth_OpenID_parseHeadings($data, '-');
$sections = array();
$n = count($headings);
for ($i = 0; $i < $n; ) {
$secdata = $headings[$i];
list($numtests, $desc) = explode(': ', $secdata['heading']);
$start = $secdata['end'];
$i += 1;
if ($i < $n) {
$blob = substr($data, $start, $headings[$i]['start'] - $start);
} else {
$blob = substr($data, $start);
}
$lines = explode("\n", trim($blob));
if (count($lines) != $numtests) {
trigger_error('Parse failure: ' . var_export($secdata, true),
E_USER_ERROR);
}
$sections[] = array('desc' => $desc, 'lines' => $lines,);
}
return $sections;
}
function Tests_Auth_OpenID_trParseTests($head, $tests)
{
$tests = array('fail' => $tests[0],
'insane' => $tests[1],
'sane' => $tests[2]);
$testobjs = array();
foreach ($tests as $expected => $testdata) {
$lines = $testdata['lines'];
foreach ($lines as $line) {
$desc = sprintf("%s - %s: %s", $head,
$testdata['desc'], var_export($line, true));
$testobjs[] = new Tests_Auth_OpenID_TRParseCase(
$desc, $line, $expected);
}
}
return $testobjs;
}
function Tests_Auth_OpenID_trMatchTests($head, $tests)
{
$tests = array(true => $tests[0], false => $tests[1]);
$testobjs = array();
foreach ($tests as $expected => $testdata) {
$lines = $testdata['lines'];
foreach ($lines as $line) {
$pat = '/^([^ ]+) +([^ ]+)$/';
preg_match($pat, $line, $matches);
list($_, $tr, $rt) = $matches;
$desc = sprintf("%s - %s: %s %s", $head, $testdata['desc'],
var_export($tr, true), var_export($rt, true));
$testobjs[] = new Tests_Auth_OpenID_TRMatchCase(
$desc, $tr, $rt, $expected);
}
}
return $testobjs;
}
function Tests_Auth_OpenID_trustRootTests()
{
$data = Tests_Auth_OpenID_readdata('trustroot.txt');
list($parsehead, $matchhead) = Tests_Auth_OpenID_parseHeadings($data, '=');
$pe = $parsehead['end'];
$parsedata = substr($data, $pe, $matchhead['start'] - $pe);
$parsetests = Tests_Auth_OpenID_getSections($parsedata);
$parsecases = Tests_Auth_OpenID_trParseTests($parsehead['heading'],
$parsetests);
$matchdata = substr($data, $matchhead['end']);
$matchtests = Tests_Auth_OpenID_getSections($matchdata);
$matchcases = Tests_Auth_OpenID_trMatchTests($matchhead['heading'],
$matchtests);
return array_merge($parsecases, $matchcases);
}
class Tests_Auth_OpenID_TrustRoot extends PHPUnit_TestSuite {
function Tests_Auth_OpenID_TrustRoot($name)
{
$this->setName($name);
foreach (Tests_Auth_OpenID_trustRootTests() as $test) {
$this->addTest($test);
}
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* Tests for the URI normalization routines used by the OpenID
* 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 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID/URINorm.php';
require_once 'Tests/Auth/OpenID/TestUtil.php';
class Tests_Auth_OpenID_URINorm_TestCase extends PHPUnit_TestCase {
function Tests_Auth_OpenID_URINorm_TestCase(
$name, $uri, $expected)
{
$this->setName($name);
$this->uri = $uri;
$this->expected = $expected;
}
function runTest()
{
$actual = Auth_OpenID_urinorm($this->uri);
$this->assertEquals($this->expected, $actual);
}
}
class Tests_Auth_OpenID_URINorm extends PHPUnit_TestSuite {
function _readTestCases()
{
$lines = Tests_Auth_OpenID_readlines('urinorm.txt');
$cases = array();
$case = array();
for ($i = 0; $i < count($lines) && ($i + 3 <= count($lines)); $i += 4) {
$name = trim($lines[$i]);
$uri = trim($lines[$i + 1]);
$expected = trim($lines[$i + 2]);
if ($expected == 'fail') {
$expected = null;
}
$cases[] = array($name, $uri, $expected);
}
return $cases;
}
function Tests_Auth_OpenID_URINorm($name)
{
$this->setName($name);
$cases = $this->_readTestCases();
foreach ($cases as $case) {
list($name, $uri, $expected) = $case;
$this->addTest(new Tests_Auth_OpenID_URINorm_TestCase($name, $uri, $expected));
}
}
}
?>

View file

@ -0,0 +1,260 @@
<?php
/**
* Tests for utility functions used by the OpenID 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 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'Auth/OpenID.php';
class Tests_Auth_OpenID_Util extends PHPUnit_TestCase {
function test_base64()
{
// This is not good for international use, but PHP doesn't
// appear to provide access to the local alphabet.
$letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$digits = "0123456789";
$extra = "+/=";
$allowed_s = $letters . $digits . $extra;
$allowed_d = array();
for ($i = 0; $i < strlen($allowed_s); $i++) {
$c = $allowed_s[$i];
$allowed_d[$c] = null;
}
function checkEncoded($obj, $str, $allowed_array)
{
for ($i = 0; $i < strlen($str); $i++) {
$obj->assertTrue(array_key_exists($str[$i],
$allowed_array));
}
}
$cases = array(
"",
"x",
"\x00",
"\x01",
str_repeat("\x00", 100),
implode("", array_map('chr', range(0, 255)))
);
foreach ($cases as $s) {
$b64 = base64_encode($s);
checkEncoded($this, $b64, $allowed_d);
$s_prime = base64_decode($b64);
$this->assertEquals($s_prime, $s);
}
function random_ordinal($unused)
{
return rand(0, 255);
}
// Randomized test
foreach (range(0, 49) as $i) {
$n = rand(0, 2048);
$s = implode("", array_map('chr',
array_map('random_ordinal',
range(0, $n))));
$b64 = base64_encode($s);
checkEncoded($this, $b64, $allowed_d);
$s_prime = base64_decode($b64);
$this->assertEquals($s_prime, $s);
}
}
function test_normalizeUrl()
{
$this->assertEquals("http://foo.com/",
Auth_OpenID::normalizeUrl("foo.com"));
$this->assertEquals("http://foo.com/",
Auth_OpenID::normalizeUrl("http://foo.com"));
$this->assertEquals("https://foo.com/",
Auth_OpenID::normalizeUrl("https://foo.com"));
$this->assertEquals("http://foo.com/bar",
Auth_OpenID::normalizeUrl("foo.com/bar"));
$this->assertEquals("http://foo.com/bar",
Auth_OpenID::normalizeUrl("http://foo.com/bar"));
$this->assertEquals("http://foo.com/",
Auth_OpenID::normalizeUrl("http://foo.com/"));
$this->assertEquals("https://foo.com/",
Auth_OpenID::normalizeUrl("https://foo.com/"));
$this->assertEquals("https://foo.com/bar" ,
Auth_OpenID::normalizeUrl("https://foo.com/bar"));
if (0) {
$this->assertEquals("http://foo.com/%E8%8D%89",
Auth_OpenID::normalizeUrl("foo.com/\u8349"));
$this->assertEquals("http://foo.com/%E8%8D%89",
Auth_OpenID::normalizeUrl("http://foo.com/\u8349"));
}
$non_ascii_domain_cases = array(
array("http://xn--vl1a.com/",
"\u8349.com"),
array("http://xn--vl1a.com/",
"http://\u8349.com"),
array("http://xn--vl1a.com/",
"\u8349.com/"),
array("http://xn--vl1a.com/",
"http://\u8349.com/"),
array("http://xn--vl1a.com/%E8%8D%89",
"\u8349.com/\u8349"),
array("http://xn--vl1a.com/%E8%8D%89",
"http://\u8349.com/\u8349"),
);
// XXX
/*
codecs.getencoder('idna')
except LookupError:
# If there is no idna codec, these cases with
# non-ascii-representable domain names should fail.
should_raise = True
else:
should_raise = False
for expected, case in non_ascii_domain_cases:
try:
actual = Auth_OpenID::normalizeUrl(case)
except UnicodeError:
assert should_raise
else:
assert not should_raise and actual == expected, case
*/
$this->assertNull(Auth_OpenID::normalizeUrl(null));
$this->assertNull(Auth_OpenID::normalizeUrl(''));
$this->assertNull(Auth_OpenID::normalizeUrl('http://'));
}
function test_appendArgs()
{
$simple = 'http://www.example.com/';
$cases = array(
array('empty list',
array($simple, array()),
$simple),
array('empty dict',
array($simple, array()),
$simple),
array('one list',
array($simple, array(array('a', 'b'))),
$simple . '?a=b'),
array('one dict',
array($simple, array('a' => 'b')),
$simple . '?a=b'),
array('two list (same)',
array($simple, array(array('a', 'b'),
array('a', 'c'))),
$simple . '?a=b&a=c'),
array('two list',
array($simple, array(array('a', 'b'),
array('b', 'c'))),
$simple . '?a=b&b=c'),
array('two list (order)',
array($simple, array(array('b', 'c'),
array('a', 'b'))),
$simple . '?b=c&a=b'),
array('two dict (order)',
array($simple, array('b' => 'c',
'a' => 'b')),
$simple . '?a=b&b=c'),
array('escape',
array($simple, array(array('=', '='))),
$simple . '?%3D=%3D'),
array('escape (URL)',
array($simple, array(array('this_url',
$simple))),
$simple .
'?this_url=http%3A%2F%2Fwww.example.com%2F'),
array('use dots',
array($simple, array(array('openid.stuff',
'bother'))),
$simple . '?openid.stuff=bother'),
array('args exist (empty)',
array($simple . '?stuff=bother', array()),
$simple . '?stuff=bother'),
array('args exist',
array($simple . '?stuff=bother',
array(array('ack', 'ack'))),
$simple . '?stuff=bother&ack=ack'),
array('args exist',
array($simple . '?stuff=bother',
array(array('ack', 'ack'))),
$simple . '?stuff=bother&ack=ack'),
array('args exist (dict)',
array($simple . '?stuff=bother',
array('ack' => 'ack')),
$simple . '?stuff=bother&ack=ack'),
array('args exist (dict 2)',
array($simple . '?stuff=bother',
array('ack' => 'ack', 'zebra' => 'lion')),
$simple . '?stuff=bother&ack=ack&zebra=lion'),
array('three args (dict)',
array($simple, array('stuff' => 'bother',
'ack' => 'ack',
'zebra' => 'lion')),
$simple . '?ack=ack&stuff=bother&zebra=lion'),
array('three args (list)',
array($simple, array(
array('stuff', 'bother'),
array('ack', 'ack'),
array('zebra', 'lion'))),
$simple . '?stuff=bother&ack=ack&zebra=lion'),
);
// Tests.
foreach ($cases as $case) {
list($desc, $data, $expected) = $case;
list($url, $query) = $data;
$this->assertEquals($expected,
Auth_OpenID::appendArgs($url, $query));
}
}
}
?>

View file

@ -0,0 +1,25 @@
2126517416 1503105722 107994742031567165457540274858809652167995244913839787589743578103625285208352685332617657612917013907706708046204110421143061814711041523408378123857885283119340960531860106377561159933747570456852792031929014142490977574671361334052940195520731561436107832724927338342871107723160776334443535305873323500256
1726325708 97982531 120276049194830643562108817809662062106546923236483207874835831104433621725428405166395533625121884325430201809382464614237831903082554679507511419420917850293136497504021313018779166242697363853538302653639224106865664333535348491338293646599577557630201300627170167862878616413985927131488374258664616392092
7191572 1822336109 122056637146069839485450157659463602136773435897258591538866604089577736061707581662949502933519623787237101424065925246227311788026398025228549571401681364284397681558495127293422405005519207011429758784679359026564663723906503072635741603815702201571462971153413211041068663899032199555395016545688330586044
228786056 1675584603 117701151389970889003978379762267752133865163176557074701079147801046451813688145274964215828365957142552336288689654120601548113748929766366564174096775189852190965077394508456052823068488823291767811224538103381867631753777200820579564678159674117155563703553000884461777658090456283332220216734612556688513
804908984 2135801843 64993745755656098807721600357147815184322152457013249228399624894951891365784451431980303960531348451816046446458097670030866575746640795583720668211568084611960590087205609635406810868042628237527172170079769791670793545166757565720288711565725471066283638538704293790079806106677373567711048953361333211210
1122280790 1614259350 3970244390792364343312138669070883638127263175075582263096706481850448381703964455173288740017450440943500797144425033043394229962284552755667989452357422108026327690718801740503572676309934059296211509079412739861064178751353597300902277808636740745335474263237517162977595705012834985895341553367459792583
554314515 998420920 123643361743671701825618173162391028658772950477687990712748798605914570427945055208640384218469111438408345424338893652923941552893606133066783001743721804859308011765507616515353882559991735865794891472781955621601467016660304188272123202130977476560852093272543539966026915472638392018462692756028817601255
719945347 612820861 103551249110130410018728043034553056272870332237608830696804104933749752848182147616875273399120950956495972830177071656956289995248469136767050516935071277259217339496102844766065836859503647533631994664364053659677674596380230790347281302210808329346735637394258018381272973124437749621859047096780954428763
1030625109 1519412381 15696617275088442746387785148060623054817901281945029743077460769180096631404983383910114661025034557230747207080210469011273591184527303007260363112037932265980126744700626024259985586947347501172863220738584453809309462129610346067407238209251289710742647393829531889303218223237301268534338307710560528439
1711254768 1710707291 57671766580359527580383190392835992822988951359063099518333951473557157636791373831886967456320589708220219137556141104065094734173377513568373511612097050435132426608350879787688784646390242899955969071718348216328885834450215105058725433533776719158074043390881257587614495125963197168525883771897032429145
350065369 319208735 44521456496863698728911963510653524876630475042295240074435222668882607096381437705850621136342821688618111659046306438623837465097724847737566157513351593063095577843263064035230462006868686576892037899378382406468120801597507834123281075093108794208153836881908434178839513489161646768450411118658866064760
2060218994 1946043223 56312469398022909670236635086334904553302665496424591277087996113064393075310693844995965663947160222486178761741935506327835516277624460430181450292802834360724976701838361338230904004764511115279873515265325364671729600765057941485718305803874368460265173324375012707038078949514720443784809672434739391394
348859478 486047271 63578529904279717544096579659734885121575283886072626718230632949600891106018451131129915865157675764791879663149288069798959505461654979937263948081560345222746334083402817181164255208790802816536212306902000509334761465449621953806270899950736579351124776383450108496252367170418747525612150563944975123906
1012847796 1311216437 107243486601777772242638374457577339776317528440551074937135087437181884726459082109032187432358497015564158022857522392034581988349463322793155498885898276448910563934149930379053835676169014345745737841013305105289515557002942278171260275549569040668192618881235525671100756834785472005323827534045854021808
1108188659 73002956 151810407586486667408500610375120927048683500513617431444670840241105574837701928593342245869128797827409414723271900961746330458448581633550115101600384307415146567797051023727766743006773542272526168313129133103058023736384944187039543948615045687254043611794926502253898300807539332514119558228945387167129
1367891527 957164137 106888874248390844568539366153235739322099571679913873665077300044384432133087328354115380340807163549209282323027334598550750155549975114208460003069900549945106924101337174939911191001778776920788324219234143471273850920009578258747072782983631129326451246547584416492036977756842649955247390532642313031673
1109319782 312705549 68015190517529836158634537187194145539209089151286211608940739126031607591236786876471227901028349157506546942329120582174574792522929377067808957872180985535362179866434055727341930725758279092655515659945664689974113139170079360543337269872976473433045950679959300152245802435127006127508284128226462808242
77629902 1442208847 80002290434058357698705037287975366182731667140415670086832039653095542318592553515737200055739316505804591069679885064388918352070456242079053263046801427045473800954002156858264359891351667747947419377687560365442620710551484084591458509139700723197713694698034159851521977928611736821392017020477832037627
1876665427 42392232 94638321177007894302596716790742601595610267055803051893624262442254201910985079684859058170169970420428917385076321338591735591117741499259022286688741758915429225227910702796255294628145448897362890044237980198958757175908654342104958253051607028469935368687936664986431151922321609065078691893879183189566
559635525 1782490275 71365295096950865667427967092027725943054589808884646377867956234326896501650860934260905567087143525158504721587985301638221372965891635800328428504369765880956526297788284176796001565732103141822914955442743632126166019769189834699258461912602048002960149362882398622111007162709444738907309082349930416022
743575094 32085276 110453584602878746921666429659148701413696049424461554973529870857842263810553552249241246313332783204009612578170167391820378603413812193480492843470042238103670641705732755583940134259794253885826115668806244330875357074130716388274584300227667628005544555311079499447940768673150499033922449576383527638186
129818206 137481306 140835473021876998102027624369898079740454145360699735493249477450544517213808389172240396819192163023303266715591396745357472463341356969319556862236385556442904650009255138279232984377682804793224148996875324569988553808409865099882747099149617352970774643108291836908871124753511856578160489828404865664010
570689556 1980693879 108353275894436996626884805554770441694866167783124178905252902978286824751598925059178987939656961896173921225105217325495780672752694383439806863122466053616930970271706866769895033633709670957150865005763659847698663978549871624628298476651867451354816053985969930735100533712902146229305011837945607699037
2103057616 691067770 27024056452709622585420653808400360576905666723601175215091499609471301967744143329187436673102391151329548614036436716051750524604202541651425479133617399916946398092715923340837394931898418514658066714568415577105694330058750941172815095999450748361179045856199026312487393802486505084466623313733605784416
481705774 1641440342 117722260864906582664053412535574009960206959347375143271559843536103545468155917636456429488071536410856812908716077868452921005581676036410474437330482920141777150620686622782118823530416466223519936589968714322291361670902315520017103387742935706013660879451297004924070011539277017717095949755265539759012

View file

@ -0,0 +1,29 @@
130706940119084053627151828062879423433929180135817317038378606310097533503449582079984816816837125851552273641820339909167103200910805078308128174143174269944095368580519322913514764528012639683546377014716235962867583443566164615728897857285824741767070432119909660645255499710701356135207437699643611094585 139808169914464096465921128085565621767096724855516655439365028496569658038844954238931647642811548254956660405394116677296461848124300258439895306367561416289126854788101396379292925819850897858045772500578222021901631436550118958972312221974009238050517034542286574826081826542722270952769078386418682059418
91966407878983240112417790733941098492087186469785726449910011271065622315680646030230288265496017310433513856308693810812043160919214636748486185212617634222158204354206411031403206076739932806412551605172319515223573351072757800448643935018534945933808900467686115619932664888581913179496050117713298715475 88086484332488517006277516020842172054013692832175783214603951240851750819999098631851571207693874357651112736088114133607400684776234181681933311972926752846692615822043533641407510569745606256772455614745111122033229877596984718963046218854103292937700694160593653595134512369959987897086639788909618660591
94633950701209990078055218830969910271587805983595045023718108184189787131629772007048606080263109446462048743696369276578815611098215686598630889831104860221067872883514840819381234786050098278403321905311637820524177879167250981289318356078312300538871435101338967079907049912435983871847334104247675360099 136836393035803488129856151345450008294260680733328546556640578838845312279198933806383329293483852515700876505956362639881210101974254765087350842271260064592406308509078284840473735904755203614987286456952991025347168970462354352741159076541157478949094536405618626397435745496863324654768971213730622037771
24685127248019769965088146297942173464487677364928435784091685260262292485380918213538979925891771204729738138857126454465630594391449913947358655368215901119137728648638547728497517587701248406019427282237279437409508871300675355166059811431191200555457304463617727969228965042729205402243355816702436970430 103488011917988946858248200111251786178288940265978921633592888293430082248387786443813155999158786903216094876295371112716734481877806417714913656921169196196571699893360825510307056269738593971532017994987406325068886420548597161498019372380511676314312298122272401348856314619382867707981701472607230523868
116791045850880292989786005885944774698035781824784400772676299590038746153860847252706167458966356897309533614849402276819438194497464696186624618374179812548893947178936305721131565012344462048549467883494038577857638815386798694225798517783768606048713198211730870155881426709644960689953998714045816205549 25767875422998856261320430397505398614439586659207416236135894343577952114994718158163212134503751463610021489053571733974769536157057815413209619147486931502025658987681202196476489081257777148377685478756033509708349637895740799542063593586769082830323796978935454479273531157121440998804334199442003857410
75582226959658406842894734694860761896800153014775231713388264961517169436476322183886891849966756849783437334069692683523296295601533803799559985845105706728538458624387103621364117548643541824878550074680443708148686601108223917493525070861593238005735446708555769966855130921562955491250908613793521520082 51100990616369611694975829054222013346248289055987940844427061856603230021472379888102172458517294080775792439385531234808129302064303666640376750139242970123503857186428797403843206765926798353022284672682073397573130625177187185114726049347844460311761033584101482859992951420083621362870301150543916815123
22852401165908224137274273646590366934616265607879280260563022941455466297431255072303172649495519837876946233272420969249841381161312477263365567831938496555136366981954001163034914812189448922853839616662859772087929140818377228980710884492996109434435597500854043325062122184466315338260530734979159890875 35017410720028595029711778101507729481023945551700945988329114663345341120595162378885287946069695772429641825579528116641336456773227542256911497084242947904528367986325800537695079726856460817606404224094336361853766354225558025931211551975334149258299477750615397616908655079967952372222383056221992235704
37364490883518159794654045194678325635036705086417851509136183713863262621334636905291385255662750747808690129471989906644041585863034419130023070856805511017402434123099100618568335168939301014148587149578150068910141065808373976114927339040964292334109797421173369274978107389084873550233108940239410902552 40916262212189137562350357241447034318002130016858244002788189310078477605649010031339865625243230798681216437501833540185827501244378529230150467789369234869122179247196276164931090039290879808162629109742198951942358028123056268054775108592325500609335947248599688175189333996086475013450537086042387719925
42030470670714872936404499074069849778147578537708230270030877866700844337372497704027708080369726758812896818567830863540507961487472657570488625639077418109017434494794778542739932765561706796300920251933107517954265066804108669800167526425723377411855061131982689717887180411017924173629124764378241885274 124652439272864857598747946875599560379786580730218192165733924418687522301721706620565030507816884907589477351553268146177293719586287258662025940181301472851649975563004543250656807255226609296537922304346339513054316391667044301386950180277940536542183725690479451746977789001659540839582630251935163344393
33176766914206542084736303652243484580303865879984981189372762326078776390896986743451688462101732968104375838228070296418541745483112261133079756514082093269959937647525005374035326747696591842313517634077723301677759648869372517403529488493581781546743147639937580084065663597330159470577639629864369972900 67485835091897238609131069363014775606263390149204621594445803179810038685760826651889895397414961195533694176706808504447269558421955735607423135937153901140512527504198912146656610630396284977496295289999655140295415981288181545277299615922576281262872097567020980675200178329219970170480653040350512964539
131497983897702298481056962402569646971797912524360547236788650961059980711719600424210346263081838703940277066368168874781981151411096949736205282734026497995296147418292226818536168555712128736975034272678008697869326747592750850184857659420541708058277866000692785617873742438060271311159568468507825422571 5400380840349873337222394910303409203226429752629134721503171858543984393161548520471799318518954232197106728096866840965784563043721652790856860155702760027304915133166173298206604451826182024471262142046935060360564569939062438160049193241369468208458085699995573492688298015026628427440418009025072261296
83265103005695640943261961853521077357830295830250157593141844209296716788437615940096402365505416686459260302419338241462783388722843946886845478224048360927114533590583464979009731440049610985062455108831881153988321298531365779084012803908832525921630534096740755274371500276660832724874701671184539131864 141285570207910287798371174771658911045525474449663877845558585668334618068814605961306961485855329182957174312715910923324965889174835444049526313968571611940626279733302104955951067959291852710640374412577070764165811275030632465290729619533330733368808295932659463215921521905553936914975786500018720073003
68435028583616495789148116911096163791710022987677894923742899873596891423986951658100606742052014161171185231735413902875605720814417622409817842932759492013585936536452615480700628719795872201528559780249210820284350401473564919576289210869896327937002173624497942136329576506818749730506884927872345019446 134655528287263100540003157571441260698452262106680191153945271167894435782028803135774578949200580551016388918860856991026082917835209212892423567114480975540305860034439015788120390011692862968771136814777768281366591257663821495720134621172848947971117885754539770645621669309650476331439675400544167728223
97765390064836080322590528352647421920257073063706996347334558390461274981996865736612531330863478931481491964338380362350271734683183807511097331539820133036984271653285063355715726806139083282458695728902452215405696318402583540317419929113959816258829534543044153959951908676300847164682178008704099351835 92552521881196975294401505656851872247567784546370503402756239533783651371688190302773864319828182042605239246779598629409815474038541272600580320815319709309111399294952620375093803971373108792300726524826209329889463854451846561437729676142864421966497641824498079067929811613947148353921163336822026640804
145767094672933012300753301037546647564595762930138884463767054235112032706630891961371504668013023047595721138624016493638510710257541241706724342585654715468628355455898091951826598092812212209834746162089753649871544789379424903025374228231365026585872808685759231756517703720396301355299998059523896918448 116669462839999965355861187716880953863237226719689755457884414384663576662696981997535568446560375442532084973721539944428004043491468494548231348032618218312515409944970197902589794303562379864012797605284844016184274353252071642511293089390472576498394410829972525726474727579603392265177009323768966538608
34172517877854802711907683049441723730724885305592620486269966708379625109832852005775048584124451699198484092407720344962116726808090368739361658889584507734617844212547181476646725256303630128954338675520938806905779837227983648887192531356390902975904503218654196581612781227843742951241442641220856414232 126013077261793777773236390821108423367648447987653714614732477073177878509574051196587476846560696305938891953527959347566502332765820074506907037627115954790645652211088723122982633069089920979477728376746424256704724173255656757918995039125823421607024407307091796807227896314403153380323770001854211384322
9979624731056222925878866378063961280844793874828281622845276060532093809300121084179730782833657205171434732875093693074415298975346410131191865198158876447591891117577190438695367929923494177555818480377241891190442070100052523008290671797937772993634966511431668500154258765510857129203107386972819651767 76559085024395996164590986654274454741199399364851956129137304209855150918182685643729981600389513229011956888957763987167398150792454613751473654448162776379362213885827651020309844507723069713820393068520302223477225569348080362344052033711960892643036147232270133731530049660264526964146237693063093765111
18162696663677410793062235946366423954875282212790518677684260521370996677183041664345920941714064628111537529793170736292618705900247450994864220481135611781148410617609559050220262121494712903009168783279356915189941268264177631458029177102542745167475619936272581126346266816618866806564180995726437177435 63244550218824945129624987597134280916829928261688093445040235408899092619821698537312158783367974202557699994650667088974727356690181336666077506063310290098995215324552449858513870629176838494348632073938023916155113126203791709810160925798130199717340478393420816876665127594623142175853115698049952126277
4817943161362708117912118300716778687157593557807116683477307391846133734701449509121209661982298574607233039490570567781316652698287671086985501523197566560479906850423709894582834963398034434055472063156147829131181965140631257939036683622084290629927807369457311894970308590034407761706800045378158588657 61612160237840981966750225147965256022861527286827877531373888434780789812764688703260066154973576040405676432586962624922734102370509771313805122788566405984830112657060375568510809122230960988304085950306616401218206390412815884549481965750553137717475620505076144744211331973240555181377832337912951699135
36363324947629373144612372870171042343590861026293829791335153646774927623889458346817049419803031378037141773848560341251355283891019532059644644509836766167835557471311319194033709837770615526356168418160386395260066262292757953919140150454538786106958252854181965875293629955562111756775391296856504912587 86831561031659073326747216166881733513938228972332631084118628692228329095617884068498116676787029033973607066377816508795286358748076949738854520048303930186595481606562375516134920902325649683618195251332651685732712539073110524182134321873838204219194459231650917098791250048469346563303077080880339797744
26406869969418301728540993821409753036653370247174689204659006239823766914991146853283367848649039747728229875444327879875275718711878211919734397349994000106499628652960403076186651083084423734034070082770589453774926850920776427074440483233447839259180467805375782600203654373428926653730090468535611335253 100139935381469543084506312717977196291289016554846164338908226931204624582010530255955411615528804421371905642197394534614355186795223905217732992497673429554618838376065777445760355552020655667172127543653684405493978325270279321013143828897100500212200358450649158287605846102419527584313353072518101626851
92613116984760565837109105383781193800503303131143575169488835702472221039082994091847595094556327985517286288659598094631489552181233202387028607421487026032402972597880028640156629614572656967808446397456622178472130864873587747608262139844319805074476178618930354824943672367046477408898479503054125369731 30023391082615178562263328892343821010986429338255434046051061316154579824472412477397496718186615690433045030046315908170615910505869972621853946234911296439134838951047107272129711854649412919542407760508235711897489847951451200722151978578883748353566191421685659370090024401368356823252748749449302536931
31485815361342085113278193504381994806529237123359718043079410511224607873725611862217941085749929342777366642477711445011074784469367917758629403998067347054115844421430072631339788256386509261291675080191633908849638316409182455648806133048549359800886124554879661473112614246869101243501787363247762961784 114503770698890543429251666713050844656853278831559195214556474458830029271801818536133531843456707474500106283648085144619097572354066554819887152106174400667929098257361286338795493838820850475790977445807435511982704395422526800272723708548541616513134676140304653112325071112865020365664833601046215694089
76882090884790547431641385530818076533805072109483843307806375918023300052767710853172670987385376253156912268523505310624133905633437815297307463917718596711590885553760690350221265675690787249135345226947453988081566088302642706234126002514517416493192624887800567412565527886687096028028124049522890448168 15056463217273240496622619354104573042767532856243223052125822509781815362480522535564283485059790932505429110157271454207173426525345813426696743168079246510944969446574354255284952839036431873039487144279164893710061580467579842173706653409487110282515691099753380094215805485573768509475850463001549608836
52345178981230648108672997265819959243255047568833938156267924185186047373470984278294897653277996726416846430969793375429223610099546622112048283560483136389901514170116723365811871938630317974150540909650396429631704968748113009366339718498979597226137532343384889080245796447593572468846438769413505393967 32148494517199936472358017244372701214529606506776255341152991328091526865643069587953759877295255050519124541457805199596762210567333445908166076384465183589342153762720515477404466193879418014196727238972417616122646440870364200208488239778452378059236162633837824948613596114768455832408342040970780086
41095268619128788015767564971105114602454449306041732792746397800275041704886345704294273937217484580365505320134717320083763349380629342859670693445658118959823430378844830923452105707338162448974869312012791385772125813291388247857971218575518319578818336960572244046567099555399203328678654466958536663208 92166550199033418923713824997841892577149715275633481076285269142670107687867024550593869464613175882141630640739938334001211714884975032600306279287443909448541179109981755796752132502127330056736913454039526413284519137059580845856736918773597087836203497066909257930043736166431682872083389105176299181629
40049143661018504441607875135884755310012910557581028447435354354754245291878800571089144452035026644953322330676651798951447670184106450649737772686119714700743396359069052813433030118630105307022867200053964644574786137276428546712005171080129190959914708907200288299169344380390093918556722227705114244981 108159089972386282154772900619022507336076619354549601813179459338897131937353741544606392560724999980281424266891537298473163753022749859939445293926707568015958367188089915420630082556748668489756475027008449860889202622698060097015044886961901650857610841562477736791450080980702347705778074391774667412741
69905259478181995876884927656894491893594530150260951315109404530530357998889589977208787140430938039028941393673520799460431992051993157468616168400324834880926190141581037597526917869362292931957289043707855837933490285814769110495657056206391880865972389421774822461752702336812585852278453803972600333734 71821415380277072313878763768684432371552628204186742842154591000123020597011744840460964835414360968627162765288463383113375595799297552681618876474019263288277398833725479226930770694271622605114061622753165584075733358178384410640349907375170170910499615355511313349300918885560131539570707695789106185664
26945345439378873515011714350080059082081595419023056538696949766471272811362104837806324694947413603019863785876836706911406330379274553386254346050697348395574746891556054334903838949157798006141473389066020212044825140294048709654273698482867946522782450500680195477050110145664069582549935651920545151500 80313315938584480048642653013876614091607852535582224914294013785054094052454758327935781971746329853786568549510067442145637007308960551652864942042189241081946607011847245280773379099020221884296226818685556430275385068764313042226925852500883894269809033380734632866477789520106865758504064806906234130588

View file

@ -0,0 +1,49 @@
test_case = 1
key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
key_len = 20
data = "Hi There"
data_len = 8
digest = 0xb617318655057264e28bc0b6fb378c8ef146be00
test_case = 2
key = "Jefe"
key_len = 4
data = "what do ya want for nothing?"
data_len = 28
digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79
test_case = 3
key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
key_len = 20
data = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
data_len = 50
digest = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3
test_case = 4
key = 0x0102030405060708090a0b0c0d0e0f10111213141516171819
key_len = 25
data = 0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
data_len = 50
digest = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da
test_case = 5
key = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
key_len = 20
data = "Test With Truncation"
data_len = 20
digest = 0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04
digest-96 = 0x4c1a03424b55e07fe7f27be1
test_case = 6
key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
key_len = 80
data = "Test Using Larger Than Block-Size Key - Hash Key First"
data_len = 54
digest = 0xaa4ae5e15272d00e95705637ce8a3b55ed402112
test_case = 7
key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
key_len = 80
data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
data_len = 73
digest = 0xe8e99d0f45237d786d6bbaa7965c7808bbff1a91

View file

@ -0,0 +1,594 @@
Num Tests: 73
OpenID link parsing test cases
Copyright (C) 2005-2006, JanRain, Inc.
See COPYING for license information.
File format
-----------
All text before the first triple-newline (this chunk) should be ignored.
This file may be interpreted as Latin-1 or UTF-8.
Test cases separated by three line separators (`\n\n\n'). The test
cases consist of a headers section followed by a data block. These are
separated by a double newline. The headers consist of the header name,
followed by a colon, a space, the value, and a newline. There must be
one, and only one, `Name' header for a test case. There may be zero or
more link headers. The `Link' header consists of whitespace-separated
attribute pairs. A link header with an empty string as a value
indicates an empty but present link tag. The attribute pairs are `='
separated and not quoted.
Optional Links and attributes have a trailing `*'. A compilant
implementation may produce this as output or may not. A compliant
implementation will not produce any output that is absent from this
file.
Name: Well-formed link rel (in CAPS)
Link: rel=openid.server href=http://www.myopenid.com/server
<HTML>
<HEAD>
<LINK REL="openid.server"
HREF="http://www.myopenid.com/server" />
</HEAD>
</HTML>
Name: No link tag at all
<html>
<head>
</head>
</html>
Name: Link element first
<link>
Name: Link inside HTML, not head
<html>
<link>
Name: Link inside head, not html
<head>
<link>
Name: Link inside html, after head
<html>
<head>
</head>
<link>
Name: Link inside html, before head
<html>
<link>
<head>
Name: Link before html and head
<link>
<html>
<head>
Name: Link after html document with head
<html>
<head>
</head>
</html>
<link>
Name: Link inside html inside head, inside another html
<html>
<head>
<html>
<link>
Name: Link inside html inside head
<head>
<html>
<link>
Name: link inside body inside head inside html
<html>
<head>
<body>
<link>
Name: Link inside head inside head inside html
<html>
<head>
<head>
<link>
Name: Link inside script inside head inside html
<html>
<head>
<script>
<link>
</script>
Name: Link inside comment inside head inside html
<html>
<head/>
<link>
Name: Link inside of head after short head
<html>
<head/>
<head>
<link>
Name: Plain vanilla
Link:
<html>
<head>
<link>
Name: Ignore tags in the <script:... > namespace
Link*:
<html>
<head>
<script:paddypan>
<link>
</script:paddypan>
Name: Short link tag
Link:
<html>
<head>
<link/>
Name: Spaces in the HTML tag
Link:
<html >
<head>
<link>
Name: Spaces in the head tag
Link:
<html>
<head >
<link>
Name: Spaces in the link tag
Link:
<html>
<head>
<link >
Name: No whitespace
Link:
<html><head><link>
Name: Closed head tag
Link:
<html>
<head>
<link>
</head>
Name: One good, one bad (after close head)
Link:
<html>
<head>
<link>
</head>
<link>
Name: One good, one bad (after open body)
Link:
<html>
<head>
<link>
<body>
<link>
Name: ill formed (missing close head)
Link:
<html>
<head>
<link>
</html>
Name: Ill formed (no close head, link after </html>)
Link:
<html>
<head>
<link>
</html>
<link>
Name: Ignore random tags inside of html
Link:
<html>
<delicata>
<head>
<title>
<link>
Name: case-folding
Link*:
<HtMl>
<hEaD>
<LiNk>
Name: unexpected tags
Link:
<butternut>
<html>
<summer>
<head>
<turban>
<link>
Name: un-closed script tags
Link*:
<html>
<head>
<script>
<link>
Name: un-closed script tags (no whitespace)
Link*:
<html><head><script><link>
Name: un-closed comment
Link*:
<html>
<head>
<!--
<link>
Name: un-closed CDATA
Link*:
<html>
<head>
<![CDATA[
<link>
Name: cdata-like
Link*:
<html>
<head>
<![ACORN[
<link>
]]>
Name: comment close only
Link:
<html>
<head>
<link>
-->
Name: Vanilla, two links
Link:
Link:
<html>
<head>
<link>
<link>
Name: extra tag, two links
Link:
Link:
<html>
<gold nugget>
<head>
<link>
<link>
Name: case-fold, body ends, two links
Link:
Link*:
<html>
<head>
<link>
<LiNk>
<body>
<link>
Name: simple, non-quoted rel
Link: rel=openid.server
<html><head><link rel=openid.server>
Name: short tag has rel
Link: rel=openid.server
<html><head><link rel=openid.server/>
Name: short tag w/space has rel
Link: rel=openid.server
<html><head><link rel=openid.server />
Name: extra non-attribute, has rel
Link: rel=openid.server
<html><head><link hubbard rel=openid.server>
Name: non-attr, has rel, short
Link: rel=openid.server
<html><head><link hubbard rel=openid.server/>
Name: non-attr, has rel, short, space
Link: rel=openid.server
<html><head><link hubbard rel=openid.server />
Name: misplaced slash has rel
Link: rel=openid.server
<html><head><link / rel=openid.server>
Name: quoted rel
Link: rel=openid.server
<html><head><link rel="openid.server">
Name: single-quoted rel
Link: rel=openid.server
<html><head><link rel='openid.server'>
Name: two links w/ rel
Link: x=y
Link: a=b
<html><head><link x=y><link a=b>
Name: non-entity
Link: x=&y
<html><head><link x=&y>
Name: quoted non-entity
Link: x=&y
<html><head><link x="&y">
Name: quoted entity
Link: x=&
<html><head><link x="&amp;">
Name: entity not processed
Link: x=&#26;
<html><head><link x="&#26;">
Name: &lt;
Link: x=<
<html><head><link x="&lt;">
Name: &gt;
Link: x=>
<html><head><link x="&gt;">
Name: &quot;
Link: x="
<html><head><link x="&quot;">
Name: &amp;&quot;
Link: x=&"
<html><head><link x="&amp;&quot;">
Name: mixed entity and non-entity
Link: x=&"&hellip;>
<html><head><link x="&amp;&quot;&hellip;&gt;">
Name: mixed entity and non-entity (w/normal chars)
Link: x=x&"&hellip;>x
<html><head><link x="x&amp;&quot;&hellip;&gt;x">
Name: broken tags
Link*: x=y
<html><head><link x=y<>
Name: missing close pointy
Link: z=y
<html><head><link x=y<link z=y />
Name: missing attribute value
Link: x=y y*=
Link: x=y
<html><head><link x=y y=><link x=y />
Name: Missing close pointy (no following)
Link*: x=y
<html><head><link x=y
Name: Should be quoted
Link: x*=<
<html><head><link x="<">
Name: Should be quoted (2)
Link: x*=>
<html><head><link x=">">
Name: Repeated attribute
Link: x=y
<html><head><link x=z x=y>
Name: Repeated attribute (2)
Link: x=y
<html><head><link x=y x=y>
Name: Two attributes
Link: x=y y=z
<html><head><link x=y y=z>
Name: Well-formed link rel="openid.server"
Link: rel=openid.server href=http://www.myopenid.com/server
<html>
<head>
<link rel="openid.server"
href="http://www.myopenid.com/server" />
</head>
</html>
Name: Well-formed link rel="openid.server" and "openid.delegate"
Link: rel=openid.server href=http://www.myopenid.com/server
Link: rel=openid.delegate href=http://example.myopenid.com/
<html><head><link rel="openid.server"
href="http://www.myopenid.com/server" />
<link rel="openid.delegate" href="http://example.myopenid.com/" />
</head></html>
Name: from brian's livejournal page
Link: rel=stylesheet href=http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711 type=text/css
Link: rel=openid.server href=http://www.livejournal.com/openid/server.bml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet"
href="http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711"
type="text/css" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="foaf:maker"
content="foaf:mbox_sha1sum '12f8abdacb5b1a806711e23249da592c0d316260'" />
<meta name="robots" content="noindex, nofollow, noarchive" />
<meta name="googlebot" content="nosnippet" />
<link rel="openid.server"
href="http://www.livejournal.com/openid/server.bml" />
<title>Brian</title>
</head>
Name: non-ascii (Latin-1 or UTF8)
Link: x=®
<html><head><link x="®">

View file

@ -0,0 +1,650 @@
AA== 0
AQ== 1
Ag== 2
Aw== 3
BA== 4
BQ== 5
Bg== 6
Bw== 7
CA== 8
CQ== 9
Cg== 10
Cw== 11
DA== 12
DQ== 13
Dg== 14
Dw== 15
EA== 16
EQ== 17
Eg== 18
Ew== 19
FA== 20
FQ== 21
Fg== 22
Fw== 23
GA== 24
GQ== 25
Gg== 26
Gw== 27
HA== 28
HQ== 29
Hg== 30
Hw== 31
IA== 32
IQ== 33
Ig== 34
Iw== 35
JA== 36
JQ== 37
Jg== 38
Jw== 39
KA== 40
KQ== 41
Kg== 42
Kw== 43
LA== 44
LQ== 45
Lg== 46
Lw== 47
MA== 48
MQ== 49
Mg== 50
Mw== 51
NA== 52
NQ== 53
Ng== 54
Nw== 55
OA== 56
OQ== 57
Og== 58
Ow== 59
PA== 60
PQ== 61
Pg== 62
Pw== 63
QA== 64
QQ== 65
Qg== 66
Qw== 67
RA== 68
RQ== 69
Rg== 70
Rw== 71
SA== 72
SQ== 73
Sg== 74
Sw== 75
TA== 76
TQ== 77
Tg== 78
Tw== 79
UA== 80
UQ== 81
Ug== 82
Uw== 83
VA== 84
VQ== 85
Vg== 86
Vw== 87
WA== 88
WQ== 89
Wg== 90
Ww== 91
XA== 92
XQ== 93
Xg== 94
Xw== 95
YA== 96
YQ== 97
Yg== 98
Yw== 99
ZA== 100
ZQ== 101
Zg== 102
Zw== 103
aA== 104
aQ== 105
ag== 106
aw== 107
bA== 108
bQ== 109
bg== 110
bw== 111
cA== 112
cQ== 113
cg== 114
cw== 115
dA== 116
dQ== 117
dg== 118
dw== 119
eA== 120
eQ== 121
eg== 122
ew== 123
fA== 124
fQ== 125
fg== 126
fw== 127
AIA= 128
AIE= 129
AII= 130
AIM= 131
AIQ= 132
AIU= 133
AIY= 134
AIc= 135
AIg= 136
AIk= 137
AIo= 138
AIs= 139
AIw= 140
AI0= 141
AI4= 142
AI8= 143
AJA= 144
AJE= 145
AJI= 146
AJM= 147
AJQ= 148
AJU= 149
AJY= 150
AJc= 151
AJg= 152
AJk= 153
AJo= 154
AJs= 155
AJw= 156
AJ0= 157
AJ4= 158
AJ8= 159
AKA= 160
AKE= 161
AKI= 162
AKM= 163
AKQ= 164
AKU= 165
AKY= 166
AKc= 167
AKg= 168
AKk= 169
AKo= 170
AKs= 171
AKw= 172
AK0= 173
AK4= 174
AK8= 175
ALA= 176
ALE= 177
ALI= 178
ALM= 179
ALQ= 180
ALU= 181
ALY= 182
ALc= 183
ALg= 184
ALk= 185
ALo= 186
ALs= 187
ALw= 188
AL0= 189
AL4= 190
AL8= 191
AMA= 192
AME= 193
AMI= 194
AMM= 195
AMQ= 196
AMU= 197
AMY= 198
AMc= 199
AMg= 200
AMk= 201
AMo= 202
AMs= 203
AMw= 204
AM0= 205
AM4= 206
AM8= 207
ANA= 208
ANE= 209
ANI= 210
ANM= 211
ANQ= 212
ANU= 213
ANY= 214
ANc= 215
ANg= 216
ANk= 217
ANo= 218
ANs= 219
ANw= 220
AN0= 221
AN4= 222
AN8= 223
AOA= 224
AOE= 225
AOI= 226
AOM= 227
AOQ= 228
AOU= 229
AOY= 230
AOc= 231
AOg= 232
AOk= 233
AOo= 234
AOs= 235
AOw= 236
AO0= 237
AO4= 238
AO8= 239
APA= 240
APE= 241
API= 242
APM= 243
APQ= 244
APU= 245
APY= 246
APc= 247
APg= 248
APk= 249
APo= 250
APs= 251
APw= 252
AP0= 253
AP4= 254
AP8= 255
AQA= 256
AQE= 257
AQI= 258
AQM= 259
AQQ= 260
AQU= 261
AQY= 262
AQc= 263
AQg= 264
AQk= 265
AQo= 266
AQs= 267
AQw= 268
AQ0= 269
AQ4= 270
AQ8= 271
ARA= 272
ARE= 273
ARI= 274
ARM= 275
ARQ= 276
ARU= 277
ARY= 278
ARc= 279
ARg= 280
ARk= 281
ARo= 282
ARs= 283
ARw= 284
AR0= 285
AR4= 286
AR8= 287
ASA= 288
ASE= 289
ASI= 290
ASM= 291
ASQ= 292
ASU= 293
ASY= 294
ASc= 295
ASg= 296
ASk= 297
ASo= 298
ASs= 299
ASw= 300
AS0= 301
AS4= 302
AS8= 303
ATA= 304
ATE= 305
ATI= 306
ATM= 307
ATQ= 308
ATU= 309
ATY= 310
ATc= 311
ATg= 312
ATk= 313
ATo= 314
ATs= 315
ATw= 316
AT0= 317
AT4= 318
AT8= 319
AUA= 320
AUE= 321
AUI= 322
AUM= 323
AUQ= 324
AUU= 325
AUY= 326
AUc= 327
AUg= 328
AUk= 329
AUo= 330
AUs= 331
AUw= 332
AU0= 333
AU4= 334
AU8= 335
AVA= 336
AVE= 337
AVI= 338
AVM= 339
AVQ= 340
AVU= 341
AVY= 342
AVc= 343
AVg= 344
AVk= 345
AVo= 346
AVs= 347
AVw= 348
AV0= 349
AV4= 350
AV8= 351
AWA= 352
AWE= 353
AWI= 354
AWM= 355
AWQ= 356
AWU= 357
AWY= 358
AWc= 359
AWg= 360
AWk= 361
AWo= 362
AWs= 363
AWw= 364
AW0= 365
AW4= 366
AW8= 367
AXA= 368
AXE= 369
AXI= 370
AXM= 371
AXQ= 372
AXU= 373
AXY= 374
AXc= 375
AXg= 376
AXk= 377
AXo= 378
AXs= 379
AXw= 380
AX0= 381
AX4= 382
AX8= 383
AYA= 384
AYE= 385
AYI= 386
AYM= 387
AYQ= 388
AYU= 389
AYY= 390
AYc= 391
AYg= 392
AYk= 393
AYo= 394
AYs= 395
AYw= 396
AY0= 397
AY4= 398
AY8= 399
AZA= 400
AZE= 401
AZI= 402
AZM= 403
AZQ= 404
AZU= 405
AZY= 406
AZc= 407
AZg= 408
AZk= 409
AZo= 410
AZs= 411
AZw= 412
AZ0= 413
AZ4= 414
AZ8= 415
AaA= 416
AaE= 417
AaI= 418
AaM= 419
AaQ= 420
AaU= 421
AaY= 422
Aac= 423
Aag= 424
Aak= 425
Aao= 426
Aas= 427
Aaw= 428
Aa0= 429
Aa4= 430
Aa8= 431
AbA= 432
AbE= 433
AbI= 434
AbM= 435
AbQ= 436
AbU= 437
AbY= 438
Abc= 439
Abg= 440
Abk= 441
Abo= 442
Abs= 443
Abw= 444
Ab0= 445
Ab4= 446
Ab8= 447
AcA= 448
AcE= 449
AcI= 450
AcM= 451
AcQ= 452
AcU= 453
AcY= 454
Acc= 455
Acg= 456
Ack= 457
Aco= 458
Acs= 459
Acw= 460
Ac0= 461
Ac4= 462
Ac8= 463
AdA= 464
AdE= 465
AdI= 466
AdM= 467
AdQ= 468
AdU= 469
AdY= 470
Adc= 471
Adg= 472
Adk= 473
Ado= 474
Ads= 475
Adw= 476
Ad0= 477
Ad4= 478
Ad8= 479
AeA= 480
AeE= 481
AeI= 482
AeM= 483
AeQ= 484
AeU= 485
AeY= 486
Aec= 487
Aeg= 488
Aek= 489
Aeo= 490
Aes= 491
Aew= 492
Ae0= 493
Ae4= 494
Ae8= 495
AfA= 496
AfE= 497
AfI= 498
AfM= 499
AfQ= 500
AfU= 501
AfY= 502
Afc= 503
Afg= 504
Afk= 505
Afo= 506
Afs= 507
Afw= 508
Af0= 509
Af4= 510
Af8= 511
AgA= 512
ALDs7paJl5xPh6ORH61iDA6pONpV0rTjGiTkLEW2JsVsRKaRiS4AGn2PTR1UZXP0vXAmRXwdSegQgWPUp3Hm3RofRcDh1SykZBLif7ulau1hVO+rhwRyKc7F8F+7LcMf/v+s73eOXUDbbI2r52wfr7skZy/IELhsC8EK6HzhACI3 124241322153253947064453752054205174382289463089695815605736438952932114700118408072544073767229325045596832952652232288773280299665950768731398747700657715829631597019676014848183966683866396215048196276450953653433516126074463193382764063985175903718735372053536664711482497859539116009770850968340298474039
AOzgU1s6Pd2IkrJlvGND8legXTe50nyDCocI5mwT9rW0YsisY5jaaEOcu51BAq9MmXBPeVX0k/jlXwH4Pn3mCpUAU1rEOsTdcmSJp35siKliDdhTZHHdZNMW+igfXGX5OCsA/BaBcGnE6NnrGWXKyTOoVUGQLEkL2T5yhNUaCT83 166340174936369324883416612727439279977041963320514134445183426741643586944819834936989524033374309932122967866930503619179389342537723598234062828695747850043368572301869699886931403612266216965783079972698791813140295203826980649434652168563255385527187360027803388963151668338040517316899628026707657178935
AO8hrpw+lDiJ13JahLtCb1RenupQcNd0wlTSck9OLL8wB/x6gAoj0PTLV05eZIbz43N3GUSDmmckjlxdHXiBJ9rsoB0P95l1CWIV+4rXblCqxmOdmlm6VZ13bqbI0x7l0cjeMrkmk+yJ067WqUolqQBlUWMTuJVfkxALJYH5xr/C 167923899524385316022824282304301434707626789716026029252173742527362300338760906999615029022863637963070711762128687835779073122264515776657475985362344360699359591353388569856862973447791264902182048648600267737826849280828116753682917256540180401899752566540869918949003470368970029744573140084219550547906
QxAn7yrdVs5tlHV+Glbqdaj67c6Ni8am3bBLOL8PV5HbdrLf2xIPmNugo6MfUwFSnT+ZPJ51+VTOsItaNwCFju0Eh1cqyP3JWyLRPE7emKuo6xRhf+5ik0pTg77LEF4JXW6ofDqirpR4alFi0G2d9yImQPphsYJwYGF/nNT8u0Q= 47093316905427544098193936500644355852669366083115552072584429220248776817916430034648347490325490701471113667554329499736495877969341478442613611948220957798780043076906836236556612316544460763366275536846463456405604189392790111985912854476264292503164100482712281088955640964034295834935468665872932715332
AI9PVzrbJUvmCihwSFans1lBKwudGEZpWWu8pkSK2zVgzGhMvUoGgMp6TG2zsUd1tV8zv7KsVD2t6pXmjT1wPUynufq97GVHI06SGpflDTt30WboYRh3DgYxvso1sOjUXpnDezcaqc2Aiz4nV5MSShkBlyBjA8z2worHDE+uXqw0 100635651531872121827765663065728398779771663753008344681972226973080394359405041113312675686974926993279775427390065833081040771269307007695807025882757371805607979134114890454059957194316765342461291139168706134406917264848659448693866813989352429841300235734400772946895458374870482441457514575059390213172
FiinVicXOqqRLpxcGTorQpSAGeQ/PfDOuzYK9ViFtmPv6D0cYPfhUH4qXEHOejvmX+0b4lfaX8MWPVZxlqpfXiU9BhG76HJxkLF4ysipukeOvhoHzvcxE5bnhSF1i//bOSifATBLBEZInwqSVg5tHHPuuCkwTL91NqhOulp7Lsk= 15560440884463435471963622630292643727112462888414585143143739400703889930416938984547754943252935620248108237258540176511252143752416771350868493435174026287082706690332705481726295797196444796135827460509780634261726494455068460028424141500629527968240913757449787164107068039175831847071025316475940056777
aYrxyQN/hkBne2ayqo2/iDLF3DZGgk080SOMJfsj9h3Z1OfFZM7TJA+y+/O7niqatosvKrfHrAw+Qs7c6tCZ6NPwYJ4QJLOF9bqH2u2a3fkI954voNUctlUagYUJsZXV8hdhLM6NwUyIZ3ZFkPcpTZp7nKQQ84tr1a8VjDIT5/o= 74114640794666001532816944350975062126079079113921109750255283189037502412929005615388097912507598112836936032143435813588205939470002911374442844578739574773399427907766548612582213272643279263782396527705126350063372192910060171635870872236876399794128383338399728947176692692942605589343038282957050865658
AMpCUeKUX/vtRslWiUUuXNl1KA9uDAWjMUkTrdsxxRDESI7iZIn3TR9lW+0kV5fzkLF18iYLAwSGBmX1PS/T0UVFmoBPJ9yS7yktNL0lpQ3noyGFn8HHZ6XB3FkH3jegIfGbvwwhnhhFzpHPrXlpO5iU5Y+rexzp2XHWt4yJGuIL 142031143422642739313498629438991149460874309300342349421794421544918823888598660275343727563280565210534243383322796489809683834300630555650331646026843796764549231159336347965502383849513994449309613369541991287590422095953275586374371960367000083487965487661436037637475372929033613295072397262739084075531
AIMIQVz0JIEKEI+PREu94m3v9XoiU/Q0CpsSuqkwSSje+Wyul5ea9oU5qgtOpdkMUOW91BJo0DW/GMZ8v3C4qyyP29TtjCcAHObJi9hfLSlnTSuzXZnDStooYYKqzfToLToCaAJKCXiXAVW0vWtapLnyqafrf/KgyGZ5u4HfXKY0 92013973253053602863003242446596060337454881568126916916519869242232429836082762281129448384605359749247852792606718908482332975424967542242332487707042773885428473061056052851768940900752317020681189773407893371297668591494665352294885305475871917069040377145530889271334616499701769138948975263435137525300
ANfP+zPBTR27afneyac1KJhOB5Pq3AXB+SoAXJvQI/GkSoNhw5KdfqoIkLcoJi8wClCm424Gm1AdrdGwDFOM/iKTSPkrvMag93+b2EbQGX66/n2X3YRFNkgq/Gtb+2M8oCcAL054Z/iiMD67aU5RWzjqS64ePHsn01bJ7dqLgpMO 151548639867177154896951257541227014781655576169318283047778755573323724856619156348444192550664853912434681577093459933599575436686424046466113215132845213008587152894642577278656978304699131916299275797578171518984206145555369576872231567191579337901913492071976578289189524123204040497290426960375042970382
AK0kHtacLGu1NFWMADq2rG8hpzM4UEYyPOL+aMJbnwXcUYptRIxb0YFZg35RN/RiZs4lQsiq+kEJKzMMV71TsJq59vMkIZhZoB3t8g9ZqBZuq0JYcTICDwRpNSttJidVpJ6P9sR3s1xPMYKdlSwt6EEc9htOXfZU+yHKYgn98X60 121583812047864398969816595368193171848971298823388059338224714026742264861090347096116404814514279627148994345584790617974476594451626305761040465570524035369799925437276511604752129817947910677564301623631349399504187314174538914591944778074509068973226322566160587813128746039859381466427380402262866230964
W3sZlWW1Aev3x/DiH9MzwCAZzBj++x9cknLfGAHwgFqkLH6vimEH/r8hi85hzlCOG5CjwhoZ0D/Hsfr26ZJ3X4chG84byrfDnek1V9mm1++v+clJvlYgcuVgn2Opsba2TILTm1MDB+Ujs9brJ2AAKrE9+ep5nvtQVeG9PUGtdlo= 64240043913835461386212515483198059541440539167395859777194837833769712010594411295323900074066077107346806786205590345517755715510695858065925747020336398305793661773798243627926904542715123849691490667964262778804487343218972081260210371192903128886030021862362141928329650003493687310970684093289133340250
FTQRk9/BIj21gbLwI22fHJWYj+8Ghdcc613hOtJ+/hQmh73HwTXLpaGK9aCptxVbpjW0r/bxaRjmgxu9u1CCZh5yRd7Z46Wk/LIPXGd3ycQzqRMFB7TISFQGJIcFoxRp3Eb5wa2OyrUg7c/D+kb7oFJq9P7mEwIh8TpLzwmu4SU= 14889529068556301710329043521845510156960298822469914567758538023025100741826628180855835334285179977296740667353391766487166458692144569279381035582718738461626140662441222061900764829681913534146898551570916312642104487829660946024590782808750587095559047648957238487820069966851521487428624726655438348581
APYXO6uGvs9qWiEAkcWsaCaCrGJJCP2Z1g++XlJ67oZIgEoWITn3T/R2/c4edAfwUUzNHAYZN1h2dSrRoqlrRXrbxFtGOuRCUrXcGLFFcEbTrtm+z5z8xGRfcorx7Cu3FIBPMK5XcGPcbRZdyP1gBkeDMvuBNAo0/To+LP/dhCNM 172810804474418448604443090732221483571611665465870520701624598983692130272337358406272727413570938793741430131635927237320173996217984860203754686741782921346604605228620148450611724714868551781003004492587584071978757421616871762681049508123223983431502852926521520561941051298696758046005573332373854233420
AIDNxhnDEe1kTJ3XGfTS8zKXeXPRdw5yifm8j8Ibzj/quExy7hFPtKct8hRskPR2qwTlMiW9Ra8Npg2USsqHV0rBoIkX7E3psxq5LBfp/q00l3SEBuLL4K2FGR87bPgU+Duk3RVrNMnColiTcnAR5XkoeWhn/r9MfJMIN9Y0FEh8 90449107125498302548188660544012777357148118984122992664008792590422284061463729084479315745509706793674355738023180454297730948397413371686013210006834869294564190666543874617716180411178090109573192518129248278410216362657350215009192850017507998797754539132540293137589672869131300859207213449571846080636
AIRWavxYRsGlH0Yr0DudwrpYtbrByf9ZsDawKom7ubiRfepqYzcBlwt4adMMnkYSaXeYtOsD4KBm2ZvLKN3++RkYNmxgkyarORBEg7ERyiThBj7Ksw57pNHCAoHtBEhH7Wp9mHhuZtPvPgCEptmwCu9rYhLt4zZp+Zq8a02dkXvM 92930601962515884925250459851491509622611227724602941760145671636277317511265759558869239180653492283311584982044597979173761619470326096725838197524704577188104121460089235709339932110663536557497112887112782062772810759971739760085128369628777812332518137107605855679096146402427144185104230596200130247628
AMNJGLcAiJtL5fUfkesWKYJurdYSnvsOZeZcrg7bemkEVjF6S9CcojimUl+ncr/YY5/EXnU0mg84fObtDxWWdJ7z7l0CFcoALTyEatDYKshT0xvdKY3u+LUShxIAyk8EcGnf+KoEaa4Mx3tg2oTBnVegXClOakNTWw8bu2ItagoQ 137134165107366719462230252606689766470445826753581409513106273517221906418464863733870948759313279128624638614534848890858250894834883265387344539280755177217350585564186248554307335197387734431939154077778003706720017441895613190141376534460438929588407764609772857975000507660651583780079804513519571438096
BmGPZt8XqqI1PuLN4K1/PZMi2rfOYtHEMrcwZdSjKRm5qTkd0Pbb/5zPV07TnM0uLRvIQYTLloEY+GYyn0K5gDTEZpEtQ8ee6Y87zYGDwcf20eqYNxkA7FVV71vqCP/Uw3Oi6B+hMvsWZbvv2vH6MkAeADCrezOtwqVS+irftyc= 4480956865245875120472829476982311611308898564405318773810939350829150182630548948231116574193987272498161864310429976564278532538229396846813874244969927890037756969704618336242255039858182439641759659872128285423988638335967412040624105824571426792562334458751137508116412821914961236269913776304372561703
APqFgCIYbJWuRyEGuOStPvcprj2PILQ0JpgwQ2jLKn3DvkWSd83qh7PWGKozGavsjh803K+ZzI7P2wP+Nc0r0El3q4nzaHvKaCtVRyMwbXv9wYLFZICeM6J1l9ljUMts4tbDoPzkIy3ScU7pYxarBWqMkcBU8qL6NN1vEdkeu0fW 175922170410080716883576123079908758276229469783745771772401183721225804343343063277676406040455068452258961299511343441961963941297631097736305638850193978800615558067791016294285848963023036905095022181004058235239390870177623185946205281141386416867569004073524130001309977475780893497185890756991672600534
APA/rCcGeH6A+6ZwaBBDM6mB6tTD8mjkrOWEo/pK3MCZ+rrErMBnFp2S19GhlLOfuY8BHS+D834Fdm8+3wKYkWnXZpGb+e3v8ofOQ34G1HvzULOYtrEiC4ISZRt2SSyz2hU+PBXjVnplsHWTRxZDmBxTJdgli4ItAqxGCxj/aJ9m 168708388929747822981923386197903561880341990893945097067702518857172133291360611402092714329372304718329568897960770488377524912057166920574319430820488930520807742026377043178502591886293565177404635365772829346030773275726024973460121300339258054215286249329967181244588558220467488638468686270735376228198
AKmwrLP108dCGWOWxE/6woJVLRi/Kra/DvdsPkkrZQmWIlUT7IvwM4gU6bUr4f6wpT08cIQls2cGh7dbSEaO0xLa3mmtKhPiAlzSnz0wuifF3JT9U3uXgUfCZuFtE0z7Oi7WTOrpl3k3GA7JFvXnY0lwblIQALVf6oWyNETnajGl 119160465301384937485959146028591622947513292915838943629387700439301197965652871741710280647524383590817798553034250156068573474278225305190573334054718387045488098320076877626430189054572361967283632592181431701411266656256255758079114072932140551282607247364388070762970060420036793573956057551235306893733
VTe3rCzAL1Sljo3QAXEkAdBy1ZARHZwtrj6ZNRa5ttqd6/l21g4z3iHCeGo4rnE2F8wYTy+NlugjXw86OS+XojW5y6UzTtx0HX5IJ4POqN64aXWmaklGzroBEYWeuFFKcgQN3NOxkuJoDQ6VElP7Epz69kj5CsKJUwL0SjbNrFY= 59841866347633473702601462509811342285929528424012250265905695635971518533504187799047710303717472950129869674786231155102509311322791323986824635569605105662070745033595366004805920086888891275288347907772640070278731650628917037915863439204501060041944275512863990729926528905725569467329169134226609384534
AIZt1xGhC/HrvpPASsvVIVdsu//tn0noyJmVYh3FdQ6yIh1uce47iCsrV1yvYqx5ZTbC0vnfnbjFcWqH+HtLX/DelgvhEwzqJ8hwQrfE1ShLG4ZjAVo1Z4GCjrDcEUMlwKcunuSJssuxeQuXwTLS92+q6QeBSS7OmfxPX29CLb4B 94399298271083745508290936113986978382457275531684761701599029877008571741877683365769553170771833981099580359640421358853566501815723434822307977440496208486103754978934472597505865596938563438311337045817621762649604204720249750058676095769230214181772215323235427976398686727606000594646472236822594174465
NIhTPpWXS82VTA0LTd6TfM+HgLgUcmvnMYtLqPpuqCKZwalAycwl0XFYNyVvaY21J94j92ts/lRYgVtHDhk7/9nLXq5js/lsUnG8rWPHJo11JTxvW+df88aX0pw8u+biOWt87vc1MW1dsMTTsJFJAeBx77qU/Cwto95IVqM7vSE= 36889590210230649939994518345793530042252563793069578097360569338647730438860274349862767107939590441616825589851005429465345268710487649366046960918184701290985280638488938340668212498212581853679035928093386035688597446809895381618260692378376844452061580510108168030682664507293277674052032318576713776417
KXdi4A2Z7tSiiX9YGtDtxUXIfQvPhcc48rUH+Q2SnXL7fLNmr+F4Rf3RiFBRiHKocPfE94pothop5qQJ5X221/DbEKWK6s+ChfQ636jvRxojoLMab3dPtaAPpDJHrfZMxbT4ZaDJT0tpA2e+zZrzBuDs+UUgCpty9nxtdm1gS7A= 29118662951481660380477444121362422614202367719725087486810943918529894738076273660245405874301505615796632229852040910511025841576465052938308369421493312085081188509808322692130449282585522349552501983296872614029139293444558468751646868108213623606366977549477663987815308260383403466635254115908032940976
AIOTBZQR2EJJRmoWdRNFLG4fceoS3KnRTHRpPdllhHODqdg+QxTOcOvqIzBqgdD0JgO12SuNAjLQOiz0jhd02qkXw9Y1adGuKvL97ARFtNEuJiNzFAj7KpDLy2zk2rPJp4Lp7cjQs0fe8BQYnTzTsNRGm+4ybln/gse1YWu9w8y5 92394618277596007469808288231093678404089765494062813665106014405059399079199990128824492247005602685377185496959522609467906358619318009231448503013528692450191782140091818984176967246749464502089280153086163239846744554575017530385347720563798041108608545014076448155956762636929707905789978331102411214009
NzfbJRBF4pqEeborJrjoknJgpfK+DZh2k9cE5dcElMPZ2Zn9im7desWGiBSQnu3KbTO4L/t4+m6nFTNcbIJnqbVSMDHdsfG72rG/t89aOuECQw0HMVVgONNNa6i/mw0jZSWnRLD4fa1YgbUlMd8jeqO9XcBDB4mVtDTxyeGa3vU= 38775530011374537813502898274019389132620116890266344603221997943675706375698597061571989090674289834838060050848545748579361837989319487970580969082824601965845786771062335733318139530316825802589479118956745739691326447349403950997231306042638797277408335778415717988679050762936401945487285814799382535925
Y4BVPZ6necuLSwaqYEPeZp0lt9tTGFl/WCJJbwg7XpyvuwYKtzagC1NLzY5ymBfwGFw1yRlQuyGsYd9mBfC99DuVCIeh0JNrhJN1bNfoSzy5UO5+dmTr+dm66VGSRS0tFcViDTfCIleTV+zxo/xuZT+Bjxq7kZue8zGkjp42Kmo= 69872189501616471647606976308259279995249122669120675885925763529037695584466011511740991152346215507625265226811128801733353566555339153627478941716586678793853828514394269931890370517258825006937741437480128878717892485074131232336852490940507703859793477547154689914725314529986438108117871674332626168426
AKCP9Mto4q/a2xNqM4N7PekbKspwt48OGPre+iqVwPrSP/jWKxg3CvvLNZzN5P+/FiUGIklMMFJ8w76OaHIPqKuwckj1gvCLECJEE+UAZWrNKPmpzd/ootN9/kQhNMuloTFCyhXAUUOXJ7Z0WVLb2u6fn4zroszSMBoWQEKC6lcq 112750701794692134675959811050012620191158543234019977304167102486465198271340022889272244811582365901584420008564301920174477182946432553537794834985703732129975734658113610563794129371053853971031300761815004524681756388784922001759202643614966614186697992611399618828963452661554240362943588548146868410154
APOTAFA2waoAODECaGNgCHa8dNN+cjMnD01M+IeQFytzo9RLMzzzg/gpTUFpyLtFMcfbCkDYQMLXwE4crTimdz5sVvjGQ+5fSFQjoDY6Bw7MO6NAcLzlV/sI/1WyNBKaLQbcl2720n16tdUcdckQNnV+cC2J48CVxYM1c7QQlxA0 171043636512232272455501595416608280460445723238023572475354665686544174728784633443479486247342724860289312593374524429736857970220153680852977711594899595712511352458264354251161579203922747468321999465061463474727943140910084880926005209538535217464825087114791420210981711903880998556269523363208766099508
AMGpxRlB8WVnsGqyyiy3/mzrPymtJW1o1HcDErK11ZwQV5PwTF3c0THwlnxDmcziLWHSWgPQwfRddVDCXMGW9BffJn+XO6aTcWDPmDAh+1DbWJPE1aqApGbHvQ8HONy90dQMZf1ayuwceWCVTuU1wnHdo9F/sIsRbuu7ic2OJDzY 135994898408425255747055209966103741651849229328236418804928584233229830656742052333413774490626915784901255640138520158698845938184666683995579777154437927013722740366497459963753542029774185193376253885864514386760437194444013834088425088260658670140534670789371556026135595577395047002643901630053097946328
AJAw4uDYdSYkOrjtwJVWLv3pi1+UxWge4RmkWKqVquTsAVcT2tRZ+MFdHM457Hl7fmFIyxvGZQy4c2v1AbHEfPR8ID2sCRQpdcfrxEUZPMDqxfnHHm0ziny6W4X6ggdBzMp/sBWaVNTBL0e61/pELBGYNRGFMzGws7HQkr/sro1D 101254336834199527040756567675327011562230719161388328289463594628690618298993695452746353237675715087353241661592074446889034411683413957950360025295995263477031608845241728493807755308798509893719674568267846671753070163272328014412744008880395248474446310603301447848026040555910147467745595720879397834051
AM09TdtXgYL4FI5CGNiVjf0T/AN/pZ5zZsBOi1MAUKMURiXnc1x8VKYTqM9Xb86mqNBBqphynIQG6/3e/YbGJgHlsSdrmKbo+P9daMr02I/7Z76/7Osa8+7Ky6lhVCbU3F0tBH4WvopkCQmuJ267afgvDD5kB+9uNr28deMH00cY 144124056591600568767398029380314564902309327093641173350205276895603332085753288682409279238417493662029954512382520307259348748813767324609446500382301421328754981718014234615523158887865271179104711373675849713359713282937065993613915015084108700238420759344034475478243507306107546245540340758766909867800
AKDhK+/BKGXbrbBh2vM61OP8LN81YwlJKe68KNwUu4tjXlQg7i49Jis7QKPI/YFPUpSNTu5N2iCgeMnCX4+r3NAfivOao9lw4N3nc9bi839SIWdlokhwBHBYmCIgjehUeBAdkU4jKqlE06pIrpRmSvBtn7O4aWTbT+C++ViYAcGF 112973480670453665543892521898882856059335781900313607790238402438320486344365203510769919022496690291280873287383392088872774202832124927485754495093552572232234532821756395965072330282810574669371524103814871172318519695921477775100282448247625395376072233777533359104085023946019406729587713120941266551173
ALxDiSxHjfxvP8ETvpE+SyDPTS7q3o3zCK519WTepygC58KSRfvDnIVIyV3toQKzgqD50kF1Ni5D/wuaSs62y3zg3kELX1g+WuBCc8+x50+kDtbHXa1Me3et/OqVS/QeppkcjK1UZMU29fXze6P/w6aQfvKQkE7koeQtZBKkYc0p 132203344567902304830160099595561253300484092355345272411265169562971473393256361094745618829297250316196312398486598077249124198329075791740755862221465178128527292695331061023291345396067863215552021206609309872689233899464919108147533679134727064586730810633196817136739658243232643507412032417747255282985
VF0YUTvy8Mfi5o6X06DEvLm87r72mAtTdyyLNr0/GXlk0Xj3L2Oi2bVUMtcXQNRXg/mkdGP88pgdaP/eMzqkUU++vJ7t3UgOC1i3SHegpiBhhZh+aZHH/wjFV8Mz2XZB5z8MpMgN+QwALK1TT2Pyt/feQTsOy0imVanB5+OvCeQ= 59242171319056188000481457618922567543461456096441095927600135114274111606802456239311634638536207588762066940095527920532936960549439269891703098017342732142860571277442598349453761561189719823290643146391349978698217357430495238876700400634593256155537598291759795109752990651995982467695091946768443574756
ezpwBt0N6QhTusiPcKrBvSB6yuk/KShTLUFQHdf5J1u1fgDYrp+aOWuXOFVfOd0bweiG4UxBQNXB2IDFWfYON0fBoaDqNk/41YyqXBSkKbiNWLi1y3zPmwTAiwK0PzYp2EPfk/t/j0HsDbvebu0ygcxb2tPqj4EQ1TXEdD007kU= 86533835313999945727720083706940213467453975054116752898416709637030456504024135513972566184073843025739226187558143854850980654667596935003124034699919861200483994576288766702308068265526535622439762454501169018136389983894783905946543636163866717367545972667876983557989192393479830223914708619684891389509
U8BT26zT46tTZnkmTNxGUAlXbJhk5cNi4AMSd8fSvZHm55siMFGJ8Jl7mtdzEFR1UFAyEztf2fUhxdtMLe8ei/OJgM0j7myQ9STucEwnsShT7QS/DjBmfvcC42sl1CRpXkb0ZLrEJCPf+crtLKGrG7ExS1oawIAgALBiMQIL6mE= 58812148564290791415180898639607206220554150794356494356250223429674091688305329629529905854147200457536549527135776329004085047145097927266797668252160196098870200925284256433894773392353678965699083286106628662506590268955650280670838340651598082083455821825076016227525614626726458235627297885815646710369
HfYii3U1SIkBZl09RHaGGA7H3np+qxyNeeCNY07PDl8LwZAaaYk/bHPeBVboan0I2X4o78zCD/gFXFBJ4rxwwUsVjHEioyO2JcpV2/oDOelJBD//78WzBMMSWt7ZKbJV9uYr9ZUM0BUD3fsk1esFCEdnDJdr86U0UMmiig2R+ME= 21039655953870571289679214995029926285040274249531458675115179004718812090027267801012507748013357317597416722235988917212676802092082137617336199787762782958420742299451435320649616271885264333948336627286638368859041172783505464468640994920853000441536629081040963398001710173320125308624362209157720438977
AICOlee3daFyqTrTdtWjVb5M2rclh9BpIo1CRvKo2bF7NYcjrU0/VvbOnTVXDwdeGMLupbi76f0BrfDxYtkzMXvIZlgoTit4g5ennnklDHFBC5cogaGlri8U28w4/h5oMunZ1O4ezdpRgVJe9nTP/sSEMYiNS5IA7Zshdvm/XccF 90275777798511290102824338787811725003177532250296755103300529948194832904403489332420505850668003332750291879153080212231952155092379375422537931240723308384652734942204313672973885652497290433943089371705605128843469306776615573873479312715317072986990219294942040272550822460408702072075001377245051602693
L0QUSVIjxvE201b1ztRZyOOxy8vkUz6626TH4tbLwXjjc+AhmrvplaVlavnOgHqve+/L18XNuAYP4BqdxIcWTx+yxBKm4ZS92dRJdcAtccvZpEJtYjdJvI6qbL5Ph6HluaVZwp4dyFyXuZOJGTfYdTb7PUWM0jNT/xsqyjxSQ2U= 33191267986826803728285073844005357792766429917696698533494382218509532051029343127452480789088572904364699220151221680328978554239767633887572649589456766209242252549993823283929686430100804479376247660556781589549613316880150951333982646510273364068770923588389668733632648346075516618646974067295703417701
APlD9ECKJuACUmQUsbd2GTOpb2PgQVT08C/5hyNEVdA5bWoICX7epmoCKCybdolk+cfEBP6fSz33j+Vn8MbeiHBLdmF6ETbmcyOjldJ902MDvU8dqAa8IgEZN5Uh5x/xzN+3dqk9o0ji7yi291u90rpfIh85PPpDat2B4l5zs9i5 175040148659257809883308984693597046378367187659749953472629929701758633206586720399909808941145946314755491399962797299295431089674294356220216615950668954164397362123668926410543898553191541662075745481299747832013627018846822876386760538344447600390187421938699064459451308870669878673306013635576901916857
KB7N0tE+A5vFhyrd/m6Qe1wTihkjqmBn+rinfmMAzRlvtxIBSyDLzQsOQs7L4oTG64ABU+YwcWVijvoeZNamaxGl4hatAH1pRqmC/r8FMvC4vqiFTbFHzQhkjM7uoHD1aKnxyBVgjMj0E0KZjrRxydZjIR2p13FXjLP3UQSFtII= 28173452509830313810392326357601136401754938805266458365469366750775669869895498658593356375710132149836430968810246171974040975430205200958564616924399794768861923079158311829444850822144940112488994119845741191519421434257276977333662656888696213514226866147767570046232093727585815615828360199830275208322
bxFgV7eXwnbQScl4VzS3RTdcMW+NY6pcGkT1UsqHIeDVyBb8DnH/2/Z+DX3zniR1iW6FPdvhJJeQyPIax1ohILa11R27C1TLxGvTrRBGUycxjEcBIxamHveBsXbECWusYLEakeSDg9x4BTWMz1rTQajkorBoeEjYuW+xBxQtXME= 77994515143740690952370766995249847650881300682406161400195705464876513409097078624084133111941171517535435606295232558665316819077765607639545069239931096306624817379462598756505457054433358548941076472902905065316335603665413114267741896000877284610377452471067725794013283338924419969559537339967562669249
AOH6E2eBzD76QdTJ6QbR/7OeF7AagUif9pEYx7fMqrIsXCJKKpLV/RHIItCDYP2WO4URCaVueoAJe3M/Shj4o6efvH9pf5Q8MLM0rn5MTHWhThivqYQDwjCp1ZsPgq1VFS+gcnmwgHhj2W7XzJxiNPeRXlxI2vL+XTT/wPBYhqEP 158686346608862569574095184731081143351413141116869402750758091813874232272198082464382169470744476593016502816563462778075467588097653320101723165887488327616477297401486647183409348122990505635004320879840358339260797834264972100385692477324858942142372580281421734058008608134075577990829273447077276721423
ANDDgNXOB/rXwmS4KEjiHj7RCDocVrMv5SU0aw6AJzNTBfseFngqidXx2AJKOEeG7RDDN2gzn4K4qJktF0AIPG2JbELlLUu0MFlpOLxamp586qyp67Cl9OuPq3UZTyQhIsSIE3VQkvxuQkGsaV1owDV3BKIWQbQEqMQI3yT4ELHm 146598844784260148346676185962272439320781765598895126402049215152385925250917998794921584290777625240122575975327405909800121511343265147922400813488099624745229653124857224399973509428158163452130086943873214460600035260925149630502192183407327427517292065083168010281295559088633086659209316582810260124134
Vprr6oBnWuxIzyTZjuxlKSdZhBc0upeNBHVIlXpQEnN1Q+XURKzp4/6Vg/koITftr3SMSgGpE7LkrERMGFgYaqM5XZ1RXYFKT9dRJnz9VRDITVZtdkDrU04bqo2Ur+jvZhvg/oHBDTgQ4nPLJfHO3+GEmUtck+g/wOVozMMgufY= 60816213163057201559480662231646403262735082707152897397414589876256824040344252799972529759737904461369360580708093117244392116003622336721789703580184437841209963565058475060017600871779929808204093448248984201640754565635410002090180110910120481044515630478472999135146756643143415057403006410330361346550
do4LGsm0afQLHl9alWF2RVyEKPxLIErsf4pTPgScRE7ZiTSVErbCDeyzd/KHzhBLQs/DhHHcw+OXj541cIRm6jaLVKiT8EwLW/dVG0AkVli83sFh2f56Kk+bCGSKvfGEQcGLY2k7nQ06zoMlYR/xbZCka6Q6kSq4YBDQgigQ1lU= 83252051731120517035090523892596419800592471447735288551342681962005778435125655090199060145942826521644585427683714084736143440310518046334877897672493531918539106001203807757254797471481884534543367685912500572052457610702790097953420236852480969038388056545966568595395722585797418296411673622376893961813
OL2Qoj4xkqRrQmuuLwrABG3BMMBNGjfBtVBNTdBf7g027Ghkk/z3aK3jKT1EPpdiOdn8zXYBSO1mTRGyK3n7Jo8ICOcnlBOF6cZtDsb9bvSVE26MOD2wzl6irU7vzS+s3vGBkN3AazrxPD4czk3xezA9y13DJVnNzgAgIQHEols= 39844525812817530522650122383059885756573694015271773938493414420875846359054562126060762455794481186614035892021706051863945033061233991184379580556219478200155757966121832613842937722944431875100059046588723473670448006803481527981834627086055642349130254917244469014754132003347635357123155857820000494171
Ljgn+3Hcg5DOf6usRumk7P+ZrdTBRmo968HdZU1mS7LwLW3Hii2KNkwMV7J77zA0P1pnvhMSEEeh1RbCUjLtSIbt3RIcOEoc+aO0eINF8r99l83xF57CBI3MDA3AAbtaYATy/NUXSC2h4W5kdsQuR88139MFi5y8E5njqxHu3UI= 32456338403763561215581247445990611953939298888251578685087656354454727113846722731945605696397627662593375001096230320486703167389461057538581895745078593206660798580358701927596287363374862536765135996838944212622199018632046955402325290145163082309469649329852148345837780541107029165352782710901375425858
AMt5/u+ZUNm+Xsucr4RQPUu6ExAOq/Jbcjm/Kb2YIAaEQ1czIL82wsu6YmpHcfMaxLjY+EnaaF+eCWQPeGd1av919+QFbQPeh5DT7ZT9klK7BFyVsN0nEDJQ3AMMJqq6lm4sUeVxDVTmMypYnkzRl7jqzyCRY1MHA+o2LyMECdOg 142886089970163885609957244378225169093559131065687633458877059657380607541767850701139140472705242750285722732461954100519608059127637509286558848391554697942686619832870045594188204522385787253648018847569919409782188708374165437385572046835539379151066214153911415525465041951116179326632238059135825466272
AMvXeHCaa+zk5VdB27KoS8XpjSUngaw7Gwlq6e2RrkEOxBhU2rGWGJ3fhq1HBNRxDf0quqfYTMd1speisaEr3cIyx9BhYwB6A+Nex/Sf9DSixezhcgEz6c5CfwUYP0QTTOiZDqzz+GcjKikjN7DKJTO0WSXMRG8qX8FBbH0rlc9l 143142496664357119491819741364830737485524654099662921673419335301323845847085335210884201567922636945282124120681371777665458057821603161276185071778040317947168899788341482064834489328957963447735297898161379277478278414388733161844053774747425459239004132791029364174047523473372650441001639174571312926565
AMxoMXHfE2i4khsAkv/lPtLQhbWUjP3kxYmlJkpacpicBB6z/TmG5zjmTC/sqzBvBn3J4UvMzKYFyk9/l7Wnuc480500a3S4HRVtMtirPueV8v/SPktL67eN2zoj1VZA/Rex0aRGjW2CzEKGwEn3G2bZSgdT8hKv7AypF69ppjz6 143539479941314279463880342636704987025205547180882175105616955926182352311179043850344463145750154442573797875223178075233807385237935671604701513551125937539235111702655902037518920150424691586943553275517626347557879039695678271564616114192941679606063184290901862703975921261779714258077775731727612132602
ODvOKg7l9RCn5CePG1FfMitkR5l9+7JK67eU+WeA5p1YXCcKS8GbYAKCtXPD2QfxmQcrNYfAc6Yb/kksaq29oW7MzZuTDzK0HXY5xBc/fJzEuvU51gaI0PR3cuU1qRlLqwmIlyt16gto+2E64BgPgIKJcAjx+TfH/EqNeJ77/W4= 39488587053253042573878502921384752550143716864908041972426777545317969264945056510991363961916339225192727727267483337259701961148978214005913510275048195308792987888118270387288989623193626554910652030960235845935461155296845475356011099372367616732243132816329531758943935324760665826550992788664237161838
AKkznyQtB+PGvbVroM5nUIzhJUjiNj7q4fC9sSFbmDgvehnwPElVlie6PimH2FKonGV4GSaxZ+osil+9omfkb4rO3pq8fy5KcFSw/gs09X/U2eEEcUt/4oSbjs2NaMIxQftM2CauULiwfkWdkMFTBkHnh7Bbyocc8dtmrBDdoI8a 118817437232756222334188081193205110010964766506378146125932730686679941224328135190204402802650523704343176483564284220367074983943319572348376466341132480772885833789613392397284313483009178508647973749522358005819092779831781339778163122774381387989185969990310049504391258988402795259963134610905036263194
AJfwWA7XnYbTjlJt+9hO/Q/OubHkUkyMYrN6Jd0cN5MG9Rg8W3i8U6oJxT18p4XozkiOgPlF1lE7hIAW9KRKJKGTue+iw0okLq5UNMu2Ha6l5/wzKi0QzRVTBnQm2zjPlQpgUorBBty5mcbt/B/Y3vOE4I3iVXklVtjQ7zIBHaNK 106695084438708194568048926154027115609888551145480521213711726807296356271397749432698558860759334362315257102647885062353922543502466463770991058956633500180245599467233361812610650830611712448187310827443315947425061886163301613989593906515923245020641415290300558869209909418659128196109640872398602216266
aCXItk5XhuNrbrqJr1Qm04U4y4AzSKDMms11PgVcdf5fCGdizibh6/oZqx5OitM26nRz2vob8F+ZIP0CIyIJU0T1M50dVTbbpwuVNdv/XI6gHekQt0d2g34x1TQJIcsT1VWwGWTPNMtht1hezBAYxwv105AGKnqdLiz04YAdEk0= 73134927546833985031652237686088635686032103401394612286045377544136784429757461671691980910279873140130943470029643791712859175007885735170485461366406852784845528918253441791024065848540598601036357817496637108534035807393364939272891745520961269029038360205258229770737579266643408540634722493263322616397
APNeoaWlyNa554OtHP8F7GAY5V9F7LMoF2ssg5wBmsgGFktrRH1C4FdyD0COrzIb0Vcko1/HiTnA9JXlfGKc3gTHEnO0gxBSDjK41L+EIgUfR0EhAD9iftTaCoBM7qZN3R1MYrSz3sevQZNMFOOnRrzwWEXnJaPKAZXvsqPzOIF9 170899982929163229592439208307232242235219591108657660041403142612622997092685093132858257827585941687488772925553142105567685213341947938835403410054637382864108739466539574004149772568683507025358331323655651148107044968424043673850583150424463706583215452211942132017052425497789362680979074312857823248765
ALhwBfBYpOk1pfJcNut0C2fEAd4hhYU03/ZQBqVe/7MgpEDjro7oMvSdba5kjH/VBssmZVqpvuZ5lG+vI9lXLukhwRKJg7m67HG8lZXvjDmjU/PCjxBPNt5r8/DziETYmMa+fhaMTw4hedZcwDe37t1VPIflvM94sBKu6be9yJAn 129516480651398210587505113546142851617282590236388547627336279692965778911450075230961856270046942312918567973875005814982283590898552829322178788678196583244198944578081007477482775130405341039067711963061287597331433268366003672643052056973656674139309732186091974604170508497340243515339072325943686631463
c9vpoiZvtnj71b8XguD67WayOF57QgOX4V4L++nG2u/RY9VT2+0tJ/C4NIawVa7ScQZAPVLuhV4J50HJX7FZgtY5n+lwMzNo0av7i0IqTS+1BBO8eNJy2wkCbWWBxNybuNnF6OK7eXdPb2Mmwm2OmhN2/j7HAr0cD7rK/Hnif7I= 81358980280155473712258342299472964374474635149963153129588784719499494479288254287754874893180126149146558961101860327826747785201363745989346818037655063262173536227595206355647880155693272153902647256175878517626925488264893732295267833614283963802283320574654949992393798458265266551024756663538388467634
APArEXNLzDydcHrieLDReJryWxFzcsN1dxjpJIVGeJp6itsJOrUtnmXVnETtaZhWsmN3/Zh0R7TgJ253f7PZ/Z2xCEdqF0hs2MmnERSywdWZQ0a0McbDUUaDjBNYFht1wvS6djbI1b8RfayrnEZ0miYdzrrP1ntU+5cM1QBAvj6T 168651870043094856205824264282870999215855903395882323164614939540734011037112413507417141209480771157672307388419164831992909066097194364645695794831939514470650008210390333649278806163193463937050083854756730458780288720541495880958909249273048328511615821480782977316719631334570687241232556472064072892051
RhGyx6xibf0OvY1XjnmX5na3G7emG8PWbvEa1kIjR6pK6K1MrMZnxFefXpHWInFS7ADESNI9LHjZB8VW5QrjRVPMksgdEAlkhY7MyQxaclUlShFl2AfKYBfIIro+vg7mUMzMctD+07BLk+jejRHtPVIxHmNnZrZYds80ve5z3Xw= 49204219353786910100605282012781696579642953908541693903348594981245301165936599174304121350092894937817100350990938057159324959104937469442065996667276651025661016077514839755853073999975805394464570132481314896694678249282338429544941873047382467276103868995474424700207571657816852575364781281563515280764
AKbFfU3GL6NILVyONPVD/X0tffk5HS//7FBp7n6JKMXu3VXvWnfTl32R0WyVHk2yP0iIyi6SUusSicOH9ncO8KJHmaoMGN9Fn+Zq94FTFqZne5NxHmCtwRAbFNDVGg4FeemGXEe1S5Kk1VcvWqnp+QgY0uwa7RtT8C7/T+1pZlwq 117110890075563714812929271250884717870581483065920538069845585667296154465072587148155060755111295509684258790280104272121160614620669593483929827848744548171793187278583947500205314283462739235860439216105116687015890394925743036369717346234391524403038196640934551590543386844279091801685432977718405127210
AJ0xZ9dfRc6P4W31bMHBymgOq+38ETEIMvMtr+wB5WTcsquZY1IUB4IVkrHaOo3W2SIr479IfJOOQhmvyRS4iB05yDI88Z/fJfXarkH53gDivECuo+5+JmV7e0S6gCvOuVamwoQjlK3G32bCV2946ry4EyIsVZ6Alk9xk7X5HfGU 110384671994603894282707302829898242894456931176497230904862171369974466400767175784681299142670706023468915238955836087425993929524341269289746060546848852729416925808186253355106621584826213979718185296723694190658548757311188764342751280681935289121682174507629679900374674992438818324999211250580434317716
fjzmb1D+YBU5Wn1GlwhxjiJS07k+fXxjeNRbOv5SjktzxOXmautO8xZ5ACOlYrTt5G2gzW2PU6sYNfByQ0xoUSyutOuQlD2r+8MnDrxCo6RxT3P0dUSX7q0IVj+oLK4GPbscnKLfe6KqUcYLMgKnDYnc+ztFD+csL6BQnM9WMLk= 88647261832601702291191332432291274285041869480562430895152086741320122435409959711452438332192792226899741738806447713240934608106883094466050154088410020909933636902495700779087737304255058561688767369900548260278700135161077055869478387490726087630962098228537973426295306997128615315548440548541717688505
YDg99aHkQSh9RjytWknbXzcgLD8MrWUEHF46yQLHYANKXaQYyf3yGM9TYPCDUqWbOapqQe+XfOCoACLyRg7vVDsnOPRDI9ZFUgCQBNG06ZOxzktEhnNJoRC99da8jyodFqqk2f9UD1lVa8tsQdatjUDocwgJaDAOpYEyGnUlbXo= 67567767932654827067250684965667741848878457020992905661955722020937161710030993261011062929936964216357930453809610708591260182295097124272956485574313839759737390934220465669626974544253750900911093325004172643146669082793591441922014060981070503803266774197958528843445580649512373693546027107823355522426
ANdsfO+cNtWsbT/QJHGkYAL2WCHWVPrX6oEz78pO8lUwiigVEow5roLI5Tm7GP7XffjF95z5WDxzpoam+Bfp4za75D6ZEHQmuFnpWQAmNLUHdKUE6UcsWN1rbV1uY+x+Nr5Vni/M7PfQi1yRTTJTYav40tFPb9rY48FsUotivoxd 151275723772668372472508916060743043308364940375633847663054782759325087560768667906829087958412643723335046123025802453213225972572697773468957759328009026531148112732519692142632237595562259864125679649273054426879080697360204352423668940795473103047320116317252295126635024518179060076282921965794883439709
D2Z8YA0G/vzEVVQ6itLPUC92r9n9FKRpf6lDPWIgpZOOfIkukPp7zzTlo9Ej5IsBrZBbtGz/eYmlHeZ8Y9pQj8HFW24HeKYqjmR0ujbNxI0QgoE+VUwPVg0HhoQsOGmq47zpXpkDwpOAZbMh/t1Bafq6r2zM0qmiwOacJ8KFUas= 10814483230552506566705634583020057064935800294861277580077052473134972003523900930560478187758928889017740705417070994563709463926267126567504805864719383185267204810142444719634360655595490833208838383875687102074846353850310954150927702228780599083427768247170427544730791038729428517279760042619935478187
XoZpSMHqlOyPYJS7dWSRNDJHCkjbo6+DECzu0FpB9O8bftcxan/06Twbo5d1lEqPlLx3w0XeWtrmCSCaeVcXVtlY3QuPjdKPv8LBnnhslPOVcbGyflaTPXU+ITWE6rwnIF+yWQl3NIwCV4EBtCT+3U//Dt/Ebif9gzfKpKltD6U= 66377743237695515693282032069691369056215169443985727092982918806809030742478033317158686828712146024066618073633406428345129492010236994055590530566431286733776441810601990431112187030942086686719669823512292071202675269428014136307286941704297995292544712278047959299939833088742083527714893795660235870117
QUbbkyJQ0Nru9c/nPbphM6VxHp5DWlai6407KIDbTGvUReVYI7de1gO/BFphL9GA7gDareYoMuej3/SVp8lEujXywtXzjiI+j2TzR3YYiMBAMhsJO1wU9pxy69Cj5xeFFlrOycjE9sPS9nrqnEEEFNPiK/GDDTHj0KuNbWSCLrI= 45838919357034925862751142472777409057791233610959872523563363744902783251621354580995921495295078179996083468819097423327554678806691589090814275138081407920379810144694354354954459732280968086760894209634364189264517251735804373673532012530665557440070501687207620525228416650281363557992436992284712644274
F+uI7ARCeAlnPLO1YR7RJj8LyhtE/EJMcY45lsNMff0YeENe8KOITZVxNA55FcxDYpg9sKi1UV3/ASqkqpH8MOxWpBdT2UwSX3oBkp6ETfJKqiag0C4MS8cQVsfcKF39BJ6KUE7X6KUEj11j2YIIRREmLPyZ0LatG7dN7Rmv2iI= 16797235966984072293396362937533957334369977688369659112225970370748312376722010874726300554329794854683394163379447263409228872034356195791733533528404245739693397078461712458035888813157166614479153484688995068722288153129390850561042173295997770817893349738328312152341860704179681230323810266038959856162
ALkEoXznA7BJlBIfA3Avl9kygQcxexEMApwduVRiXeYG0uEXMQU4rgMJBlPqs+ly8LTIcLFaLnJAG2KFQn2GXz2TNa7w4xkegkrslIJEtBWX/lc7VzRtcLbhaXEs0Ci1ValnW9Up7dYOj3Qw9eNo/9M9b1fD9TI+0QXFtp1ge728 129924120553920201168632484268654219915712271781591182777925696006023100660478316445751842982460082888615429513674356810187315558964251402722465707617058251479494744427428152566665405423424700027316505872162698141109433045594670140335040479559124757490095995568556894332243767736124299898808796118800328801724
Ki3FNTEE870E9GaNtbT418CLSmf++s6Di3hzAy8NgiDOFo+uuicJa54V3JNRxOBc99sl/chfZuaBQt14BFOQ0i+9rm2KD82okNABd+SNfXOb0Ow2taZX8CpkVJYDyphFPyHbPIKmzwMShNx9X2z9w4++tJgzBzGcFTPv1nhAlxc= 29618953883711174042338818332957726953262658484143534778541769862244883781157097499904047532839425875312731531093860721544220959674634750905085721866390609141599426547378130082409488797303960018348798930232014390380383063108812922828160584483043190739354817699497573863286563890071313017508437166939160221463
AJq8tcSnAq6M32ViO4hVGiHY7Tb08cLVyxpl/v0Y5adYblvjrbsFcCmsNDi5PnBOBl5awR7KZdQ1xgq6jIs+SQbccEMvJvGUZW5MgcHrXBj9XVd+8oB0z0eahqXpgYBqLDeHLU6238xR3dJYFf+Xrcrzjg8swx66OmQKkAQVJtdq 108660120968150664552423780971948386965268856900017812123107864829782135741514930439461240950044759098603910762272795612101834680870627850178371693837566833495418727543557712057554231215186486008080050486837716071537742708913279026303380104388546316647349432118287628353129105425052237438199445863950767806314
AI3mfrgcRwtE3mA12gSoQV1xyIGy/YA4pCCvja4mTjvzQOAfiZL0efadxZH5awohCC1SpZDCFsE9yYp4LugHKu/A8zMcp4k5ena8sTPDkSod1yucjybgmVJ5h17Pru28AzHQ/YUmCnojQv55aV2+AUhxzIfojY+NT2PKRqr+vuf+ 99645829268436288676280252226747461064597487404802430565833102291706103139410465131373666856042539909746769688396958963177805479987372681967013633920910376342526433530508868114301205524789149997372160919406352823342811006288909548557622230243808373083272214426118230701324879006645047374853535922112549545982
TmXQ+D8XFKSclXwnTIH8d+sb1IV0gfm7GagJahaFL6A9rvYaZ0NTizkG5DQ0RmXyo0wPmLork/296whsdNdUxVAwnGFlWWvMV0ftR1fOvN9KoT0WtVZ4Rmu6Fuc7q1PskAZzIp7MkOAxILO4iX5dNuVC+GLZYIbpTel3Ga8fXuU= 55052751096768041533898435453266875315629605001878362193939750978427494147944918632414581744895066623527980497732722163665712245580312596487741856071020477624754815927936394948233480228964159047139170955663289543349257377302556035170334384320502468579367401821986660515827461352578142560630318492817238744805
EF6KIBWQiQoHOnBdJs1p+WIcAv9ILt0cnQVo+o/2niOtI0C+eFBSiNgeddhotkQFgHvGUjq8BPYgtLC8A5IFKGzXu4SYj5ziagka0hqfhVs9zVHKNx2NUoMhPDG5R7+giwEGGPOayGHVNbsBf1FBYG91+mwy8hnNbhcHSnvLGk4= 11494909948912248031301686864833544028186348338729984264372557659364976118965740281229664413031002362633393381744365783802034700038490736736266032000546393704814403638058993380993275865674190555703046732456017652317200288968188655019374159412919163798248766655991273308390043613040731449231289437754791500366
AL7wCh8tkFe07qChFAzRkrnNehvda/Teroj65X1Bmcr14+/zeJlZDObYRYBOm8YYSYNgJekcL3o9lLFE34sCMbSJgm4dGwpEVexiLVi+zc8ndnqBDSAnRqtC+3jbInm/v8l6cUvuzrUNtzXIQ/H4FrmPMiVy0EMerkMtkfw5GBsd 134080980697158076909534078193319899756347955848461100874771253577754225619652121295523443912922220564492468474647193062555347746840044705102003079330399499915801536721237211615317000955332058281901995149084303143543150689010335818219129745452688372571010816270728441637278434982752674030696337642893239393053
APunLhlblRi3bbRBwSV8dsw8h5SvT8ncAmXPnca+e1dLzrQZzL7P2OhFope0mW1MCDl2kJPiGTdK3SiYJVsAFeR3r/0z96g3oq+8uS66T6VaJym0QToMsqQF4/fUMaTo9HsukyPyOgjVIU+6TiFd3SxQKIu1/GpQWVQIP2pkHFKM 176716779397275986910036615967409090183531310366246043951791503601618945774743601662530806467045971394247287367421508126613573039423674729894091424105133906122821596079925540513892022311039293333114333317886304014722168786051080135090242879622144693440448171583324154550086458411590240882982297314605229953676
MM6B5AgdJKe5OLlPzcXwi9WhqQjx5KsnBYxxa3kWdGNTdk/IN6TVd4Ptn8lWkLm78mw3DXP4Ol1sQbIfkHRoKFUN6TaWg5aDCJBDXyHSTZI2FDc1di0Te1SwziYn0sIOe+R+rfuLuHlcT1xaZBgL6+dDLAZaZza36UEjn5i/pTs= 34273208848307582992498656582721015257885595139328466874135636009184357438445251703533153492315835793684794951576799764181908090765379592683793969576893243386892292517067596035059342970830813419330530731370385186653239446376170533147020072285887964430731437765184844167400169982662183791828762458682426369339
AJK1dx77ZA4F0sYCgRL1LKSTvjGTKBHd4QBeVnE6FKJxIow82puqtsVZ7TBxbECex+LkLQPrEbuQaVr3giUDjg0aJCE0D9ZVXCUS06qulqcCCdWgGFHXDOQzTWDn6TlJCGxtTEMbMxSlUq1q0iKZ19kwMHiT3GydBn8/G7tIYd23 103022457217861194294329435482792508957642944252832971366936865663608381648431732294396977429863681671686490913575377744795372643599438468695483808375208871881849232129651519218503507811863794426234594709451104684234156597418383183271923307418704786548452806494411689822939919114966188329657999811363991575991
fPZNsqUYBbVGA2FAiglnByxGJOZkVSpj8Y4QNW5wq6o/1e/PRwp0TLYJXIoCJRs82pAj0QDpQbHl5lCZmNxEIQP8o8xI//HCPxPIdgBJmSfm3VGetrOpqEGU0KJJqK4IsjoVpAfPFMUMOpGNz9CSvCHGk1AKrtYvrTJEKmETuig= 87751387019308584846595931543798879607048239290774788042055795835726250309378365187899578817976976035304304847968410200168743967600896348021636654074952051821111673620467434295067182213181329543946368332581250062140819766061014427755090798550122401239987766844126425179573454145697756278292448630509686471208
EmT6DUd0bxcdprYhAnycQaxm89kltJOlIOGFFRmEK90H3RhzBGr5PRVTJVqemFVpVliO1gy1nPHgqDGVNIE1GXhrhyFJU6m+HJeNcduippRe38xPCiuraRkXao79X7WAiVYUq6RIH+UIRnfTvHBgzTwjrOvKJ5853hYmGaanjh0= 12917015385266582065020051081997430892582163827812227349569911846746592973268746845211126663077128575098045461893559476227689488349263954564361736197688317585888118974603264677576027836032271531903881104937422976121352854003385726888601980526287956222142458858211589791399646989299770657341412683499692330525
APtOYyWzdY1A/YU0SGrtjPdMZA5E50Y3hJVXppwuuSk04TjXzcbu2Sqp7sMnKYbToRW4nB5p2UnaLPhTRy0yszOd1auLngW+0ttCybD6nTcVoP65gYOwXGfSEQysqKLb1OfV8kYq5Ba92Efn+CcWWWuS0wEr97W5M/Hccx9bGu0r 176473215292413922394356058789571494026727424839036665031567966488209592078148711908841964690807374236235612412767651029865069639786447019874344449598703213025389428836803984245755885691094364960118900160737925054803955567361126391353868279642836569627177281508980029006921064654964339077608785831304875404587
Vs6bjpYfFA1R/QTeCfhMuZLZ+Zxo6wxq1jFZpi5SBR1LaUwAtOAj38OJC8L7zmxSOj/RGEmJHkulI3E1MH7P7xlWbY468/azfot5fX9BgHrtptV6Q0dkBUg7H91+tcxdbm4/V0HGQGa2rZp+XK1rO+U/d0ki6iNbsCsCR+OeyvI= 60957991334776853645581868230398759578123373154273044785333939425321390401088800849629483265841435899835570419798325123273632247193463641611211088549152950252041797959644227170492417662363676228611376046334386877555777556575818860902071813120592757466883038430756577949025778080997296219236534786815367760626
GiauT9A+wmwJsFbS2OPIM6ultIbU+kT2NgACn1jFAy+vNBahdfHMCH0jJdCs5TbmKTCeiEf3ITc5TV1OSvIejJ0GRkTf80nY47TAhiP1aehZvMAv59NQHHTDUE1U4TPVYKIyFpm1V1A+JBHKJzuGrB4lvqB2ed7k4m/ZD5lFLMM= 18363925023885496669420377869542744504974590667921570026763131637088916425434675950812384919000566852243714758512996458727914094904422651029609645299422563453163291342992902510788457007623888307499601267675322986672697397389663297565071582648674012080122614260400848960757021864980761735684874056409664531651
AL/9KOZLtZu4+ZQYQsmOgbST8F4RV4N/Z+l8qsbCFlHdXHqTTkcN0chsccE/3KkVTZsAnAyJqogbAvB/RZqttaK5a8iKlOEoerUS92FVQw/42WhsVaFggR9cHVuvCD6QqclZjSBQKQzUMy0YWPWlycAZDIv96tooA+V+Fk0jbcFs 134819194171226950171930028888667967094069342154233489571728632904658607624703819928943642011918061760802468868660586005724399808048609316802502143143910585363214684061242274402109137825176291816945489430125510625857564490981683683589784133305376252294774711594646923226452625156299996630452243345104727556460
AK5x2N/4+PKlsW/fNrw76CnE+nS76Rd7Ugo3IKhMTB/IuCc5xG4MQHo5MlWE0oVkZ+Gs4CxUpvD/WCCjHHFlSxKG4mC6ehz3NVLglBt+f1RWfPkF28JPd0UaIOG3um8kG4J3JDN48PXOPP86A0H8ZYbE5+ImmXsGAcwvScUQRInU 122499245103202714319465533564374494931278163571999934877854825659720649344163774228004853964635693562785966889622928722984134944784141208867445419597834322541679973956606275877526560988151196822256754309120410807075405427166696093800381410682490767468563176131997424692783482903880902119461752084196789357012
ALZ12i0hqFhwRAikcoahYzH/BUolhgZ9Jz6adLvvTO4wk6LLOpNC/zCz+LjM7HazZomT1SqeYJ2X+WeGFLADHuWo+Gp/I3S0UEneYHKJxoU7OoOtE0mB0BCncLckHao/LmbpnQpS+Lx5bRsr0yE6oWNea6gbyRm/R0to74MI3/KK 128128022342420083856194424802390993133863171077961467523372211039771843125192435716337829530528063182315478279257832480290950255315151577221042903861075751839976362752440630888566422581799720709574650482021111126414843635330535518992034746102956214991673417580508389225948159518319625680855827280146399752842
APXxvLifWgehdwdTRAJP5KrchRzgbUsyMWKcPGm2ZkwGDJjoTl2LIOOGVFiL4CyPBxahkEHf0nMxBN5oNGX/Y4W4PuOAC8gMgHzdLkPWkpnTcyoe5DD+fQsqNuKVw9nvyB15fx8k0d6b056nfFjnnRqgybby7MSllAWSKRYRdxVm 172707950911363219032118650562553641123743396229371815589867086054370029540557395298194067635069298952836929253340374819975848769009260895874615676938511747311585257140973518651959463416682165208985512233703837931718385346209362040743041262031997793519095342415901373534535662377972036003546589624834285049190
O+9ohtZ9SzGLJoZM8IRQAjhc/GPt2X5G+M22ZidYjx9WgOTrZDXorSyxLuHxay6djsJSgjxYMj8MuanYSn/DzPWBB1Gn4cDmIsfeYuzO+vUJ4l6d0nIvBg9Iqs61/PGFd46YxhnDiVQ9HEznyTjzESnNqc0+/OkQVJcwNHAcZBg= 42087920806448980363073662127262313840530298932643042322138035915324224188032438119079107631420338701086802583985117830416851550991102672642532160807467909040086448764318690465254898516502941122327185894900817634110254371864896139724173087625913998657136384357741816102965779105122269429701537815263708996632
VJOZmvqrqsIUTQSSJpZPhbQIYN2tsfBhAciWnfAYpwjK9/ts7OP4Qgdp6T/V2EsSRPnfZ0VKdLg1CnEWDhfcODo+/BZcUrJ0AviFAEtdeUhoMSWXtjel9Ln2guHY4s33z2cN70+e8gfjes65lCzrxUIXEF4nKxzKBnScoooQP5k= 59391682001673484862915842850714742391303140646889359425353339320546979084250010101273851580028171449840778038774656177449549941659895629203970455580974953864068394275066532699748911169800076515776388213090834432354601344176559839798153004796057709798368011673585434643656820656931921831615507416411999846297
FRyJCOgPziO6RDHX1JgYGZRcSAuoQFIZM4niD/B0twK3l+TRpmVigKZAJnZZFtmX+0JQkDwQn3lcBGQIL6mgy+j0hD58U2/Wd6xebuHSzf4OHVGo1cYoqZLplszA+hVCoDVTHi2YAZ+GtfQEggumcNVxqfEZd6D9Nu//hm0t21M= 14824975573460749317081504809641216868382341402512168178334301409725840669112911061147252565570697788806398498723577368905065980113760265945344671897779830912242224090954834750057278285419880820811348943398148063418809729356397202526234113316098584002071850758705282845646489058224513019380757604894853946195
dUk5LyS7mduFJlvh5o8R73kJIeeTh0Zli/y3XjtIXfCaNRf+wDlD/pX91JEwsQ5Mvj8yq/Uq13QyWhoNwsPpXVcJtJ+02wtIn5darsBDfzcD/LbWhl7zTRUeMjZ72gAWi1djx94SWjrZJS2oWZU92Og1yOyKRG+ua0AhHfYYh6g= 82361050315899968537319599868832189063658136463903643442673674137187842597528653416212822014359684261704550279153006971937114135373937934986951573613797195556144113400128502946618028800530164890707031379614952207482505803377774320259789692177752930767589642007257364960987343146063216186985472686575891023784
AI6rejwEznR35rIPuIz0CP2aWyhRUR3unJ90YfxyuVYxrqOJQGSDTSf6SGDDw5MqpZXa9pWuwpyrb6smOq4ZtC3Er7lipJfXDjhy+0k1qcfMjmqbATUscwXGpgW+MO71cttccEz6vhbjndi8gvG5M/vfL2l1jA8nXuBd4e254dbz 100186164434910864539376019601151338080943067893748898987236087770762310617199833479771711726248130012472861788210345311298499515751355424063761182369333224929721733015910055321263016834247318907562652286587380604998130368845939290804442878127169587599285040969551065995197981341260363722618429042861484922611
AJ5vLZX0fSs8dUSBqd5hki48T9cYuR0atxR+qv7cRu9nD1vP8uNVR8dLitg3XH0RARt3ZmOgi/AuggZt6tTxuIBg+9JhBY9WW+BLL5CnYWHC3AKMi7MQBWciLtmBpyF152bDaEcV1PXxtml2KxX0Ba0C+hGVDmJSdi8Kjd4AkfU6 111256341508463539324514225759801553679558662737345522765042612717818066374840372549356543720386819501973783940451033901079765311790026584654529398345993992144903839534037331533660672892487693477412528974248713261092693018326068480417183236210881306241164169849090833681510163753605662526243408192127670285626
ZhXtSzn1GiFfHHnSKUYZiTcEWqlI8owyCKFjCQ+VEvkdk50m8uN7RCQ6ZhI545tN7Uy0WdLstJhgJETBYLHHIoWsJn07mgPxuyO0XsqNroICMQEOO/YWQFk1c0VqZifcohQAwJj7fONzM7hTcA22/7gVigJ3iLq178jZOJsEPQs= 71686982768953132894579286530164112027530221141251507987469672039995314435159469907420372652392376452531392493658576814100773556880394271726970628960571077839124343525055625420896355363707908511865700866168843075071778015504724409171911254647909938237551680861008772396291072284353858575645679153885560978699
Vc8Cw5m5yI+bJ5sUJYm/F2wyZ5x3D4ydyL0uU/3eVF2ZJu55OOlC9pUyyv7WGExClHvWpR9mhMnsqCLyseLfM2Q/YXJ7cjGPKp2xd+fvwHa4hRi1FdOxs96rJnb+HUt9hTwQByXgzpnUfs7AqrqaNf4WSlBNMu0IOOqDdB4iVHU= 60256873326783629723455608618518793848697944184579877638436234491615392142659293975260290798403892159720925893207048153291000664050780029732557737984085196691225472664027706406879051455184548871511448456651238810812870905640934953489289909009741493031472382758586341375517766302753448531830002512912250459253
QmeUn6cbpE8YrDfMETz/+KVFaK+d4NHHzcdj/MnjcmqQSLpP/XwCW/aeudlN3SfKd6rNo1XZefunZO/ek+PHEIy899WzjiJaajhf2X05fl9WuPEaMES3Yrr+ClogFNQ+9jL8+7L+J8lDuqQzvchT0U0RPay5HSNZw+ZouVCiQ18= 46630904037845609335515965570673490721137364238213103678233212262384415738654627185220187275286458759154841820256007930773120637898228224906635911124921895934056288121005350040349882413280772888907627838315559544636626856478316691755270725623680935763476199888127096014398699432042227882284223578563208692575
ALUBYIShA4w5kRUa6iNF8S33DqaprdOWjVBnO+j9CCGtUh+NNwfpKR8AKf536MtuFFtwaQvRIlkLpaTYXuRxzyU/YG2+UfRQF3pEmXQhcMxJqFzqZ5nWCIWlJ/KtYS4lcC/B7hD2UGAktnIdjVUTSxX60VzA+zxeunV2iBZXQlEs 127106299687401374061881872616647348819431126560557369258073443762502337592227172639640997680536372567116568811258505773087926491911004324918919511363985868314578663758269650473780772688462266790559846182685481907703974916356209771821075179827563487466641669110315430790405454641953880582274165368514679034156
ANyAdMnVCVjmUZGiVdyvGE5mUQpKoJOJINqMAfzVUGvvxXFmGdoAx+xsDRNAP4KoijpXk6E3yPBPBZEWyhiHnyjEkktK/gX6gnb745afS0QIlsjhKCk/W/BHXkzC862Llnc1ZGAIsERnGceEoZHdICfDUh/7nMFp5WuSMzPB7nEO 154841617115465511611746667401422322067517612306328612547616471923266281876818466022676728696273611923942543658633762267658490816264271663863494188027433799849037906883352478212451733963905925106470599843045599411842850386623187980045961158399934160107237440980574028985561404965317132715808604373199725949198
AJ4nfhDe+HojR2YrprDHW9FVUxsZvoIekwlNL2iKFRFcTB9IcEdh6QnGcaRinev7yEYUsL6saSxUj39uWlqo8udJFdszuuQUmnloIi34L5uj0m1OpLy2dawpFQr8pqyA7go4ugMMj6XCtiVnISUcK8wjHgY3Jed/EKK8k5ce0Jxt 111059703393618496515021583605572584329116596402705082562306930876194742195701060137568030171429700588269665205795898835699633817098262654446852249498668467827435829513531633390969638488553144849154126899372953755511962841193763362947708260103832329116485114451074371844037650417731807385491783373627950406765
AL+heSTflb2MkRYFTKghfzqlVQ1oE5vcx0eCIsy9NJ2NGFXCRRvoGDVoB8UEsUWIRnaA+MIpwDKGpbOS8kRQrvBvPe/xM/t3jrGkaS6pN064+bCBx8Y/Jq31ZXNG8oUol+y1Eo6fkUKNl4EOetmZWK8VmhVwol5YngDffj4Q8ned 134567692290185631768518572983694048149859804864902017394351513816079806629664302312927579302025923096596995134868068794900003728293470554490807959649153000914807604036531509869958441069678002226922395630284261949256022972967357884468325217602330254290548618134453007903724438628204981673400911693835033278365
AI272d2sbYIi637kHZC+6lievgcDvT5VKaCnus3fHwm2vfao7oYu31P4st9DlqPWJ635X6QtLkU5HgvVSy66MDj2fcOfwVL09ffkZYnoGNdhMADVgOq62Ro5cCpOdw8Ko0cCyVpVIaSysPuqY7kiClf9GTdyZz/uYHDgwWeNrc4R 99528854246023003959943182132914587584844397870416002887630245681136432049666385367430032197518895755482367603560037194955739661569172773017279832774100155646116233705958563163070414171045438199561777058338188494271322834524386565519620661180246416329082614115142485663975718653564590519408413408765689056785
AN9S8vPzo4SkyKsk07nfyD0um1riJzRqqWF9KCL+kWMHajurgPACikYzu61tL7l1mNEaIU16Ndz541o+y76DgsTLYszu4KXUOEt1Gu3eHy05Fq18zCDlNesSVjkZjPmuJr2ku+p0cP0TLLMn7/KuVOm4GlEVc6OvBNZuEzRriSYZ 156823459768092337875922818543729136404805918580285507923139232733465414368775678369646914249412830351437211620056021568154043505276475345347569200977945836210758870414054407438380975491139001471954448623922841964684437333066353208837709613982022690623722155151315252634380695513434502419141555410441456920089
AMc5H8kywLgiT4zz5xgoI90jejsHorbqUGtBeX9wke7zyvEKyWxRKScZwzRbinjDZzN48eg/30qTZOV2Rw97JFg+EA63iZ0vqfF8jErIt3hODniKX8zayCuNmiSb5kiZL0UDU1SNh8ER4m6o5vshBKkmqs0PeozfCGQtR3bZXlx4 139899247405256530335276706333424670310599977544642091674186635734421385499036688803073040921114325725234673132788498809189814711681909865484671959982394306416477300458309408833281654917008031099378445580498219376391819745965887864647387211647794422908411100892195529730435423964537342228510107659017578765432
AKv+3H/TruTX3wdMWnLzD05em8u/QMl6lCHT4VkK+uZwBXoLeji54Tcs/hZIhj0Bdj0URrRt+7JdGSTy4Sr986AtVFxBJZA3lT+JT4JSrq3oY1Tv+tX/yg8ZodQmbpQyyfaFg3BgeHNmsUoCrdqhj4IwBqEXoOBRIXnzaTuqqSEw 120779384043726135670909127168686589868907326577918074234323699599475436892003731971700278391108690400460261929381703781833059801757700386671579819341589048987186473249926590758009001670959004477454905417357202448886738669226760846888369186457452643053236389556969071303251275912453385963613554945645058007344
ANXIB+HxOyJd3YYsscMpqZpi/eYjZi5q6A0MohU4BiWEJK/E4uIObLJDH5yd4ng+hn7UMhc+R/AxG88hIdOc5NyG/QyFs95ZLUC26F9rkRifu2CBkgqR5EQi2cgwC8jGxQOkC62YND6cAn/ILsKTYaH0iavtO9Tz04vQp9Ypc82H 150122383481070201614242107655752525590609186454390549085509458064289390813495886095936526832230958746095739308601699615024239939948911472291507190108935262129646691795733786714291498653838550751365834947465294261687773081563139416397262227609481906371677917295227469553787085145970923979142676551778927103367
ZQLFoW+dJ7vrHdMlcLRGKY6T6PZKnE2L3NjXymS/55my2CDBLdDf3oXwLlRjVt9KnEiXyQzLhyY2PrFA4k3N/3P5lVDLHero5c36TMshbHgbIKRGN2CGWPEFeQ4j040IwVbQCPJeuF3jL5ikCxWZFXfeEnTL6TqumLfD9yLQfKA= 70932215714423143395949105745758445705072524008235214324766464113352968998429901322485575506330607802260244612268338586532462314021433435523464635419846126736185176246740838082062856583684393425704173881940108783636582561707441482446854068022535943408999200681879161519209676205165680598258447492092651404448
LzzvPw0FdtM2G/RRiqoajJiIH+Lw3jpL4H+08yOpp1bNITR2Aq0beu2nP0H4o2Z1/FNr2hzuGakkAhVbmmRXc8keoOkeaAQAP/8OYxHpjrqou3WPWaKx+vUCTSqVYYf8gnVKpAAC2cD+3lW+/ZJ538o+c0ovbUKNu1u1j1OBtA0= 33171669664542509840621265032202455391098253465550501094201777336478104142847268103467889435377685359857979277521589539506627375165485879405453566052091202280471235979376217319335800766353336252760793484157724210008639813552207624049019149744883918494762511376489708611103181576211531366514802868659603747853
APrGj1lIIlxA57DNh+bTEAFbJK2Y2P3MxLShb4fPx2aY6j88k3umoe07ISQLf9PzNPeml4/0I3w0KNd2x4s9KHbj7NsIT64lhO6eQSEteqZXZGXUYUyNzhrTbAjt+Q9LVKItQhsTkTW2HTQ5RQZfGrkL118b/I18J4P+T8CGZdDz 176100632478477421621142147788721746818712752858710594712903769452749028606541677227413333567013253138397373757811889654342173021761934591400685421771460440213093509170325205622261487145789848227404883040799927313402244625239515162996390018403365063394514244196976794479529075569412676472840544017222373593331
Fvcl/LemWk29I5LCjU1QedTjGlkvFF/kZXNkRJv+vNZ7qgq6pX8WB9yVkk6AoclDYAhCRfKTKuEpR23iafVuHpprPfNXcqBH8n01kq3U27xqIy2hS+D6BRBK67PQaekq31EB0aOcEb/DuNaXakS9+mtTMx6BKt+WoEY+NkzHK6c= 16126868736093163702771491576570380743773057522016869811780571865928979861357811080042796140032050364543242385458140594532945509386155523162799601656485075247603490060565663264947465987286983338572455184901756399862440455644131755848583379822279676555143231305246033911608913609591095831135803702269767527335
AKW8tvaB8YZ7J5W2lmquBniJzUhRfqFdPZPqvBoMzR4cRh1CMNdSFsYsnsaF3KolNzogdsxFpHAaEMG6zSvpNJAoi4nixCqb5SETXrSLASXvNjI9MvCoE2JCRq7kMbjPL7cem+mBPWZITGUI6KVlJPLxQngHYSFxukqlx7jznwJH 116384596458828069344020651216200368975621068920641012055593076864629080375946542748377736186556382088448816531408136815533164209947323588157210859294774679831647934533061547276394884474877353537242203645373945111105805934070657589374883764420038511061919092743520704686962593876316976299391579463759429567047
D5N2P4FrqDf7/2Z2BJsqah4SjUtolic/yNqdNzvNEogDKZKAJyGq4zhnHvkYXkEm2ueU/FDPJRqisszG0oULdU6c7p8acirEwsGLVh4RamnFRgmQSK1vbiYB3bR+P+iFX/bZ+TWjN2Y3YMa5UB//I6Zb5kEIjmTpjY2LEPI1e6s= 10937855369372570149476727082965401421189236366492771695094788039313362971972373068736123833330006002198346944149230147444718818161877123407713821100752433128205189334393732633989950841577315682292180735057952587083688644195300641998709155269462601925653013312848413290208844194513502358901613104779186502571
V/A1ktS0xrcwlI8xrYqvlLCFYrdVp8tEzZaZ9iNNpPH/pzVsA0WbnnUeHbdilkje+4OdoX9C4U2xaOuWOfvqLR0c7GeCkSffCqyf4ZsBmjy/BQL6rCpxMF0gIHXO5O8aJ1h17hy9LTuNzWm4zVh4pNFuHC9L6nAcf92udMiIQzk= 61752386563628388546439207444896778638632243226541303179646524864765343154194512297447627825411023405896612559648434895675553567405277169056807223959390559391191382555701580549902639604424290133917402316755076644943742815711432111554988540913643347167948778404861099845961151998728662878854088239266688156473
APoPgEKA0/r1FYmt/Iso6ChYK6dDU62Y+vH5h/LVE00biBYG1f7aL3GdllUTN+XQSHpqlDw8CD+9xojwZIMfgpgjOwLbbe7Aso460zLrg3R8aHBpbVt8iZUgjACwPYr5UyKbFzIAWaXcnYYQ+tCO9aDIuOz+/7eIF62C81zXFJVZ 175598490446477604563905754135475294999639698464908622773037381109011373179895295130424828038708319325919451724985361900259676699137657615076219968061941008972496322083528922054390781811699677037439989404270415929836486610353098273115864435328533577114470407444852521009919911888840405368858409835197558461785
cL54ymLJhRx3U20Y9aUTIsXy9Ags+XHy4qk3F7uJyO46eiXSL7VrrR9vTQXAbETbu1YiVWfslsPht810eUDUVaVir6yLnXkywn46Ci42FEvVoTEFjO22uYcCh8nqB8H589w/+lVSlNrcILugwfdfCvK1iZzVimOO6l3qzfXToOU= 79171550718114578361958369278761819285111811576818442980166457146638966315793211967882077899426611721874954146020093740153495693185472340728106727284441726113022873005252623222594060645383105757498856463065370975867121188445567981809371870213273555432308279508351518168027875538720367440153667708369625129189
QdQN4qW2QZq8/fmSaqlRiPSoDbhmF0oYjaY29HcKYGHdlOH0AMJb+RUIq1aszvVtjh7AYay2TNhaZMWQ6Qi3c42SNk3A1MVknT6zqiRCGjNFfxf/matbRLbTFQF832MAId708vrFLF/o2HpekMkc5hcHB6bkUUhEI1NLcMXwGck= 46226230186280253581676626651942823886592433541360244612432763620730826574920825070086312767146345247802570752482654580909236388357139147786783758670999083804670979821212991224400629053427330483809790366665043598754931511997925850227997764381723288657884346974360232490075739442406431704368767588177525348809
cxHvCK/dyVDvaqCCQyLeaiBGA36mV5el+1lc2eUTkHGUzX5gU0QQCEp+iSXNJhIOON8VFpKOFsziuV0Z+3cegWRw/VnxnjXcBh6IDKdupzOPB+Yl8MA1ti/GrQjLC6ikcNYNjQT0ZThL7KTqEvvZJH68WYmD0IuK26swjNGIGaI= 80804939616399473443737611589382762718815989847332356984276911837267997590368701684135326680567847542004499684038240485603420973682522792156533112356849436451918522884749244246467852622918805139990256619014116276456718693703261686778030658826952213058982142604346352178078750879100976710761147710018148637090
AIQ3OIZevkYoRGBmsFaXJobSfLeInuKKReVYNjP5VEPoMq0mXTltY6l09/rQ3d1JjsMD1PfA7emhxex+H9t3leBIfCi6Ux34GQEjXWpQc4awuiy9tbR077HaJyecvb8Qy1FTnOHoH5C043QJzrKYT/sFXjgB60piI8Y0R/hwxO4r 92845026347218330987427785323244729176754623818531419911990153715676845614711324345879159989637824921793015074978358052562420379797956750450245721653716740651389924718711940869162230097839047895842495414221110468446944827052871968998907462191349838598297775847512250220907563815783358238473966349820476321323
LoG6ib5lUh57rdmSkZSWzBoudytFohS4uoU/uly6OaQDOi34GeNVxu/yr6RszJyL9JWkGNgFaBIv/HirH5zA9VQAL/6kpL93a0/GQ/nuHkHy3GWZPF/2+yJ0PfazQ40fWhHZfRxBngWslbguFPjj1XaJ37YzpQAYb/+QcUai9ic= 32658152290878644668906121702816147999633088014476055330179597550087921141413344679134407016170035735846077181424615228657687216737432274043674411132745299610950657139041836412322040866250189120286839287690983293111362228893996267791120043532014262644480689231457941173330523718758287779526551822788227954215
AKu2jgOQCCfYZ3CLkXEH44aO4TtwMPeK/eq4FtNj9HZ9FxT0LLNJh0ZXPOaPJjgznvIw5C7/hNm7rUs1JeV8I8dj3nbS3EVERQz1gc/ckYB3H1bViWREOD5+TScDusi86YO/z4ar3dauKkg5kT1kKDuU/OP5kNMWvtJjHc4Vd3L3 120581042599355202025471829872601846477331097842315143148145881424071317426176264583672725691485724160094190478865850305422057632110749683552966861219554215519032344086824849470294473808177223497912069335635933312949412445851201918768630656712413082629164792850095444166888072453190903931430551124946191872759
ANLs7OsR7oBM5jSjVADrk+Mx9d0TeieTIkxwWiJ5STKNQmW2EzPOjgbfcLhbYEhzzDFJveXO2dzz6/c8V5oW2yqg7VMx88DzEbpQnQpk/rOQRw9jbI4fxXNJHkNZCeysEVvFfLJb4ecsGA0xJ3Aylny/jP10ahPv2z5K99edGZSU 148116916208650944522110872759145096907599612943009577897396622287067669897712748449324334650112672914917664881091633448764667172850435775162090891556266912697811031318228334453406561952979778127173704706529448647577013482442758465809198730066784986763500579667100246958959793527011919373534159474250508506260
AL+Er3n1qj+SBsZVtOMJYg4m0CN+DE6gRnC1F7nPvd2XnBe+QE0+LKfcpUDHVNxoydW4BDzNVwnUNbyjXZ+iuddPtO9hchVEI36UiuL0ydeldFpOZ9mtHJaAF6abd0MlHw4vXRf8CbOvXb5N4s76ggijlZBjRtU563sSmBcyq6Zt 134488725667189507159811764480908602790838430340670328479145818969651133017546803581865897303917708192047926432630297993507146075655594931523561067937580218599890162311074002344315818494246433967228889645359283635389151927472221799543158424012020308449895562192866672439712148770104592027035768027605661099629
AK/04XOBSjjPpuFXTDF82RNWnKqZz9mJQbS2B5bn0ehFnBa6j+B+MazX+AxXTL/d5+hPLT1uexcnSMl3DcGGwKipOXg7Dtuj3pfJXHTrCqXAUYrIXI+8vKVQO55yQPGfzIg9SVgetwW1sDk+a28ZhJ5a9OddqNoi5C+dLce7ZtNb 123560902006294001923570614486104726169564351074482936927091682096999779538353161007361361829586988452098646362280351148131540524964916445100589671458589346440250329883789099771417949746709217272531950438336245613419967556433467843237384555807236658182067742367748737224684334525934210197178231424396818830171
PzOEGHlihiveoWFAALY+LOfkRJfm0NUF/uR6cSU/tbpGAq4onNpr+iZIzEP5o3JBLOtDC595/NBPI0fzaXl0vQvgJs6KG8iKANjsLKQjIpZBkoKhdbG9MzTVQuAeuDW0w3sn2iMZ/v2dgAzRwfqmQYXJr3I2BbcwWraIJuZXw5A= 44381416070253681813077725822442106641846565789204187691647505370231831464947935035197059366680327425453811558282831465960889061956588244308214943856009686127871667376028831540813257349779756631357122923723235595360268572998278795110672666089470210929411514949652537714634611421849780859192966935514197771152
APnuduN01GS9dO2m2uCLs400AR2lX7elOnIPC5U6e17qbukxWYzNhilZlM4kdGXAIeYpzFdSIW/gxRMZe6TXq9krFWRaaPyT2QwRfGHYnazS9F1QNYmW1zXdt+qVp0JGxmh5PyDstbP8Z3x50/E8Mb0gLLPhNAvzY2Jnr9A8Q1Hy 175507868985304663005133968393406051624825489142498103948374797086106732382869120248515993626061853699363294022457032257026588816021007648668265488426495800459085474654859258116280251546902009156490112550154951965894022789029787886785376415437170872937201839249103828294508088966180386198213606090453461193202
QHEhL4iVzNdUsfG0izTEepwTOvxka8t/9MwuF1Ey6kxsI+ry4g4sJPgR2xMnbtOmvQn2NitAkfvA8JPCiL7a8+gmf+DVRDjKDfpfrtgAVmo+3rH+uJYTrKhAp8R7ggU2xIrvbIrgeUj7ieThPI3Rtap+IdkPCL853JC/+oKtytM= 45252649968839515171157821292772647085425694172492111870169593872127007254353374581972876464918186509502070064028725519394859148593053614163356612260257013360168930649423732336969778875205250872728821432415158634190866775855521719727700464116412886964736859295086745723651735554245035077902615220578218265299
APeaekK4mVhEShCfM0mkRebcg1Iq5CgrFIEGOoh1nHzgebr5A9Wrhm9yD1Vd3e+fFD9urDRB4y5MHPJHX1U2NFToC+H8nQkFXL8bfd/9Wl2c7y8m0Mxwi53pLIdzETLbbfeOOtJvuSYYT3n8+/PeMnJ46UD8OfqtnFuS0/bVpFLS 173873040145444066957050580959132871919216036714423404143335635770937773583761934638398867981658394368476005882852706046614562314432695052874974848076542261910660410561876043187368112065303981001507235893831108658530338308496461162623683138693880482650786841100027392293758260448606244283355655751440485602002
FqC/wgZDPTUoObPFSH5w4QR79zj/O+ZiHGTEnsBMwNZD3Gl/ClRDIsFMDDupNLgwgXsqCQbpwSOHOtAvUuAFwRpzt5B7lwIgtP5ism/AZRno5p+9WVSmUAM3glHsNtvYydz2MkXtnXzSMIR1ZVoLrdwMnckE4pbMzggqz+JZqxw= 15889870005716350976759704672045310928616256175405784574141006779373730686049218680335525720670897894546334915362899913262232170795516176419192840427996647372619000239408311568577050460995518058850793096827271653902583271225799114408537346367483775593212272587811309978019791973449354003275559762102731778844
AJXNbv2AMWadF5h99ZAUy5gLnVK/hMaakFo0ZedtPNRJobxPmwj+h52G+Czd0U48G0V0wpdeUJC9v/4BhjzhCvNhNsdAT1+vQXDuteYQ1aspsEKLQ6b+NknO88QSbRJw53+KeOY2xe7PKOa4V89XnFFBF7wljRnIYrM8vvcqVQDk 105194875227030598769888785590198577650278341586165110611689226597424766274486797264032300493674927704016605741286512271390703088626381669060095573361828932336327125438452066548897528158329044309005232090053420259033538936293519762277428283316506398965916381374819450858053512398634116052299066189424983605476
AIDRnUpBHepjBqYAlU4MG/8JxzX1mPxVNHpWvnEVgvqTQx/bisFPpXrYs3jAKIR/lzevYwhH0K/8Vvw4NK9iTMFqgSnU44AZztKsoxUXsEsl1UU56UscY5C7ciKU6vjjWI7nm/uHNOXdE82TQXkk2WX8ferNqZU5DaLFCb+zxb7w 90459642084794142567976043425270153270545560059973413835786695756473295513758287577749768786155290305189883600338986370836806413936196854410098516254596146039255388020628703824195128439558127783534033672712705194483515442668075394018677699876614329419492391568463215822656901183478205197671375262145069825776
AIdvVNzJqWPgAShvi3GhbhMQft+SLigKGrhoqas2Saz/bA9u9Td6fAxa2LjrAqshW6cnm2aalc3Yv6RW/Y8vg7Ho31NSaRjT4zMUenykcC0/Y88UNxREi85wdnHwGytms6Lq49H8/7EFGJIyL1PLRWPmZn6XFkegscI/HUq/hiKm 95105613103051650721863964216778532448106311156426028879315612217763044797186635476805213120469258258125661666950525364331551671653846368977016286153840829836509696804585927581668281228810410814602664419962214359687545209312836366693384158782798559255789953908588601637765910472073600954502095647132310971046
DdchOPjXrI6lpV84IdKCisPmdqZan8AARXRLADEhixsfXCYuO+WhNatI/fM1vgv+/TxwwIQjIfG1vOZcB36JUfjHYdItYQ70vUXaVFdpqvoBGyfOTU50Ds/11iGPCF8mWiQwR30/XAXytqDZtaVJVWsgHD3RigBSnSHhnvZAWYg= 9719024770319024562623340689338530708271347986326272393419504304391837979619189392867902307307106771234732135400958362219711925045600118964223238147375808749507928768896918369395426933218443166133187066167663170936604731896932630589251946733237697936733924510107175304126061649311812536190882160340308613512
I+Z6rdTOt26/v3dtUP1plITb15fjb6aMDvqFS3AD1+nxBqnnk7ISGE9j6dv762EIWQpMzcCG5NCCq35KOHEwRXP28zup6olOMt3CBFgYVcBE2pWOpGiO19G/iFweYZXZPY5HgIkex7HBbb7l6HhomPc2sLL/IRhh2oogyHx2JMM= 25210054612455888156900839678249806510561198051210010474517915819801056434402727631042894881559517808906460418029149538469607239850657781476308872923928122553395468026744382526167194202058040459679991391557937527079948356545086684521068912222036707113005006607012596093923970784177288565193670152033981048003
ALbBoyelCs4UkfnPjMT3S67ujhBHBEE0uxLx6kSGZq2IOMU/QdWYPFElRgYC/y++334FSEycjS6NAJJo2ITpZCO5AjNJ93J3WYgbDLiwu1VzKHX6ItfFNEk45km+QTi07+pDKcKNd1k0mxqpLd/PuZd5hRpPDDoKBb6i+mrCb2yF 128335905497646745013379107761994003743181143126608677203818152878840562628631384684712779135591095534911406031545494164782375276574093777950840330452805743803067864740000758175436633463846967335728314347497013853264454015790847388463800323796888198433722196292529074568758149650782323407298620158495364705413
ANwlxEkeqmqYTxw1ZwMi1v2wo4ntPaEYZYoTLTJQfa+kuIksnHW9va243HAiOixd+rviVdm1dEwzESBbX0wiJNtRBpP+bnRxy4xOBjNoOB0c/tfka5JVwu5eeskyHx4V3inLviUaj86Yck42n5NaJFMfBvhzVftZ/YF9WBITI8g6 154592850289860621115358362871905683265658659789986179554827712019629689749439795961607030363152337159590319622241556795951071651584979664762468782303706550885785493534656062553770262954861884613383561063525714923031691298088562054236178003658891902606245782350998076658704876516153027797371814038658244397114

View file

@ -0,0 +1,120 @@
========================================
Trust root parsing checking
========================================
----------------------------------------
14: Does not parse
----------------------------------------
baz.org
*.foo.com
http://*.schtuff.*/
ftp://foo.com
ftp://*.foo.com
http://*.foo.com:80:90/
foo.*.com
http://foo.*.com
http://www.*
http://*foo.com/
5
----------------------------------------
12: Insane
----------------------------------------
http://*/
https://*/
http://*.com
http://*.com/
https://*.com/
http://*.com.au/
http://*.co.uk/
http://*.foo.notatld/
https://*.foo.notatld/
http://*.museum/
https://*.museum/
http://kink.fm/should/be/sane
----------------------------------------
14: Sane
----------------------------------------
http://*.schtuff.com/
http://*.foo.schtuff.com/
http://*.schtuff.com
http://www.schtuff.com/
http://www.schutff.com
http://*.this.that.schtuff.com/
http://*.foo.com/path
http://*.foo.com/path?action=foo2
http://x.foo.com/path?action=foo2
http://x.foo.com/path?action=%3D
http://localhost:8081/
http://localhost:8082/?action=openid
https://foo.com/
http://goathack.livejournal.org:8020/openid/login.bml
========================================
return_to matching
========================================
----------------------------------------
30: matches
----------------------------------------
http://*/ http://cnn.com/
http://*/ http://livejournal.com/
http://*/ http://met.museum/
http://*:8081/ http://met.museum:8081/
http://localhost:8081/x?action=openid http://localhost:8081/x?action=openid
http://*.foo.com http://b.foo.com
http://*.foo.com http://b.foo.com/
http://*.foo.com/ http://b.foo.com
http://b.foo.com http://b.foo.com
http://b.foo.com http://b.foo.com/
http://b.foo.com/ http://b.foo.com
http://*.b.foo.com http://b.foo.com
http://*.b.foo.com http://b.foo.com/
http://*.b.foo.com/ http://b.foo.com
http://*.b.foo.com http://x.b.foo.com
http://*.b.foo.com http://w.x.b.foo.com
http://*.bar.co.uk http://www.bar.co.uk
http://*.uoregon.edu http://x.cs.uoregon.edu
http://x.com/abc http://x.com/abc
http://x.com/abc http://x.com/abc/def
http://*.x.com http://x.com/gallery
http://*.x.com http://foo.x.com/gallery
http://foo.x.com http://foo.x.com/gallery/xxx
http://*.x.com/gallery http://foo.x.com/gallery
http://localhost:8082/?action=openid http://localhost:8082/?action=openid
http://goathack.livejournal.org:8020/ http://goathack.livejournal.org:8020/openid/login.bml
https://foo.com https://foo.com
http://Foo.com http://foo.com
http://foo.com http://Foo.com
http://foo.com/?x=y http://foo.com/?x=y&a=b
----------------------------------------
23: does not match
----------------------------------------
http://*/ ftp://foo.com/
http://*/ xxx
http://*.x.com/abc http://foo.x.com
http://*.x.com/abc http://*.x.com
http://*.com/ http://*.com/
http://x.com/abc http://x.com/
http://x.com/abc http://x.com/a
http://x.com/abc http://x.com/ab
http://x.com/abc http://x.com/abcd
http://*.cs.uoregon.edu http://x.uoregon.edu
http://*.foo.com http://bar.com
http://*.foo.com http://www.bar.com
http://*.bar.co.uk http://xxx.co.uk
https://foo.com http://foo.com
http://foo.com https://foo.com
http://foo.com:80 http://foo.com
http://foo.com http://foo.com:80
http://foo.com:81 http://foo.com:80
http://*:80 http://foo.com:81
http://foo.com/?a=b http://foo.com/?x=y
http://foo.com/?a=b http://foo.com/?x=y&a=b
http://foo.com/?a=b http://foo.com/
http://*.oo.com/ http://foo.com/

View file

@ -0,0 +1,79 @@
Already normal form
http://example.com/
http://example.com/
Add a trailing slash
http://example.com
http://example.com/
Remove an empty port segment
http://example.com:/
http://example.com/
Remove a default port segment
http://example.com:80/
http://example.com/
Capitalization in host names
http://wWw.exaMPLE.COm/
http://www.example.com/
Capitalization in scheme names
htTP://example.com/
http://example.com/
Capitalization in percent-escaped reserved characters
http://example.com/foo%2cbar
http://example.com/foo%2Cbar
Unescape percent-encoded unreserved characters
http://example.com/foo%2Dbar%2dbaz
http://example.com/foo-bar-baz
remove_dot_segments example 1
http://example.com/a/b/c/./../../g
http://example.com/a/g
remove_dot_segments example 2
http://example.com/mid/content=5/../6
http://example.com/mid/6
remove_dot_segments: single-dot
http://example.com/a/./b
http://example.com/a/b
remove_dot_segments: double-dot
http://example.com/a/../b
http://example.com/b
remove_dot_segments: leading double-dot
http://example.com/../b
http://example.com/b
remove_dot_segments: trailing single-dot
http://example.com/a/.
http://example.com/a/
remove_dot_segments: trailing double-dot
http://example.com/a/..
http://example.com/
remove_dot_segments: trailing single-dot-slash
http://example.com/a/./
http://example.com/a/
remove_dot_segments: trailing double-dot-slash
http://example.com/a/../
http://example.com/
Test of all kinds of syntax-based normalization
hTTPS://a/./b/../b/%63/%7bfoo%7d
https://a/b/c/%7Bfoo%7D
Unsupported scheme
ftp://example.com/
fail
Non-absolute URI
http:/foo
fail

View file

@ -0,0 +1,171 @@
<?php
require_once "PHPUnit.php";
require_once "Tests/Services/Yadis/DiscoverData.php";
require_once "Services/Yadis/Yadis.php";
require_once "Services/Yadis/HTTPFetcher.php";
$__status_header_re = '/Status: (\d+) .*?$/m';
function mkResponse($data)
{
global $__status_header_re;
$matches = array();
$status_mo = preg_match($__status_header_re, $data, $matches);
list($headers_str, $body) = explode("\n\n", $data, 2);
$headers = array();
foreach (explode("\n", $headers_str) as $line) {
list($k, $v) = explode(":", $line, 2);
$k = strtolower(trim($k));
$v = trim($v);
$headers[$k] = $v;
}
$status = intval($matches[1]);
$r = new Services_Yadis_HTTPResponse(null, $status, $headers, $body);
return $r;
}
class TestFetcher {
function TestFetcher($base_url)
{
$this->base_url = $base_url;
}
function get($url, $headers = null)
{
$current_url = $url;
while (true) {
$parsed = parse_url($current_url);
$path = substr($parsed['path'], 1);
$data = generateSample($path, $this->base_url);
if ($data === null) {
return new Services_Yadis_HTTPResponse($current_url,
404,
array(),
'');
}
$response = mkResponse($data);
if (in_array($response->status, array(301, 302, 303, 307))) {
$current_url = $response->headers['location'];
} else {
$response->final_url = $current_url;
return $response;
}
}
}
}
class MockFetcher {
function MockFetcher() {
$this->count = 0;
}
function get($uri, $headers = null, $body = null)
{
$this->count++;
if ($this->count == 1) {
$headers = array(strtolower('X-XRDS-Location') . ': http://unittest/404');
return new Services_Yadis_HTTPResponse($uri, 200, $headers, '');
} else {
return new Services_Yadis_HTTPResponse($uri, 404);
}
}
}
class TestSecondGet extends PHPUnit_TestCase {
function test_404()
{
$uri = "http://something.unittest/";
$response = null;
$fetcher = new MockFetcher();
$this->assertTrue(
Services_Yadis_Yadis::discover($uri, $response, $fetcher) === null);
}
}
class _TestCase extends PHPUnit_TestCase {
var $base_url = 'http://invalid.unittest/';
function _TestCase($input_name, $id_name, $result_name, $success)
{
$this->input_name = $input_name;
$this->id_name = $id_name;
$this->result_name = $result_name;
$this->success = $success;
$this->fetcher = new TestFetcher($this->base_url);
parent::PHPUnit_TestCase();
}
function setUp()
{
list($this->input_url, $this->expected) = generateResult($this->base_url,
$this->input_name,
$this->id_name,
$this->result_name,
$this->success);
}
function runTest()
{
if ($this->expected === null) {
$response = array();
$this->assertTrue(
Services_Yadis_Yadis::discover($this->input_url, $response,
$this->fetcher) === null);
} else {
$response = array();
$result = Services_Yadis_Yadis::discover($this->input_url,
$response,
$this->fetcher);
if ($result === null) {
$this->fail("Discovery result was null");
return;
}
$this->assertEquals($this->input_url, $result->request_uri);
$msg = 'Identity URL mismatch: actual = %s, expected = %s';
$msg = sprintf($msg, $result->uri, $this->expected->uri);
$this->assertEquals($this->expected->uri, $result->uri, $msg);
$msg = 'Content mismatch: actual = %s, expected = %s';
$msg = sprintf($msg, $result->body, $this->expected->body);
$this->assertEquals($this->expected->body, $result->body, $msg);
$this->assertEquals($this->expected->xrds_uri, $result->xrds_uri);
$this->assertEquals($this->expected->content_type, $result->content_type);
}
}
function getName()
{
if ($this->input_url) {
return $this->input_url;
} else {
return $this->input_name;
}
}
}
class Tests_Services_Yadis_Discover extends PHPUnit_TestSuite {
function getName()
{
return "Tests_Services_Yadis_Discover";
}
function Tests_Services_Yadis_Discover()
{
global $testlist;
foreach ($testlist as $test) {
list($success, $input_name, $id_name, $result_name) = $test;
$this->addTest(new _TestCase($input_name, $id_name, $result_name, $success));
}
}
}
?>

View file

@ -0,0 +1,148 @@
<?php
require_once "Services/Yadis/Yadis.php";
require_once "Tests/Services/Yadis/TestUtil.php";
$testlist = array(
// success, input_name, id_name, result_name
array(true, "equiv", "equiv", "xrds"),
array(true, "header", "header", "xrds"),
array(true, "lowercase_header", "lowercase_header", "xrds"),
array(true, "xrds", "xrds", "xrds"),
array(true, "xrds_ctparam", "xrds_ctparam", "xrds_ctparam"),
array(true, "xrds_ctcase", "xrds_ctcase", "xrds_ctcase"),
array(false, "xrds_html", "xrds_html", "xrds_html"),
array(true, "redir_equiv", "equiv", "xrds"),
array(true, "redir_header", "header", "xrds"),
array(true, "redir_xrds", "xrds", "xrds"),
array(false, "redir_xrds_html", "xrds_html", "xrds_html"),
array(true, "redir_redir_equiv", "equiv", "xrds"),
array(false, "404_server_response", null, null),
array(false, "404_with_header", null, null),
array(false, "404_with_meta", null, null),
array(false, "201_server_response", null, null),
array(false, "500_server_response", null, null)
);
function getExampleXRDS()
{
return Tests_Services_Yadis_readdata('example-xrds.xml');
}
$example_xrds = getExampleXRDS();
$default_test_file = 'test1-discover.txt';
$discover_tests = array();
function readTests($filename)
{
$data = Tests_Services_Yadis_readdata($filename);
if ($data === null) {
return null;
}
$tests = array();
foreach (preg_split("/\f\n/", $data) as $case) {
list($name, $content) = explode("\n", $case, 2);
$tests[$name] = $content;
}
return $tests;
}
function getData($filename, $name)
{
global $discover_tests;
if (!array_key_exists($filename, $discover_tests)) {
$data = readTests($filename);
if ($data === null) {
return null;
}
$discover_tests[$filename] = $data;
}
$file_tests = $discover_tests[$filename];
if (array_key_exists($name, $file_tests)) {
return $file_tests[$name];
} else {
return null;
}
}
function fillTemplate($test_name, $template, $base_url, $example_xrds)
{
$mapping = array(
array('URL_BASE/', $base_url),
array('<XRDS Content>', $example_xrds),
array('YADIS_HEADER', 'X-XRDS-Location'),
array('NAME', $test_name));
foreach ($mapping as $pair) {
list($k, $v) = $pair;
$template = str_replace($k, $v, $template);
}
return $template;
}
function generateSample($test_name, $base_url,
$_example_xrds = null, $filename = null)
{
global $example_xrds, $default_test_file;
if ($_example_xrds === null) {
$_example_xrds = $example_xrds;
}
if ($filename === null) {
$filename = $default_test_file;
}
$template = getData($filename, $test_name);
if ($template === null) {
return null;
}
return fillTemplate($test_name, $template, $base_url, $_example_xrds);
}
function generateResult($base_url, $input_name, $id_name, $result_name, $success)
{
$input_url = $base_url . $input_name; // urlparse.urljoin(base_url, input_name)
// If the name is null then we expect the protocol to fail, which
// we represent by null
if ($id_name === null) {
// assert result_name is null
return array($input_url, null); // DiscoveryFailure
}
$result = generateSample($result_name, $base_url);
list($headers, $content) = explode("\n\n", $result, 2);
$header_lines = explode("\n", $headers);
$ctype = null;
foreach ($header_lines as $header_line) {
if (strpos($header_line, 'Content-Type:') === 0) {
list($temp, $ctype) = explode(":", $header_line, 2);
$ctype = trim($ctype);
break;
}
}
$id_url = $base_url . $id_name;
$result = new Services_Yadis_Yadis();
$result->uri = $id_url;
if ($success) {
$result->xrds_uri = $base_url . $result_name;
} else {
$result->xrds_uri = null;
}
$result->content_type = $ctype;
$result->body = $content;
return array($input_url, $result);
}
?>

View file

@ -0,0 +1,88 @@
<?php
/**
* Tests for the Yadis HTML parsing functionality.
*
* 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_once 'Tests/Services/Yadis/TestUtil.php';
require_once 'Services/Yadis/ParseHTML.php';
require_once 'PHPUnit.php';
class Tests_Services_Yadis_ParseTest extends PHPUnit_TestCase {
function Tests_Services_Yadis_ParseTest($case)
{
list($result, $comment, $html) = $case;
$this->result = $result;
$this->comment = $comment;
$this->html_string = $html;
$this->parser = new Services_Yadis_ParseHTML();
}
function getName()
{
return $this->comment;
}
function runTest()
{
$value = $this->parser->getHTTPEquiv($this->html_string);
if ($this->result == "EOF") {
$this->assertTrue($value === null);
} else if ($this->result == "None") {
$this->assertTrue($value === null);
} else {
$this->assertEquals($this->result, $value);
}
}
}
class Tests_Services_Yadis_ParseHTML extends PHPUnit_TestSuite {
function getName()
{
return "Tests_Services_Yadis_Parse";
}
function parseTests($s)
{
$tests = array();
$cases = preg_split("/\f\n/", $s);
foreach ($cases as $case) {
// Split the case text on newline, and keep the first two
// lines and re-join the rest (those are the HTML).
$parts = explode("\n", $case);
$result = $parts[0];
$html_comment = $parts[1];
$html_string = implode("\n", array_slice($parts, 2));
$tests[] = array($result, $html_comment, $html_string);
}
return $tests;
}
function Tests_Services_Yadis_ParseHTML()
{
$test_data = Tests_Services_Yadis_readdata('test1-parsehtml.txt');
$test_cases = $this->parseTests($test_data);
foreach ($test_cases as $case) {
$this->addTest(new Tests_Services_Yadis_ParseTest($case));
}
}
}
?>

View file

@ -0,0 +1,28 @@
<?php
/**
* Utilites for test functions
*/
function Tests_Services_Yadis_datafile($name, $reader)
{
$path = dirname(realpath(__FILE__));
$sep = DIRECTORY_SEPARATOR;
$filename = $path . $sep . 'data' . $sep . $name;
$data = $reader($filename);
if ($data === false) {
$msg = "Failed to open data file: $name";
trigger_error($msg, E_USER_ERROR);
}
return $data;
}
function Tests_Services_Yadis_readdata($name)
{
return Tests_Services_Yadis_datafile($name, 'file_get_contents');
}
function Tests_Services_Yadis_readlines($name)
{
return Tests_Services_Yadis_datafile($name, 'file');
}

View file

@ -0,0 +1,241 @@
<?php
/**
* XRDS-parsing tests for the Yadis library.
*/
require_once 'PHPUnit.php';
require_once 'Services/Yadis/XRDS.php';
require_once 'Services/Yadis/XRIRes.php';
require_once 'Services/Yadis/XRI.php';
require_once 'Tests/Services/Yadis/TestUtil.php';
class Tests_Services_Yadis_XRDS extends PHPUnit_TestCase {
function test_good()
{
$files = array(
'brian.xrds' => 1,
'pip.xrds' => 2
);
foreach ($files as $filename => $service_count) {
$xml = Tests_Services_Yadis_readdata($filename);
$xrds = Services_Yadis_XRDS::parseXRDS($xml);
$this->assertTrue($xrds !== null);
if ($xrds) {
$this->assertEquals(count($xrds->services()), $service_count);
} else {
$this->fail("Could not test XRDS service list because the ".
"XRDS object is null");
}
}
}
function test_good_multi()
{
$xml = Tests_Services_Yadis_readdata("brian.multi.xrds");
$xrds = Services_Yadis_XRDS::parseXRDS($xml);
$this->assertTrue($xrds !== null);
$this->assertEquals(count($xrds->services()), 1);
$s = $xrds->services();
$s = $s[0];
$types = $s->getTypes();
$this->assertTrue(count($types) == 1);
$this->assertEquals('http://openid.net/signon/1.0',
$types[0]);
}
function test_good_uri_multi()
{
$xml = Tests_Services_Yadis_readdata("brian.multi_uri.xrds");
$xrds = Services_Yadis_XRDS::parseXRDS($xml);
$this->assertTrue($xrds !== null);
$this->assertEquals(1, count($xrds->services()));
}
function test_uri_sorting()
{
$xml = Tests_Services_Yadis_readdata("uri_priority.xrds");
$xrds = Services_Yadis_XRDS::parseXRDS($xml);
$services = $xrds->services();
$uris = $services[0]->getURIs();
$expected_uris = array(
"http://zero.priority/",
"http://one.priority/",
"http://no.priority/"
);
$this->assertEquals($uris, $expected_uris);
}
function test_bad()
{
$this->assertTrue(Services_Yadis_XRDS::parseXRDS(null) === null);
$this->assertTrue(Services_Yadis_XRDS::parseXRDS(5) === null);
$this->assertTrue(Services_Yadis_XRDS::parseXRDS('') === null);
$this->assertTrue(Services_Yadis_XRDS::parseXRDS('<html></html>') ===
null);
$this->assertTrue(Services_Yadis_XRDS::parseXRDS("\x00") === null);
}
function test_getCanonicalID()
{
$canonicalIDtests = array(
array("@ootao*test1", "delegated-20060809.xrds",
"@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
array("@ootao*test1", "delegated-20060809-r1.xrds",
"@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
array("@ootao*test1", "delegated-20060809-r2.xrds",
"@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
array("@ootao*test1", "sometimesprefix.xrds",
"@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
array("@ootao*test1", "prefixsometimes.xrds",
"@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
array("=keturn*isDrummond", "spoof1.xrds", null),
array("=keturn*isDrummond", "spoof2.xrds", null),
array("@keturn*is*drummond", "spoof3.xrds", null),
// Don't let IRI authorities be canonical for the GCS.
array("phreak.example.com", "delegated-20060809-r2.xrds", null)
// TODO: Refs
// ("@ootao*test.ref", "ref.xrds", "@!BAE.A650.823B.2475")
);
foreach ($canonicalIDtests as $tupl) {
list($iname, $filename, $expectedID) = $tupl;
$xml = Tests_Services_Yadis_readdata($filename);
$xrds = Services_Yadis_XRDS::parseXRDS($xml);
$this->_getCanonicalID($iname, $xrds, $expectedID);
}
}
function _getCanonicalID($iname, $xrds, $expectedID)
{
if ($expectedID === null) {
$result = Services_Yadis_getCanonicalID($iname, $xrds);
if ($result !== false) {
$this->fail($iname.' (got '.$result.')');
}
} else {
$cid = Services_Yadis_getCanonicalID($iname, $xrds);
$this->assertEquals(Services_Yadis_XRI($expectedID), $cid);
}
}
function test_services_filters()
{
// First, just be sure that service objects do the right
// thing.
$xml = Tests_Services_Yadis_readdata("brian_priority.xrds");
$xrds = Services_Yadis_XRDS::parseXRDS($xml,
array('openid' =>
'http://openid.net/xmlns/1.0'));
$this->assertTrue($xrds !== null);
// Get list of service objects.
$services = $xrds->services();
$this->assertEquals(count($services), 2, "first service count");
// Query the two service objecs.
$s1 = $services[0];
$this->assertEquals($s1->getPriority(), 1, "first priority check");
$types = $s1->getTypes();
$this->assertEquals(count($types), 1, "first type check");
$s2 = $services[1];
$this->assertEquals($s2->getPriority(), 2, "second priority check");
$types = $s2->getTypes();
$this->assertEquals(count($types), 1, "second type check");
function _DelegateFilter(&$service)
{
if ($service->getElements('openid:Delegate')) {
return true;
}
return false;
}
// Make sure that a filter which matches both DOES match both.
$this->assertEquals(count(
$xrds->services(array("_DelegateFilter"))), 2,
"_DelegateFilter check");
// This filter should match all services in the document.
function _HasTypeAndURI(&$service)
{
if ($service->getTypes() &&
$service->getURIs()) {
return true;
}
return false;
}
// This filter should only match one.
function _URIMatchesSchtuff(&$service)
{
$uris = $service->getURIs();
foreach ($uris as $uri) {
if (preg_match("|schtuff|", $uri)) {
return true;
}
}
return false;
}
// This filter should only match one.
function _URIMatchesMyOpenID(&$service)
{
$uris = $service->getURIs();
foreach ($uris as $uri) {
if (preg_match("|myopenid|", $uri)) {
return true;
}
}
return false;
}
// Make sure a pair of filters in ALL mode only match one service.
$this->assertEquals(count(
$xrds->services(array("_HasTypeAndURI",
"_URIMatchesSchtuff"),
SERVICES_YADIS_MATCH_ALL)), 1,
"_HasTypeAndURI / _URIMatchesSchtuff check");
// Make sure a pair of filters in ALL mode only match one service.
$this->assertEquals(count(
$xrds->services(array("_HasTypeAndURI",
"_URIMatchesMyOpenID"),
SERVICES_YADIS_MATCH_ALL)), 1,
"_HasTypeAndURI / _URIMatchesMyOpenID check");
// Make sure a pair of filters in ANY mode matches both services.
$this->assertEquals(count(
$xrds->services(array("_URIMatchesMyOpenID",
"_URIMatchesSchtuff"))), 2,
"_URIMatchesMyOpenID / _URIMatchesSchtuff check");
// Make sure the order of the services returned (when using
// filters) is correct.
$s = $xrds->services(array("_URIMatchesMyOpenID",
"_URIMatchesSchtuff"));
$this->assertTrue($s[0]->getPriority() === 1, "s[0] priority check");
$this->assertTrue($s[1]->getPriority() === 2, "s[1] priority check");
// Make sure a bad filter mode gets us a null service list.
$this->assertTrue($xrds->services(array("_URIMatchesMyOpenID",
"_URIMatchesSchtuff"),
"bogus") === null,
"bogus filter check");
}
}
?>

View file

@ -0,0 +1,141 @@
<?php
/**
* XRI resolution / handling tests.
*
* @package Yadis
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once "PHPUnit.php";
require_once "Services/Yadis/XRIRes.php";
require_once "Services/Yadis/XRI.php";
require_once "Services/Yadis/Yadis.php";
class Tests_Services_Yadis_XriDiscoveryTestCase extends PHPUnit_TestCase {
function runTest()
{
$this->assertEquals(
Services_Yadis_identifierScheme('=john.smith'), 'XRI');
$this->assertEquals(
Services_Yadis_identifierScheme('@smiths/john'), 'XRI');
$this->assertEquals(
Services_Yadis_identifierScheme('smoker.myopenid.com'), 'URI');
$this->assertEquals(
Services_Yadis_identifierScheme('xri://=john'), 'XRI');
}
}
class Tests_Services_Yadis_XriEscapingTestCase extends PHPUnit_TestCase {
function test_escaping_percents()
{
$this->assertEquals(Services_Yadis_escapeForIRI('@example/abc%2Fd/ef'),
'@example/abc%252Fd/ef');
}
function runTest()
{
// no escapes
$this->assertEquals('@example/foo/(@bar)',
Services_Yadis_escapeForIRI('@example/foo/(@bar)'));
// escape slashes
$this->assertEquals('@example/foo/(@bar%2Fbaz)',
Services_Yadis_escapeForIRI('@example/foo/(@bar/baz)'));
$this->assertEquals('@example/foo/(@bar%2Fbaz)/(+a%2Fb)',
Services_Yadis_escapeForIRI('@example/foo/(@bar/baz)/(+a/b)'));
// escape query ? and fragment
$this->assertEquals('@example/foo/(@baz%3Fp=q%23r)?i=j#k',
Services_Yadis_escapeForIRI('@example/foo/(@baz?p=q#r)?i=j#k'));
}
}
class Tests_Services_Yadis_ProxyQueryTestCase extends PHPUnit_TestCase {
function setUp()
{
$this->proxy_url = 'http://xri.example.com/';
$this->fetcher = Services_Yadis_Yadis::getHTTPFetcher();
$this->proxy = new Services_Yadis_ProxyResolver($fetcher,
$this->proxy_url);
$this->servicetype = 'xri://+i-service*(+forwarding)*($v*1.0)';
$this->servicetype_enc = 'xri%3A%2F%2F%2Bi-service%2A%28%2Bforwarding%29%2A%28%24v%2A1.0%29';
}
function runTest()
{
$st = $this->servicetype;
$ste = $this->servicetype_enc;
$args_esc = "_xrd_r=application%2Fxrds%2Bxml&_xrd_t=" . $ste;
$h = $this->proxy_url;
$this->assertEquals($h . '=foo?' . $args_esc,
$this->proxy->queryURL('=foo', $st));
$this->assertEquals($h . '=foo/bar?baz&' . $args_esc,
$this->proxy->queryURL('=foo/bar?baz', $st));
$this->assertEquals($h . '=foo/bar?baz=quux&' . $args_esc,
$this->proxy->queryURL('=foo/bar?baz=quux', $st));
$this->assertEquals($h . '=foo/bar?mi=fa&so=la&' . $args_esc,
$this->proxy->queryURL('=foo/bar?mi=fa&so=la', $st));
$args_esc = "_xrd_r=application%2Fxrds%2Bxml&_xrd_t=" . $ste;
$h = $this->proxy_url;
$this->assertEquals($h . '=foo/bar??' . $args_esc,
$this->proxy->queryURL('=foo/bar?', $st));
$this->assertEquals($h . '=foo/bar????' . $args_esc,
$this->proxy->queryURL('=foo/bar???', $st));
}
}
class Tests_Services_Yadis_TestGetRootAuthority extends PHPUnit_TestCase {
function runTest()
{
$xris = array(
array("@foo", "@"),
array("@foo*bar", "@"),
array("@*foo*bar", "@"),
array("@foo/bar", "@"),
array("!!990!991", "!"),
array("!1001!02", "!"),
array("=foo*bar", "="),
array("(example.com)/foo", "(example.com)"),
array("(example.com)*bar/foo", "(example.com)"),
array("baz.example.com/foo", "baz.example.com"),
array("baz.example.com:8080/foo", "baz.example.com:8080")
// Looking at the ABNF in XRI Syntax 2.0, I don't think you can
// have example.com*bar. You can do (example.com)*bar, but that
// would mean something else.
// ("example.com*bar/(=baz)", "example.com*bar"),
// ("baz.example.com!01/foo", "baz.example.com!01"),
);
foreach ($xris as $tupl) {
list($thexri, $expected_root) = $tupl;
$this->assertEquals(Services_Yadis_XRI($expected_root),
Services_Yadis_rootAuthority($thexri),
'rootAuthority test ('.$thexri.')');
}
}
}
class Tests_Services_Yadis_XRI extends PHPUnit_TestSuite {
function getName()
{
return "Tests_Services_Yadis_XRI";
}
function Tests_Services_Yadis_XRI()
{
$this->addTest(new Tests_Services_Yadis_ProxyQueryTestCase());
$this->addTest(new Tests_Services_Yadis_XriEscapingTestCase());
$this->addTest(new Tests_Services_Yadis_XriDiscoveryTestCase());
$this->addTest(new Tests_Services_Yadis_TestGetRootAuthority());
}
}
?>

View file

@ -0,0 +1,91 @@
<?php
/**
* Tests for the core of the PHP Yadis library.
*/
require_once 'PHPUnit.php';
require_once 'Services/Yadis/Yadis.php';
require_once 'Tests/Services/Yadis/TestUtil.php';
class Tests_Services_Yadis_DiscoveryTest extends PHPUnit_TestCase {
function Tests_Services_Yadis_DiscoveryTest($input_url, $redir_uri,
$xrds_uri, $num)
{
$this->input_url = $input_url;
$this->redir_uri = $redir_uri;
$this->xrds_uri = $xrds_uri;
$this->num = $num;
}
function getName()
{
return "Yadis discovery test ".$this->num;
}
function runTest()
{
$http_response = null;
$fetcher = Services_Yadis_Yadis::getHTTPFetcher();
$y = Services_Yadis_Yadis::discover(
$this->input_url, $http_response, $fetcher);
$this->assertTrue($y !== null);
// Compare parts of returned Yadis object to expected URLs.
$this->assertEquals($this->redir_uri, $y->uri);
if ($this->xrds_uri) {
$this->assertEquals($this->xrds_uri, $y->xrds_uri);
// Compare contents of actual HTTP GET with that of Yadis
// response.
$f = Services_Yadis_Yadis::getHTTPFetcher();
$response = $f->get($this->xrds_uri);
$this->assertEquals($response->body, $y->body);
} else {
$this->assertTrue($y->xrds_uri === null);
}
}
}
class Tests_Services_Yadis_Yadis extends PHPUnit_TestSuite {
function getName()
{
return "Tests_Services_Yadis_Yadis";
}
function parseTests($data)
{
$cases = explode("\n", $data);
$tests = array();
foreach ($cases as $line) {
if ($line && ($line[0] != "#")) {
$tests[] = explode("\t", $line, 3);
}
}
return $tests;
}
function Tests_Services_Yadis_Yadis()
{
$test_data = Tests_Services_Yadis_readdata('manifest.txt');
$test_cases = $this->parseTests($test_data);
$i = 0;
foreach ($test_cases as $case) {
$i++;
list($input, $redir, $xrds) = $case;
$this->addTest(new Tests_Services_Yadis_DiscoveryTest($input,
$redir,
$xrds, $i));
}
}
}
?>

View file

@ -0,0 +1,12 @@
delegated-20060809.xrds - results from proxy.xri.net, determined by
Drummond and Kevin to be incorrect.
delegated-20060809-r1.xrds - Drummond's 1st correction
delegated-20060809-r2.xrds - Drummond's 2nd correction
spoofs: keturn's (=!E4)'s attempts to log in with Drummond's i-number (=!D2)
spoof1.xrds
spoof2.xrds
spoof3.xrds - attempt to steal @!C0!D2 by having "at least one" CanonicalID
match the $res service ProviderID.
ref.xrds - resolving @ootao*test.ref, which refers to a neustar XRI.

View file

@ -0,0 +1,118 @@
# Accept: [Accept: header value from RFC2616,
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html]
# Available: [whitespace-separated content types]
# Expected: [Accept-header like list, containing the available content
# types with their q-values]
Accept: */*
Available: text/plain
Expected: text/plain; q=1.0
Accept: */*
Available: text/plain, text/html
Expected: text/plain; q=1.0, text/html; q=1.0
# The order matters
Accept: */*
Available: text/html, text/plain
Expected: text/html; q=1.0, text/plain; q=1.0
Accept: text/*, */*; q=0.9
Available: text/plain, image/jpeg
Expected: text/plain; q=1.0, image/jpeg; q=0.9
Accept: text/*, */*; q=0.9
Available: image/jpeg, text/plain
Expected: text/plain; q=1.0, image/jpeg; q=0.9
# wildcard subtypes still reject differing main types
Accept: text/*
Available: image/jpeg, text/plain
Expected: text/plain; q=1.0
Accept: text/html
Available: text/html
Expected: text/html; q=1.0
Accept: text/html, text/*
Available: text/html
Expected: text/html; q=1.0
Accept: text/html, text/*
Available: text/plain, text/html
Expected: text/plain; q=1.0, text/html; q=1.0
Accept: text/html, text/*; q=0.9
Available: text/plain, text/html
Expected: text/html; q=1.0, text/plain; q=0.9
# If a more specific type has a higher q-value, then the higher value wins
Accept: text/*; q=0.9, text/html
Available: text/plain, text/html
Expected: text/html; q=1.0, text/plain; q=0.9
Accept: */*, text/*; q=0.9, text/html; q=0.1
Available: text/plain, text/html, image/monkeys
Expected: image/monkeys; q=1.0, text/plain; q=0.9, text/html; q=0.1
Accept: text/*, text/html; q=0
Available: text/html
Expected:
Accept: text/*, text/html; q=0
Available: text/html, text/plain
Expected: text/plain; q=1.0
Accept: text/html
Available: text/plain
Expected:
Accept: application/xrds+xml, text/html; q=0.9
Available: application/xrds+xml, text/html
Expected: application/xrds+xml; q=1.0, text/html; q=0.9
Accept: application/xrds+xml, */*; q=0.9
Available: application/xrds+xml, text/html
Expected: application/xrds+xml; q=1.0, text/html; q=0.9
Accept: application/xrds+xml, application/xhtml+xml; q=0.9, text/html; q=0.8, text/xml; q=0.7
Available: application/xrds+xml, text/html
Expected: application/xrds+xml; q=1.0, text/html; q=0.8
# See http://www.rfc-editor.org/rfc/rfc3023.txt, section A.13
Accept: application/xrds
Available: application/xrds+xml
Expected:
Accept: application/xrds+xml
Available: application/xrds
Expected:
Accept: application/xml
Available: application/xrds+xml
Expected:
Available: application/xrds+xml
Accept: application/xml
Expected:
#################################################
# The tests below this line are documentation of how this library
# works. If the implementation changes, it's acceptable to change the
# test to reflect that. These are specified so that we can make sure
# that the current implementation actually works the way that we
# expect it to given these inputs.
Accept: text/html;level=1
Available: text/html
Expected: text/html; q=1.0
Accept: text/html; level=1, text/html; level=9; q=0.1
Available: text/html
Expected: text/html; q=1.0
Accept: text/html; level=9; q=0.1, text/html; level=1
Available: text/html
Expected: text/html; q=1.0

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="2">
<Type>http://openid.net/signon/1.1</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://frank.myopenid.com/</openid:Delegate>
</Service>
</XRD>
<XRD>
<Service priority="1">
<Type>http://bar.com/</Type>
<URI>http://bar.com/server</URI>
</Service>
<Service priority="2">
<Type>http://foo.com</Type>
<URI>http://foo.com/server</URI>
</Service>
</XRD>
<XRD>
<Service priority="0">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://brian.myopenid.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service>
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<URI>http://example.com/server</URI>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://brian.myopenid.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="2">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.schtuff.com/?action=openid_server</URI>
<openid:Delegate>http://users.schtuff.com/brian</openid:Delegate>
</Service>
<Service priority="1">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://brian.myopenid.com/</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-09T22:07:13.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID>xri://!!1003</ProviderID>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test1</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://!!1003</ProviderID>
<LocalID>!0000.0000.3B9A.CA01</LocalID>
<CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-09T22:07:13.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test1</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<LocalID>!0000.0000.3B9A.CA01</LocalID>
<CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-09T22:07:13.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID/>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test1</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://!!1003</ProviderID>
<LocalID>!0000.0000.3B9A.CA01</LocalID>
<CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sample XRDS file at: NAME -->
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://example.com/</Type>
<URI>http://www.openidenabled.com/</URI>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,30 @@
# This file contains test cases for doing YADIS identity URL and
# service discovery. For each case, there are three URLs. The first
# URL is the user input. The second is the identity URL and the third
# is the URL from which the XRDS document should be read.
#
# The file format is as follows:
# User URL <tab> Identity URL <tab> XRDS URL <newline>
#
# blank lines and lines starting with # should be ignored.
#
# To use this test:
#
# 1. Run your discovery routine on the User URL.
#
# 2. Compare the identity URL returned by the discovery routine to the
# identity URL on that line of the file. It must be an EXACT match.
#
# 3. Do a regular HTTP GET on the XRDS URL. Compare the content that
# was returned by your discovery routine with the content returned
# from that URL. It should also be an exact match.
http://www.openidenabled.com/resources/yadis-test/discover/equiv http://www.openidenabled.com/resources/yadis-test/discover/equiv http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/header http://www.openidenabled.com/resources/yadis-test/discover/header http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/xrds http://www.openidenabled.com/resources/yadis-test/discover/xrds http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/xrds_html http://www.openidenabled.com/resources/yadis-test/discover/xrds_html
http://www.openidenabled.com/resources/yadis-test/discover/redir_equiv http://www.openidenabled.com/resources/yadis-test/discover/equiv http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/redir_header http://www.openidenabled.com/resources/yadis-test/discover/header http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/redir_xrds http://www.openidenabled.com/resources/yadis-test/discover/xrds http://www.openidenabled.com/resources/yadis-test/discover/xrds
http://www.openidenabled.com/resources/yadis-test/discover/redir_xrds_html http://www.openidenabled.com/resources/yadis-test/discover/xrds_html
http://www.openidenabled.com/resources/yadis-test/discover/redir_redir_equiv http://www.openidenabled.com/resources/yadis-test/discover/equiv http://www.openidenabled.com/resources/yadis-test/discover/xrds

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns:typekey="http://typekey.com/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
</xrds:XRDS>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<x></x>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="10">
<Type>http://openid.net/signon/1.1</Type>
<Type>http://openid.net/sreg/1.0</Type>
<URI>https://pip.verisignlabs.com/server</URI>
</Service>
<Service priority="20">
<Type>http://openid.net/signon/1.0</Type>
<Type>http://openid.net/sreg/1.0</Type>
<URI>https://pip.verisignlabs.com/server</URI>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-09T22:07:13.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test1</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<LocalID>!0000.0000.3B9A.CA01</LocalID>
<CanonicalID>xri://@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test.ref" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-15T18:56:09.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID/>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test.ref</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://!!1003</ProviderID>
<LocalID>!0000.0000.3B9A.CA03</LocalID>
<CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA03</CanonicalID>
<Ref>@!BAE.A650.823B.2475</Ref>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRDS ref="xri://@!BAE.A650.823B.2475" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>!BAE.A650.823B.2475</Query>
<Status code="100"/>
<Expires>2006-08-15T18:56:10.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!BAE.A650.823B.2475</LocalID>
<CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID>
<Service priority="10">
<Type select="true">(+wdnc)</Type>
<ProviderID/>
<Path select="true">(+wdnc)</Path>
<URI append="none" priority="10">http://www.tcpacompliance.us</URI>
</Service>
<Service priority="10">
<Type match="content" select="true">xri://$res*auth*($v*2.0)</Type>
<ProviderID/>
<MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://dev.dready.org/cgi-bin/xri</URI>
</Service>
<Service priority="10">
<Type match="content" select="true">(+i-name)</Type>
<ProviderID/>
<Path match="content" select="true">(+i-name)</Path>
<URI append="none" priority="10">http://www.inames.net</URI>
</Service>
<Service priority="10">
<Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type>
<Type match="default" select="false"/>
<ProviderID>xri://!!1001</ProviderID>
<Path select="true">(+contact)</Path>
<Path match="null" select="false"/>
<MediaType select="false">text/html</MediaType>
<MediaType match="default" select="false"/>
<URI append="none" priority="10">http://www.neustar.biz</URI>
</Service>
</XRD>
</XRDS>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>!BAE.A650.823B.2475</Query>
<Status code="100"/>
<Expires>2006-08-15T18:56:10.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!BAE.A650.823B.2475</LocalID>
<CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID>
<Service priority="10">
<Type select="true">(+wdnc)</Type>
<ProviderID/>
<Path select="true">(+wdnc)</Path>
<URI append="none" priority="10">http://www.tcpacompliance.us</URI>
</Service>
<Service priority="10">
<Type match="content" select="true">xri://$res*auth*($v*2.0)</Type>
<ProviderID/>
<MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://dev.dready.org/cgi-bin/xri</URI>
</Service>
<Service priority="10">
<Type match="content" select="true">(+i-name)</Type>
<ProviderID/>
<Path match="content" select="true">(+i-name)</Path>
<URI append="none" priority="10">http://www.inames.net</URI>
</Service>
<Service priority="10">
<Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type>
<Type match="default" select="false"/>
<ProviderID>xri://!!1001</ProviderID>
<Path select="true">(+contact)</Path>
<Path match="null" select="false"/>
<MediaType select="false">text/html</MediaType>
<MediaType match="default" select="false"/>
<URI append="none" priority="10">http://www.neustar.biz</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*ootao</Query>
<Status code="100"/>
<Expires>2006-08-09T22:07:13.000Z</Expires>
<ProviderID>xri://@</ProviderID>
<LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
<CanonicalID priority="10">xri://@!5BAD.2AA.3C72.AF46</CanonicalID>
<Service priority="10">
<Type>xri://$res*auth*($v*2.0)</Type>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<MediaType>application/xrds+xml;trust=none</MediaType>
<URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
</Service>
<Service priority="10">
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*test1</Query>
<Status code="100">SUCCESS</Status>
<ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
<LocalID>!0000.0000.3B9A.CA01</LocalID>
<CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
<Service>
<Type select="true">http://openid.net/signon/1.0</Type>
<ProviderID/>
<URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*keturn</Query>
<ProviderID>xri://=</ProviderID>
<LocalID>!E4</LocalID>
<CanonicalID>=!E4</CanonicalID>
<Service>
<Type>xri://$res*auth*($v*2.0)</Type>
<URI>http://keturn.example.com/resolve/</URI>
<ProviderID>=!E4</ProviderID>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*isDrummond</Query>
<ProviderID>=!E4</ProviderID>
<LocalID>!D2</LocalID>
<CanonicalID>=!D2</CanonicalID>
<Service>
<Type>http://openid.net/signon/1.0</Type>
<URI>http://keturn.example.com/openid</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*keturn</Query>
<ProviderID>xri://=</ProviderID>
<LocalID>!E4</LocalID>
<CanonicalID>=!E4</CanonicalID>
<Service>
<Type>xri://$res*auth*($v*2.0)</Type>
<URI>http://keturn.example.com/resolve/</URI>
<ProviderID>xri://=</ProviderID>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*isDrummond</Query>
<ProviderID>xri://=</ProviderID>
<LocalID>!D2</LocalID>
<CanonicalID>=!D2</CanonicalID>
<Service>
<Type>http://openid.net/signon/1.0</Type>
<URI>http://keturn.example.com/openid</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*keturn</Query>
<ProviderID>xri://@</ProviderID>
<LocalID>@E4</LocalID>
<CanonicalID>@!E4</CanonicalID>
<Service>
<Type>xri://$res*auth*($v*2.0)</Type>
<URI>http://keturn.example.com/resolve/</URI>
<ProviderID>@!E4</ProviderID>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*is</Query>
<ProviderID>@!E4</ProviderID>
<LocalID>!D2</LocalID>
<CanonicalID>=!C0</CanonicalID>
<CanonicalID>=!E4!01</CanonicalID>
<Service>
<Type>xri://$res*auth*($v*2.0)</Type>
<URI>http://keturn.example.com/resolve/</URI>
<ProviderID>@!C0</ProviderID>
</Service>
</XRD>
<XRD xmlns="xri://$xrd*($v*2.0)">
<Query>*drummond</Query>
<ProviderID>@!C0</ProviderID>
<LocalID>!D2</LocalID>
<CanonicalID>@!C0!D2</CanonicalID>
<Service>
<Type>http://openid.net/signon/1.0</Type>
<URI>http://keturn.example.com/openid</URI>
</Service>
</XRD>
</XRDS>

View file

@ -0,0 +1,137 @@
equiv
Status: 200 OK
Content-Type: text/html
<html>
<head>
<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds">
<title>Joe Schmoe's Homepage</title>
</head>
<body>
<h1>Joe Schmoe's Homepage</h1>
<p>Blah blah blah blah blah blah blah</p>
</body>
</html>
header
Status: 200 OK
Content-Type: text/html
YADIS_HEADER: URL_BASE/xrds
<html>
<head>
<title>Joe Schmoe's Homepage</title>
</head>
<body>
<h1>Joe Schmoe's Homepage</h1>
<p>Blah blah blah blah blah blah blah</p>
</body>
xrds
Status: 200 OK
Content-Type: application/xrds+xml
<XRDS Content>
xrds_ctparam
Status: 200 OK
Content-Type: application/xrds+xml; charset=UTF8
<XRDS Content>
xrds_ctcase
Status: 200 OK
Content-Type: appliCATION/XRDS+xml
<XRDS Content>
xrds_html
Status: 200 OK
Content-Type: text/html
<XRDS Content>
redir_equiv
Status: 302 Found
Content-Type: text/plain
Location: URL_BASE/equiv
You are presently being redirected.
redir_header
Status: 302 Found
Content-Type: text/plain
Location: URL_BASE/header
You are presently being redirected.
redir_xrds
Status: 302 Found
Content-Type: application/xrds+xml
Location: URL_BASE/xrds
<XRDS Content>
redir_xrds_html
Status: 302 Found
Content-Type: text/plain
Location: URL_BASE/xrds_html
You are presently being redirected.
redir_redir_equiv
Status: 302 Found
Content-Type: text/plain
Location: URL_BASE/redir_equiv
You are presently being redirected.
lowercase_header
Status: 200 OK
Content-Type: text/html
x-xrds-location: URL_BASE/xrds
<html>
<head>
<title>Joe Schmoe's Homepage</title>
</head>
<body>
<h1>Joe Schmoe's Homepage</h1>
<p>Blah blah blah blah blah blah blah</p>
</body>
404_server_response
Status: 404 Not Found
EEk!
500_server_response
Status: 500 Server error
EEk!
201_server_response
Status: 201 Created
EEk!
404_with_header
Status: 404 Not Found
YADIS_HEADER: URL_BASE/xrds
EEk!
404_with_meta
Status: 404 Not Found
Content-Type: text/html
<html>
<head>
<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds">
<title>Joe Schmoe's Homepage</title>
</head>
<body>
<h1>Joe Schmoe's Homepage</h1>
<p>Blah blah blah blah blah blah blah</p>
</body>
</html>

View file

@ -0,0 +1,133 @@
found
<!-- minimal well-formed success case -->
<html><head><meta http-equiv="X-XRDS-Location" content="found"></head></html>
found
<!-- minimal success case -->
<html><head><meta http-equiv="X-XRDS-Location" content="found">
found
<!-- ignore bogus top-level tags -->
</porky><html><head><meta http-equiv="X-XRDS-Location" content="found">
found
<!-- Case folding for header name -->
<html><head><meta http-equiv="x-xrds-location" content="found">
found
<!-- missing <html> tag -->
<head><meta http-equiv="X-XRDS-Location" content="found">
found
<!-- case folding for tag names -->
<html><head><META http-equiv="X-XRDS-Location" content="found">
found
<!-- Stop after first one found -->
<html><head>
<meta http-equiv="x-xrds-location" content="found">
<meta http-equiv="x-xrds-location" content="not-found">
&
<!-- standard entity -->
<head><meta http-equiv="X-XRDS-Location" content="&amp;">
found
<!-- hex entity -->
<html>
<head>
<meta http-equiv="X-XRDS-Location" content="&#x66;ound">
</head>
</html>
found
<!-- decimal entity -->
<html>
<head>
<meta http-equiv="X-XRDS-Location" content="&#102;ound">
</head>
</html>
/
<!-- hex entity -->
<html>
<head>
<meta http-equiv="X-XRDS-Location" content="&#x2f;">
</head>
</html>
<!-- empty string -->
<html><head><meta http-equiv="X-XRDS-Location" content="">
EOF
<!-- No markup, except this comment -->
None
<!-- No meta, just standard HTML -->
<html>
<head>
<title>A boring document</title>
</head>
<body>
<h1>A boring document</h1>
<p>There's really nothing interesting about this</p>
</body>
</html>
EOF
<!-- No <html> or <head> -->
<meta http-equiv="X-XRDS-Location" content="found">
EOF
<!-- No <head> tag -->
<html><meta http-equiv="X-XRDS-Location" content="found">
None
<!-- No <html> or <head> and a <body> -->
<body><meta http-equiv="X-XRDS-Location" content="found">
None
<!-- <head> and <html> reversed -->
<head><html><meta http-equiv="X-XRDS-Location" content="found">
None
<!-- <meta> is inside of <body> -->
<html><head><body><meta http-equiv="X-XRDS-Location" content="found">
None
<!-- <meta> is inside comment -->
<html>
<head>
<!--<meta http-equiv="X-XRDS-Location" content="found">-->
</head>
</html>
None
<!-- <meta> is inside of <body> -->
<html>
<head>
<title>Someone's blog</title>
</head>
<body>
<h1>My blog</h1>
<p>This is my blog</p>
<h2>Comments</h2>
<p><meta http-equiv="X-XRDS-Location" content="found"></p>
</body>
</html>
None
<!-- short head tag -->
<html><head/>
<meta http-equiv="X-XRDS-Location" content="found">
None
<!-- <body> comes first -->
<body><html><head>
<meta http-equiv="X-XRDS-Location" content="found">
None
<!-- </body> comes first -->
</body><html><head>
<meta http-equiv="X-XRDS-Location" content="found">

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns:typekey="http://typekey.com/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.myopenid.com/server</URI>
<openid:Delegate>http://josh.myopenid.com/</openid:Delegate>
</Service>
<Service priority="20">
<Type>http://lid.netmesh.org/sso/2.0b5</Type>
<Type>http://lid.netmesh.org/2.0b5</Type>
<URI>http://mylid.net/josh</URI>
</Service>
<Service priority="10">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.livejournal.com/openid/server.bml</URI>
<openid:Delegate>http://www.livejournal.com/users/nedthealpaca/</openid:Delegate>
</Service>
<Service priority="15">
<Type>http://typekey.com/services/1.0</Type>
<typekey:MemberName>joshhoyt</typekey:MemberName>
</Service>
<Service priority="5">
<Type>http://openid.net/signon/1.0</Type>
<URI>http://www.schtuff.com/openid</URI>
<openid:Delegate>http://users.schtuff.com/josh</openid:Delegate>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service>
<Type>http://openid.net/signon/1.0</Type>
<URI>http://no.priority/</URI>
<URI priority="1">http://one.priority/</URI>
<URI priority="0">http://zero.priority/</URI>
</Service>
</XRD>
</xrds:XRDS>

View file

@ -0,0 +1,189 @@
<?php
/**
* A driver for the PHP OpenID unit tests.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
require_once 'PHPUnit.php';
require_once 'PHPUnit/GUI/HTML.php';
error_reporting(E_ALL);
$__test_errors = array();
function __handler($code, $message)
{
global $__test_errors;
if ($code == E_USER_WARNING) {
$__test_errors[] = $message;
}
}
function __raiseError($message)
{
set_error_handler('__handler');
trigger_error($message, E_USER_WARNING);
restore_error_handler();
}
function __getError()
{
global $__test_errors;
if ($__test_errors) {
return array_pop($__test_errors);
}
return null;
}
/**
* Load the tests that are defined in the named modules.
*
* If you have Tests/Foo.php which defines a test class called
* Tests_Foo, the call would look like:
*
* loadTests('Tests/', array('Foo'))
*
* @param string $test_dir The root of the test hierarchy. Must end
* with a /
*
* @param array $test_names The names of the modules in which the
* tests are defined. This should not include the root of the test
* hierarchy.
*/
function loadTests($test_dir, $test_names)
{
$suites = array();
foreach ($test_names as $filename) {
$filename = $test_dir . $filename . '.php';
$class_name = str_replace('/', '_', $filename);
$class_name = basename($class_name, '.php');
if (!global_require_once($filename)) {
continue;
}
$test = new $class_name($class_name);
if (is_a($test, 'PHPUnit_TestCase')) {
$s = new PHPUnit_TestSuite();
$s->setName($class_name);
$s->addTestSuite($class_name);
$test = $s;
$tc_array_name = $class_name . '_other';
if (array_key_exists($tc_array_name, $GLOBALS) &&
is_array($GLOBALS[$tc_array_name])) {
foreach ($GLOBALS[$tc_array_name] as $tc) {
$test->addTestSuite(get_class($tc));
}
}
}
$suites[] = $test;
}
return $suites;
}
function global_require_once($name)
{
$f = @include_once $name;
if (!$f) {
return false;
}
foreach (get_defined_vars() as $k => $v) {
if (!in_array($k, array('name', 'GLOBALS'))) {
$GLOBALS[$k] = $v;
}
}
return true;
}
$_tests = array(
array(
'dir' => 'Tests/Auth/OpenID/',
'files' => array(
'Association',
'BigMath',
'Consumer',
'CryptUtil',
'DiffieHellman',
'HMACSHA1',
'KVForm',
'Util',
'Parse',
'StoreTest',
'Server',
'TrustRoot',
'Discover',
'OpenID_Yadis',
'URINorm'),
),
array(
'dir' => 'Tests/Services/Yadis/',
'files' => array(
'ParseHTML',
'XRDS',
'Yadis',
'Discover',
'XRI'
)
)
);
function selectTests($names)
{
global $_tests;
$lnames = array_map('strtolower', $names);
$include = array();
$exclude = array();
foreach ($_tests as $package) {
foreach ($package['files'] as $t) {
$l = strtolower($t);
if (in_array($l, $lnames)) {
$include[] = $t;
}
if (in_array("/$l", $lnames)) {
$exclude[] = $t;
}
}
}
if (!count($include)) {
$include = array();
foreach ($_tests as $package) {
$include = array_merge($include, $package['files']);
}
}
return array_diff($include, $exclude);
}
// Load OpenID library tests
function loadSuite($names=null)
{
global $_tests;
if ($names === null) {
$names = array();
foreach ($_tests as $package) {
$names = array_merge($names, $package['files']);
}
}
$selected = selectTests($names);
$result = array();
foreach ($_tests as $package) {
$result = array_merge($result, loadTests($package['dir'], $selected));
}
return $result;
}
?>

View file

@ -0,0 +1,30 @@
<?php
/**
* Add a directory to the include path
*
* @param dir: The directory to add to the path
* @param at_start: If true, place this directory at the beginning of
* the include path. Otherwise, place it at the end.
*/
function includeAdd($dir, $at_start=false)
{
$path = ini_get('include_path');
if (strlen($path)) {
$newpath = $at_start ? "$dir:$path" : "$path:$dir";
} else {
$newpath = $dir;
}
ini_set('include_path', $newpath);
}
/**
* Return the parent directory of this module.
*/
function getParent()
{
return dirname(dirname(realpath(__FILE__)));
}
?>

View file

@ -0,0 +1,81 @@
#!/usr/bin/env perl -w
use strict;
my $filename = $ARGV[0];
if (!$filename) {
print "Usage: modified_otb.pl <filename>\n";
exit(1);
}
my @results = ();
my $line_num = 0;
my ($NONE, $BRACE, $PAREN) = (0, 1, 2);
my $looking_for = $NONE;
my $last_func_name = "";
open(HANDLE, "<", $filename) or die "Cannot open $filename\n";
# Read the file and track the lines with length > $max_length.
while (<HANDLE>) {
$line_num++;
# Subtract one because the newline doesn't count toward the
# length.
chomp;
if (!$looking_for &&
($_ =~ /^\s*function/) &&
($_ =~ /\{/)) {
# Done (bad): we found a function whose opening line ends with
# a brace, which goes against the PEAR coding guidelines.
($last_func_name) = $_ =~ /function\s*(.*)\(/;
push @results, "'$last_func_name' prototype ends with opening ".
"brace, line $line_num";
} elsif (!$looking_for &&
($_ =~ /^\s*function/) &&
($_ !~ /\)/)) {
($last_func_name) = $_ =~ /function\s*(.*)\(/;
$looking_for = $PAREN;
} elsif (($looking_for == $PAREN) &&
($_ =~ /\)/) &&
($_ =~ /\{/)) {
# Done (bad): function prototype and brace are on the same
# line.
push @results, "'$last_func_name' prototype ends with with ".
"opening brace, line $line_num";
$looking_for = $NONE;
} elsif (($looking_for == $PAREN) &&
($_ =~ /\)/) &&
($_ !~ /\{/)) {
$looking_for = $BRACE;
} elsif (!$looking_for &&
($_ =~ /^\s*function/) &&
($_ =~ /\)/) &&
($_ !~ /\{/)) {
($last_func_name) = $_ =~ /function\s*(.*)\(/;
$looking_for = $BRACE;
} elsif (($looking_for == $BRACE) &&
($_ eq "{")) {
$looking_for = $NONE;
# Done (good): the brace was found on the line after the
# function prototype.
} else {
# We got here because we got a line that we're not interested
# in.
$looking_for = $NONE;
}
}
# If any long lines were found, notify and exit(1); otherwise,
# exit(0).
if (@results) {
foreach my $result (@results) {
print "$filename: $result\n";
}
exit(1);
} else {
exit(0);
}

View file

@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php
require_once $argv[1];
?>

View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
./admin/findphp | xargs -L 1 ./admin/checkimport

Some files were not shown because too many files have changed in this diff Show more