Browse Source

[PROMOTE] Zend_Oauth

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@20232 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew 16 years ago
parent
commit
432a6805df
38 changed files with 4571 additions and 0 deletions
  1. 9 0
      documentation/manual/en/manual.xml.in
  2. 45 0
      documentation/manual/en/module_specs/Zend_Oauth-Introduction.xml
  3. 75 0
      documentation/manual/en/module_specs/Zend_Oauth-ProtocolWorkflow.xml
  4. 79 0
      documentation/manual/en/module_specs/Zend_Oauth-SecurityArchitecture.xml
  5. 88 0
      library/Zend/Oauth.php
  6. 194 0
      library/Zend/Oauth/Client.php
  7. 658 0
      library/Zend/Oauth/Config.php
  8. 75 0
      library/Zend/Oauth/Config/ConfigInterface.php
  9. 272 0
      library/Zend/Oauth/Consumer.php
  10. 32 0
      library/Zend/Oauth/Exception.php
  11. 266 0
      library/Zend/Oauth/Http.php
  12. 189 0
      library/Zend/Oauth/Http/AccessToken.php
  13. 162 0
      library/Zend/Oauth/Http/RequestToken.php
  14. 78 0
      library/Zend/Oauth/Http/UserAuthorization.php
  15. 213 0
      library/Zend/Oauth/Http/Utility.php
  16. 54 0
      library/Zend/Oauth/Signature/Hmac.php
  17. 49 0
      library/Zend/Oauth/Signature/Plaintext.php
  18. 65 0
      library/Zend/Oauth/Signature/Rsa.php
  19. 183 0
      library/Zend/Oauth/Signature/SignatureAbstract.php
  20. 285 0
      library/Zend/Oauth/Token.php
  21. 99 0
      library/Zend/Oauth/Token/Access.php
  22. 102 0
      library/Zend/Oauth/Token/AuthorizedRequest.php
  23. 50 0
      library/Zend/Oauth/Token/Request.php
  24. 2 0
      tests/Zend/AllTests.php
  25. 57 0
      tests/Zend/Oauth/AllTests.php
  26. 253 0
      tests/Zend/Oauth/Oauth/ConsumerTest.php
  27. 174 0
      tests/Zend/Oauth/Oauth/Http/AccessTokenTest.php
  28. 196 0
      tests/Zend/Oauth/Oauth/Http/RequestTokenTest.php
  29. 58 0
      tests/Zend/Oauth/Oauth/Http/UserAuthorizationTest.php
  30. 70 0
      tests/Zend/Oauth/Oauth/Http/UtilityTest.php
  31. 51 0
      tests/Zend/Oauth/Oauth/Signature/AbstractTest.php
  32. 37 0
      tests/Zend/Oauth/Oauth/Signature/HmacTest.php
  33. 37 0
      tests/Zend/Oauth/Oauth/Signature/PlaintextTest.php
  34. 19 0
      tests/Zend/Oauth/Oauth/Signature/RsaTest.php
  35. 110 0
      tests/Zend/Oauth/Oauth/Token/AccessTest.php
  36. 70 0
      tests/Zend/Oauth/Oauth/Token/AuthorizedRequestTest.php
  37. 74 0
      tests/Zend/Oauth/Oauth/Token/RequestTest.php
  38. 41 0
      tests/Zend/Oauth/OauthTest.php

+ 9 - 0
documentation/manual/en/manual.xml.in

@@ -1455,6 +1455,15 @@
             </xi:include>
         </chapter>
 
+        <chapter id="zend.oauth">
+            <title>Zend_Oauth</title>
+            <xi:include href="module_specs/Zend_Oauth-Introduction.xml" parse="xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Oauth-Introduction.xml" />
+                </xi:fallback>
+            <xi:include>
+        </chapter>
+
         <chapter id="zend.openid">
             <title>Zend_OpenId</title>
             <xi:include href="module_specs/Zend_OpenId-Introduction.xml">

+ 45 - 0
documentation/manual/en/module_specs/Zend_Oauth-Introduction.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.oauth.introduction" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>Introduction to OAuth</title>
+
+    <para>
+        OAuth allows you to approve access by any application to your private data stored a website
+        without being forced to disclose your username or password. If you think about it, the
+        practice of handing over your username and password for sites like Yahoo Mail or Twitter has
+        been endemic for quite a while. This has raised some serious concerns because there's
+        nothing to prevent other applications from misusing this data.  Yes, some services may
+        appear trustworthy but that is never guaranteed.  OAuth resolves this problem by eliminating
+        the need for any username and password sharing, replacing it with a user controlled
+        authorization process.
+    </para>
+
+    <para>
+        This authorization process is token based. If you authorize an application (and by
+        application we can include any web based or desktop application) to access your data, it
+        will be in receipt of an Access Token associated with your account. Using this Access Token,
+        the application can access your private data without continually requiring your credentials.
+        In all this authorization delegation style of protocol is simply a more secure solution to
+        the problem of accessing private data via any web service API.
+    </para>
+
+    <para>
+        OAuth is not a completely new idea, rather it is a standardized protocol building on the
+        existing properties of protocols such as Google AuthSub, Yahoo BBAuth, Flickr API, etc.
+        These all to some extent operate on the basis of exchanging user credentials for an Access
+        Token of some description. The power of a standardized specification like OAuth is that it
+        only requires a single implementation as opposed to many disparate ones depending on the web
+        service. This standardization has not occurred independently of the major players, and
+        indeed many now support OAuth as an alternative and future replacement for their own
+        solutions.
+    </para>
+
+    <para>
+        Zend Framework's <classname>Zend_Oauth</classname> currently implements a full OAuth
+        Consumer conforming to the OAuth Core 1.0 Revision A Specification (24 June 2009) via the
+        <classname>Zend_Oauth_Consumer</classname> class.
+    </para>
+
+    <xi:include href="Zend_Oauth-ProtocolWorkflow.xml" />
+    <xi:include href="Zend_Oauth-SecurityArchitecture.xml" />
+</sect1>

