incubator-heraldry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ket...@apache.org
Subject svn commit: r463021 [2/12] - in /incubator/heraldry/libraries/php: ./ openid/ openid/trunk/ openid/trunk/Auth/ openid/trunk/Auth/OpenID/ openid/trunk/Services/ openid/trunk/Services/Yadis/ openid/trunk/Tests/ openid/trunk/Tests/Auth/ openid/trunk/Tests...
Date Wed, 11 Oct 2006 22:49:59 GMT
Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Consumer.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Consumer.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Consumer.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Consumer.php Wed Oct 11 15:49:50 2006
@@ -0,0 +1,1170 @@
+<?php
+
+/**
+ * This module documents the main interface with the OpenID consumer
+ * library.  The only part of the library which has to be used and
+ * isn't documented in full here is the store required to create an
+ * Auth_OpenID_Consumer instance.  More on the abstract store type and
+ * concrete implementations of it that are provided in the
+ * documentation for the Auth_OpenID_Consumer constructor.
+ *
+ * OVERVIEW
+ *
+ * The OpenID identity verification process most commonly uses the
+ * following steps, as visible to the user of this library:
+ *
+ *   1. The user enters their OpenID into a field on the consumer's
+ *      site, and hits a login button.
+ *   2. The consumer site discovers the user's OpenID server using the
+ *      YADIS protocol.
+ *   3. The consumer site sends the browser a redirect to the identity
+ *      server.  This is the authentication request as described in
+ *      the OpenID specification.
+ *   4. The identity server's site sends the browser a redirect back
+ *      to the consumer site.  This redirect contains the server's
+ *      response to the authentication request.
+ *
+ * The most important part of the flow to note is the consumer's site
+ * must handle two separate HTTP requests in order to perform the full
+ * identity check.
+ *
+ * LIBRARY DESIGN
+ * 
+ * This consumer library is designed with that flow in mind.  The goal
+ * is to make it as easy as possible to perform the above steps
+ * securely.
+ *
+ * At a high level, there are two important parts in the consumer
+ * library.  The first important part is this module, which contains
+ * the interface to actually use this library.  The second is the
+ * Auth_OpenID_Interface class, which describes the interface to use
+ * if you need to create a custom method for storing the state this
+ * library needs to maintain between requests.
+ *
+ * In general, the second part is less important for users of the
+ * library to know about, as several implementations are provided
+ * which cover a wide variety of situations in which consumers may use
+ * the library.
+ *
+ * This module contains a class, Auth_OpenID_Consumer, with methods
+ * corresponding to the actions necessary in each of steps 2, 3, and 4
+ * described in the overview.  Use of this library should be as easy
+ * as creating an Auth_OpenID_Consumer instance and calling the
+ * methods appropriate for the action the site wants to take.
+ *
+ * STORES AND DUMB MODE
+ *
+ * OpenID is a protocol that works best when the consumer site is able
+ * to store some state.  This is the normal mode of operation for the
+ * protocol, and is sometimes referred to as smart mode.  There is
+ * also a fallback mode, known as dumb mode, which is available when
+ * the consumer site is not able to store state.  This mode should be
+ * avoided when possible, as it leaves the implementation more
+ * vulnerable to replay attacks.
+ *
+ * The mode the library works in for normal operation is determined by
+ * the store that it is given.  The store is an abstraction that
+ * handles the data that the consumer needs to manage between http
+ * requests in order to operate efficiently and securely.
+ *
+ * Several store implementation are provided, and the interface is
+ * fully documented so that custom stores can be used as well.  See
+ * the documentation for the Auth_OpenID_Consumer class for more
+ * information on the interface for stores.  The implementations that
+ * are provided allow the consumer site to store the necessary data in
+ * several different ways, including several SQL databases and normal
+ * files on disk.
+ *
+ * There is an additional concrete store provided that puts the system
+ * in dumb mode.  This is not recommended, as it removes the library's
+ * ability to stop replay attacks reliably.  It still uses time-based
+ * checking to make replay attacks only possible within a small
+ * window, but they remain possible within that window.  This store
+ * should only be used if the consumer site has no way to retain data
+ * between requests at all.
+ *
+ * IMMEDIATE MODE
+ *
+ * In the flow described above, the user may need to confirm to the
+ * lidentity server that it's ok to authorize his or her identity.
+ * The server may draw pages asking for information from the user
+ * before it redirects the browser back to the consumer's site.  This
+ * is generally transparent to the consumer site, so it is typically
+ * ignored as an implementation detail.
+ *
+ * There can be times, however, where the consumer site wants to get a
+ * response immediately.  When this is the case, the consumer can put
+ * the library in immediate mode.  In immediate mode, there is an
+ * extra response possible from the server, which is essentially the
+ * server reporting that it doesn't have enough information to answer
+ * the question yet.  In addition to saying that, the identity server
+ * provides a URL to which the user can be sent to provide the needed
+ * information and let the server finish handling the original
+ * request.
+ *
+ * USING THIS LIBRARY
+ *
+ * Integrating this library into an application is usually a
+ * relatively straightforward process.  The process should basically
+ * follow this plan:
+ *
+ * Add an OpenID login field somewhere on your site.  When an OpenID
+ * is entered in that field and the form is submitted, it should make
+ * a request to the your site which includes that OpenID URL.
+ *
+ * First, the application should instantiate the Auth_OpenID_Consumer
+ * class using the store of choice (Auth_OpenID_FileStore or one of
+ * the SQL-based stores).  If the application has any sort of session
+ * framework that provides per-client state management, a dict-like
+ * object to access the session should be passed as the optional
+ * second parameter.  (The default behavior is to use PHP's standard
+ * session machinery.)
+ *
+ * Next, the application should call the Auth_OpenID_Consumer object's
+ * 'begin' method.  This method takes the OpenID URL.  The 'begin'
+ * method returns an Auth_OpenID_AuthRequest object.
+ *
+ * Next, the application should call the 'redirectURL' method of the
+ * Auth_OpenID_AuthRequest object.  The 'return_to' URL parameter is
+ * the URL that the OpenID server will send the user back to after
+ * attempting to verify his or her identity.  The 'trust_root' is the
+ * URL (or URL pattern) that identifies your web site to the user when
+ * he or she is authorizing it.  Send a redirect to the resulting URL
+ * to the user's browser.
+ *
+ * That's the first half of the authentication process.  The second
+ * half of the process is done after the user's ID server sends the
+ * user's browser a redirect back to your site to complete their
+ * login.
+ *
+ * When that happens, the user will contact your site at the URL given
+ * as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL
+ * call made above.  The request will have several query parameters
+ * added to the URL by the identity server as the information
+ * necessary to finish the request.
+ *
+ * Lastly, instantiate an Auth_OpenID_Consumer instance as above and
+ * call its 'complete' method, passing in all the received query
+ * arguments.
+ *
+ * There are multiple possible return types possible from that
+ * method. These indicate the whether or not the login was successful,
+ * and include any additional information appropriate for their type.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005 Janrain, Inc.
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ */
+
+/**
+ * Require utility classes and functions for the consumer.
+ */
+require_once "Auth/OpenID.php";
+require_once "Auth/OpenID/HMACSHA1.php";
+require_once "Auth/OpenID/Association.php";
+require_once "Auth/OpenID/CryptUtil.php";
+require_once "Auth/OpenID/DiffieHellman.php";
+require_once "Auth/OpenID/KVForm.php";
+require_once "Auth/OpenID/Nonce.php";
+require_once "Auth/OpenID/Discover.php";
+require_once "Services/Yadis/Manager.php";
+require_once "Services/Yadis/XRI.php";
+
+/**
+ * This is the status code returned when the complete method returns
+ * successfully.
+ */
+define('Auth_OpenID_SUCCESS', 'success');
+
+/**
+ * Status to indicate cancellation of OpenID authentication.
+ */
+define('Auth_OpenID_CANCEL', 'cancel');
+
+/**
+ * This is the status code completeAuth returns when the value it
+ * received indicated an invalid login.
+ */
+define('Auth_OpenID_FAILURE', 'failure');
+
+/**
+ * This is the status code completeAuth returns when the
+ * {@link Auth_OpenID_Consumer} instance is in immediate mode, and the
+ * identity server sends back a URL to send the user to to complete his
+ * or her login.
+ */
+define('Auth_OpenID_SETUP_NEEDED', 'setup needed');
+
+/**
+ * This is the status code beginAuth returns when the page fetched
+ * from the entered OpenID URL doesn't contain the necessary link tags
+ * to function as an identity page.
+ */
+define('Auth_OpenID_PARSE_ERROR', 'parse error');
+
+/**
+ * An OpenID consumer implementation that performs discovery and does
+ * session management.  See the Consumer.php file documentation for
+ * more information.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Consumer {
+
+    /**
+     * @access private
+     */
+    var $session_key_prefix = "_openid_consumer_";
+
+    /**
+     * @access private
+     */
+    var $_token_suffix = "last_token";
+
+    /**
+     * Initialize a Consumer instance.
+     *
+     * You should create a new instance of the Consumer object with
+     * every HTTP request that handles OpenID transactions.
+     *
+     * @param Auth_OpenID_OpenIDStore $store This must be an object
+     * that implements the interface in {@link
+     * Auth_OpenID_OpenIDStore}.  Several concrete implementations are
+     * provided, to cover most common use cases.  For stores backed by
+     * MySQL, PostgreSQL, or SQLite, see the {@link
+     * Auth_OpenID_SQLStore} class and its sublcasses.  For a
+     * filesystem-backed store, see the {@link Auth_OpenID_FileStore}
+     * module.  As a last resort, if it isn't possible for the server
+     * to store state at all, an instance of {@link
+     * Auth_OpenID_DumbStore} can be used.
+     *
+     * @param mixed session An object which implements the interface
+     * of the Services_Yadis_Session class.  Particularly, this object
+     * is expected to have these methods: get($key), set($key,
+     * $value), and del($key).  This defaults to a session object
+     * which wraps PHP's native session machinery.  You should only
+     * need to pass something here if you have your own sessioning
+     * implementation.
+     */
+    function Auth_OpenID_Consumer(&$store, $session = null)
+    {
+        if ($session === null) {
+            $session = new Services_Yadis_PHPSession();
+        }
+
+        $this->session =& $session;
+        $this->consumer =& new Auth_OpenID_GenericConsumer($store);
+        $this->_token_key = $this->session_key_prefix . $this->_token_suffix;
+    }
+
+    /**
+     * Start the OpenID authentication process. See steps 1-2 in the
+     * overview at the top of this file.
+     *
+     * @param User_url: Identity URL given by the user. This method
+     * performs a textual transformation of the URL to try and make
+     * sure it is normalized. For example, a user_url of example.com
+     * will be normalized to http://example.com/ normalizing and
+     * resolving any redirects the server might issue.
+     *
+     * @return Auth_OpenID_AuthRequest $auth_request An object
+     * containing the discovered information will be returned, with a
+     * method for building a redirect URL to the server, as described
+     * in step 3 of the overview. This object may also be used to add
+     * extension arguments to the request, using its 'addExtensionArg'
+     * method.
+     */
+    function begin($user_url)
+    {
+        $discoverMethod = '_Auth_OpenID_discoverServiceList';
+        $openid_url = $user_url;
+
+        if (Services_Yadis_identifierScheme($user_url) == 'XRI') {
+            $discoverMethod = '_Auth_OpenID_discoverXRIServiceList';
+        } else {
+            $openid_url = Auth_OpenID::normalizeUrl($user_url);
+        }
+
+        $disco =& new Services_Yadis_Discovery(&$this->session,
+                                               $openid_url,
+                                               $this->session_key_prefix);
+
+        // Set the 'stale' attribute of the manager.  If discovery
+        // fails in a fatal way, the stale flag will cause the manager
+        // to be cleaned up next time discovery is attempted.
+
+        $m = $disco->getManager();
+        $loader = new Services_Yadis_ManagerLoader();
+
+        if ($m) {
+            if ($m->stale) {
+                $disco->destroyManager();
+            } else {
+                $m->stale = true;
+                $disco->session->set($disco->session_key,
+                                     serialize($loader->toSession($m)));
+            }
+        }
+
+        $endpoint = $disco->getNextService($discoverMethod,
+                                           $this->consumer->fetcher);
+
+        // Reset the 'stale' attribute of the manager.
+        $m =& $disco->getManager();
+        if ($m) {
+            $m->stale = false;
+            $disco->session->set($disco->session_key,
+                                 serialize($loader->toSession($m)));
+        }
+
+        if ($endpoint === null) {
+            return null;
+        } else {
+            return $this->beginWithoutDiscovery($endpoint);
+        }
+    }
+
+    /**
+     * Start OpenID verification without doing OpenID server
+     * discovery. This method is used internally by Consumer.begin
+     * after discovery is performed, and exists to provide an
+     * interface for library users needing to perform their own
+     * discovery.
+     *
+     * @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service
+     * endpoint descriptor.
+     *
+     * @return Auth_OpenID_AuthRequest $auth_request An OpenID
+     * authentication request object.
+     */
+    function &beginWithoutDiscovery($endpoint)
+    {
+        $loader = new Auth_OpenID_ServiceEndpointLoader();
+        $auth_req = $this->consumer->begin($endpoint);
+        $this->session->set($this->_token_key,
+              $loader->toSession($auth_req->endpoint));
+        return $auth_req;
+    }
+
+    /**
+     * Called to interpret the server's response to an OpenID
+     * request. It is called in step 4 of the flow described in the
+     * consumer overview.
+     *
+     * @param array $query An array of the query parameters (key =>
+     * value pairs) for this HTTP request.
+     *
+     * @return Auth_OpenID_ConsumerResponse $response A instance of an
+     * Auth_OpenID_ConsumerResponse subclass. The type of response is
+     * indicated by the status attribute, which will be one of
+     * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
+     */
+    function complete($query)
+    {
+        $query = Auth_OpenID::fixArgs($query);
+
+        $loader = new Auth_OpenID_ServiceEndpointLoader();
+        $endpoint_data = $this->session->get($this->_token_key);
+        $endpoint =
+            $loader->fromSession($endpoint_data);
+
+        if ($endpoint === null) {
+            $response = new Auth_OpenID_FailureResponse(null,
+                                                   'No session state found');
+        } else {
+            $response = $this->consumer->complete($query, $endpoint);
+            $this->session->del($this->_token_key);
+        }
+
+        if (in_array($response->status, array(Auth_OpenID_SUCCESS,
+                                              Auth_OpenID_CANCEL))) {
+            if ($response->identity_url !== null) {
+                $disco = new Services_Yadis_Discovery($this->session,
+                                                  $response->identity_url,
+                                                  $this->session_key_prefix);
+                $disco->cleanup();
+            }
+        }
+
+        return $response;
+    }
+}
+
+class Auth_OpenID_DiffieHellmanConsumerSession {
+    var $session_type = 'DH-SHA1';
+
+    function Auth_OpenID_DiffieHellmanConsumerSession($dh = null)
+    {
+        if ($dh === null) {
+            $dh = new Auth_OpenID_DiffieHellman();
+        }
+
+        $this->dh = $dh;
+    }
+
+    function getRequest()
+    {
+        $math =& Auth_OpenID_getMathLib();
+
+        $cpub = $math->longToBase64($this->dh->public);
+
+        $args = array('openid.dh_consumer_public' => $cpub);
+
+        if (!$this->dh->usingDefaultValues()) {
+            $args = array_merge($args, array(
+                'openid.dh_modulus' =>
+                     $math->longToBase64($this->dh->mod),
+                'openid.dh_gen' =>
+                $math->longToBase64($this->dh->gen)));
+        }
+
+        return $args;
+    }
+
+    function extractSecret($response)
+    {
+        if (!array_key_exists('dh_server_public', $response)) {
+            return null;
+        }
+
+        if (!array_key_exists('enc_mac_key', $response)) {
+            return null;
+        }
+
+        $math =& Auth_OpenID_getMathLib();
+        $spub = $math->base64ToLong($response['dh_server_public']);
+        $enc_mac_key = base64_decode($response['enc_mac_key']);
+
+        return $this->dh->xorSecret($spub, $enc_mac_key);
+    }
+}
+
+class Auth_OpenID_PlainTextConsumerSession {
+    var $session_type = null;
+
+    function getRequest()
+    {
+        return array();
+    }
+
+    function extractSecret($response)
+    {
+        if (!array_key_exists('mac_key', $response)) {
+            return null;
+        }
+
+        return base64_decode($response['mac_key']);
+    }
+}
+
+/**
+ * This class is the interface to the OpenID consumer logic.
+ * Instances of it maintain no per-request state, so they can be
+ * reused (or even used by multiple threads concurrently) as needed.
+ *
+ * @package OpenID
+ * @access private
+ */
+class Auth_OpenID_GenericConsumer {
+    /**
+     * This consumer's store object.
+     */
+    var $store;
+
+    /**
+     * @access private
+     */
+    var $_use_assocs;
+
+    /**
+     * This method initializes a new {@link Auth_OpenID_Consumer}
+     * instance to access the library.
+     *
+     * @param Auth_OpenID_OpenIDStore $store This must be an object
+     * that implements the interface in {@link Auth_OpenID_OpenIDStore}.
+     * Several concrete implementations are provided, to cover most common use
+     * cases.  For stores backed by MySQL, PostgreSQL, or SQLite, see
+     * the {@link Auth_OpenID_SQLStore} class and its sublcasses.  For a
+     * filesystem-backed store, see the {@link Auth_OpenID_FileStore} module.
+     * As a last resort, if it isn't possible for the server to store
+     * state at all, an instance of {@link Auth_OpenID_DumbStore} can be used.
+     *
+     * @param bool $immediate This is an optional boolean value.  It
+     * controls whether the library uses immediate mode, as explained
+     * in the module description.  The default value is False, which
+     * disables immediate mode.
+     */
+    function Auth_OpenID_GenericConsumer(&$store)
+    {
+        $this->store =& $store;
+        $this->_use_assocs = !($this->store && $this->store->isDumb());
+
+        $this->fetcher = Services_Yadis_Yadis::getHTTPFetcher();
+    }
+
+    function begin($service_endpoint)
+    {
+        $nonce = Auth_OpenID_mkNonce();
+        $assoc = $this->_getAssociation($service_endpoint->server_url);
+        $r = new Auth_OpenID_AuthRequest($assoc, $service_endpoint);
+        $r->return_to_args['nonce'] = $nonce;
+        return $r;
+    }
+
+    function complete($query, $endpoint)
+    {
+        $mode = Auth_OpenID::arrayGet($query, 'openid.mode',
+                                      '<no mode specified>');
+
+        if ($mode == Auth_OpenID_CANCEL) {
+            return new Auth_OpenID_CancelResponse($endpoint);
+        } else if ($mode == 'error') {
+            $error = Auth_OpenID::arrayGet($query, 'openid.error');
+            return new Auth_OpenID_FailureResponse($endpoint, $error);
+        } else if ($mode == 'id_res') {
+            if ($endpoint->identity_url === null) {
+                return new Auth_OpenID_FailureResponse($endpoint,
+                                               "No session state found");
+            }
+
+            $response = $this->_doIdRes($query, $endpoint);
+
+            if ($response === null) {
+                return new Auth_OpenID_FailureResponse($endpoint,
+                                                       "HTTP request failed");
+            }
+            if ($response->status == Auth_OpenID_SUCCESS) {
+                return $this->_checkNonce($endpoint->server_url,
+                                          $response);
+            } else {
+                return $response;
+            }
+        } else {
+            return new Auth_OpenID_FailureResponse($endpoint,
+                                           sprintf("Invalid openid.mode '%s'",
+                                                   $mode));
+        }
+    }
+
+    /**
+     * @access private
+     */
+    function _doIdRes($query, $endpoint)
+    {
+        $user_setup_url = Auth_OpenID::arrayGet($query,
+                                                'openid.user_setup_url');
+
+        if ($user_setup_url !== null) {
+            return new Auth_OpenID_SetupNeededResponse($endpoint,
+                                                       $user_setup_url);
+        }
+
+        $return_to = Auth_OpenID::arrayGet($query, 'openid.return_to', null);
+        $server_id2 = Auth_OpenID::arrayGet($query, 'openid.identity', null);
+        $assoc_handle = Auth_OpenID::arrayGet($query,
+                                             'openid.assoc_handle', null);
+
+        if (($return_to === null) ||
+            ($server_id2 === null) ||
+            ($assoc_handle === null)) {
+            return new Auth_OpenID_FailureResponse($endpoint,
+                                                   "Missing required field");
+        }
+
+        if ($endpoint->getServerID() != $server_id2) {
+            return new Auth_OpenID_FailureResponse($endpoint,
+                                             "Server ID (delegate) mismatch");
+        }
+
+        $signed = Auth_OpenID::arrayGet($query, 'openid.signed');
+
+        $assoc = $this->store->getAssociation($endpoint->server_url,
+                                              $assoc_handle);
+
+        if ($assoc === null) {
+            // It's not an association we know about.  Dumb mode is
+            // our only possible path for recovery.
+            if ($this->_checkAuth($query, $endpoint->server_url)) {
+                return new Auth_OpenID_SuccessResponse($endpoint, $query,
+                                                       $signed);
+            } else {
+                return new Auth_OpenID_FailureResponse($endpoint,
+                                       "Server denied check_authentication");
+            }
+        }
+
+        if ($assoc->getExpiresIn() <= 0) {
+            $msg = sprintf("Association with %s expired",
+                           $endpoint->server_url);
+            return new Auth_OpenID_FailureResponse($endpoint, $msg);
+        }
+
+        // Check the signature
+        $sig = Auth_OpenID::arrayGet($query, 'openid.sig', null);
+        if (($sig === null) ||
+            ($signed === null)) {
+            return new Auth_OpenID_FailureResponse($endpoint,
+                                               "Missing argument signature");
+        }
+
+        $signed_list = explode(",", $signed);
+
+        //Fail if the identity field is present but not signed
+        if (($endpoint->identity_url !== null) &&
+            (!in_array('identity', $signed_list))) {
+            $msg = '"openid.identity" not signed';
+            return new Auth_OpenID_FailureResponse($endpoint, $msg);
+        }
+
+        $v_sig = $assoc->signDict($signed_list, $query);
+
+        if ($v_sig != $sig) {
+            return new Auth_OpenID_FailureResponse($endpoint,
+                                                   "Bad signature");
+        }
+
+        return Auth_OpenID_SuccessResponse::fromQuery($endpoint,
+                                                      $query, $signed);
+    }
+
+    /**
+     * @access private
+     */
+    function _checkAuth($query, $server_url)
+    {
+        $request = $this->_createCheckAuthRequest($query);
+        if ($request === null) {
+            return false;
+        }
+
+        $response = $this->_makeKVPost($request, $server_url);
+        if ($response == null) {
+            return false;
+        }
+
+        return $this->_processCheckAuthResponse($response, $server_url);
+    }
+
+    /**
+     * @access private
+     */
+    function _createCheckAuthRequest($query)
+    {
+        $signed = Auth_OpenID::arrayGet($query, 'openid.signed', null);
+        if ($signed === null) {
+            return null;
+        }
+
+        $whitelist = array('assoc_handle', 'sig',
+                           'signed', 'invalidate_handle');
+
+        $signed = array_merge(explode(",", $signed), $whitelist);
+
+        $check_args = array();
+
+        foreach ($query as $key => $value) {
+            if (in_array(substr($key, 7), $signed)) {
+                $check_args[$key] = $value;
+            }
+        }
+
+        $check_args['openid.mode'] = 'check_authentication';
+        return $check_args;
+    }
+
+    /**
+     * @access private
+     */
+    function _processCheckAuthResponse($response, $server_url)
+    {
+        $is_valid = Auth_OpenID::arrayGet($response, 'is_valid', 'false');
+
+        $invalidate_handle = Auth_OpenID::arrayGet($response,
+                                                   'invalidate_handle');
+
+        if ($invalidate_handle !== null) {
+            $this->store->removeAssociation($server_url,
+                                            $invalidate_handle);
+        }
+
+        if ($is_valid == 'true') {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @access private
+     */
+    function _makeKVPost($args, $server_url)
+    {
+        $mode = $args['openid.mode'];
+
+        $pairs = array();
+        foreach ($args as $k => $v) {
+            $v = urlencode($v);
+            $pairs[] = "$k=$v";
+        }
+
+        $body = implode("&", $pairs);
+
+        $resp = $this->fetcher->post($server_url, $body);
+
+        if ($resp === null) {
+            return null;
+        }
+
+        $response = Auth_OpenID_KVForm::toArray($resp->body);
+
+        if ($resp->status == 400) {
+            return null;
+        } else if ($resp->status != 200) {
+            return null;
+        }
+
+        return $response;
+    }
+
+    /**
+     * @access private
+     */
+    function _checkNonce($server_url, $response)
+    {
+        $nonce = $response->getNonce();
+        if ($nonce === null) {
+            $parsed_url = parse_url($response->getReturnTo());
+            $query_str = @$parsed_url['query'];
+            $query = array();
+            parse_str($query_str, $query);
+
+            $found = false;
+
+            foreach ($query as $k => $v) {
+                if ($k == 'nonce') {
+                    $server_url = '';
+                    $nonce = $v;
+                    $found = true;
+                    break;
+                }
+            }
+
+
+            if (!$found) {
+                return new Auth_OpenID_FailureResponse($response,
+                    sprintf("Nonce missing from return_to: %s",
+                            $response->getReturnTo()));
+            }
+        }
+
+        list($timestamp, $salt) = Auth_OpenID_splitNonce($nonce);
+
+        if (!($timestamp && $salt)) {
+            return new Auth_OpenID_FailureResponse($response,
+                                                   'Malformed nonce');
+        }
+
+        if (!$this->store->useNonce($server_url,
+                                    $timestamp, $salt)) {
+            return new Auth_OpenID_FailureResponse($response,
+                                                   "Nonce missing from store");
+        }
+
+        return $response;
+    }
+
+    /**
+     * @access protected
+     */
+    function _createDiffieHellman()
+    {
+        return new Auth_OpenID_DiffieHellman();
+    }
+
+    /**
+     * @access private
+     */
+    function _getAssociation($server_url)
+    {
+        if (!$this->_use_assocs) {
+            return null;
+        }
+
+        $assoc = $this->store->getAssociation($server_url);
+
+        if (($assoc === null) ||
+            ($assoc->getExpiresIn() <= 0)) {
+
+            $parts = $this->_createAssociateRequest($server_url);
+
+            if ($parts === null) {
+                return null;
+            }
+
+            list($assoc_session, $args) = $parts;
+
+            $response = $this->_makeKVPost($args, $server_url);
+
+            if ($response === null) {
+                $assoc = null;
+            } else {
+                $assoc = $this->_parseAssociation($response, $assoc_session,
+                                                  $server_url);
+            }
+        }
+
+        return $assoc;
+    }
+
+    function _createAssociateRequest($server_url)
+    {
+        $parts = parse_url($server_url);
+
+        if ($parts === false) {
+            return null;
+        }
+
+        if (array_key_exists('scheme', $parts)) {
+            $proto = $parts['scheme'];
+        } else {
+            $proto = 'http';
+        }
+
+        if ($proto == 'https' || defined('Auth_OpenID_NO_MATH_SUPPORT')) {
+            $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
+        } else {
+            $assoc_session = new Auth_OpenID_DiffieHellmanConsumerSession();
+        }
+
+        $args = array(
+            'openid.mode' => 'associate',
+            'openid.assoc_type' => 'HMAC-SHA1');
+
+        if ($assoc_session->session_type !== null) {
+            $args['openid.session_type'] = $assoc_session->session_type;
+        }
+
+        $args = array_merge($args, $assoc_session->getRequest());
+        return array($assoc_session, $args);
+    }
+
+    /**
+     * @access private
+     */
+    function _parseAssociation($results, $assoc_session, $server_url)
+    {
+        $required_keys = array('assoc_type', 'assoc_handle',
+                               'expires_in');
+
+        foreach ($required_keys as $key) {
+            if (!array_key_exists($key, $results)) {
+                return null;
+            }
+        }
+
+        $assoc_type = $results['assoc_type'];
+        $assoc_handle = $results['assoc_handle'];
+        $expires_in_str = $results['expires_in'];
+
+        if ($assoc_type != 'HMAC-SHA1') {
+            return null;
+        }
+
+        $expires_in = intval($expires_in_str);
+
+        if ($expires_in <= 0) {
+            return null;
+        }
+
+        $session_type = Auth_OpenID::arrayGet($results, 'session_type');
+        if ($session_type != $assoc_session->session_type) {
+            if ($session_type === null) {
+                $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
+            } else {
+                return null;
+            }
+        }
+
+        $secret = $assoc_session->extractSecret($results);
+
+        if (!$secret) {
+            return null;
+        }
+
+        $assoc = Auth_OpenID_Association::fromExpiresIn(
+                         $expires_in, $assoc_handle, $secret, $assoc_type);
+        $this->store->storeAssociation($server_url, $assoc);
+
+        return $assoc;
+    }
+}
+
+/**
+ * This class represents an authentication request from a consumer to
+ * an OpenID server.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_AuthRequest {
+
+    /**
+     * Initialize an authentication request with the specified token,
+     * association, and endpoint.
+     *
+     * Users of this library should not create instances of this
+     * class.  Instances of this class are created by the library when
+     * needed.
+     */
+    function Auth_OpenID_AuthRequest($assoc, $endpoint)
+    {
+        $this->assoc = $assoc;
+        $this->endpoint = $endpoint;
+        $this->extra_args = array();
+        $this->return_to_args = array();
+    }
+
+    /**
+     * Add an extension argument to this OpenID authentication
+     * request.
+     *
+     * Use caution when adding arguments, because they will be
+     * URL-escaped and appended to the redirect URL, which can easily
+     * get quite long.
+     *
+     * @param string $namespace The namespace for the extension. For
+     * example, the simple registration extension uses the namespace
+     * 'sreg'.
+     *
+     * @param string $key The key within the extension namespace. For
+     * example, the nickname field in the simple registration
+     * extension's key is 'nickname'.
+     *
+     * @param string $value The value to provide to the server for
+     * this argument.
+     */
+    function addExtensionArg($namespace, $key, $value)
+    {
+        $arg_name = implode('.', array('openid', $namespace, $key));
+        $this->extra_args[$arg_name] = $value;
+    }
+
+    /**
+     * Compute the appropriate redirection URL for this request based
+     * on a specified trust root and return-to.
+     *
+     * @param string $trust_root The trust root URI for your
+     * application.
+     *
+     * @param string$ $return_to The return-to URL to be used when the
+     * OpenID server redirects the user back to your site.
+     *
+     * @return string $redirect_url The resulting redirect URL that
+     * you should send to the user agent.
+     */
+    function redirectURL($trust_root, $return_to, $immediate=false)
+    {
+        if ($immediate) {
+            $mode = 'checkid_immediate';
+        } else {
+            $mode = 'checkid_setup';
+        }
+
+        $return_to = Auth_OpenID::appendArgs($return_to, $this->return_to_args);
+
+        $redir_args = array(
+            'openid.mode' => $mode,
+            'openid.identity' => $this->endpoint->getServerID(),
+            'openid.return_to' => $return_to,
+            'openid.trust_root' => $trust_root);
+
+        if ($this->assoc) {
+            $redir_args['openid.assoc_handle'] = $this->assoc->handle;
+        }
+
+        $redir_args = array_merge($redir_args, $this->extra_args);
+
+        return Auth_OpenID::appendArgs($this->endpoint->server_url,
+                                       $redir_args);
+    }
+}
+
+/**
+ * The base class for responses from the Auth_OpenID_Consumer.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ConsumerResponse {
+    var $status = null;
+}
+
+/**
+ * A response with a status of Auth_OpenID_SUCCESS. Indicates that
+ * this request is a successful acknowledgement from the OpenID server
+ * that the supplied URL is, indeed controlled by the requesting
+ * agent.  This has three relevant attributes:
+ *
+ * identity_url - The identity URL that has been authenticated
+ *
+ * signed_args - The arguments in the server's response that were
+ * signed and verified.
+ *
+ * status - Auth_OpenID_SUCCESS.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse {
+    var $status = Auth_OpenID_SUCCESS;
+
+    /**
+     * @access private
+     */
+    function Auth_OpenID_SuccessResponse($endpoint, $signed_args)
+    {
+        $this->endpoint = $endpoint;
+        $this->identity_url = $endpoint->identity_url;
+        $this->signed_args = $signed_args;
+    }
+
+    /**
+     * @access private
+     */
+    function fromQuery($endpoint, $query, $signed)
+    {
+        $signed_args = array();
+        foreach (explode(",", $signed) as $field_name) {
+            $field_name = 'openid.' . $field_name;
+            $signed_args[$field_name] = Auth_OpenID::arrayGet($query,
+                                                              $field_name, '');
+        }
+        return new Auth_OpenID_SuccessResponse($endpoint, $signed_args);
+    }
+
+    /**
+     * Extract signed extension data from the server's response.
+     *
+     * @param string $prefix The extension namespace from which to
+     * extract the extension data.
+     */
+    function extensionResponse($prefix)
+    {
+        $response = array();
+        $prefix = sprintf('openid.%s.', $prefix);
+        $prefix_len = strlen($prefix);
+        foreach ($this->signed_args as $k => $v) {
+            if (strpos($k, $prefix) === 0) {
+                $response_key = substr($k, $prefix_len);
+                $response[$response_key] = $v;
+            }
+        }
+
+        return $response;
+    }
+
+    /**
+     * Get the openid.return_to argument from this response.
+     *
+     * This is useful for verifying that this request was initiated by
+     * this consumer.
+     *
+     * @return string $return_to The return_to URL supplied to the
+     * server on the initial request, or null if the response did not
+     * contain an 'openid.return_to' argument.
+    */
+    function getReturnTo()
+    {
+        return Auth_OpenID::arrayGet($this->signed_args, 'openid.return_to');
+    }
+
+    function getNonce()
+    {
+        return Auth_OpenID::arrayGet($this->signed_args, 'openid.nonce');
+    }
+}
+
+/**
+ * A response with a status of Auth_OpenID_FAILURE. Indicates that the
+ * OpenID protocol has failed. This could be locally or remotely
+ * triggered.  This has three relevant attributes:
+ *
+ * identity_url - The identity URL for which authentication was
+ * attempted, if it can be determined.  Otherwise, null.
+ *
+ * message - A message indicating why the request failed, if one is
+ * supplied.  Otherwise, null.
+ *
+ * status - Auth_OpenID_FAILURE.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse {
+    var $status = Auth_OpenID_FAILURE;
+
+    function Auth_OpenID_FailureResponse($endpoint, $message = null)
+    {
+        $this->endpoint = $endpoint;
+        if ($endpoint !== null) {
+            $this->identity_url = $endpoint->identity_url;
+        } else {
+            $this->identity_url = null;
+        }
+        $this->message = $message;
+    }
+}
+
+/**
+ * A response with a status of Auth_OpenID_CANCEL. Indicates that the
+ * user cancelled the OpenID authentication request.  This has two
+ * relevant attributes:
+ *
+ * identity_url - The identity URL for which authentication was
+ * attempted, if it can be determined.  Otherwise, null.
+ *
+ * status - Auth_OpenID_SUCCESS.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse {
+    var $status = Auth_OpenID_CANCEL;
+
+    function Auth_OpenID_CancelResponse($endpoint)
+    {
+        $this->endpoint = $endpoint;
+        $this->identity_url = $endpoint->identity_url;
+    }
+}
+
+/**
+ * A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates
+ * that the request was in immediate mode, and the server is unable to
+ * authenticate the user without further interaction.
+ *
+ * identity_url - The identity URL for which authentication was
+ * attempted.
+ *
+ * setup_url - A URL that can be used to send the user to the server
+ * to set up for authentication. The user should be redirected in to
+ * the setup_url, either in the current window or in a new browser
+ * window.
+ *
+ * status - Auth_OpenID_SETUP_NEEDED.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse {
+    var $status = Auth_OpenID_SETUP_NEEDED;
+
+    function Auth_OpenID_SetupNeededResponse($endpoint,
+                                             $setup_url = null)
+    {
+        $this->endpoint = $endpoint;
+        $this->identity_url = $endpoint->identity_url;
+        $this->setup_url = $setup_url;
+    }
+}
+
+?>

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/CryptUtil.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/CryptUtil.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/CryptUtil.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/CryptUtil.php Wed Oct 11 15:49:50 2006
@@ -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;
+    }
+}
+
+?>
\ No newline at end of file

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DatabaseConnection.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DatabaseConnection.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DatabaseConnection.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DatabaseConnection.php Wed Oct 11 15:49:50 2006
@@ -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())
+    {
+    }
+}
+
+?>
\ No newline at end of file

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DiffieHellman.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DiffieHellman.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DiffieHellman.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DiffieHellman.php Wed Oct 11 15:49:50 2006
@@ -0,0 +1,179 @@
+<?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';
+
+$_Auth_OpenID_DEFAULT_MOD = '155172898181473697471232257763715539915724801'.
+'966915404479707795314057629378541917580651227423698188993727816152646631'.
+'438561595825688188889951272158842675419950341258706556549803580104870537'.
+'681476726513255747040765857479291291572334510643245094715007229621094194'.
+'349783925984760375594985848253359305585439638443';
+
+$_Auth_OpenID_DEFAULT_GEN = '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)
+    {
+        global $_Auth_OpenID_DEFAULT_MOD, $_Auth_OpenID_DEFAULT_GEN;
+
+        if ($lib === null) {
+            $this->lib =& Auth_OpenID_getMathLib();
+        } else {
+            $this->lib =& $lib;
+        }
+
+        if ($mod === null) {
+            $this->mod = $this->lib->init($_Auth_OpenID_DEFAULT_MOD);
+        } else {
+            $this->mod = $mod;
+        }
+
+        if ($gen === null) {
+            $this->gen = $this->lib->init($_Auth_OpenID_DEFAULT_GEN);
+        } 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()
+    {
+        global $_Auth_OpenID_DEFAULT_MOD, $_Auth_OpenID_DEFAULT_GEN;
+
+        $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_DEFAULT_MOD) ||
+            $this->lib->cmp($this->gen, $_Auth_OpenID_DEFAULT_GEN)) {
+            $args['openid.dh_modulus'] = $this->lib->longToBase64($this->mod);
+            $args['openid.dh_gen'] = $this->lib->longToBase64($this->gen);
+        }
+
+        return $args;
+    }
+
+    function usingDefaultValues()
+    {
+        global $_Auth_OpenID_DEFAULT_GEN, $_Auth_OpenID_DEFAULT_MOD;
+
+        return ($this->mod == $_Auth_OpenID_DEFAULT_MOD &&
+                $this->gen == $_Auth_OpenID_DEFAULT_GEN);
+    }
+
+    /**
+     * 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;
+    }
+}

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Discover.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Discover.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Discover.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/Discover.php Wed Oct 11 15:49:50 2006
@@ -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);
+}
+
+?>
\ No newline at end of file

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DumbStore.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DumbStore.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DumbStore.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/DumbStore.php Wed Oct 11 15:49:50 2006
@@ -0,0 +1,109 @@
+<?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;
+    }
+
+    /**
+     * 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($server_url, $timestamp, $salt)
+    {
+        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;
+    }
+}
+
+?>
\ No newline at end of file

Added: incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/FileStore.php
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/FileStore.php?view=auto&rev=463021
==============================================================================
--- incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/FileStore.php (added)
+++ incubator/heraldry/libraries/php/openid/trunk/Auth/OpenID/FileStore.php Wed Oct 11 15:49:50 2006
@@ -0,0 +1,654 @@
+<?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);
+        }
+    }
+
+    /**
+     * Return whether this nonce is present. As a side effect, mark it
+     * as no longer present.
+     *
+     * @return bool $present
+     */
+    function useNonce($server_url, $timestamp, $salt)
+    {
+        if (!$this->active) {
+            trigger_error("FileStore no longer active", E_USER_ERROR);
+            return null;
+        }
+
+        if ($server_url) {
+            list($proto, $rest) = explode('://', $server_url, 2);
+        } else {
+            $proto = '';
+            $rest = '';
+        }
+
+        $parts = explode('/', $rest, 2);
+        $domain = $this->_filenameEscape($parts[0]);
+        $url_hash = $this->_safe64($server_url);
+        $salt_hash = $this->_safe64($salt);
+
+        $filename = sprintf('%08x-%s-%s-%s-%s', $timestamp, $proto,
+                            $domain, $url_hash, $salt_hash);
+        $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $filename;
+
+        $result = @fopen($filename, 'x');
+
+        if ($result === false) {
+            return false;
+        } else {
+            fclose($result);
+            return true;
+        }
+    }
+
+    /**
+     * 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) {
+            if (!Auth_OpenID_checkTimestamp($nonce, $now)) {
+                $filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
+                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);
+    }
+}
+
+?>



Mime
View raw message