+ 75 - 0
documentation/manual/en/module_specs/Zend_Oauth-ProtocolWorkflow.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect2 id="zend.oauth.introduction.protocol-workflow">
+    <title>Protocol Workflow</title>
+
+    <para>
+        Before implementing OAuth it makes sense to understand how the protocol operates. To do so
+        we'll take the example of Twitter which currently implements OAuth based on the OAuth Core
+        1.0 Revision A Specification. This example looks at the protocol from the perspectives of
+        the User (who will approve access), the Consumer (who is seeking access) and the Provider
+        (who holds the User's private data). Access may be read-only or read and write.
+    </para>
+
+    <para>
+        By chance, our User has decided that they want to utilise a new service called TweetExpress
+        which claims to be capable of reposting your blog posts to Twitter in a manner of seconds.
+        TweetExpress is a registered application on Twitter meaning that it has access to a Consumer
+        Key and a Consumer Secret (all OAuth applications must have these from the Provider they
+        will be accessing) which identify its requests to Twitter and that ensure all requests can
+        be signed using the Consumer Secret to verify their origin.
+    </para>
+
+    <para>
+        To use TweetExpress you are asked to register for a new account, and after your registration
+        is confirmed you are informed that TweetExpress will seek to associate your Twitter account
+        with the service. 
+    </para>
+
+    <para>
+        In the meantime TweetExpress has been busy. Before gaining your approval from Twitter, it
+        has sent a HTTP request to Twitter's service asking for a new unauthorized Request Token.
+        This token is not User specific from Twitter's perspective, but TweetExpress may use it
+        specifically for the current User and should associate it with their account and store it
+        for future use. TweetExpress now redirects the User to Twitter so they can approve
+        TweetExpress' access. The URL for this redirect will be signed using TweetExpress' Consumer
+        Secret and it will contain the unauthorized Request Token as a parameter.
+    </para>
+
+    <para>
+        At this point the User may be asked to log into Twitter and will now be faced with a Twitter
+        screen asking if they approve this request by TweetExpress to access Twitter's API on the
+        User's behalf. Twitter will record the response which we'll assume was positive. Based on
+        the User's approval, Twitter will record the current unauthorized Request Token as having
+        been approved by the User (thus making it User specific) and will generate a new value in
+        the form of a verification code. The User is now redirected back to a specific callback URL
+        used by TweetExpress (this callback URL may be registered with Twitter or dynamically set
+        using an oauth_callback parameter in requests). The redirect URL will contain the newly
+        generated verification code.
+    </para>
+
+    <para>
+        TweetExpress' callback URL will trigger an examination of the response to determine whether
+        the User has granted their approval to Twitter. Assuming so, it may now exchange it's
+        unauthorized Request Token for a fully authorized Access Token by sending a request back to
+        Twitter including the Request Token and the received verification code.  Twitter should now
+        send back a response containing this Access Token which must be used in all requests used to
+        access Twitter's API on behalf of the User. Twitter will only do this once they have
+        confirmed the attached Request Token has not already been used to retrieve another Access
+        Token. At this point, TweetExpress may confirm the receipt of the approval to the User and
+        delete the original Request Token which is no longer needed.
+    </para>
+
+    <para>
+        From this point forward, TweetExpress may use Twitter's API to post new tweets on the User's
+        behalf simply by accessing the API endpoints with a request that has been digitally signed
+        (via HMAC-SHA1) with a combination of TweetExpress' Consumer Secret and the Access Key being
+        used.
+    </para>
+
+    <para>
+        Although Twitter do not currently expire Access Tokens, the User is free to deauthorize
+        TweetExpress from their Twitter account settings.  Once deauthorized, TweetExpress' access
+        will be cut off and their Access Token rendered invalid.
+    </para>
+</sect2>

+ 79 - 0
documentation/manual/en/module_specs/Zend_Oauth-SecurityArchitecture.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect2 id="zend.oauth.introduction.security-architecture">
+    <title>Security Architecture</title>
+
+    <para>
+        OAuth was designed specifically to operate over an insecure HTTP connection and so the use
+        of HTTPS is not required though obviously it would be desireable if available. Should a
+        HTTPS connection be feasible, OAuth offers a signature method implementation called
+        PLAINTEXT which may be utilised. Over a typical unsecured HTTP connection, the use of
+        PLAINTEXT must be avoided and an alternate scheme using. The OAuth specification defines two
+        such signature methods: HMAC-SHA1 and RSA-SHA1. Both are fully supported by
+        <classname>Zend_Oauth</classname>.
+    </para>
+
+    <para>
+        These signature methods are quite easy to understand. As you can imagine, a PLAINTEXT
+        signature method does nothing that bears mentioning since it relies on HTTPS. If you were to
+        use PLAINTEXT over HTTP, you are left with a significant problem: there's no way to be sure
+        that the content of any OAuth enabled request (which would include the OAuth Access Token)
+        was altered en route. This is because unsecured HTTP requests are always at risk of
+        eavesdropping, Man In The Middle (MITM) attacks, or other risks whereby a request can be
+        retooled so to speak to perform tasks on behalf of the attacker by masquerading as the
+        origin application without being noticed by the service provider.
+    </para>
+
+    <para>
+        HMAC-SHA1 and RSA-SHA1 alleviate this risk by digitally signing all OAuth requests with the
+        original application's registered Consumer Secret. Assuming only the Consumer and the
+        Provider know what this secret is, a middle-man can alter requests all they wish - but they
+        will not be able to validly sign them and unsigned or invalidly signed requests would be
+        discarded by both parties. Digital signatures therefore offer a guarantee that validly
+        signed requests do come from the expected party and have not been altered en route. This is
+        the core of why OAuth can operate over an unsecure connection.
+    </para>
+
+    <para>
+        How these digital signatures operate depends on the method used, i.e. HMAC-SHA1, RSA-SHA1 or
+        perhaps another method defined by the service provider. HMAC-SHA1 is a simple mechanism
+        which generates a Message Authentication Code (MAC) using a cryptographic hash function
+        (i.e. SHA1) in combination with a secret key known only to the message sender and receiver
+        (i.e. the OAuth Consumer Secret and the authorized Access Key combined). This hashing
+        mechanism is applied to the parameters and content of any OAuth requests which are
+        concatenated into a "base signature string" as defined by the OAuth specification. 
+    </para>
+
+    <para>
+        RSA-SHA1 operates on similar principles except that the shared secret is, as you would
+        expect, each parties' RSA private key. Both sides would have the other's public key with
+        which to verify digital signatures. This does pose a level of risk compared to HMAC-SHA1
+        since the RSA method does not use the Access Key as part of the shared secret.  This means
+        that if the RSA private key of any Consumer is compromised, then all Access Tokens assigned
+        to that Consumer are also. RSA imposes an all or nothing scheme. In general, the majority of
+        service providers offering OAuth authorization have therefore tended to use HMAC-SHA1 by
+        default, and those who offer RSA-SHA1 may offer fallback support to HMAC-SHA1.
+    </para>
+
+    <para>
+        While digital signatures add to OAuth's security they are still vulnerable to other forms of
+        attack, such as replay attacks which copy earlier requests which were intercepted and
+        validly signed at that time.  An attacker can now resend the exact same request to a
+        Provider at will at any time and intercept its results. This poses a significant risk but it
+        is quiet simple to defend against - add a unique string (i.e. a nonce) to all requests which
+        changes per request (thus continually changing the signature string) but which can never be
+        reused because Providers actively track used nonces within the a certain window defined by
+        the timestamp also attached to a request. You might first suspect that once you stop
+        tracking a particular nonce, the replay could work but this ignore the timestamp which can
+        be used to determine a request's age at the time it was validly signed. One can assume that
+        a week old request used in an attempted replay should be summarily discarded!
+    </para>
+
+    <para>
+        As a final point, this is not an exhaustive look at the security architecture in OAuth. For
+        example, what if HTTP requests which contain both the Access Token and the Consumer Secret
+        are eavesdropped? The system relies on at one in the clear transmission of each unless HTTPS
+        is active, so the obvious conclusion is that where feasible HTTPS is to be preferred leaving
+        unsecured HTTP in place only where it is not possible or affordable to do so.
+    </para>
+</sect2>

+ 88 - 0
library/Zend/Oauth.php

@@ -0,0 +1,88 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Http_Client */
+require_once 'Zend/Http/Client.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth
+{
+    const REQUEST_SCHEME_HEADER      = 'header';
+    const REQUEST_SCHEME_POSTBODY    = 'postbody';
+    const REQUEST_SCHEME_QUERYSTRING = 'querystring';
+    const GET                        = 'GET';
+    const POST                       = 'POST';
+    const PUT                        = 'PUT';
+    const DELETE                     = 'DELETE';
+
+    /**
+     * Singleton instance if required of the HTTP client
+     *
+     * @var Zend_Http_Client
+     */
+    protected static $httpClient = null;
+
+    /**
+     * Allows the external environment to make Zend_Oauth use a specific
+     * Client instance.
+     *
+     * @param Zend_Http_Client $httpClient
+     * @return void
+     */
+    public static function setHttpClient(Zend_Http_Client $httpClient)
+    {
+        self::$httpClient = $httpClient;
+    }
+
+    /**
+     * Return the singleton instance of the HTTP Client. Note that
+     * the instance is reset and cleared of previous parameters and
+     * Authorization header values.
+     *
+     * @return Zend_Http_Client
+     */
+    public static function getHttpClient()
+    {
+        if (!isset(self::$httpClient)) {
+            self::$httpClient = new Zend_Http_Client;
+        } else {
+            self::$httpClient->setHeaders('Authorization', null);
+            self::$httpClient->resetParameters();
+        }
+        return self::$httpClient;
+    }
+
+    /**
+     * Simple mechanism to delete the entire singleton HTTP Client instance
+     * which forces an new instantiation for subsequent requests.
+     *
+     * @return void
+     */
+    public static function clearHttpClient()
+    {
+        self::$httpClient = null;
+    }
+}

+ 194 - 0
library/Zend/Oauth/Client.php

@@ -0,0 +1,194 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth */
+require_once 'Zend/Oauth.php';
+
+/** Zend_Http_Client */
+require_once 'Zend/Http/Client.php';
+
+/** Zend_Oauth_Http_Utility */
+require_once 'Zend/Oauth/Http/Utility.php';
+
+/** Zend_Oauth_Config */
+require_once 'Zend/Oauth/Config.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Client extends Zend_Http_Client
+{
+    /**
+     * Flag to indicate that the client has detected the server as supporting
+     * OAuth 1.0a
+     */
+    public static $supportsRevisionA = false;
+
+    /**
+     * Holds the current OAuth Configuration set encapsulated in an instance
+     * of Zend_Oauth_Config; it's not a Zend_Config instance since that level
+     * of abstraction is unnecessary and doesn't let me escape the accessors
+     * and mutators anyway!
+     *
+     * @var Zend_Oauth_Config
+     */
+    protected $_config = null;
+
+    /**
+     * Constructor; creates a new HTTP Client instance which itself is
+     * just a typical Zend_Http_Client subclass with some OAuth icing to
+     * assist in automating OAuth parameter generation, addition and
+     * cryptographioc signing of requests.
+     *
+     * @param  array $oauthOptions
+     * @param  string $uri
+     * @param  array|Zend_Config $config
+     * @return void
+     */
+    public function __construct(array $oauthOptions, $uri = null, $config = null)
+    {
+        parent::__construct($uri, $config);
+        $this->_config = new Zend_Oauth_Config;
+        if (!is_null($oauthOptions)) {
+            if ($oauthOptions instanceof Zend_Config) {
+                $oauthOptions = $oauthOptions->toArray();
+            }
+            $this->_config->setOptions($oauthOptions);
+        }
+    }
+
+    /**
+     * Same as Zend_Http_Client::setMethod() except it also creates an
+     * Oauth specific reference to the method type.
+     * Might be defunct and removed in a later iteration.
+     *
+     * @param  string $method
+     * @return Zend_Http_Client
+     */
+    public function setMethod($method = self::GET)
+    {
+        if ($method == self::GET) {
+            $this->setRequestMethod(self::GET);
+        } elseif($method == self::POST) {
+            $this->setRequestMethod(self::POST);
+        }
+        return parent::setMethod($method);
+    }
+
+    /**
+     * Same as Zend_Http_Client::request() except just before the request is
+     * executed, we automatically append any necessary OAuth parameters and
+     * sign the request using the relevant signature method.
+     *
+     * @param  string $method
+     * @return Zend_Http_Response
+     */
+    public function request($method = null)
+    {
+        if (!is_null($method)) {
+            $this->setMethod($method);
+        }
+        $this->prepareOauth();
+        return parent::request();
+    }
+
+    /**
+     * Performs OAuth preparation on the request before sending.
+     *
+     * This primarily means taking a request, correctly encoding and signing
+     * all parameters, and applying the correct OAuth scheme to the method
+     * being used.
+     *
+     * @return void
+     * @throws Zend_Oauth_Exception If POSTBODY scheme requested, but GET request method used; or if invalid request scheme provided
+     */
+    public function prepareOauth()
+    {
+        $requestScheme = $this->getRequestScheme();
+        $requestMethod = $this->getRequestMethod();
+        $query = null;
+        if ($requestScheme == Zend_Oauth::REQUEST_SCHEME_HEADER) {
+            $params = array();
+            if (!empty($this->paramsGet)) {
+                $params = array_merge($params, $this->paramsGet);
+                $query  = $this->getToken()->toQueryString(
+                    $this->getUri(true), $this->_config, $params
+                );
+            }
+            if (!empty($this->paramsPost)) {
+                $params = array_merge($params, $this->paramsPost);
+                $query  = $this->getToken()->toQueryString(
+                    $this->getUri(true), $this->_config, $params
+                );
+            }
+            $oauthHeaderValue = $this->getToken()->toHeader(
+                $this->getUri(true), $this->_config, $params
+            );
+            $this->setHeaders('Authorization', $oauthHeaderValue);
+        } elseif ($requestScheme == Zend_Oauth::REQUEST_SCHEME_POSTBODY) {
+            if ($requestMethod == self::GET) {
+                require_once 'Zend/Oauth/Exception.php';
+                throw new Zend_Oauth_Exception(
+                    'The client is configured to'
+                    . ' pass OAuth parameters through a POST body but request method'
+                    . ' is set to GET'
+                );
+            }
+            $raw = $this->getToken()->toQueryString(
+                $this->getUri(true), $this->_config, $this->paramsPost
+            );
+            $this->setRawData($raw);
+            $this->paramsPost = array();
+        } elseif ($requestScheme == Zend_Oauth::REQUEST_SCHEME_QUERYSTRING) {
+            $this->getUri()->setQuery('');
+            $query = $this->getToken()->toQueryString(
+                $this->getUri(true), $this->_config, $this->paramsGet
+            );
+            $this->getUri()->setQuery($query);
+            $this->paramsGet = array();
+        } else {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Invalid request scheme: ' . $requestScheme);
+        }
+    }
+
+    /**
+     * Simple Proxy to the current Zend_Oauth_Config method. It's that instance
+     * which holds all configuration methods and values this object also presents
+     * as it's API.
+     *
+     * @param  string $method
+     * @param  array $args
+     * @return mixed
+     * @throws Zend_Oauth_Exception if method does not exist in config object
+     */
+    public function __call($method, array $args)
+    {
+        if (!method_exists($this->_config, $method)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Method does not exist: ' . $method);
+        }
+        return call_user_func_array(array($this->_config,$method), $args);
+    }
+}

+ 658 - 0
library/Zend/Oauth/Config.php

@@ -0,0 +1,658 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth */
+require_once 'Zend/Oauth.php';
+
+/** Zend_Uri */
+require_once 'Zend/Uri.php';
+
+/** Zend_Oauth_Config_Interface */
+require_once 'Zend/Oauth/Config/ConfigInterface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Config implements Zend_Oauth_Config_ConfigInterface
+{
+    /**
+     * Signature method used when signing all parameters for an HTTP request
+     *
+     * @var string
+     */
+    protected $_signatureMethod = 'HMAC-SHA1';
+
+    /**
+     * Three request schemes are defined by OAuth, of which passing
+     * all OAuth parameters by Header is preferred. The other two are
+     * POST Body and Query String.
+     *
+     * @var string
+     */
+    protected $_requestScheme = Zend_Oauth::REQUEST_SCHEME_HEADER;
+
+    /**
+     * Preferred request Method - one of GET or POST - which Zend_Oauth
+     * will enforce as standard throughout the library. Generally a default
+     * of POST works fine unless a Provider specifically requires otherwise.
+     *
+     * @var string
+     */
+    protected $_requestMethod = Zend_Oauth::POST;
+
+    /**
+     * OAuth Version; This defaults to 1.0 - Must not be changed!
+     *
+     * @var string
+     */
+    protected $_version = '1.0';
+
+    /**
+     * This optional value is used to define where the user is redirected to
+     * after authorizing a Request Token from an OAuth Providers website.
+     * It's optional since a Provider may ask for this to be defined in advance
+     * when registering a new application for a Consumer Key.
+     *
+     * @var string
+     */
+    protected $_callbackUrl = null;
+
+    /**
+     * The URL root to append default OAuth endpoint paths.
+     *
+     * @var string
+     */
+    protected $_siteUrl = null;
+
+    /**
+     * The URL to which requests for a Request Token should be directed.
+     * When absent, assumed siteUrl+'/request_token'
+     *
+     * @var string
+     */
+    protected $_requestTokenUrl = null;
+
+    /**
+     * The URL to which requests for an Access Token should be directed.
+     * When absent, assumed siteUrl+'/access_token'
+     *
+     * @var string
+     */
+    protected $_accessTokenUrl = null;
+
+    /**
+     * The URL to which users should be redirected to authorize a Request Token.
+     * When absent, assumed siteUrl+'/authorize'
+     *
+     * @var string
+     */
+    protected $_authorizeUrl = null;
+
+    /**
+     * An OAuth application's Consumer Key.
+     *
+     * @var string
+     */
+    protected $_consumerKey = null;
+
+    /**
+     * Every Consumer Key has a Consumer Secret unless you're in RSA-land.
+     *
+     * @var string
+     */
+    protected $_consumerSecret = null;
+
+    /**
+     * If relevant, a PEM encoded RSA private key encapsulated as a
+     * Zend_Crypt_Rsa Key
+     *
+     * @var Zend_Crypt_Rsa_Key_Private
+     */
+    protected $_rsaPrivateKey = null;
+
+    /**
+     * If relevant, a PEM encoded RSA public key encapsulated as a
+     * Zend_Crypt_Rsa Key
+     *
+     * @var Zend_Crypt_Rsa_Key_Public
+     */
+    protected $_rsaPublicKey = null;
+
+    /**
+     * Generally this will nearly always be an Access Token represented as a
+     * Zend_Oauth_Token_Access object.
+     *
+     * @var Zend_Oauth_Token
+     */
+    protected $_token = null;
+
+    /**
+     * Constructor; create a new object with an optional array|Zend_Config
+     * instance containing initialising options.
+     *
+     * @param  array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        if (!is_null($options)) {
+            if ($options instanceof Zend_Config) {
+                $options = $options->toArray();
+            }
+            $this->setOptions($options);
+        }
+    }
+
+    /**
+     * Parse option array or Zend_Config instance and setup options using their
+     * relevant mutators.
+     *
+     * @param  array|Zend_Config $options
+     * @return Zend_Oauth_Config
+     */
+    public function setOptions(array $options)
+    {
+        foreach ($options as $key => $value) {
+            switch ($key) {
+                case 'consumerKey':
+                    $this->setConsumerKey($value);
+                    break;
+                case 'consumerSecret':
+                    $this->setConsumerSecret($value);
+                    break;
+                case 'signatureMethod':
+                    $this->setSignatureMethod($value);
+                    break;
+                case 'version':
+                    $this->setVersion($value);
+                    break;
+                case 'callbackUrl':
+                    $this->setCallbackUrl($value);
+                    break;
+                case 'siteUrl':
+                    $this->setSiteUrl($value);
+                    break;
+                case 'requestTokenUrl':
+                    $this->setRequestTokenUrl($value);
+                    break;
+                case 'accessTokenUrl':
+                    $this->setAccessTokenUrl($value);
+                    break;
+                case 'userAuthorizationUrl':
+                    $this->setUserAuthorizationUrl($value);
+                    break;
+                case 'authorizeUrl':
+                    $this->setAuthorizeUrl($value);
+                    break;
+                case 'requestMethod':
+                    $this->setRequestMethod($value);
+                    break;
+                case 'rsaPrivateKey':
+                    $this->setRsaPrivateKey($value);
+                    break;
+                case 'rsaPublicKey':
+                    $this->setRsaPublicKey($value);
+                    break;
+            }
+        }
+        if (isset($options['requestScheme'])) {
+            $this->setRequestScheme($options['requestScheme']);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set consumer key
+     *
+     * @param  string $key
+     * @return Zend_Oauth_Config
+     */
+    public function setConsumerKey($key)
+    {
+        $this->_consumerKey = $key;
+        return $this;
+    }
+
+    /**
+     * Get consumer key
+     *
+     * @return string
+     */
+    public function getConsumerKey()
+    {
+        return $this->_consumerKey;
+    }
+
+    /**
+     * Set consumer secret
+     *
+     * @param  string $secret
+     * @return Zend_Oauth_Config
+     */
+    public function setConsumerSecret($secret)
+    {
+        $this->_consumerSecret = $secret;
+        return $this;
+    }
+
+    /**
+     * Get consumer secret
+     *
+     * Returns RSA private key if set; otherwise, returns any previously set 
+     * consumer secret.
+     *
+     * @return string
+     */
+    public function getConsumerSecret()
+    {
+        if (!is_null($this->_rsaPrivateKey)) {
+            return $this->_rsaPrivateKey;
+        }
+        return $this->_consumerSecret;
+    }
+
+    /**
+     * Set signature method
+     *
+     * @param  string $method
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception if unsupported signature method specified
+     */
+    public function setSignatureMethod($method)
+    {
+        $method = strtoupper($method);
+        if (!in_array($method, array(
+                'HMAC-SHA1', 'HMAC-SHA256', 'RSA-SHA1', 'PLAINTEXT'
+            ))
+        ) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Unsupported signature method: '
+                . $method
+                . '. Supported are HMAC-SHA1, RSA-SHA1, PLAINTEXT and HMAC-SHA256');
+        }
+        $this->_signatureMethod = $method;;
+        return $this;
+    }
+
+    /**
+     * Get signature method
+     *
+     * @return string
+     */
+    public function getSignatureMethod()
+    {
+        return $this->_signatureMethod;
+    }
+
+    /**
+     * Set request scheme
+     *
+     * @param  string $scheme
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception if invalid scheme specified, or if POSTBODY set when request method of GET is specified
+     */
+    public function setRequestScheme($scheme)
+    {
+        $scheme = strtolower($scheme);
+        if (!in_array($scheme, array(
+                Zend_Oauth::REQUEST_SCHEME_HEADER,
+                Zend_Oauth::REQUEST_SCHEME_POSTBODY,
+                Zend_Oauth::REQUEST_SCHEME_QUERYSTRING,
+            ))
+        ) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $scheme . '\' is an unsupported request scheme'
+            );
+        }
+        if ($scheme == Zend_Oauth::REQUEST_SCHEME_POSTBODY
+            && $this->getRequestMethod() == Zend_Oauth::GET
+        ) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                'Cannot set POSTBODY request method if HTTP method set to GET'
+            );
+        }
+        $this->_requestScheme = $scheme;
+        return $this;
+    }
+
+    /**
+     * Get request scheme
+     *
+     * @return string
+     */
+    public function getRequestScheme()
+    {
+        return $this->_requestScheme;
+    }
+
+    /**
+     * Set version
+     *
+     * @param  string $version
+     * @return Zend_Oauth_Config
+     */
+    public function setVersion($version)
+    {
+        $this->_version = $version;
+        return $this;
+    }
+
+    /**
+     * Get version
+     *
+     * @return string
+     */
+    public function getVersion()
+    {
+        return $this->_version;
+    }
+
+    /**
+     * Set callback URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setCallbackUrl($url)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $this->_callbackUrl = $url;
+        return $this;
+    }
+
+    /**
+     * Get callback URL
+     *
+     * @return string
+     */
+    public function getCallbackUrl()
+    {
+        return $this->_callbackUrl;
+    }
+
+    /**
+     * Set site URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setSiteUrl($url)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $this->_siteUrl = $url;
+        return $this;
+    }
+
+    /**
+     * Get site URL
+     *
+     * @return string
+     */
+    public function getSiteUrl()
+    {
+        return $this->_siteUrl;
+    }
+
+    /**
+     * Set request token URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setRequestTokenUrl($url)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $this->_requestTokenUrl = rtrim($url, '/');
+        return $this;
+    }
+
+    /**
+     * Get request token URL
+     *
+     * If no request token URL has been set, but a site URL has, returns the 
+     * site URL with the string "/request_token" appended.
+     *
+     * @return string
+     */
+    public function getRequestTokenUrl()
+    {
+        if (!$this->_requestTokenUrl && $this->_siteUrl) {
+            return $this->_siteUrl . '/request_token';
+        }
+        return $this->_requestTokenUrl;
+    }
+
+    /**
+     * Set access token URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setAccessTokenUrl($url)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $this->_accessTokenUrl = rtrim($url, '/');
+        return $this;
+    }
+
+    /**
+     * Get access token URL
+     *
+     * If no access token URL has been set, but a site URL has, returns the 
+     * site URL with the string "/access_token" appended.
+     *
+     * @return string
+     */
+    public function getAccessTokenUrl()
+    {
+        if (!$this->_accessTokenUrl && $this->_siteUrl) {
+            return $this->_siteUrl . '/access_token';
+        }
+        return $this->_accessTokenUrl;
+    }
+
+    /**
+     * Set user authorization URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setUserAuthorizationUrl($url)
+    {
+        return $this->setAuthorizeUrl($url);
+    }
+
+    /**
+     * Set authorization URL
+     *
+     * @param  string $url
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid URLs
+     */
+    public function setAuthorizeUrl($url)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $this->_authorizeUrl = rtrim($url, '/');
+        return $this;
+    }
+
+    /**
+     * Get user authorization URL
+     *
+     * @return string
+     */
+    public function getUserAuthorizationUrl()
+    {
+        return $this->getAuthorizeUrl();
+    }
+
+    /**
+     * Get authorization URL
+     *
+     * If no authorization URL has been set, but a site URL has, returns the 
+     * site URL with the string "/authorize" appended.
+     *
+     * @return string
+     */
+    public function getAuthorizeUrl()
+    {
+        if (!$this->_authorizeUrl && $this->_siteUrl) {
+            return $this->_siteUrl . '/authorize';
+        }
+        return $this->_authorizeUrl;
+    }
+
+    /**
+     * Set request method
+     *
+     * @param  string $method
+     * @return Zend_Oauth_Config
+     * @throws Zend_Oauth_Exception for invalid request methods
+     */
+    public function setRequestMethod($method)
+    {
+        $method = strtoupper($method);
+        if (!in_array($method, array(
+                Zend_Oauth::GET, 
+                Zend_Oauth::POST, 
+                Zend_Oauth::PUT, 
+                Zend_Oauth::DELETE,
+            ))
+        ) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Invalid method: ' . $method);
+        }
+        $this->_requestMethod = $method;
+        return $this;
+    }
+
+    /**
+     * Get request method
+     *
+     * @return string
+     */
+    public function getRequestMethod()
+    {
+        return $this->_requestMethod;
+    }
+
+    /**
+     * Set RSA public key
+     *
+     * @param  Zend_Crypt_Rsa_Key_Public $key
+     * @return Zend_Oauth_Config
+     */
+    public function setRsaPublicKey(Zend_Crypt_Rsa_Key_Public $key)
+    {
+        $this->_rsaPublicKey = $key;
+        return $this;
+    }
+
+    /**
+     * Get RSA public key
+     *
+     * @return Zend_Crypt_Rsa_Key_Public
+     */
+    public function getRsaPublicKey()
+    {
+        return $this->_rsaPublicKey;
+    }
+
+    /**
+     * Set RSA private key
+     *
+     * @param  Zend_Crypt_Rsa_Key_Private $key
+     * @return Zend_Oauth_Config
+     */
+    public function setRsaPrivateKey(Zend_Crypt_Rsa_Key_Private $key)
+    {
+        $this->_rsaPrivateKey = $key;
+        return $this;
+    }
+
+    /**
+     * Get RSA private key
+     *
+     * @return Zend_Crypt_Rsa_Key_Private
+     */
+    public function getRsaPrivateKey()
+    {
+        return $this->_rsaPrivateKey;
+    }
+
+    /**
+     * Set OAuth token
+     *
+     * @param  Zend_Oauth_Token $token
+     * @return Zend_Oauth_Config
+     */
+    public function setToken(Zend_Oauth_Token $token)
+    {
+        $this->_token = $token;
+        return $this;
+    }
+
+    /**
+     * Get OAuth token
+     *
+     * @return Zend_Oauth_Token
+     */
+    public function getToken()
+    {
+        return $this->_token;
+    }
+}

+ 75 - 0
library/Zend/Oauth/Config/ConfigInterface.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Oauth_Config_ConfigInterface
+{
+    public function setOptions(array $options);
+
+    public function setConsumerKey($key);
+
+    public function getConsumerKey();
+
+    public function setConsumerSecret($secret);
+
+    public function getConsumerSecret();
+
+    public function setSignatureMethod($method);
+
+    public function getSignatureMethod();
+
+    public function setRequestScheme($scheme);
+
+    public function getRequestScheme();
+
+    public function setVersion($version);
+
+    public function getVersion();
+
+    public function setCallbackUrl($url);
+
+    public function getCallbackUrl();
+
+    public function setRequestTokenUrl($url);
+
+    public function getRequestTokenUrl();
+
+    public function setRequestMethod($method);
+
+    public function getRequestMethod();
+
+    public function setAccessTokenUrl($url);
+
+    public function getAccessTokenUrl();
+
+    public function setUserAuthorizationUrl($url);
+
+    public function getUserAuthorizationUrl();
+
+    public function setToken(Zend_Oauth_Token $token);
+
+    public function getToken();
+}

+ 272 - 0
library/Zend/Oauth/Consumer.php

@@ -0,0 +1,272 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth */
+require_once 'Zend/Oauth.php';
+
+/** Zend_Uri */
+require_once 'Zend/Uri.php';
+
+/** Zend_Oauth_Http_RequestToken */
+require_once 'Zend/Oauth/Http/RequestToken.php';
+
+/** Zend_Oauth_Http_UserAuthorization */
+require_once 'Zend/Oauth/Http/UserAuthorization.php';
+
+/** Zend_Oauth_Http_AccessToken */
+require_once 'Zend/Oauth/Http/AccessToken.php';
+
+/** Zend_Oauth_Token_AuthorizedRequest */
+require_once 'Zend/Oauth/Token/AuthorizedRequest.php';
+
+/** Zend_Oauth_Config */
+require_once 'Zend/Oauth/Config.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Consumer extends Zend_Oauth
+{
+    public $switcheroo = false; // replace later when this works
+
+    /**
+     * Request Token retrieved from OAuth Provider
+     *
+     * @var Zend_Oauth_Token_Request
+     */
+    protected $_requestToken = null;
+
+    /**
+     * Access token retrieved from OAuth Provider
+     *
+     * @var Zend_Oauth_Token_Access
+     */
+    protected $_accessToken = null;
+
+    /**
+     * @var Zend_Oauth_Config
+     */
+    protected $_config = null;
+
+    /**
+     * Constructor; create a new object with an optional array|Zend_Config
+     * instance containing initialising options.
+     *
+     * @param  array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        $this->_config = new Zend_Oauth_Config;
+        if (!is_null($options)) {
+            if ($options instanceof Zend_Config) {
+                $options = $options->toArray();
+            }
+            $this->_config->setOptions($options);
+        }
+    }
+
+    /**
+     * Attempts to retrieve a Request Token from an OAuth Provider which is
+     * later exchanged for an authorized Access Token used to access the
+     * protected resources exposed by a web service API.
+     *
+     * @param  null|array $customServiceParameters Non-OAuth Provider-specified parameters
+     * @param  null|string $httpMethod
+     * @param  null|Zend_Oauth_Http_RequestToken $request
+     * @return Zend_Oauth_Token_Request
+     */
+    public function getRequestToken(
+        array $customServiceParameters = null,
+        $httpMethod = null,
+        Zend_Oauth_Http_RequestToken $request = null
+    ) {
+        if (is_null($request)) {
+            $request = new Zend_Oauth_Http_RequestToken($this, $customServiceParameters);
+        } elseif(!is_null($customServiceParameters)) {
+            $request->setParameters($customServiceParameters);
+        }
+        if (!is_null($httpMethod)) {
+            $request->setMethod($httpMethod);
+        } else {
+            $request->setMethod($this->getRequestMethod());
+        }
+        $this->_requestToken = $request->execute();
+        return $this->_requestToken;
+    }
+
+    /**
+     * After a Request Token is retrieved, the user may be redirected to the
+     * OAuth Provider to authorize the application's access to their
+     * protected resources - the redirect URL being provided by this method.
+     * Once the user has authorized the application for access, they are
+     * redirected back to the application which can now exchange the previous
+     * Request Token for a fully authorized Access Token.
+     *
+     * @param  null|array $customServiceParameters
+     * @param  null|Zend_Oauth_Token_Request $token
+     * @param  null|Zend_OAuth_Http_UserAuthorization $redirect
+     * @return string
+     */
+    public function getRedirectUrl(
+        array $customServiceParameters = null,
+        Zend_Oauth_Token_Request $token = null,
+        Zend_Oauth_Http_UserAuthorization $redirect = null
+    ) {
+        if (is_null($redirect)) {
+            $redirect = new Zend_Oauth_Http_UserAuthorization($this, $customServiceParameters);
+        } elseif(!is_null($customServiceParameters)) {
+            $redirect->setParameters($customServiceParameters);
+        }
+        if (!is_null($token)) {
+            $this->_requestToken = $token;
+        }
+        return $redirect->getUrl();
+    }
+
+    /**
+     * Rather than retrieve a redirect URL for use, e.g. from a controller,
+     * one may perform an immediate redirect.
+     *
+     * Sends headers and exit()s on completion.
+     *
+     * @param  null|array $customServiceParameters
+     * @param  null|Zend_Oauth_Http_UserAuthorization $request
+     * @return void
+     */
+    public function redirect(
+        array $customServiceParameters = null,
+        Zend_Oauth_Http_UserAuthorization $request = null
+    ) {
+        $redirectUrl = $this->getRedirectUrl($customServiceParameters, $request);
+        header('Location: ' . $redirectUrl);
+        exit(1);
+    }
+
+    /**
+     * Retrieve an Access Token in exchange for a previously received/authorized
+     * Request Token.
+     *
+     * @param  array $queryData GET data returned in user's redirect from Provider
+     * @param  Zend_Oauth_Token_Request Request Token information
+     * @param  string $httpMethod
+     * @param  Zend_Oauth_Http_AccessToken $request
+     * @return Zend_Oauth_Token_Access
+     * @throws Zend_Oauth_Exception on invalid authorization token, non-matching response authorization token, or unprovided authorization token
+     */
+    public function getAccessToken(
+        $queryData, 
+        Zend_Oauth_Token_Request $token,
+        $httpMethod = null, 
+        Zend_Oauth_Http_AccessToken $request = null
+    ) {
+        $authorizedToken = new Zend_Oauth_Token_AuthorizedRequest($queryData);
+        if (!$authorizedToken->isValid()) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                'Response from Service Provider is not a valid authorized request token');
+        }
+        if (is_null($request)) {
+            $request = new Zend_Oauth_Http_AccessToken($this);
+        }
+
+        // OAuth 1.0a Verifier
+        if (!is_null($authorizedToken->getParam('oauth_verifier'))) {
+            $request->setParameters(array(
+                'oauth_verifier' => $authorizedToken->getParam('oauth_verifier')
+            ));
+        }
+        if (!is_null($httpMethod)) {
+            $request->setMethod($httpMethod);
+        } else {
+            $request->setMethod($this->getRequestMethod());
+        }
+        if (isset($token)) {
+            if ($authorizedToken->getToken() !== $token->getToken()) {
+                require_once 'Zend/Oauth/Exception.php';
+                throw new Zend_Oauth_Exception(
+                    'Authorized token from Service Provider does not match'
+                    . ' supplied Request Token details'
+                );
+            }
+        } else {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Request token must be passed to method');
+        }
+        $this->_requestToken = $token;
+        $this->_accessToken = $request->execute();
+        return $this->_accessToken;
+    }
+
+    /**
+     * Return whatever the last Request Token retrieved was while using the
+     * current Consumer instance.
+     *
+     * @return Zend_Oauth_Token_Request
+     */
+    public function getLastRequestToken()
+    {
+        return $this->_requestToken;
+    }
+
+    /**
+     * Return whatever the last Access Token retrieved was while using the
+     * current Consumer instance.
+     *
+     * @return Zend_Oauth_Token_Access
+     */
+    public function getLastAccessToken()
+    {
+        return $this->_accessToken;
+    }
+
+    /**
+     * Alias to self::getLastAccessToken()
+     *
+     * @return Zend_Oauth_Token_Access
+     */
+    public function getToken()
+    {
+        return $this->_accessToken;
+    }
+
+    /**
+     * Simple Proxy to the current Zend_Oauth_Config method. It's that instance
+     * which holds all configuration methods and values this object also presents
+     * as it's API.
+     *
+     * @param  string $method
+     * @param  array $args
+     * @return mixed
+     * @throws Zend_Oauth_Exception if method does not exist in config object
+     */
+    public function __call($method, array $args)
+    {
+        if (!method_exists($this->_config, $method)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Method does not exist: '.$method);
+        }
+        return call_user_func_array(array($this->_config,$method), $args);
+    }
+}

+ 32 - 0
library/Zend/Oauth/Exception.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Exception extends Zend_Exception {}

+ 266 - 0
library/Zend/Oauth/Http.php

@@ -0,0 +1,266 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http_Utility */
+require_once 'Zend/Oauth/Http/Utility.php';
+
+/** Zend_Uri_Http */
+require_once 'Zend/Uri/Http.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Http
+{
+    /**
+     * Array of all custom service parameters to be sent in the HTTP request
+     * in addition to the usual OAuth parameters.
+     *
+     * @var array
+     */
+    protected $_parameters = array();
+
+    /**
+     * Reference to the Zend_Oauth_Consumer instance in use.
+     *
+     * @var string
+     */
+    protected $_consumer = null;
+
+    /**
+     * OAuth specifies three request methods, this holds the current preferred
+     * one which by default uses the Authorization Header approach for passing
+     * OAuth parameters, and a POST body for non-OAuth custom parameters.
+     *
+     * @var string
+     */
+    protected $_preferredRequestScheme = null;
+
+    /**
+     * Request Method for the HTTP Request.
+     *
+     * @var string
+     */
+    protected $_preferredRequestMethod = Zend_Oauth::POST;
+
+    /**
+     * Instance of the general Zend_Oauth_Http_Utility class.
+     *
+     * @var Zend_Oauth_Http_Utility
+     */
+    protected $_httpUtility = null;
+
+    /**
+     * Constructor
+     *
+     * @param  Zend_Oauth_Consumer $consumer
+     * @param  null|array $parameters
+     * @param  null|Zend_Oauth_Http_Utility $utility
+     * @return void
+     */
+    public function __construct(
+        Zend_Oauth_Consumer $consumer, 
+        array $parameters = null,
+        Zend_Oauth_Http_Utility $utility = null
+    ) {
+        $this->_consumer = $consumer;
+        $this->_preferredRequestScheme = $this->_consumer->getRequestScheme();
+        if (!is_null($parameters)) {
+            $this->setParameters($parameters);
+        }
+        if (!is_null($utility)) {
+            $this->_httpUtility = $utility;
+        } else {
+            $this->_httpUtility = new Zend_Oauth_Http_Utility;
+        }
+    }
+
+    /**
+     * Set a preferred HTTP request method.
+     *
+     * @param  string $method
+     * @return Zend_Oauth_Http
+     */
+    public function setMethod($method)
+    {
+        if (!in_array($method, array(Zend_Oauth::POST, Zend_Oauth::GET))) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('invalid HTTP method: ' . $method);
+        }
+        $this->_preferredRequestMethod = $method;
+        return $this;
+    }
+
+    /**
+     * Preferred HTTP request method accessor.
+     *
+     * @return string
+     */
+    public function getMethod()
+    {
+        return $this->_preferredRequestMethod;
+    }
+
+    /**
+     * Mutator to set an array of custom parameters for the HTTP request.
+     *
+     * @param  array $customServiceParameters
+     * @return Zend_Oauth_Http
+     */
+    public function setParameters(array $customServiceParameters)
+    {
+        $this->_parameters = $customServiceParameters;
+        return $this;
+    }
+
+    /**
+     * Accessor for an array of custom parameters.
+     *
+     * @return array
+     */
+    public function getParameters()
+    {
+        return $this->_parameters;
+    }
+
+    /**
+     * Return the Consumer instance in use.
+     *
+     * @return Zend_Oauth_Consumer
+     */
+    public function getConsumer()
+    {
+        return $this->_consumer;
+    }
+
+    /**
+     * Commence a request cycle where the current HTTP method and OAuth
+     * request scheme set an upper preferred HTTP request style and where
+     * failures generate a new HTTP request style further down the OAuth
+     * preference list for OAuth Request Schemes.
+     * On success, return the Request object that results for processing.
+     *
+     * @param  array $params
+     * @return Zend_Http_Response
+     * @throws Zend_Oauth_Exception on HTTP request errors
+     * @todo   Remove cycling?; Replace with upfront do-or-die configuration
+     */
+    public function startRequestCycle(array $params)
+    {
+        $response = null;
+        $body     = null;
+        $status   = null;
+        try {
+            $response = $this->_attemptRequest($params);
+        } catch (Zend_Http_Client_Exception $e) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception('Error in HTTP request', null, $e);
+        }
+        if (!is_null($response)) {
+            $body   = $response->getBody();
+            $status = $response->getStatus();
+        }
+        if (is_null($response) // Request failure/exception
+            || $status == 500  // Internal Server Error
+            || $status == 400  // Bad Request
+            || $status == 401  // Unauthorized
+            || empty($body)    // Missing token
+        ) {
+            $this->_assessRequestAttempt($response);
+            $response = $this->startRequestCycle($params);
+        }
+        return $response;
+    }
+
+    /**
+     * Return an instance of Zend_Http_Client configured to use the Query
+     * String scheme for an OAuth driven HTTP request.
+     *
+     * @param array $params
+     * @param string $url
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemeQueryStringClient(array $params, $url)
+    {
+        $client = Zend_Oauth::getHttpClient();
+        $client->setUri($url);
+        $client->getUri()->setQuery(
+            $this->_httpUtility->toEncodedQueryString($params)
+        );
+        $client->setMethod($this->_preferredRequestMethod);
+        return $client;
+    }
+
+    /**
+     * Manages the switch from OAuth request scheme to another lower preference
+     * scheme during a request cycle.
+     *
+     * @param  Zend_Http_Response
+     * @return void
+     * @throws Zend_Oauth_Exception if unable to retrieve valid token response
+     */
+    protected function _assessRequestAttempt(Zend_Http_Response $response = null)
+    {
+        switch ($this->_preferredRequestScheme) {
+            case Zend_Oauth::REQUEST_SCHEME_HEADER:
+                $this->_preferredRequestScheme = Zend_Oauth::REQUEST_SCHEME_POSTBODY;
+                break;
+            case Zend_Oauth::REQUEST_SCHEME_POSTBODY:
+                $this->_preferredRequestScheme = Zend_Oauth::REQUEST_SCHEME_QUERYSTRING;
+                break;
+            default:
+                require_once 'Zend/Oauth/Exception.php';
+                throw new Zend_Oauth_Exception(
+                    'Could not retrieve a valid Token response from Token URL:'
+                    . (!is_null($response) 
+                        ? PHP_EOL . $response->getBody()
+                        : ' No body - check for headers')
+                );
+        }
+    }
+
+    /**
+     * Generates a valid OAuth Authorization header based on the provided
+     * parameters and realm.
+     *
+     * @param  array $params
+     * @param  string $realm
+     * @return string
+     */
+    protected function _toAuthorizationHeader(array $params, $realm = null)
+    {
+        $headerValue = array();
+        $headerValue[] = 'OAuth realm="' . $realm . '"';
+        foreach ($params as $key => $value) {
+            if (!preg_match("/^oauth_/", $key)) {
+                continue;
+            }
+            $headerValue[] = Zend_Oauth_Http_Utility::urlEncode($key)
+                           . '="'
+                           . Zend_Oauth_Http_Utility::urlEncode($value)
+                           . '"';
+        }
+        return implode(",", $headerValue);
+    }
+}

+ 189 - 0
library/Zend/Oauth/Http/AccessToken.php

@@ -0,0 +1,189 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http */
+require_once 'Zend/Oauth/Http.php';
+
+/** Zend_Oauth_Token_Access */
+require_once 'Zend/Oauth/Token/Access.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Http_AccessToken extends Zend_Oauth_Http
+{
+    /**
+     * Singleton instance if required of the HTTP client
+     *
+     * @var Zend_Http_Client
+     */
+    protected $_httpClient = null;
+
+    /**
+     * Initiate a HTTP request to retrieve an Access Token.
+     *
+     * @return Zend_Oauth_Token_Access
+     */
+    public function execute()
+    {
+        $params   = $this->assembleParams();
+        $response = $this->startRequestCycle($params);
+        $return   = new Zend_Oauth_Token_Access($response);
+        return $return;
+    }
+
+    /**
+     * Assemble all parameters for an OAuth Access Token request.
+     *
+     * @return array
+     */
+    public function assembleParams()
+    {
+        $params = array(
+            'oauth_consumer_key'     => $this->_consumer->getConsumerKey(),
+            'oauth_nonce'            => $this->_httpUtility->generateNonce(),
+            'oauth_signature_method' => $this->_consumer->getSignatureMethod(),
+            'oauth_timestamp'        => $this->_httpUtility->generateTimestamp(),
+            'oauth_token'            => $this->_consumer->getLastRequestToken()->getToken(),
+            'oauth_version'          => $this->_consumer->getVersion(),
+        );
+
+        if (!empty($this->_parameters)) {
+            $params = array_merge($params, $this->_parameters);
+        }
+
+        $params['oauth_signature'] = $this->_httpUtility->sign(
+            $params,
+            $this->_consumer->getSignatureMethod(),
+            $this->_consumer->getConsumerSecret(),
+            $this->_consumer->getLastRequestToken()->getTokenSecret(),
+            $this->_preferredRequestMethod,
+            $this->_consumer->getAccessTokenUrl()
+        );
+
+        return $params;
+    }
+
+    /**
+     * Generate and return a HTTP Client configured for the Header Request Scheme
+     * specified by OAuth, for use in requesting an Access Token.
+     *
+     * @param  array $params
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemeHeaderClient(array $params)
+    {
+        $params      = $this->_cleanParamsOfIllegalCustomParameters($params);
+        $headerValue = $this->_toAuthorizationHeader($params);
+        $client      = Zend_Oauth::getHttpClient();
+
+        $client->setUri($this->_consumer->getAccessTokenUrl());
+        $client->setHeaders('Authorization', $headerValue);
+        $client->setMethod($this->_preferredRequestMethod);
+
+        return $client;
+    }
+
+    /**
+     * Generate and return a HTTP Client configured for the POST Body Request
+     * Scheme specified by OAuth, for use in requesting an Access Token.
+     *
+     * @param  array $params
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemePostBodyClient(array $params)
+    {
+        $params = $this->_cleanParamsOfIllegalCustomParameters($params);
+        $client = Zend_Oauth::getHttpClient();
+        $client->setUri($this->_consumer->getAccessTokenUrl());
+        $client->setMethod($this->_preferredRequestMethod);
+        $client->setRawData(
+            $this->_httpUtility->toEncodedQueryString($params)
+        );
+        $client->setHeaders(
+            Zend_Http_Client::CONTENT_TYPE,
+            Zend_Http_Client::ENC_URLENCODED
+        );
+        return $client;
+    }
+
+    /**
+     * Generate and return a HTTP Client configured for the Query String Request
+     * Scheme specified by OAuth, for use in requesting an Access Token.
+     *
+     * @param  array $params
+     * @param  string $url
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemeQueryStringClient(array $params, $url)
+    {
+        $params = $this->_cleanParamsOfIllegalCustomParameters($params);
+        return parent::getRequestSchemeQueryStringClient($params, $url);
+    }
+
+    /**
+     * Attempt a request based on the current configured OAuth Request Scheme and
+     * return the resulting HTTP Response.
+     *
+     * @param  array $params
+     * @return Zend_Http_Response
+     */
+    protected function _attemptRequest(array $params)
+    {
+        switch ($this->_preferredRequestScheme) {
+            case Zend_Oauth::REQUEST_SCHEME_HEADER:
+                $httpClient = $this->getRequestSchemeHeaderClient($params);
+                break;
+            case Zend_Oauth::REQUEST_SCHEME_POSTBODY:
+                $httpClient = $this->getRequestSchemePostBodyClient($params);
+                break;
+            case Zend_Oauth::REQUEST_SCHEME_QUERYSTRING:
+                $httpClient = $this->getRequestSchemeQueryStringClient($params,
+                    $this->_consumer->getAccessTokenUrl());
+                break;
+        }
+        return $httpClient->request();
+    }
+
+    /**
+     * Access Token requests specifically may not contain non-OAuth parameters.
+     * So these should be striped out and excluded. Detection is easy since
+     * specified OAuth parameters start with "oauth_", Extension params start
+     * with "xouth_", and no other parameters should use these prefixes.
+     *
+     * xouth params are not currently allowable.
+     *
+     * @param  array $params
+     * @return array
+     */
+    protected function _cleanParamsOfIllegalCustomParameters(array $params)
+    {
+        foreach ($params as $key=>$value) {
+            if (!preg_match("/^oauth_/", $key)) {
+                unset($params[$key]);
+            }
+        }
+        return $params;
+    }
+}

+ 162 - 0
library/Zend/Oauth/Http/RequestToken.php

@@ -0,0 +1,162 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http */
+require_once 'Zend/Oauth/Http.php';
+
+/** Zend_Oauth_Token_Request */
+require_once 'Zend/Oauth/Token/Request.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Http_RequestToken extends Zend_Oauth_Http
+{
+    /**
+     * Singleton instance if required of the HTTP client
+     *
+     * @var Zend_Http_Client
+     */
+    protected $_httpClient = null;
+
+    /**
+     * Initiate a HTTP request to retrieve a Request Token.
+     *
+     * @return Zend_Oauth_Token_Request
+     */
+    public function execute()
+    {
+        $params   = $this->assembleParams();
+        $response = $this->startRequestCycle($params);
+        $return   = new Zend_Oauth_Token_Request($response);
+        return $return;
+    }
+
+    /**
+     * Assemble all parameters for an OAuth Request Token request.
+     *
+     * @return array
+     */
+    public function assembleParams()
+    {
+        $params = array(
+            'oauth_consumer_key'     => $this->_consumer->getConsumerKey(),
+            'oauth_nonce'            => $this->_httpUtility->generateNonce(),
+            'oauth_timestamp'        => $this->_httpUtility->generateTimestamp(),
+            'oauth_signature_method' => $this->_consumer->getSignatureMethod(),
+            'oauth_version'          => $this->_consumer->getVersion(),
+        );
+
+        // indicates we support 1.0a
+        if ($this->_consumer->getCallbackUrl()) {
+            $params['oauth_callback'] = $this->_consumer->getCallbackUrl();
+        } else {
+            $params['oauth_callback'] = 'oob';
+        }
+
+        if (!empty($this->_parameters)) {
+            $params = array_merge($params, $this->_parameters);
+        }
+
+        $params['oauth_signature'] = $this->_httpUtility->sign(
+            $params,
+            $this->_consumer->getSignatureMethod(),
+            $this->_consumer->getConsumerSecret(),
+            null,
+            $this->_preferredRequestMethod,
+            $this->_consumer->getRequestTokenUrl()
+        );
+
+        return $params;
+    }
+
+    /**
+     * Generate and return a HTTP Client configured for the Header Request Scheme
+     * specified by OAuth, for use in requesting a Request Token.
+     *
+     * @param array $params
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemeHeaderClient(array $params)
+    {
+        $headerValue = $this->_httpUtility->toAuthorizationHeader(
+            $params
+        );
+        $client = Zend_Oauth::getHttpClient();
+        $client->setUri($this->_consumer->getRequestTokenUrl());
+        $client->setHeaders('Authorization', $headerValue);
+        $rawdata = $this->_httpUtility->toEncodedQueryString($params, true);
+        if (!empty($rawdata)) {
+            $client->setRawData($rawdata);
+        }
+        $client->setMethod($this->_preferredRequestMethod);
+        return $client;
+    }
+
+    /**
+     * Generate and return a HTTP Client configured for the POST Body Request
+     * Scheme specified by OAuth, for use in requesting a Request Token.
+     *
+     * @param  array $params
+     * @return Zend_Http_Client
+     */
+    public function getRequestSchemePostBodyClient(array $params)
+    {
+        $client = Zend_Oauth::getHttpClient();
+        $client->setUri($this->_consumer->getRequestTokenUrl());
+        $client->setMethod($this->_preferredRequestMethod);
+        $client->setRawData(
+            $this->_httpUtility->toEncodedQueryString($params)
+        );
+        $client->setHeaders(
+            Zend_Http_Client::CONTENT_TYPE,
+            Zend_Http_Client::ENC_URLENCODED
+        );
+        return $client;
+    }
+
+    /**
+     * Attempt a request based on the current configured OAuth Request Scheme and
+     * return the resulting HTTP Response.
+     *
+     * @param  array $params
+     * @return Zend_Http_Response
+     */
+    protected function _attemptRequest(array $params)
+    {
+        switch ($this->_preferredRequestScheme) {
+            case Zend_Oauth::REQUEST_SCHEME_HEADER:
+                $httpClient = $this->getRequestSchemeHeaderClient($params);
+                break;
+            case Zend_Oauth::REQUEST_SCHEME_POSTBODY:
+                $httpClient = $this->getRequestSchemePostBodyClient($params);
+                break;
+            case Zend_Oauth::REQUEST_SCHEME_QUERYSTRING:
+                $httpClient = $this->getRequestSchemeQueryStringClient($params,
+                    $this->_consumer->getRequestTokenUrl());
+                break;
+        }
+        return $httpClient->request();
+    }
+}

+ 78 - 0
library/Zend/Oauth/Http/UserAuthorization.php

@@ -0,0 +1,78 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http */
+require_once 'Zend/Oauth/Http.php';
+
+/** Zend_Uri_Http */
+require_once 'Zend/Uri/Http.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Http_UserAuthorization extends Zend_Oauth_Http
+{
+    /**
+     * Generate a redirect URL from the allowable parameters and configured
+     * values.
+     *
+     * @return string
+     */
+    public function getUrl()
+    {
+        $params = $this->assembleParams();
+        $uri    = Zend_Uri_Http::fromString($this->_consumer->getUserAuthorizationUrl());
+
+        $uri->setQuery(
+            $this->_httpUtility->toEncodedQueryString($params)
+        );
+
+        return $uri->getUri();
+    }
+
+    /**
+     * Assemble all parameters for inclusion in a redirect URL.
+     *
+     * @return array
+     */
+    public function assembleParams()
+    {
+        $params = array(
+            'oauth_token' => $this->_consumer->getLastRequestToken()->getToken(),
+        );
+
+        if (!Zend_Oauth_Client::$supportsRevisionA) {
+            $callback = $this->_consumer->getCallbackUrl();
+            if (!empty($callback)) {
+                $params['oauth_callback'] = $callback;
+            }
+        }
+
+        if (!empty($this->_parameters)) {
+            $params = array_merge($params, $this->_parameters);
+        }
+
+        return $params;
+    }
+}

+ 213 - 0
library/Zend/Oauth/Http/Utility.php

@@ -0,0 +1,213 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth */
+require_once 'Zend/Oauth.php';
+
+/** Zend_Oauth_Http */
+require_once 'Zend/Oauth/Http.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Http_Utility
+{
+    /**
+     * Assemble all parameters for a generic OAuth request - i.e. no special
+     * params other than the defaults expected for any OAuth query.
+     *
+     * @param  string $url
+     * @param  Zend_Oauth_Config_ConfigInterface $config
+     * @param  null|array $serviceProviderParams
+     * @return array
+     */
+    public function assembleParams(
+        $url, 
+        Zend_Oauth_Config_ConfigInterface $config,
+        array $serviceProviderParams = null
+    ) {
+        $params = array(
+            'oauth_consumer_key'     => $config->getConsumerKey(),
+            'oauth_nonce'            => $this->generateNonce(),
+            'oauth_signature_method' => $config->getSignatureMethod(),
+            'oauth_timestamp'        => $this->generateTimestamp(),
+            'oauth_token'            => $config->getToken()->getToken(),
+            'oauth_version'          => $config->getVersion(),
+        );
+
+        if (!is_null($serviceProviderParams)) {
+            $params = array_merge($params, $serviceProviderParams);
+        }
+
+        $params['oauth_signature'] = $this->sign(
+            $params,
+            $config->getSignatureMethod(),
+            $config->getConsumerSecret(),
+            $config->getToken()->getTokenSecret(),
+            $config->getRequestMethod(),
+            $url
+        );
+
+        return $params;
+    }
+
+    /**
+     * Given both OAuth parameters and any custom parametere, generate an
+     * encoded query string. This method expects parameters to have been
+     * assembled and signed beforehand.
+     *
+     * @param array $params
+     * @param bool $customParamsOnly Ignores OAuth params e.g. for requests using OAuth Header
+     * @return string
+     */
+    public function toEncodedQueryString(array $params, $customParamsOnly = false)
+    {
+        if ($customParamsOnly) {
+            foreach ($params as $key=>$value) {
+                if (preg_match("/^oauth_/", $key)) {
+                    unset($params[$key]);
+                }
+            }
+        }
+        $encodedParams = array();
+        foreach ($params as $key => $value) {
+            $encodedParams[] = self::urlEncode($key) 
+                             . '=' 
+                             . self::urlEncode($value);
+        }
+        return implode('&', $encodedParams);
+    }
+
+    /**
+     * Cast to authorization header
+     * 
+     * @param  array $params 
+     * @param  null|string $realm 
+     * @param  bool $excludeCustomParams 
+     * @return void
+     */
+    public function toAuthorizationHeader(array $params, $realm = null, $excludeCustomParams = true)
+    {
+        $headerValue = array(
+            'OAuth realm="' . $realm . '"',
+        );
+
+        foreach ($params as $key => $value) {
+            if ($excludeCustomParams) {
+                if (!preg_match("/^oauth_/", $key)) {
+                    continue;
+                }
+            }
+            $headerValue[] = self::urlEncode($key) 
+                           . '="'
+                           . self::urlEncode($value) . '"';
+        }
+        return implode(",", $headerValue);
+    }
+
+    /**
+     * Sign request
+     * 
+     * @param  array $params 
+     * @param  string $signatureMethod 
+     * @param  string $consumerSecret 
+     * @param  null|string $tokenSecret 
+     * @param  null|string $method 
+     * @param  null|string $url 
+     * @return string
+     */
+    public function sign(
+        array $params, $signatureMethod, $consumerSecret, $tokenSecret = null, $method = null, $url = null
+    ) {
+        $className = '';
+        $hashAlgo  = null;
+        $parts     = explode('-', $signatureMethod);
+        if (count($parts) > 1) {
+            $className = 'Zend_Oauth_Signature_' . ucfirst(strtolower($parts[0]));
+            $hashAlgo  = $parts[1];
+        } else {
+            $className = 'Zend_Oauth_Signature_' . ucfirst(strtolower($signatureMethod));
+        }
+
+        require_once str_replace('_', '/', $className) . '.php';
+        $signatureObject = new $className($consumerSecret, $tokenSecret, $hashAlgo);
+        return $signatureObject->sign($params, $method, $url);
+    }
+
+    /**
+     * Parse query string
+     * 
+     * @param  mixed $query 
+     * @return array
+     */
+    public function parseQueryString($query)
+    {
+        $params = array();
+        if (empty($query)) {
+            return array();
+        }
+
+        // Not remotely perfect but beats parse_str() which converts
+        // periods and uses urldecode, not rawurldecode.
+        $parts = explode('&', $query);
+        foreach ($parts as $pair) {
+            $kv = explode('=', $pair);
+            $params[rawurldecode($kv[0])] = rawurldecode($kv[1]);
+        }
+        return $params;
+    }
+
+    /**
+     * Generate nonce
+     * 
+     * @return string
+     */
+    public function generateNonce()
+    {
+        return md5(uniqid(rand(), true));
+    }
+
+    /**
+     * Generate timestamp
+     * 
+     * @return int
+     */
+    public function generateTimestamp()
+    {
+        return time();
+    }
+
+    /**
+     * urlencode a value
+     * 
+     * @param  string $value 
+     * @return string
+     */
+    public static function urlEncode($value)
+    {
+        $encoded = rawurlencode($value);
+        $encoded = str_replace('%7E', '~', $encoded);
+        return $encoded;
+    }
+}

+ 54 - 0
library/Zend/Oauth/Signature/Hmac.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Signature_SignatureAbstract */
+require_once 'Zend/Oauth/Signature/SignatureAbstract.php';
+
+/** Zend_Crypt_Hmac */
+require_once 'Zend/Crypt/Hmac.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Signature_Hmac extends Zend_Oauth_Signature_SignatureAbstract
+{
+    /**
+     * Sign a request
+     * 
+     * @param  array $params 
+     * @param  mixed $method 
+     * @param  mixed $url 
+     * @return string
+     */
+    public function sign(array $params, $method = null, $url = null)
+    {
+        $binaryHash = Zend_Crypt_Hmac::compute(
+            $this->_key,
+            $this->_hashAlgorithm,
+            $this->_getBaseSignatureString($params, $method, $url),
+            Zend_Crypt_Hmac::BINARY
+        );
+        return base64_encode($binaryHash);
+    }
+}

+ 49 - 0
library/Zend/Oauth/Signature/Plaintext.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Signature_SignatureAbstract */
+require_once 'Zend/Oauth/Signature/SignatureAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Signature_Plaintext extends Zend_Oauth_Signature_SignatureAbstract
+{
+    /**
+     * Sign a request
+     * 
+     * @param  array $params 
+     * @param  null|string $method 
+     * @param  null|string $url 
+     * @return string
+     */
+    public function sign(array $params, $method = null, $url = null)
+    {
+        if (is_null($this->_tokenSecret)) {
+            return $this->_consumerSecret . '&';
+        }
+        $return = implode('&', array($this->_consumerSecret, $this->_tokenSecret));
+        return $return;
+    }
+}

+ 65 - 0
library/Zend/Oauth/Signature/Rsa.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Signature_SignatureAbstract */
+require_once 'Zend/Oauth/Signature/SignatureAbstract.php';
+
+/** Zend_Crypt_Rsa */
+require_once 'Zend/Crypt/Rsa.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Signature_Rsa extends Zend_Oauth_Signature_SignatureAbstract
+{
+    /**
+     * Sign a request
+     * 
+     * @param  array $params 
+     * @param  null|string $method 
+     * @param  null|string $url 
+     * @return string
+     */
+    public function sign(array $params, $method = null, $url = null) 
+    {
+        $rsa = new Zend_Crypt_Rsa;
+        $rsa->setHashAlgorithm($this->_hashAlgorithm);
+        $sign = $rsa->sign(
+            $this->_getBaseSignatureString($params, $method, $url),
+            $this->_key,
+            Zend_Crypt_Rsa::BASE64
+        );
+        return $sign;
+    }
+
+    /**
+     * Assemble encryption key
+     * 
+     * @return string
+     */
+    protected function _assembleKey()
+    {
+        return $this->_consumerSecret;
+    }
+}

+ 183 - 0
library/Zend/Oauth/Signature/SignatureAbstract.php

@@ -0,0 +1,183 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http_Utility */
+require_once 'Zend/Oauth/Http/Utility.php';
+
+/** Zend_Uri_Http */
+require_once 'Zend/Uri/Http.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Oauth_Signature_SignatureAbstract
+{
+    /**
+     * Hash algorithm to use when generating signature
+     * @var string
+     */
+    protected $_hashAlgorithm = null;
+
+    /**
+     * Key to use when signing
+     * @var string
+     */
+    protected $_key = null;
+
+    /**
+     * Consumer secret
+     * @var string
+     */
+    protected $_consumerSecret = null;
+
+    /**
+     * Token secret
+     * @var string
+     */
+    protected $_tokenSecret = '';
+
+    /**
+     * Constructor
+     * 
+     * @param  string $consumerSecret 
+     * @param  null|string $tokenSecret 
+     * @param  null|string $hashAlgo 
+     * @return void
+     */
+    public function __construct($consumerSecret, $tokenSecret = null, $hashAlgo = null)
+    {
+        $this->_consumerSecret = $consumerSecret;
+        if (isset($tokenSecret)) {
+            $this->_tokenSecret = $tokenSecret;
+        }
+        $this->_key = $this->_assembleKey();
+        if (isset($hashAlgo)) {
+            $this->_hashAlgorithm = $hashAlgo;
+        }
+    }
+
+    /**
+     * Sign a request
+     * 
+     * @param  array $params 
+     * @param  null|string $method 
+     * @param  null|string $url 
+     * @return string
+     */
+    public abstract function sign(array $params, $method = null, $url = null);
+
+    /**
+     * Normalize the base signature URL
+     * 
+     * @param  string $url 
+     * @return string
+     */
+    public function normaliseBaseSignatureUrl($url)
+    {
+        $uri = Zend_Uri_Http::fromString($url);
+        if ($uri->getScheme() == 'http' && $uri->getPort() == '80') {
+            $uri->setPort('');
+        } elseif ($uri->getScheme() == 'https' && $uri->getPort() == '443') {
+            $uri->setPort('');
+        }
+        $uri->setQuery('');
+        $uri->setFragment('');
+        $uri->setHost(strtolower($uri->getHost()));
+        return $uri->getUri(true);
+    }
+
+    /**
+     * Assemble key from consumer and token secrets
+     * 
+     * @return string
+     */
+    protected function _assembleKey()
+    {
+        $parts = array($this->_consumerSecret);
+        if (!is_null($this->_tokenSecret)) {
+            $parts[] = $this->_tokenSecret;
+        }
+        foreach ($parts as $key => $secret) {
+            $parts[$key] = Zend_Oauth_Http_Utility::urlEncode($secret);
+        }
+        return implode('&', $parts);
+    }
+
+    /**
+     * Get base signature string
+     * 
+     * @param  array $params 
+     * @param  null|string $method 
+     * @param  null|string $url 
+     * @return string
+     */
+    protected function _getBaseSignatureString(array $params, $method = null, $url = null)
+    {
+        $encodedParams = array();
+        foreach ($params as $key => $value) {
+            $encodedParams[Zend_Oauth_Http_Utility::urlEncode($key)] = 
+                Zend_Oauth_Http_Utility::urlEncode($value);
+        }
+        $baseStrings = array();
+        if (isset($method)) {
+            $baseStrings[] = strtoupper($method);
+        }
+        if (isset($url)) {
+            // should normalise later
+            $baseStrings[] = Zend_Oauth_Http_Utility::urlEncode(
+                $this->normaliseBaseSignatureUrl($url)
+            );
+        }
+        if (isset($encodedParams['oauth_signature'])) {
+            unset($encodedParams['oauth_signature']);
+        }
+        $baseStrings[] = Zend_Oauth_Http_Utility::urlEncode(
+            $this->_toByteValueOrderedQueryString($encodedParams)
+        );
+        return implode('&', $baseStrings);
+    }
+
+    /**
+     * Transform an array to a byte value ordered query string
+     * 
+     * @param  array $params 
+     * @return string
+     */
+    protected function _toByteValueOrderedQueryString(array $params)
+    {
+        $return = array();
+        uksort($params, 'strnatcmp');
+        foreach ($params as $key => $value) {
+            if (is_array($value)) {
+                natsort($value);
+                foreach ($value as $keyduplicate) {
+                    $return[] = $key . '=' . $keyduplicate;
+                }
+            } else {
+                $return[] = $key . '=' . $value;
+            }
+        }
+        return implode('&', $return);
+    }
+}

+ 285 - 0
library/Zend/Oauth/Token.php

@@ -0,0 +1,285 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Http_Utility */
+require_once 'Zend/Oauth/Http/Utility.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Oauth_Token
+{
+    /**@+
+     * Token constants
+     */
+    const TOKEN_PARAM_KEY                = 'oauth_token';
+    const TOKEN_SECRET_PARAM_KEY         = 'oauth_token_secret';
+    const TOKEN_PARAM_CALLBACK_CONFIRMED = 'oauth_callback_confirmed';
+    /**@-*/
+
+    /**
+     * Token parameters
+     * 
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * OAuth response object
+     * 
+     * @var Zend_Http_Response
+     */
+    protected $_response = null;
+
+    /**
+     * @var Zend_Oauth_Http_Utility
+     */
+    protected $_httpUtility = null;
+
+    /**
+     * Constructor; basic setup for any Token subclass.
+     *
+     * @param  null|Zend_Http_Response $response
+     * @param  null|Zend_Oauth_Http_Utility $utility
+     * @return void
+     */
+    public function __construct(
+        Zend_Http_Response $response = null,
+        Zend_Oauth_Http_Utility $utility = null
+    ) {
+        if (!is_null($response)) {
+            $this->_response = $response;
+            $params = $this->_parseParameters($response);
+            if (count($params) > 0) {
+                $this->setParams($params);
+            }
+        }
+        if (!is_null($utility)) {
+            $this->_httpUtility = $utility;
+        } else {
+            $this->_httpUtility = new Zend_Oauth_Http_Utility;
+        }
+    }
+
+    /**
+     * Attempts to validate the Token parsed from the HTTP response - really
+     * it's just very basic existence checks which are minimal.
+     *
+     * @return bool
+     */
+    public function isValid()
+    {
+        if (isset($this->_params[self::TOKEN_PARAM_KEY])
+            && !empty($this->_params[self::TOKEN_PARAM_KEY])
+            && isset($this->_params[self::TOKEN_SECRET_PARAM_KEY])
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Return the HTTP response object used to initialise this instance.
+     *
+     * @return Zend_Http_Response
+     */
+    public function getResponse()
+    {
+        return $this->_response;
+    }
+
+    /**
+     * Sets the value for the this Token's secret which may be used when signing
+     * requests with this Token.
+     *
+     * @param  string $secret
+     * @return Zend_Oauth_Token
+     */
+    public function setTokenSecret($secret)
+    {
+        $this->setParam(self::TOKEN_SECRET_PARAM_KEY, $secret);
+        return $this;
+    }
+
+    /**
+     * Retrieve this Token's secret which may be used when signing
+     * requests with this Token.
+     *
+     * @return string
+     */
+    public function getTokenSecret()
+    {
+        return $this->getParam(self::TOKEN_SECRET_PARAM_KEY);
+    }
+
+    /**
+     * Sets the value for a parameter (e.g. token secret or other) and run
+     * a simple filter to remove any trailing newlines.
+     *
+     * @param  string $key
+     * @param  string $value
+     * @return Zend_Oauth_Token
+     */
+    public function setParam($key, $value)
+    {
+        $this->_params[$key] = trim($value, "\n");
+        return $this;
+    }
+
+    /**
+     * Sets the value for some parameters (e.g. token secret or other) and run
+     * a simple filter to remove any trailing newlines.
+     *
+     * @param  array $params
+     * @return Zend_Oauth_Token
+     */
+    public function setParams(array $params)
+    {
+        foreach ($params as $key=>$value) {
+            $this->setParam($key, $value);
+        }
+        return $this;
+    }
+
+    /**
+     * Get the value for a parameter (e.g. token secret or other).
+     *
+     * @param  string $key
+     * @return mixed
+     */
+    public function getParam($key)
+    {
+        if (isset($this->_params[$key])) {
+            return $this->_params[$key];
+        }
+        return null;
+    }
+
+    /**
+     * Sets the value for a Token.
+     *
+     * @param  string $token
+     * @return Zend_Oauth_Token
+     */
+    public function setToken($token)
+    {
+        $this->setParam(self::TOKEN_PARAM_KEY, $token);
+        return $this;
+    }
+
+    /**
+     * Gets the value for a Token.
+     *
+     * @return string
+     */
+    public function getToken()
+    {
+        return $this->getParam(self::TOKEN_PARAM_KEY);
+    }
+
+    /**
+     * Generic accessor to enable access as public properties.
+     *
+     * @return string
+     */
+    public function __get($key)
+    {
+        return $this->getParam($key);
+    }
+
+    /**
+     * Generic mutator to enable access as public properties.
+     *
+     * @param  string $key
+     * @param  string $value
+     * @return void
+     */
+    public function __set($key, $value)
+    {
+        $this->setParam($key, $value);
+    }
+
+    /**
+     * Convert Token to a string, specifically a raw encoded query string.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->_httpUtility->toEncodedQueryString($this->_params);
+    }
+
+    /**
+     * Convert Token to a string, specifically a raw encoded query string.
+     * Aliases to self::toString()
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Parse a HTTP response body and collect returned parameters
+     * as raw url decoded key-value pairs in an associative array.
+     *
+     * @param  Zend_Http_Response $response
+     * @return array
+     */
+    protected function _parseParameters(Zend_Http_Response $response)
+    {
+        $params = array();
+        $body   = $response->getBody();
+        if (empty($body)) {
+            return;
+        }
+
+        // validate body based on acceptable characters...todo
+        $parts = explode('&', $body);
+        foreach ($parts as $kvpair) {
+            $pair = explode('=', $kvpair);
+            $params[rawurldecode($pair[0])] = rawurldecode($pair[1]);
+        }
+        return $params;
+    }
+    
+    /**
+     * Limit serialisation stored data to the parameters
+     */
+    public function __sleep() 
+    {
+        return array('_params');
+    }
+
+    /**
+     * After serialisation, re-instantiate a HTTP utility class for use
+     */
+    public function __wakeup() 
+    {
+        if (is_null($this->_httpUtility)) {
+            $this->_httpUtility = new Zend_Oauth_Http_Utility;
+        }
+    }
+}

+ 99 - 0
library/Zend/Oauth/Token/Access.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Token */
+require_once 'Zend/Oauth/Token.php';
+
+/** Zend_Oauth_Http */
+require_once 'Zend/Oauth/Http.php';
+
+/** Zend_Uri_Http */
+require_once 'Zend/Uri/Http.php';
+
+/** Zend_Oauth_Client */
+require_once 'Zend/Oauth/Client.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Token_Access extends Zend_Oauth_Token
+{
+    /**
+     * Cast to HTTP header
+     * 
+     * @param  string $url 
+     * @param  Zend_Oauth_Config_ConfigInterface $config 
+     * @param  null|array $customParams 
+     * @param  null|string $realm 
+     * @return string
+     */
+    public function toHeader(
+        $url, Zend_Oauth_Config_ConfigInterface $config, array $customParams = null, $realm = null
+    ) {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $params = $this->_httpUtility->assembleParams($url, $config, $customParams);
+        return $this->_httpUtility->toAuthorizationHeader($params, $realm);
+    }
+
+    /**
+     * Cast to HTTP query string
+     * 
+     * @param  mixed $url 
+     * @param  Zend_Oauth_Config_ConfigInterface $config 
+     * @param  null|array $params 
+     * @return string
+     */
+    public function toQueryString($url, Zend_Oauth_Config_ConfigInterface $config, array $params = null)
+    {
+        if (!Zend_Uri::check($url)) {
+            require_once 'Zend/Oauth/Exception.php';
+            throw new Zend_Oauth_Exception(
+                '\'' . $url . '\' is not a valid URI'
+            );
+        }
+        $params = $this->_httpUtility->assembleParams($url, $config, $params);
+        return $this->_httpUtility->toEncodedQueryString($params);
+    }
+
+    /**
+     * Get OAuth client
+     * 
+     * @param  array $oauthOptions 
+     * @param  null|string $uri 
+     * @param  null|array|Zend_Config $config 
+     * @param  bool $excludeCustomParamsFromHeader 
+     * @return Zend_Oauth_Client
+     */
+    public function getHttpClient(array $oauthOptions, $uri = null, $config = null, $excludeCustomParamsFromHeader = true)
+    {
+        $client = new Zend_Oauth_Client($oauthOptions, $uri, $config, $excludeCustomParamsFromHeader);
+        $client->setToken($this);
+        return $client;
+    }
+}

+ 102 - 0
library/Zend/Oauth/Token/AuthorizedRequest.php

@@ -0,0 +1,102 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Token */
+require_once 'Zend/Oauth/Token.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Token_AuthorizedRequest extends Zend_Oauth_Token
+{
+    /**
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Constructor
+     *
+     * @param  null|array $data
+     * @param  null|Zend_Oauth_Http_Utility $utility
+     * @return void
+     */
+    public function __construct(array $data = null, Zend_Oauth_Http_Utility $utility = null)
+    {
+        if (!is_null($data)) {
+            $this->_data = $data;
+            $params = $this->_parseData();
+            if (count($params) > 0) {
+                $this->setParams($params);
+            }
+        }
+        if (!is_null($utility)) {
+            $this->_httpUtility = $utility;
+        } else {
+            $this->_httpUtility = new Zend_Oauth_Http_Utility;
+        }
+    }
+
+    /**
+     * Retrieve token data
+     * 
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Indicate if token is valid
+     * 
+     * @return bool
+     */
+    public function isValid()
+    {
+        if (isset($this->_params[self::TOKEN_PARAM_KEY])
+            && !empty($this->_params[self::TOKEN_PARAM_KEY])
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Parse string data into array
+     * 
+     * @return array
+     */
+    protected function _parseData()
+    {
+        $params = array();
+        if (empty($this->_data)) {
+            return;
+        }
+        foreach ($this->_data as $key => $value) {
+            $params[rawurldecode($key)] = rawurldecode($value);
+        }
+        return $params;
+    }
+}

+ 50 - 0
library/Zend/Oauth/Token/Request.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** Zend_Oauth_Token */
+require_once 'Zend/Oauth/Token.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Oauth
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Oauth_Token_Request extends Zend_Oauth_Token
+{
+    /**
+     * Constructor
+     *
+     * @param null|Zend_Http_Response $response
+     * @param null|Zend_Oauth_Http_Utility $utility
+     */
+    public function __construct(
+        Zend_Http_Response $response = null,
+        Zend_Oauth_Http_Utility $utility = null
+    ) {
+        parent::__construct($response, $utility);
+
+        // detect if server supports OAuth 1.0a
+        if (isset($this->_params[Zend_Oauth_Token::TOKEN_PARAM_CALLBACK_CONFIRMED])) {
+            Zend_Oauth_Client::$supportsRevisionA = true;
+        }
+    }
+}

+ 2 - 0
tests/Zend/AllTests.php

@@ -71,6 +71,7 @@ require_once 'Zend/Measure/AllTests.php';
 require_once 'Zend/Memory/AllTests.php';
 require_once 'Zend/MimeTest.php';
 require_once 'Zend/Mime/AllTests.php';
+require_once 'Zend/Oauth/AllTests.php';
 require_once 'Zend/OpenIdTest.php';
 require_once 'Zend/OpenId/AllTests.php';
 require_once 'Zend/Paginator/AllTests.php';
@@ -205,6 +206,7 @@ class Zend_AllTests
         $suite->addTest(Zend_Measure_AllTests::suite());
         $suite->addTestSuite('Zend_MimeTest');
         $suite->addTest(Zend_Mime_AllTests::suite());
+        $suite->addTest(Zend_Oauth_AllTests::suite());
         $suite->addTest(Zend_Paginator_AllTests::suite());
         $suite->addTest(Zend_Pdf_AllTests::suite());
         $suite->addTestSuite('Zend_RegistryTest');

+ 57 - 0
tests/Zend/Oauth/AllTests.php

@@ -0,0 +1,57 @@
+<?php
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Oauth_AllTests::main');
+}
+
+require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+require_once 'OauthTest.php';
+require_once 'Oauth/ConsumerTest.php';
+require_once 'Oauth/Signature/AbstractTest.php';
+require_once 'Oauth/Signature/PlaintextTest.php';
+require_once 'Oauth/Signature/HmacTest.php';
+require_once 'Oauth/Signature/RsaTest.php';
+require_once 'Oauth/Http/RequestTokenTest.php';
+require_once 'Oauth/Http/UserAuthorizationTest.php';
+require_once 'Oauth/Http/AccessTokenTest.php';
+require_once 'Oauth/Http/UtilityTest.php';
+require_once 'Oauth/Token/RequestTest.php';
+require_once 'Oauth/Token/AuthorizedRequestTest.php';
+require_once 'Oauth/Token/AccessTest.php';
+
+class Zend_Oauth_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Zend Framework - Zend_Oauth');
+
+        $suite->addTestSuite('Zend_OauthTest');
+        $suite->addTestSuite('Zend_Oauth_ConsumerTest');
+        $suite->addTestSuite('Zend_Oauth_Signature_AbstractTest');
+        $suite->addTestSuite('Zend_Oauth_Signature_PlaintextTest');
+        $suite->addTestSuite('Zend_Oauth_Signature_HmacTest');
+        $suite->addTestSuite('Zend_Oauth_Signature_RsaTest');
+        $suite->addTestSuite('Zend_Oauth_Http_RequestTokenTest');
+        $suite->addTestSuite('Zend_Oauth_Http_UserAuthorizationTest');
+        $suite->addTestSuite('Zend_Oauth_Http_AccessTokenTest');
+        $suite->addTestSuite('Zend_Oauth_Http_UtilityTest');
+        $suite->addTestSuite('Zend_Oauth_Token_RequestTest');
+        $suite->addTestSuite('Zend_Oauth_Token_AuthorizedRequestTest');
+        $suite->addTestSuite('Zend_Oauth_Token_AccessTest');
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Oauth_AllTests::main') {
+    Zend_Oauth_AllTests::main();
+}

+ 253 - 0
tests/Zend/Oauth/Oauth/ConsumerTest.php

@@ -0,0 +1,253 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Consumer.php';
+
+class Zend_Oauth_ConsumerTest extends PHPUnit_Framework_TestCase
+{
+
+    public function teardown()
+    {
+        Zend_Oauth::clearHttpClient();
+    }
+
+    public function testConstructorSetsConsumerKey()
+    {
+        $config = array('consumerKey'=>'1234567890');
+        $consumer = new Zend_Oauth_Consumer($config);
+        $this->assertEquals('1234567890', $consumer->getConsumerKey());
+    }
+
+    public function testConstructorSetsConsumerSecret()
+    {
+        $config = array('consumerSecret'=>'0987654321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        $this->assertEquals('0987654321', $consumer->getConsumerSecret());
+    }
+
+    public function testSetsSignatureMethodFromOptionsArray()
+    {
+        $options = array(
+            'signatureMethod' => 'rsa-sha1'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('RSA-SHA1', $consumer->getSignatureMethod());
+    }
+
+    public function testSetsRequestMethodFromOptionsArray() // add back
+    {
+        $options = array(
+            'requestMethod' => Zend_Oauth::GET
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals(Zend_Oauth::GET, $consumer->getRequestMethod());
+    }
+
+    public function testSetsRequestSchemeFromOptionsArray()
+    {
+        $options = array(
+            'requestScheme' => Zend_Oauth::REQUEST_SCHEME_POSTBODY
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals(Zend_Oauth::REQUEST_SCHEME_POSTBODY, $consumer->getRequestScheme());
+    }
+
+    public function testSetsVersionFromOptionsArray()
+    {
+        $options = array(
+            'version' => '1.1'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('1.1', $consumer->getVersion());
+    }
+
+    public function testSetsCallbackUrlFromOptionsArray()
+    {
+        $options = array(
+            'callbackUrl' => 'http://www.example.com/local'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('http://www.example.com/local', $consumer->getCallbackUrl());
+    }
+
+    public function testSetsRequestTokenUrlFromOptionsArray()
+    {
+        $options = array(
+            'requestTokenUrl' => 'http://www.example.com/request'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('http://www.example.com/request', $consumer->getRequestTokenUrl());
+    }
+
+    public function testSetsUserAuthorizationUrlFromOptionsArray()
+    {
+        $options = array(
+            'userAuthorizationUrl' => 'http://www.example.com/authorize'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('http://www.example.com/authorize', $consumer->getUserAuthorizationUrl());
+    }
+
+    public function testSetsAccessTokenUrlFromOptionsArray()
+    {
+        $options = array(
+            'accessTokenUrl' => 'http://www.example.com/access'
+        );
+        $consumer = new Zend_Oauth_Consumer($options);
+        $this->assertEquals('http://www.example.com/access', $consumer->getAccessTokenUrl());
+    }
+
+    public function testSetSignatureMethodThrowsExceptionForInvalidMethod()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setSignatureMethod('buckyball');
+            $this->fail('Invalid signature method accepted by setSignatureMethod');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetRequestMethodThrowsExceptionForInvalidMethod()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setRequestMethod('buckyball');
+            $this->fail('Invalid request method accepted by setRequestMethod');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetRequestSchemeThrowsExceptionForInvalidMethod()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setRequestScheme('buckyball');
+            $this->fail('Invalid request scheme accepted by setRequestScheme');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetLocalUrlThrowsExceptionForInvalidUrl()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setLocalUrl('buckyball');
+            $this->fail('Invalid url accepted by setLocalUrl');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetRequestTokenUrlThrowsExceptionForInvalidUrl()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setRequestTokenUrl('buckyball');
+            $this->fail('Invalid url accepted by setRequestUrl');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetUserAuthorizationUrlThrowsExceptionForInvalidUrl()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setUserAuthorizationUrl('buckyball');
+            $this->fail('Invalid url accepted by setUserAuthorizationUrl');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testSetAccessTokenUrlThrowsExceptionForInvalidUrl()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        try {
+            $consumer->setAccessTokenUrl('buckyball');
+            $this->fail('Invalid url accepted by setAccessTokenUrl');
+        } catch (Zend_Oauth_Exception $e) {
+        }
+    }
+
+    public function testGetRequestTokenReturnsInstanceOfOauthTokenRequest()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        $token = $consumer->getRequestToken(null, null, new Test_Http_RequestToken_48231);
+        $this->assertType('Zend_Oauth_Token_Request', $token);
+    }
+
+    public function testGetRedirectUrlReturnsUserAuthorizationUrlWithParameters()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321',
+            'userAuthorizationUrl'=>'http://www.example.com/authorize');
+        $consumer = new Test_Consumer_48231($config);
+        $params = array('foo'=>'bar');
+        $uauth = new Zend_Oauth_Http_UserAuthorization($consumer, $params);
+        $token = new Zend_Oauth_Token_Request;
+        $token->setParams(array('oauth_token'=>'123456', 'oauth_token_secret'=>'654321'));
+        $redirectUrl = $consumer->getRedirectUrl($params, $token, $uauth);
+        $this->assertEquals(
+            'http://www.example.com/authorize?oauth_token=123456&oauth_callback=http%3A%2F%2Fwww.example.com%2Flocal&foo=bar',
+            $redirectUrl
+        );
+    }
+
+    public function testGetAccessTokenReturnsInstanceOfOauthTokenAccess()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Zend_Oauth_Consumer($config);
+        $rtoken = new Zend_Oauth_Token_Request;
+        $rtoken->setToken('token');
+        $token = $consumer->getAccessToken(array('oauth_token'=>'token'), $rtoken, null, new Test_Http_AccessToken_48231);
+        $this->assertType('Zend_Oauth_Token_Access', $token);
+    }
+
+    public function testGetLastRequestTokenReturnsInstanceWhenExists()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Test_Consumer_48231($config);
+        $this->assertType('Zend_Oauth_Token_Request', $consumer->getLastRequestToken());
+    }
+
+    public function testGetLastAccessTokenReturnsInstanceWhenExists()
+    {
+        $config = array('consumerKey'=>'12345','consumerSecret'=>'54321');
+        $consumer = new Test_Consumer_48231($config);
+        $this->assertType('Zend_Oauth_Token_Access', $consumer->getLastAccessToken());
+    }
+
+}
+
+class Test_Http_RequestToken_48231 extends Zend_Oauth_Http_RequestToken
+{
+    public function __construct(){}
+    public function execute(array $params = null){
+        $return = new Zend_Oauth_Token_Request;
+        return $return;}
+    public function setParams(array $customServiceParameters){}
+}
+
+class Test_Http_AccessToken_48231 extends Zend_Oauth_Http_AccessToken
+{
+    public function __construct(){}
+    public function execute(array $params = null){
+        $return = new Zend_Oauth_Token_Access;
+        return $return;}
+    public function setParams(array $customServiceParameters){}
+}
+
+class Test_Consumer_48231 extends Zend_Oauth_Consumer
+{
+    public function __construct(array $options = array()){
+        $this->_requestToken = new Zend_Oauth_Token_Request;
+        $this->_accessToken = new Zend_Oauth_Token_Access;
+        parent::__construct($options);}
+    public function getCallbackUrl(){
+        return 'http://www.example.com/local';}
+}

+ 174 - 0
tests/Zend/Oauth/Oauth/Http/AccessTokenTest.php

@@ -0,0 +1,174 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Http/AccessToken.php';
+
+class Zend_Oauth_Http_AccessTokenTest extends PHPUnit_Framework_TestCase
+{
+
+    protected $stubConsumer = null;
+
+    public function setup()
+    {
+        $this->stubConsumer = new Test_Consumer_39745;
+        $this->stubHttpUtility = new Test_Http_Utility_39745;
+        Zend_Oauth::setHttpClient(new Test_Client_39745);
+    }
+
+    public function teardown()
+    {
+        Zend_Oauth::clearHttpClient();
+    }
+
+    public function testConstructorSetsConsumerInstance()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $this->assertType('Test_Consumer_39745', $request->getConsumer());
+    }
+
+    public function testConstructorSetsCustomServiceParameters()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, array(1,2,3), $this->stubHttpUtility);
+        $this->assertEquals(array(1,2,3), $request->getParameters());
+    }
+
+    public function testAssembleParametersCorrectlyAggregatesOauthParameters()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $expectedParams = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_token' => '0987654321',
+            'oauth_version' => '1.0',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c'
+        );
+        $this->assertEquals($expectedParams, $request->assembleParams());
+    }
+    public function testAssembleParametersCorrectlyIgnoresCustomParameters()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, array(
+            'custom_param1'=>'foo',
+            'custom_param2'=>'bar'
+        ), $this->stubHttpUtility);
+        $expectedParams = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_token' => '0987654321',
+            'oauth_version' => '1.0',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c'
+        );
+        $this->assertEquals($expectedParams, $request->assembleParams());
+    }
+
+    public function testGetRequestSchemeHeaderClientSetsCorrectlyEncodedAuthorizationHeader()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_token' => '0987654321',
+            'oauth_version' => '1.0',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c~',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemeHeaderClient($params);
+        $this->assertEquals(
+        'OAuth realm="",oauth_consumer_key="1234567890",oauth_nonce="e807f1fcf82d132f9b'
+        .'b018ca6738a19f",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'
+        .'12345678901",oauth_token="0987654321",oauth_version="1.0",oauth_sign'
+        .'ature="6fb42da0e32e07b61c9f0251fe627a9c~"',
+            $client->getHeader('Authorization')
+        );
+    }
+
+    public function testGetRequestSchemePostBodyClientSetsCorrectlyEncodedRawData()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_token' => '0987654321',
+            'oauth_version' => '1.0',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c~',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemePostBodyClient($params);
+        $this->assertEquals(
+            'oauth_consumer_key=1234567890&oauth_nonce=e807f1fcf82d132f9bb018c'
+            .'a6738a19f&oauth_signature_method=HMAC-SHA1&oauth_timestamp=12345'
+            .'678901&oauth_token=0987654321&oauth_version=1.0&oauth_signature='
+            .'6fb42da0e32e07b61c9f0251fe627a9c~',
+            $client->getRawData()
+        );
+    }
+
+    public function testGetRequestSchemeQueryStringClientSetsCorrectlyEncodedQueryString()
+    {
+        $request = new Zend_Oauth_Http_AccessToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_token' => '0987654321',
+            'oauth_version' => '1.0',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemeQueryStringClient($params, 'http://www.example.com');
+        $this->assertEquals(
+            'oauth_consumer_key=1234567890&oauth_nonce=e807f1fcf82d132f9bb018c'
+            .'a6738a19f&oauth_signature_method=HMAC-SHA1&oauth_timestamp=12345'
+            .'678901&oauth_token=0987654321&oauth_version=1.0&oauth_signature='
+            .'6fb42da0e32e07b61c9f0251fe627a9c',
+            $client->getUri()->getQuery()
+        );
+    }
+
+}
+
+class Test_Consumer_39745 extends Zend_Oauth_Consumer
+{
+    public function getConsumerKey(){return '1234567890';}
+    public function getSignatureMethod(){return 'HMAC-SHA1';}
+    public function getVersion(){return '1.0';}
+    public function getAccessTokenUrl(){return 'http://www.example.com/access';}
+    public function getLastRequestToken()
+    {
+        $return = new Test_Token_39745;
+        return $return;
+    }
+}
+
+class Test_Http_Utility_39745 extends Zend_Oauth_Http_Utility
+{
+    public function __construct(){}
+    public function generateNonce(){return md5('1234567890');}
+    public function generateTimestamp(){return '12345678901';}
+    public function sign(array $params, $signatureMethod, $consumerSecret,
+        $accessTokenSecret = null, $method = null, $url = null)
+    {
+        return md5('0987654321');
+    }
+}
+
+class Test_Client_39745 extends Zend_Http_Client
+{
+    public function getRawData(){return $this->raw_post_data;}
+}
+
+class Test_Token_39745 extends Zend_Oauth_Token_Request
+{
+    public function getToken(){return '0987654321';}
+}

+ 196 - 0
tests/Zend/Oauth/Oauth/Http/RequestTokenTest.php

@@ -0,0 +1,196 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Http/RequestToken.php';
+
+class Zend_Oauth_Http_RequestTokenTest extends PHPUnit_Framework_TestCase
+{
+
+    protected $stubConsumer = null;
+
+    public function setup()
+    {
+        $this->stubConsumer = new Test_Consumer_32874;
+        $this->stubConsumer2 = new Test_Consumer_32874b;
+        $this->stubHttpUtility = new Test_Http_Utility_32874;
+        Zend_Oauth::setHttpClient(new Test_Client_32874);
+    }
+
+    public function teardown()
+    {
+        Zend_Oauth::clearHttpClient();
+    }
+
+    public function testConstructorSetsConsumerInstance()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $this->assertType('Test_Consumer_32874', $request->getConsumer());
+    }
+
+    public function testConstructorSetsCustomServiceParameters()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, array(1,2,3), $this->stubHttpUtility);
+        $this->assertEquals(array(1,2,3), $request->getParameters());
+    }
+
+    public function testAssembleParametersCorrectlyAggregatesOauthParameters()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $expectedParams = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'http://www.example.com/local',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c'
+        );
+        $this->assertEquals($expectedParams, $request->assembleParams());
+    }
+
+    public function testAssembleParametersCorrectlyAggregatesOauthParametersIfCallbackUrlMissing()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer2, null, $this->stubHttpUtility);
+        $expectedParams = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'oob', // out-of-band when missing callback - 1.0a
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c'
+
+        );
+        $this->assertEquals($expectedParams, $request->assembleParams());
+    }
+
+    public function testAssembleParametersCorrectlyAggregatesCustomParameters()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, array(
+            'custom_param1'=>'foo',
+            'custom_param2'=>'bar'
+        ), $this->stubHttpUtility);
+        $expectedParams = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'http://www.example.com/local',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $this->assertEquals($expectedParams, $request->assembleParams());
+    }
+
+    public function testGetRequestSchemeHeaderClientSetsCorrectlyEncodedAuthorizationHeader()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'http://www.example.com/local',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c~',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemeHeaderClient($params);
+        $this->assertEquals(
+        'OAuth realm="",oauth_consumer_key="1234567890",oauth_nonce="e807f1fcf82d132f9b'
+        .'b018ca6738a19f",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'
+        .'12345678901",oauth_version="1.0",oauth_callback_url='
+        .'"http%3A%2F%2Fwww.example.com%2Flocal",oauth_signature="6fb42da0e32e07b61c9f0251fe627a9c~"',
+            $client->getHeader('Authorization')
+        );
+    }
+
+    public function testGetRequestSchemePostBodyClientSetsCorrectlyEncodedRawData()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'http://www.example.com/local',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c~',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemePostBodyClient($params);
+        $this->assertEquals(
+            'oauth_consumer_key=1234567890&oauth_nonce=e807f1fcf82d132f9bb018c'
+            .'a6738a19f&oauth_signature_method=HMAC-SHA1&oauth_timestamp=12345'
+            .'678901&oauth_version=1.0&oauth_callback_url=http%3A%2F%2Fwww.example.com%2Flocal'
+            .'&oauth_signature=6fb42da0e32e07b61c9f0251fe627a9c~'
+            .'&custom_param1=foo&custom_param2=bar',
+            $client->getRawData()
+        );
+    }
+
+    public function testGetRequestSchemeQueryStringClientSetsCorrectlyEncodedQueryString()
+    {
+        $request = new Zend_Oauth_Http_RequestToken($this->stubConsumer, null, $this->stubHttpUtility);
+        $params = array (
+            'oauth_consumer_key' => '1234567890',
+            'oauth_nonce' => 'e807f1fcf82d132f9bb018ca6738a19f',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '12345678901',
+            'oauth_version' => '1.0',
+            'oauth_callback_url' => 'http://www.example.com/local',
+            'oauth_signature' => '6fb42da0e32e07b61c9f0251fe627a9c',
+            'custom_param1' => 'foo',
+            'custom_param2' => 'bar'
+        );
+        $client = $request->getRequestSchemeQueryStringClient($params, 'http://www.example.com');
+        $this->assertEquals(
+            'oauth_consumer_key=1234567890&oauth_nonce=e807f1fcf82d132f9bb018c'
+            .'a6738a19f&oauth_signature_method=HMAC-SHA1&oauth_timestamp=12345'
+            .'678901&oauth_version=1.0&oauth_callback_url=http%3A%2F%2Fwww.example.com%2Flocal'
+            .'&oauth_signature=6fb42da0e32e07b61c9f0251fe627a9c'
+            .'&custom_param1=foo&custom_param2=bar',
+            $client->getUri()->getQuery()
+        );
+    }
+
+}
+
+class Test_Consumer_32874 extends Zend_Oauth_Consumer
+{
+    public function getConsumerKey(){return '1234567890';}
+    public function getSignatureMethod(){return 'HMAC-SHA1';}
+    public function getVersion(){return '1.0';}
+    public function getRequestTokenUrl(){return 'http://www.example.com/request';}
+    public function getCallbackUrl(){return 'http://www.example.com/local';}
+}
+
+class Test_Consumer_32874b extends Zend_Oauth_Consumer
+{
+    public function getConsumerKey(){return '1234567890';}
+    public function getSignatureMethod(){return 'HMAC-SHA1';}
+    public function getVersion(){return '1.0';}
+    public function getRequestTokenUrl(){return 'http://www.example.com/request';}
+    public function getCallbackUrl(){return null;}
+}
+
+class Test_Http_Utility_32874 extends Zend_Oauth_Http_Utility
+{
+    public function __construct(){}
+    public function generateNonce(){return md5('1234567890');}
+    public function generateTimestamp(){return '12345678901';}
+    public function sign(array $params, $signatureMethod, $consumerSecret,
+        $accessTokenSecret = null, $method = null, $url = null)
+    {
+        return md5('0987654321');
+    }
+}
+
+class Test_Client_32874 extends Zend_Http_Client
+{
+    public function getRawData(){return $this->raw_post_data;}
+}

+ 58 - 0
tests/Zend/Oauth/Oauth/Http/UserAuthorizationTest.php

@@ -0,0 +1,58 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Http/UserAuthorization.php';
+
+class Zend_Oauth_Http_UserAuthorizationTest extends PHPUnit_Framework_TestCase
+{
+
+    protected $stubConsumer = null;
+
+    public function setup()
+    {
+        $this->stubConsumer = new Test_Consumer_34879;
+    }
+
+    public function testConstructorSetsConsumerInstance()
+    {
+        $redirect = new Zend_Oauth_Http_UserAuthorization($this->stubConsumer);
+        $this->assertType('Test_Consumer_34879', $redirect->getConsumer());
+    }
+
+    public function testConstructorSetsCustomServiceParameters()
+    {
+        $redirect = new Zend_Oauth_Http_UserAuthorization($this->stubConsumer, array(1,2,3));
+        $this->assertEquals(array(1,2,3), $redirect->getParameters());
+    }
+
+    public function testAssembleParametersReturnsUserAuthorizationParamArray()
+    {
+        $redirect = new Zend_Oauth_Http_UserAuthorization($this->stubConsumer, array('foo '=>'bar~'));
+        $expected = array(
+            'oauth_token'=>'1234567890',
+            'oauth_callback'=>'http://www.example.com/local',
+            'foo '=>'bar~'
+        );
+        $this->assertEquals($expected, $redirect->assembleParams());
+    }
+
+    public function testGetUrlReturnsEncodedQueryStringParamsAppendedToLocalUrl()
+    {
+        $redirect = new Zend_Oauth_Http_UserAuthorization($this->stubConsumer, array('foo '=>'bar~'));
+        $expected =
+            'http://www.example.com/authorize?oauth_token=1234567890&oauth_callback=http%3A%2F%2Fwww.example.com%2Flocal&foo%20=bar~';
+        $this->assertEquals($expected, $redirect->getUrl());
+    }
+
+}
+
+class Test_Consumer_34879 extends Zend_Oauth_Consumer
+{
+    public function getUserAuthorizationUrl(){return 'http://www.example.com/authorize';}
+    public function getCallbackUrl(){return 'http://www.example.com/local';}
+    public function getLastRequestToken(){$r=new Test_Token_34879;return $r;}
+}
+class Test_Token_34879
+{
+    public function getToken(){return '1234567890';}
+}

+ 70 - 0
tests/Zend/Oauth/Oauth/Http/UtilityTest.php

@@ -0,0 +1,70 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Http/Utility.php';
+
+class Zend_Oauth_Http_UtilityTest extends PHPUnit_Framework_TestCase
+{
+    // see: http://wiki.oauth.net/TestCases (Parameter Encoding Tests)
+
+    public function testUrlEncodeCorrectlyEncodesAlnum()
+    {
+        $string = 'abcABC123';
+        $this->assertEquals('abcABC123', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesUnreserved()
+    {
+        $string = '-._~';
+        $this->assertEquals('-._~', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesPercentSign()
+    {
+        $string = '%';
+        $this->assertEquals('%25', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesPlusSign()
+    {
+        $string = '+';
+        $this->assertEquals('%2B', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesAmpEqualsAndAsterix()
+    {
+        $string = '&=*';
+        $this->assertEquals('%26%3D%2A', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesSpace()
+    {
+        $string = ' ';
+        $this->assertEquals('%20', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesLineFeed()
+    {
+        $string = "\n";
+        $this->assertEquals('%0A', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesU007F()
+    {
+        $string = chr(127);
+        $this->assertEquals('%7F', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesU0080()
+    {
+        $string = "\xC2\x80";
+        $this->assertEquals('%C2%80', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+    public function testUrlEncodeCorrectlyEncodesU3001()
+    {
+        $string = '、';
+        $this->assertEquals('%E3%80%81', Zend_Oauth_Http_Utility::urlEncode($string));
+    }
+
+}

+ 51 - 0
tests/Zend/Oauth/Oauth/Signature/AbstractTest.php

@@ -0,0 +1,51 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Signature/Plaintext.php';
+
+class Zend_Oauth_Signature_AbstractTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testNormaliseHttpBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'HTTP://WWW.EXAMPLE.COM:80/REQUEST';
+        $this->assertEquals('http://www.example.com/REQUEST', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+    public function testNormaliseHttpsBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'HTTPS://WWW.EXAMPLE.COM:443/REQUEST';
+        $this->assertEquals('https://www.example.com/REQUEST', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+    public function testNormaliseHttpPortBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'HTTP://WWW.EXAMPLE.COM:443/REQUEST';
+        $this->assertEquals('http://www.example.com:443/REQUEST', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+    public function testNormaliseHttpsPortBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'HTTPS://WWW.EXAMPLE.COM:80/REQUEST';
+        $this->assertEquals('https://www.example.com:80/REQUEST', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+    public function testNormaliseHttpsRemovesFragmentFromBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'https://www.example.com/request#foo';
+        $this->assertEquals('https://www.example.com/request', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+    public function testNormaliseHttpsRemovesQueryFromBaseSignatureUrl() 
+    {
+        $sign = new Zend_Oauth_Signature_Plaintext('foo');
+        $url = 'https://www.example.com/request?foo=bar';
+        $this->assertEquals('https://www.example.com/request', $sign->normaliseBaseSignatureUrl($url));
+    }
+
+}

+ 37 - 0
tests/Zend/Oauth/Oauth/Signature/HmacTest.php

@@ -0,0 +1,37 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Signature/Hmac.php';
+
+class Zend_Oauth_Signature_HmacTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testSignatureWithoutAccessSecretIsHashedWithConsumerSecret()
+    {
+        $params = array(
+            'oauth_version' => '1.0',
+            'oauth_consumer_key' => 'dpf43f3p2l4k3l03',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '1191242090',
+            'oauth_nonce' => 'hsu94j3884jdopsl',
+            'oauth_version' => '1.0'
+        );
+        $signature = new Zend_Oauth_Signature_Hmac('1234567890', null, 'SHA1');
+        $this->assertEquals('XYkaERjLVjp2yP/klDCGQ+hZ2So=', $signature->sign($params));
+    }
+
+    public function testSignatureWithAccessSecretIsHashedWithConsumerAndAccessSecret()
+    {
+        $params = array(
+            'oauth_version' => '1.0',
+            'oauth_consumer_key' => 'dpf43f3p2l4k3l03',
+            'oauth_signature_method' => 'HMAC-SHA1',
+            'oauth_timestamp' => '1191242090',
+            'oauth_nonce' => 'hsu94j3884jdopsl',
+            'oauth_version' => '1.0'
+        );
+        $signature = new Zend_Oauth_Signature_Hmac('1234567890', '0987654321', 'SHA1');
+        $this->assertEquals('b0J6H0jCEo+tvzVJy2G615sM6/M=', $signature->sign($params));
+    }
+
+}

+ 37 - 0
tests/Zend/Oauth/Oauth/Signature/PlaintextTest.php

@@ -0,0 +1,37 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Signature/Plaintext.php';
+
+class Zend_Oauth_Signature_PlaintextTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testSignatureWithoutAccessSecretIsOnlyConsumerSecretString()
+    {
+        $params = array(
+            'oauth_version' => '1.0',
+            'oauth_consumer_key' => 'dpf43f3p2l4k3l03',
+            'oauth_signature_method' => 'PLAINTEXT',
+            'oauth_timestamp' => '1191242090',
+            'oauth_nonce' => 'hsu94j3884jdopsl',
+            'oauth_version' => '1.0'
+        );
+        $signature = new Zend_Oauth_Signature_Plaintext('1234567890');
+        $this->assertEquals('1234567890&', $signature->sign($params));
+    }
+
+    public function testSignatureWithAccessSecretIsConsumerAndAccessSecretStringsConcatWithAmp()
+    {
+        $params = array(
+            'oauth_version' => '1.0',
+            'oauth_consumer_key' => 'dpf43f3p2l4k3l03',
+            'oauth_signature_method' => 'PLAINTEXT',
+            'oauth_timestamp' => '1191242090',
+            'oauth_nonce' => 'hsu94j3884jdopsl',
+            'oauth_version' => '1.0'
+        );
+        $signature = new Zend_Oauth_Signature_Plaintext('1234567890', '0987654321');
+        $this->assertEquals('1234567890&0987654321', $signature->sign($params));
+    }
+
+}

+ 19 - 0
tests/Zend/Oauth/Oauth/Signature/RsaTest.php

@@ -0,0 +1,19 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+//require_once 'Zend/Oauth/Signature/Rsa.php';
+
+class Zend_Oauth_Signature_RsaTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testSignatureWithoutAccessSecretIsHashedWithConsumerSecret() 
+    {
+        $this->markTestIncomplete('Zend_Crypt_Rsa finalisation outstanding');
+    }
+
+    public function testSignatureWithAccessSecretIsHashedWithConsumerAndAccessSecret() 
+    {
+        $this->markTestIncomplete('Zend_Crypt_Rsa finalisation outstanding');
+    }
+
+}

+ 110 - 0
tests/Zend/Oauth/Oauth/Token/AccessTest.php

@@ -0,0 +1,110 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Token/Access.php';
+require_once 'Zend/Oauth/Config.php';
+
+class Zend_Oauth_Token_AccessTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testConstructorSetsResponseObject()
+    {
+        $response = new Zend_Http_Response(200, array());
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertType('Zend_Http_Response', $token->getResponse());
+    }
+
+    public function testConstructorParsesRequestTokenFromResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertEquals('jZaee4GF52O3lUb9', $token->getToken());
+    }
+
+    public function testConstructorParsesRequestTokenSecretFromResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertEquals('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri', $token->getTokenSecret());
+    }
+
+    public function testPropertyAccessWorks()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri&foo=bar';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertEquals('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri', $token->oauth_token_secret);
+    }
+
+    public function testTokenCastsToEncodedResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $token = new Zend_Oauth_Token_Access();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $token->setTokenSecret('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri');
+        $this->assertEquals($body, (string) $token);
+    }
+
+    public function testToStringReturnsEncodedResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $token = new Zend_Oauth_Token_Access();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $token->setTokenSecret('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri');
+        $this->assertEquals($body, $token->toString());
+    }
+
+    public function testIsValidDetectsBadResponse()
+    {
+        $body = 'oauthtoken=jZaee4GF52O3lUb9&oauthtokensecret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertFalse($token->isValid());
+    }
+
+    public function testIsValidDetectsGoodResponse()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Access($response);
+        $this->assertTrue($token->isValid());
+    }
+
+    public function testToHeaderReturnsValidHeaderString() 
+    {
+        $token = new Zend_Oauth_Token_Access(null, new Test_Http_Utility_90244);
+        $value = $token->toHeader(
+            'http://www.example.com',
+            new Test_Config_90244
+        );
+        $this->assertEquals('OAuth realm="",oauth_consumer_key="1234567890",oauth_nonce="e807f1fcf82d132f9bb018ca6738a19f",oauth_signature_method="HMAC-SHA1",oauth_timestamp="12345678901",oauth_token="abcde",oauth_version="1.0",oauth_signature="6fb42da0e32e07b61c9f0251fe627a9c"', $value);
+    }
+
+}
+
+class Test_Http_Utility_90244 extends Zend_Oauth_Http_Utility
+{
+    public function __construct(){}
+    public function generateNonce(){return md5('1234567890');}
+    public function generateTimestamp(){return '12345678901';}
+    public function sign(array $params, $signatureMethod, $consumerSecret,
+        $accessTokenSecret = null, $method = null, $url = null)
+    {
+        return md5('0987654321');
+    }
+}
+
+class Test_Config_90244 extends Zend_Oauth_Config
+{
+    public function getConsumerKey(){return '1234567890';}
+    public function getSignatureMethod(){return 'HMAC-SHA1';}
+    public function getVersion(){return '1.0';}
+    public function getRequestTokenUrl(){return 'http://www.example.com/request';}
+    public function getToken(){$token = new Zend_Oauth_Token_Access;
+        $token->setToken('abcde');
+        return $token;}
+    public function getRequestMethod() 
+    {return 'POST';}
+}

+ 70 - 0
tests/Zend/Oauth/Oauth/Token/AuthorizedRequestTest.php

@@ -0,0 +1,70 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Token/AuthorizedRequest.php';
+
+class Zend_Oauth_Token_AuthorizedRequestTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testConstructorSetsInputData()
+    {
+        $data = array('foo'=>'bar');
+        $token = new Zend_Oauth_Token_AuthorizedRequest($data);
+        $this->assertEquals($data, $token->getData());
+    }
+
+    public function testConstructorParsesAccessTokenFromInputData()
+    {
+        $data = array(
+            'oauth_token'=>'jZaee4GF52O3lUb9'
+        );
+        $token = new Zend_Oauth_Token_AuthorizedRequest($data);
+        $this->assertEquals('jZaee4GF52O3lUb9', $token->getToken());
+    }
+
+    public function testPropertyAccessWorks()
+    {
+        $data = array(
+            'oauth_token'=>'jZaee4GF52O3lUb9'
+        );
+        $token = new Zend_Oauth_Token_AuthorizedRequest($data);
+        $this->assertEquals('jZaee4GF52O3lUb9', $token->oauth_token);
+    }
+
+    public function testTokenCastsToEncodedQueryString()
+    {
+        $queryString = 'oauth_token=jZaee4GF52O3lUb9&foo%20=bar~';
+        $token = new Zend_Oauth_Token_AuthorizedRequest();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $token->setParam('foo ', 'bar~');
+        $this->assertEquals($queryString, (string) $token);
+    }
+
+    public function testToStringReturnsEncodedQueryString()
+    {
+        $queryString = 'oauth_token=jZaee4GF52O3lUb9';
+        $token = new Zend_Oauth_Token_AuthorizedRequest();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $this->assertEquals($queryString, $token->toString());
+    }
+
+    public function testIsValidDetectsBadResponse()
+    {
+        $data = array(
+            'missing_oauth_token'=>'jZaee4GF52O3lUb9'
+        );
+        $token = new Zend_Oauth_Token_AuthorizedRequest($data);
+        $this->assertFalse($token->isValid());
+    }
+
+    public function testIsValidDetectsGoodResponse()
+    {
+        $data = array(
+            'oauth_token'=>'jZaee4GF52O3lUb9',
+            'foo'=>'bar'
+        );
+        $token = new Zend_Oauth_Token_AuthorizedRequest($data);
+        $this->assertTrue($token->isValid());
+    }
+
+}

+ 74 - 0
tests/Zend/Oauth/Oauth/Token/RequestTest.php

@@ -0,0 +1,74 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth/Token/Request.php';
+
+class Zend_Oauth_Token_RequestTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testConstructorSetsResponseObject()
+    {
+        $response = new Zend_Http_Response(200, array());
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertType('Zend_Http_Response', $token->getResponse());
+    }
+
+    public function testConstructorParsesRequestTokenFromResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertEquals('jZaee4GF52O3lUb9', $token->getToken());
+    }
+
+    public function testConstructorParsesRequestTokenSecretFromResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertEquals('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri', $token->getTokenSecret());
+    }
+
+    public function testPropertyAccessWorks()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri&foo=bar';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertEquals('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri', $token->oauth_token_secret);
+    }
+
+    public function testTokenCastsToEncodedResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $token = new Zend_Oauth_Token_Request();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $token->setTokenSecret('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri');
+        $this->assertEquals($body, (string) $token);
+    }
+
+    public function testToStringReturnsEncodedResponseBody()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $token = new Zend_Oauth_Token_Request();
+        $token->setToken('jZaee4GF52O3lUb9');
+        $token->setTokenSecret('J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri');
+        $this->assertEquals($body, $token->toString());
+    }
+
+    public function testIsValidDetectsBadResponse()
+    {
+        $body = 'oauthtoken=jZaee4GF52O3lUb9&oauthtokensecret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertFalse($token->isValid());
+    }
+
+    public function testIsValidDetectsGoodResponse()
+    {
+        $body = 'oauth_token=jZaee4GF52O3lUb9&oauth_token_secret=J4Ms4n8sxjYc0A8K0KOQFCTL0EwUQTri';
+        $response = new Zend_Http_Response(200, array(), $body);
+        $token = new Zend_Oauth_Token_Request($response);
+        $this->assertTrue($token->isValid());
+    }
+
+}

+ 41 - 0
tests/Zend/Oauth/OauthTest.php

@@ -0,0 +1,41 @@
+<?php
+
+require_once 'PHPUnit/Framework/TestCase.php';
+require_once 'Zend/Oauth.php';
+
+class Test_Http_Client_19485876 extends Zend_Http_Client {}
+
+class Zend_OauthTest extends PHPUnit_Framework_TestCase
+{
+
+    public function teardown()
+    {
+        Zend_Oauth::clearHttpClient();
+    }
+
+    public function testCanSetCustomHttpClient()
+    {
+        Zend_Oauth::setHttpClient(new Test_Http_Client_19485876());
+        $this->assertType('Test_Http_Client_19485876', Zend_Oauth::getHttpClient());
+    }
+
+    public function testGetHttpClientResetsParameters()
+    {
+        $client = new Test_Http_Client_19485876();
+        $client->setParameterGet(array('key'=>'value'));
+        Zend_Oauth::setHttpClient($client);
+        $resetClient = Zend_Oauth::getHttpClient();
+        $resetClient->setUri('http://www.example.com');
+        $this->assertEquals('http://www.example.com:80', $resetClient->getUri(true));
+    }
+
+    public function testGetHttpClientResetsAuthorizationHeader()
+    {
+        $client = new Test_Http_Client_19485876();
+        $client->setHeaders('Authorization', 'realm="http://www.example.com",oauth_version="1.0"');
+        Zend_Oauth::setHttpClient($client);
+        $resetClient = Zend_Oauth::getHttpClient();
+        $this->assertEquals(null, $resetClient->getHeader('Authorization'));
+    }
+
+}