You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2008/07/16 01:33:51 UTC

svn commit: r677097 - /incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php

Author: chabotc
Date: Tue Jul 15 16:33:51 2008
New Revision: 677097

URL: http://svn.apache.org/viewvc?rev=677097&view=rev
Log:
Get signed oauth requests working & remove partuza hack, better patch incomming (we hope)

Modified:
    incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php

Modified: incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php?rev=677097&r1=677096&r2=677097&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/oauth/OAuth.php Tue Jul 15 16:33:51 2008
@@ -1,650 +1,652 @@
-<?php
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-class OAuth {
-	public static $VERSION_1_0 = "1.0";
-	public static $ENCODING = "UTF-8";
-	public static $FORM_ENCODED = "application/x-www-form-urlencoded";
-	public static $OAUTH_CONSUMER_KEY = "oauth_consumer_key";
-	public static $OAUTH_TOKEN = "oauth_token";
-	public static $OAUTH_TOKEN_SECRET = "oauth_token_secret";
-	public static $OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
-	public static $OAUTH_SIGNATURE = "oauth_signature";
-	public static $OAUTH_TIMESTAMP = "oauth_timestamp";
-	public static $OAUTH_NONCE = "oauth_nonce";
-	public static $OAUTH_VERSION = "oauth_version";
-	public static $HMAC_SHA1 = "HMAC_SHA1";
-	public static $RSA_SHA1 = "RSA_SHA1";
-	public static $BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----";
-	public static $END_PRIVATE_KEY = "-----END PRIVATE KEY-----";
-	public static $OAUTH_PROBLEM = "oauth_problem";
-}
-
-/* Generic exception class
- */
-class OAuthException extends Exception {}
-
-class OAuthProblemException extends Exception {}
-
-class OAuthProtocolException extends Exception {}
-
-class OAuthConsumer {
-	public $key;
-	public $secret;
-	public $callback_url;
-	private $properties = array();
-
-	function __construct($key, $secret, $callback_url = NULL)
-	{
-		$this->key = $key;
-		$this->secret = $secret;
-		$this->callback_url = $callback_url;
-	}
-
-	public function getProperty($name)
-	{
-		return $this->properties[$name];
-	}
-
-	public function setProperty($name, $value)
-	{
-		$this->properties[$name] = $value;
-	}
-
-}
-
-class OAuthToken {
-	// access tokens and request tokens
-	public $key;
-	public $secret;
-
-	/**
-	 * key = the token
-	 * secret = the token secret
-	 */
-	function __construct($key, $secret)
-	{
-		$this->key = $key;
-		$this->secret = $secret;
-	}
-
-	/**
-	 * generates the basic string serialization of a token that a server
-	 * would respond to request_token and access_token calls with
-	 */
-	function to_string()
-	{
-		return "oauth_token=" . OAuthUtil::urlencodeRFC3986($this->key) . "&oauth_token_secret=" . OAuthUtil::urlencodeRFC3986($this->secret);
-	}
-
-	function __toString()
-	{
-		return $this->to_string();
-	}
-}
-
-class OAuthSignatureMethod {
-
-	public function check_signature(&$request, $consumer, $token, $signature)
-	{
-		$built = $this->build_signature($request, $consumer, $token);
-		return $built == $signature;
-	}
-}
-
-class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
-
-	function get_name()
-	{
-		return "HMAC-SHA1";
-	}
-
-	public function build_signature($request, $consumer, $token)
-	{
-		$base_string = $request->get_signature_base_string();
-		$request->base_string = $base_string;
-		$key_parts = array($consumer->secret, (isset($token)) ? $token : "");
-		$key_parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $key_parts);
-		$key = implode('&', $key_parts);
-		return base64_encode(hash_hmac('sha1', $base_string, $key, true));
-	}
-
-	//TODO: Double check this!
-	public function check_signature(&$request, $consumer, $token, $signature)
-	{
-		$sign = $this->build_signature($request, $consumer, $token);
-		return $sign == $signature;
-	}
-}
-
-class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
-
-	public function get_name()
-	{
-		return "PLAINTEXT";
-	}
-
-	public function build_signature($request, $consumer, $token)
-	{
-		$sig = array(OAuthUtil::urlencodeRFC3986($consumer->secret));
-		if ($token) {
-			array_push($sig, OAuthUtil::urlencodeRFC3986($token->secret));
-		} else {
-			array_push($sig, '');
-		}
-		$raw = implode("&", $sig);
-		// for debug purposes
-		$request->base_string = $raw;
-		return OAuthUtil::urlencodeRFC3986($raw);
-	}
-
-	//TODO: Double check this!
-	public function check_signature(&$request, $consumer, $token, $signature)
-	{
-		$raw = OAuthUtil::urldecodeRFC3986($request->base_string);
-		$sig = explode("&", $raw);
-		array_pop($sig);
-		$secret = array(OAuthUtil::urldecodeRFC3986($consumer->secret));
-		return $sig == $secret;
-	}
-}
-
-class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
-	public static $PRIVATE_KEY = "RSA-SHA1.PrivateKey";
-
-	public function get_name()
-	{
-		return "RSA-SHA1";
-	}
-
-	protected function fetch_public_cert(&$request)
-	{
-		// not implemented yet, ideas are:
-		// (1) do a lookup in a table of trusted certs keyed off of consumer
-		// (2) fetch via http using a url provided by the requester
-		// (3) some sort of specific discovery code based on request
-		//
-		// either way should return a string representation of the certificate
-		throw Exception("fetch_public_cert not implemented");
-	}
-
-	protected function fetch_private_cert(&$request)
-	{
-		// not implemented yet, ideas are:
-		// (1) do a lookup in a table of trusted certs keyed off of consumer
-		//
-		// either way should return a string representation of the certificate
-		throw new Exception("fetch_private_cert not implemented");
-	}
-
-	public function build_signature(&$request, OAuthConsumer $consumer, $token)
-	{
-		$base_string = $request->get_signature_base_string();
-		// Fetch the private key cert based on the request
-		$cert = $consumer->getProperty(OAuthSignatureMethod_RSA_SHA1::$PRIVATE_KEY);
-		// Pull the private key ID from the certificate
-		//FIXME this function seems to be called both for a oauth.json action where
-		// there is no phrase required, but for signed requests too, which do require it
-		// this is a dirty hack to make it work .. kinda
-		if (! $privatekeyid = @openssl_pkey_get_private($cert)) {
-			if (! $privatekeyid = @openssl_pkey_get_private($cert, Config::get('private_key_phrase') != '' ? (Config::get('private_key_phrase')) : null)) {
-				throw new Exception("Could not load private key");
-			}
-		}
-		// Sign using the key
-		$signature = '';
-		if (($ok = openssl_sign($base_string, $signature, $privatekeyid)) === false) {
-			throw new OAuthException("Could not create signature");
-		}
-		// Release the key resource
-		@openssl_free_key($privatekeyid);
-		return base64_encode($signature);
-	}
-
-	public function check_signature(&$request, $consumer, $token, $signature)
-	{
-		$decoded_sig = base64_decode($signature);
-		$base_string = $request->get_signature_base_string();
-		// Fetch the public key cert based on the request
-		$cert = $this->fetch_public_cert($request);
-		// Pull the public key ID from the certificate
-		$publickeyid = openssl_get_publickey($cert);
-		// Check the computed signature against the one passed in the query
-		$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
-		// Release the key resource
-		@openssl_free_key($publickeyid);
-		return $ok == 1;
-	}
-}
-
-class OAuthRequest {
-	private $parameters;
-	private $http_method;
-	private $http_url;
-	public $base_string;
-	public static $version = '1.0';
-
-	function __construct($http_method, $http_url, $parameters = NULL)
-	{
-		@$parameters or $parameters = array();
-		$this->parameters = $parameters;
-		$this->http_method = $http_method;
-		$this->http_url = $http_url;
-	}
-
-	/**
-	 * attempt to build up a request from what was passed to the server
-	 */
-	public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL)
-	{
-		$scheme = (! isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https';
-		@$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-		@$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
-		$request_headers = OAuthRequest::get_headers();
-		// let the library user override things however they'd like, if they know
-		// which parameters to use then go for it, for example XMLRPC might want to
-		// do this
-		if ($parameters) {
-			$req = new OAuthRequest($http_method, $http_url, $parameters);
-		} elseif (@substr($request_headers['Authorization'], 0, 5) == "OAuth") {
-			// next check for the auth header, we need to do some extra stuff
-			// if that is the case, namely suck in the parameters from GET or POST
-			// so that we can include them in the signature
-			$header_parameters = OAuthRequest::split_header($request_headers['Authorization']);
-			if ($http_method == "GET") {
-				$req_parameters = $_GET;
-			} else 
-				if ($http_method = "POST") {
-					$req_parameters = $_POST;
-				}
-			$parameters = array_merge($header_parameters, $req_parameters);
-			$req = new OAuthRequest($http_method, $http_url, $parameters);
-		} elseif ($http_method == "GET") {
-			$req = new OAuthRequest($http_method, $http_url, $_GET);
-		} elseif ($http_method == "POST") {
-			$req = new OAuthRequest($http_method, $http_url, $_POST);
-		}
-		return $req;
-	}
-
-	/**
-	 * pretty much a helper function to set up the request
-	 */
-	public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = NULL)
-	{
-		$parameters = is_array($parameters) ? $parameters : array();
-		$defaults = array("oauth_nonce" => OAuthRequest::generate_nonce(), 
-				"oauth_timestamp" => OAuthRequest::generate_timestamp(), 
-				"oauth_consumer_key" => $consumer->key, 'synd' => 'partuza', 
-				'container' => 'partuza');
-		$parameters = array_merge($defaults, $parameters);
-		if (isset($token)) {
-			$parameters['oauth_token'] = $token;
-		}
-		return new OAuthRequest($http_method, $http_url, $parameters);
-	}
-
-	public function set_parameter($name, $value)
-	{
-		$this->parameters[$name] = $value;
-	}
-
-	public function get_parameter($name)
-	{
-		return @$this->parameters[$name];
-	}
-
-	public function get_parameters()
-	{
-		return $this->parameters;
-	}
-
-	public function set_parameters($params)
-	{
-		return $this->parameters = $params;
-	}
-
+<?php
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+class OAuth {
+	public static $VERSION_1_0 = "1.0";
+	public static $ENCODING = "UTF-8";
+	public static $FORM_ENCODED = "application/x-www-form-urlencoded";
+	public static $OAUTH_CONSUMER_KEY = "oauth_consumer_key";
+	public static $OAUTH_TOKEN = "oauth_token";
+	public static $OAUTH_TOKEN_SECRET = "oauth_token_secret";
+	public static $OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
+	public static $OAUTH_SIGNATURE = "oauth_signature";
+	public static $OAUTH_TIMESTAMP = "oauth_timestamp";
+	public static $OAUTH_NONCE = "oauth_nonce";
+	public static $OAUTH_VERSION = "oauth_version";
+	public static $HMAC_SHA1 = "HMAC_SHA1";
+	public static $RSA_SHA1 = "RSA_SHA1";
+	public static $BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----";
+	public static $END_PRIVATE_KEY = "-----END PRIVATE KEY-----";
+	public static $OAUTH_PROBLEM = "oauth_problem";
+}
+
+/* Generic exception class
+ */
+class OAuthException extends Exception {}
+
+class OAuthProblemException extends Exception {}
+
+class OAuthProtocolException extends Exception {}
+
+class OAuthConsumer {
+	public $key;
+	public $secret;
+	public $callback_url;
+	private $properties = array();
+
+	function __construct($key, $secret, $callback_url = NULL)
+	{
+		$this->key = $key;
+		$this->secret = $secret;
+		$this->callback_url = $callback_url;
+	}
+
+	public function getProperty($name)
+	{
+		return $this->properties[$name];
+	}
+
+	public function setProperty($name, $value)
+	{
+		$this->properties[$name] = $value;
+	}
+
+}
+
+class OAuthToken {
+	// access tokens and request tokens
+	public $key;
+	public $secret;
+
+	/**
+	 * key = the token
+	 * secret = the token secret
+	 */
+	function __construct($key, $secret)
+	{
+		$this->key = $key;
+		$this->secret = $secret;
+	}
+
+	/**
+	 * generates the basic string serialization of a token that a server
+	 * would respond to request_token and access_token calls with
+	 */
+	function to_string()
+	{
+		return "oauth_token=" . OAuthUtil::urlencodeRFC3986($this->key) . "&oauth_token_secret=" . OAuthUtil::urlencodeRFC3986($this->secret);
+	}
+
+	function __toString()
+	{
+		return $this->to_string();
+	}
+}
+
+class OAuthSignatureMethod {
+
+	public function check_signature(&$request, $consumer, $token, $signature)
+	{
+		$built = $this->build_signature($request, $consumer, $token);
+		return $built == $signature;
+	}
+}
+
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+
+	function get_name()
+	{
+		return "HMAC-SHA1";
+	}
+
+	public function build_signature($request, $consumer, $token)
+	{
+		$base_string = $request->get_signature_base_string();
+		$request->base_string = $base_string;
+		$key_parts = array($consumer->secret, (isset($token)) ? $token : "");
+		$key_parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $key_parts);
+		$key = implode('&', $key_parts);
+		return base64_encode(hash_hmac('sha1', $base_string, $key, true));
+	}
+
+	//TODO: Double check this!
+	public function check_signature(&$request, $consumer, $token, $signature)
+	{
+		$sign = $this->build_signature($request, $consumer, $token);
+		return $sign == $signature;
+	}
+}
+
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+
+	public function get_name()
+	{
+		return "PLAINTEXT";
+	}
+
+	public function build_signature($request, $consumer, $token)
+	{
+		$sig = array(OAuthUtil::urlencodeRFC3986($consumer->secret));
+		if ($token) {
+			array_push($sig, OAuthUtil::urlencodeRFC3986($token->secret));
+		} else {
+			array_push($sig, '');
+		}
+		$raw = implode("&", $sig);
+		// for debug purposes
+		$request->base_string = $raw;
+		return OAuthUtil::urlencodeRFC3986($raw);
+	}
+
+	//TODO: Double check this!
+	public function check_signature(&$request, $consumer, $token, $signature)
+	{
+		$raw = OAuthUtil::urldecodeRFC3986($request->base_string);
+		$sig = explode("&", $raw);
+		array_pop($sig);
+		$secret = array(OAuthUtil::urldecodeRFC3986($consumer->secret));
+		return $sig == $secret;
+	}
+}
+
+class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+	public static $PRIVATE_KEY = "RSA-SHA1.PrivateKey";
+
+	public function get_name()
+	{
+		return "RSA-SHA1";
+	}
+
+	protected function fetch_public_cert(&$request)
+	{
+		// not implemented yet, ideas are:
+		// (1) do a lookup in a table of trusted certs keyed off of consumer
+		// (2) fetch via http using a url provided by the requester
+		// (3) some sort of specific discovery code based on request
+		//
+		// either way should return a string representation of the certificate
+		throw Exception("fetch_public_cert not implemented");
+	}
+
+	protected function fetch_private_cert(&$request)
+	{
+		// not implemented yet, ideas are:
+		// (1) do a lookup in a table of trusted certs keyed off of consumer
+		//
+		// either way should return a string representation of the certificate
+		throw new Exception("fetch_private_cert not implemented");
+	}
+
+	public function build_signature(&$request, OAuthConsumer $consumer, $token)
+	{
+		$base_string = $request->get_signature_base_string();
+		// Fetch the private key cert based on the request
+		$cert = $consumer->getProperty(OAuthSignatureMethod_RSA_SHA1::$PRIVATE_KEY);
+		// Pull the private key ID from the certificate
+		//FIXME this function seems to be called both for a oauth.json action where
+		// there is no phrase required, but for signed requests too, which do require it
+		// this is a dirty hack to make it work .. kinda
+		if (! $privatekeyid = @openssl_pkey_get_private($cert)) {
+			if (! $privatekeyid = @openssl_pkey_get_private($cert, Config::get('private_key_phrase') != '' ? (Config::get('private_key_phrase')) : null)) {
+				throw new Exception("Could not load private key");
+			}
+		}
+		// Sign using the key
+		$signature = '';
+		if (($ok = openssl_sign($base_string, $signature, $privatekeyid)) === false) {
+			throw new OAuthException("Could not create signature");
+		}
+		// Release the key resource
+		@openssl_free_key($privatekeyid);
+		return base64_encode($signature);
+	}
+
+	public function check_signature(&$request, $consumer, $token, $signature)
+	{
+		$decoded_sig = base64_decode($signature);
+		$base_string = $request->get_signature_base_string();
+		// Fetch the public key cert based on the request
+		$cert = $this->fetch_public_cert($request);
+		// Pull the public key ID from the certificate
+		$publickeyid = openssl_get_publickey($cert);
+		// Check the computed signature against the one passed in the query
+		$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
+		// Release the key resource
+		@openssl_free_key($publickeyid);
+		return $ok == 1;
+	}
+}
+
+class OAuthRequest {
+	private $parameters;
+	private $http_method;
+	private $http_url;
+	public $base_string;
+	public static $version = '1.0';
+
+	function __construct($http_method, $http_url, $parameters = NULL)
+	{
+		@$parameters or $parameters = array();
+		$this->parameters = $parameters;
+		$this->http_method = $http_method;
+		$this->http_url = $http_url;
+	}
+
+	/**
+	 * attempt to build up a request from what was passed to the server
+	 */
+	public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL)
+	{
+		$scheme = (! isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https';
+		@$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+		@$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
+		$request_headers = OAuthRequest::get_headers();
+		// let the library user override things however they'd like, if they know
+		// which parameters to use then go for it, for example XMLRPC might want to
+		// do this
+		if ($parameters) {
+			$req = new OAuthRequest($http_method, $http_url, $parameters);
+		} elseif (@substr($request_headers['Authorization'], 0, 5) == "OAuth") {
+			// next check for the auth header, we need to do some extra stuff
+			// if that is the case, namely suck in the parameters from GET or POST
+			// so that we can include them in the signature
+			$header_parameters = OAuthRequest::split_header($request_headers['Authorization']);
+			if ($http_method == "GET") {
+				$req_parameters = $_GET;
+			} else 
+				if ($http_method = "POST") {
+					$req_parameters = $_POST;
+				}
+			$parameters = array_merge($header_parameters, $req_parameters);
+			$req = new OAuthRequest($http_method, $http_url, $parameters);
+		} elseif ($http_method == "GET") {
+			$req = new OAuthRequest($http_method, $http_url, $_GET);
+		} elseif ($http_method == "POST") {
+			$req = new OAuthRequest($http_method, $http_url, $_POST);
+		}
+		return $req;
+	}
+
+	/**
+	 * pretty much a helper function to set up the request
+	 */
+	public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = NULL)
+	{
+		$parameters = is_array($parameters) ? $parameters : array();
+		$defaults = array("oauth_nonce" => OAuthRequest::generate_nonce(), 
+				"oauth_timestamp" => OAuthRequest::generate_timestamp(), 
+				"oauth_consumer_key" => $consumer->key);
+		$parameters = array_merge($defaults, $parameters);
+		if (isset($token)) {
+			$parameters['oauth_token'] = $token;
+		}
+		return new OAuthRequest($http_method, $http_url, $parameters);
+	}
+
+	public function set_parameter($name, $value)
+	{
+		$this->parameters[$name] = $value;
+	}
+
+	public function get_parameter($name)
+	{
+		return @$this->parameters[$name];
+	}
+
+	public function get_parameters()
+	{
+		return $this->parameters;
+	}
+
+	public function set_parameters($params)
+	{
+		return $this->parameters = $params;
+	}
+
 	//TODO double check if hash can be used
-	public function requireParameters($names)
-	{
-		$present = $this->parameters;
-		$absent = array();
-		foreach ($names as $required) {
-			if (! in_array($required, $present)) {
-				$absent[] = $required;
-			}
-		}
-		if (count($absent) == 0) {
-			throw new OAuthProblemException("oauth_parameters_absent: " . OAuthUtil::urlencodeRFC3986($absent));
-		}
-	}
-
-	/**
-	 * Returns the normalized parameters of the request
-	 *
-	 * This will be all (except oauth_signature) parameters,
-	 * sorted first by key, and if duplicate keys, then by
-	 * value.
-	 *
-	 * The returned string will be all the key=value pairs
-	 * concated by &.
-	 *
-	 * @return string
-	 */
-	public function get_signable_parameters()
-	{
-		// Grab all parameters
-		$params = $this->parameters;
-		// Remove oauth_signature if present
-		if (isset($params['oauth_signature'])) {
-			unset($params['oauth_signature']);
-		}
-		// Urlencode both keys and values
-		$keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params));
-		$values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params));
-		$params = array_combine($keys, $values);
-		// Sort by keys (natsort)
-		uksort($params, 'strnatcmp');
-		// Generate key=value pairs
-		$pairs = array();
-		foreach ($params as $key => $value) {
-			if (is_array($value)) {
-				// If the value is an array, it's because there are multiple
-				// with the same key, sort them, then add all the pairs
-				natsort($value);
-				foreach ($value as $v2) {
-					$pairs[] = $key . '=' . $v2;
-				}
-			} else {
-				$pairs[] = $key . '=' . $value;
-			}
-		}
-		// Return the pairs, concated with &
-		return implode('&', $pairs);
-	}
-
-	/**
-	 * Returns the base string of this request
-	 *
-	 * The base string defined as the method, the url
-	 * and the parameters (normalized), each urlencoded
-	 * and the concated with &.
-	 */
-	public function get_signature_base_string()
-	{
-		$tmp = $this->parameters;
-		$parts = parse_url($this->http_url);
-		parse_str(@$parts['query'], $params);
-		foreach ($params as $key => $value) {
-			$this->parameters[$key] = $value;
-		}
-		$parts = array($this->get_normalized_http_method(), $this->get_normalized_http_url(), 
-				$this->get_signable_parameters());
-		$parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts);
-		$this->parameters = $tmp;
-		return implode('&', $parts);
-	}
-
-	/**
-	 * just uppercases the http method
-	 */
-	public function get_normalized_http_method()
-	{
-		return strtoupper($this->http_method);
-	}
-
-	/**
-	 * parses the url and rebuilds it to be
-	 * scheme://host/path
-	 */
-	public function get_normalized_http_url()
-	{
-		$parts = parse_url($this->http_url);
-		// FIXME: port should handle according to http://groups.google.com/group/oauth/browse_thread/thread/1b203a51d9590226
-		$port = (isset($parts['port']) && $parts['port'] != '80') ? ':' . $parts['port'] : '';
-		$path = (isset($parts['path'])) ? $parts['path'] : '';
-		
-		return $parts['scheme'] . '://' . $parts['host'] . $port . $path;
-	}
-
-	/**
-	 * builds a url usable for a GET request
-	 */
-	public function to_url()
-	{
-		$out = $this->get_normalized_http_url() . "?";
-		$out .= $this->to_postdata();
-		$parts = parse_url($this->http_url);
-		$out .= "&" . @$parts['query'];
-		return $out;
-	}
-
-	public function get_url()
-	{
-		return $this->http_url;
-	}
-
-	/**
-	 * builds the data one would send in a POST request
-	 */
-	public function to_postdata()
-	{
-		$total = array();
-		foreach ($this->parameters as $k => $v) {
-			$total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v);
-		}
-		$out = implode("&", $total);
-		return $out;
-	}
-
-	/**
-	 * builds the Authorization: header
-	 */
-	public function to_header()
-	{
-		$out = '"Authorization: OAuth realm="",';
-		foreach ($this->parameters as $k => $v) {
-			if (substr($k, 0, 5) != "oauth")
-				continue;
-			$out .= ',' . OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '"';
-		}
-		return $out;
-	}
-
-	public function __toString()
-	{
-		return $this->to_url();
-	}
-
-	public function sign_request($signature_method, $consumer, $token)
-	{
-		$this->set_parameter("oauth_signature_method", $signature_method->get_name());
-		$signature = $this->build_signature($signature_method, $consumer, $token);
-		$this->set_parameter("oauth_signature", $signature);
-	}
-
-	public function build_signature($signature_method, $consumer, $token)
-	{
-		$signature = $signature_method->build_signature($this, $consumer, $token);
-		return $signature;
-	}
-
-	/**
-	 * util function: current timestamp
-	 */
-	private static function generate_timestamp()
-	{
-		return time();
-	}
-
-	/**
-	 * util function: current nonce
-	 */
-	public static function generate_nonce()
-	{
-		$mt = microtime();
-		$rand = mt_rand();
-		return md5($mt . $rand); // md5s look nicer than numbers
-	}
-
-	/**
-	 * util function for turning the Authorization: header into
-	 * parameters, has to do some unescaping
-	 */
-	private static function split_header($header)
-	{
-		// this should be a regex
-		// error cases: commas in parameter values
-		$parts = explode(",", $header);
-		$out = array();
-		foreach ($parts as $param) {
-			$param = ltrim($param);
-			// skip the "realm" param, nobody ever uses it anyway
-			if (substr($param, 0, 5) != "oauth")
-				continue;
-			$param_parts = explode("=", $param);
-			// rawurldecode() used because urldecode() will turn a "+" in the
-			// value into a space
-			$out[$param_parts[0]] = rawurldecode(substr($param_parts[1], 1, - 1));
-		}
-		return $out;
-	}
-
-	/**
-	 * helper to try to sort out headers for people who aren't running apache
-	 */
-	private static function get_headers()
-	{
-		if (function_exists('apache_request_headers')) {
-			// we need this to get the actual Authorization: header
-			// because apache tends to tell us it doesn't exist
-			return apache_request_headers();
-		}
-		// otherwise we don't have apache and are just going to have to hope
-		// that $_SERVER actually contains what we need
-		$out = array();
-		foreach ($_SERVER as $key => $value) {
-			if (substr($key, 0, 5) == "HTTP_") {
-				// this is chaos, basically it is just there to capitalize the first
-				// letter of every word that is not an initial HTTP and strip HTTP
-				// code from przemek
-				$key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))));
-				$out[$key] = $value;
-			}
-		}
-		return $out;
-	}
-}
-
-class OAuthUtil {
-	
-	public static $AUTH_SCHEME = "OAuth";
-	private static $AUTHORIZATION = "\ *[a-zA-Z0-9*]\ +(.*)";
-	private static $NVP = "(\\S*)\\s*\\=\\s*\"([^\"]*)\"";
-	
-	public static function getPostBodyString(Array $params)
-	{
-		$result = '';
-		$first = true;
-		foreach ($params as $key => $val) {
-			if ($first) {
-				$first = false;
-			} else {
-				$result .= '&';
-			}
-			$result .= OAuthUtil::urlencodeRFC3986($key) . "=" . OAuthUtil::urlencodeRFC3986($val);
-		}
-		return $result;
-	}
-
-	public static function urlencodeRFC3986($string)
-	{
-		return str_replace('%7E', '~', rawurlencode($string));
-	}
-
-	public static function urldecodeRFC3986($string)
-	{
-		return rawurldecode($string);
-	}
-
-	/** Return true if the given Content-Type header means FORM_ENCODED. */
-	public static function isFormEncoded($contentType)
-	{
-		if (! isset($contentType)) {
-			return false;
-		}
-		$semi = strpos($contentType, ";");
-		if ($semi >= 0) {
-			$contentType = substr($contentType, 0, $semi);
-		}
-		return strtolower(OAuth::$FORM_ENCODED) == strtolower(trim($contentType));
-	}
-
-	public static function addParameters($url, $oauthParams)
-	{
-		$url .= strchr($url, '?') === false ? '?' : '&';
-		foreach ($oauthParams as $key => $value) {
-			$url .= "$key=$value&";
-		}
-		return $url;
-	}
-
-	public static function decodeForm($form)
-	{
-		$parameters = array();
-		$explodedForm = explode("&", $form);
-		foreach ($explodedForm as $params) {
-			$value = explode("=", $params);
-			if (! empty($value[0]) && ! empty($value[1])) {
-				$parameters[OAuthUtil::urldecodeRFC3986($value[0])] = OAuthUtil::urldecodeRFC3986($value[1]);
-			}
-		}
-		return $parameters;
-	}
-
-	/**
-	 * Parse the parameters from an OAuth Authorization or WWW-Authenticate
-	 * header. The realm is included as a parameter. If the given header doesn't
-	 * start with "OAuth ", return an empty list.
-	 */
-	public static function decodeAuthorization($authorization)
-	{
-		$into = array();
-		if ($authorization != null) {
-			$m = ereg(self::$AUTHORIZATION, $authorization);
-			if ($m !== false) {
-				if (strpos($authorization, OAuthUtil::$AUTH_SCHEME) == 0) {
-					$authorization = str_replace("OAuth ", "", $authorization);
-					$authParams = explode(", ", $authorization);
-					foreach ($authParams as $params) {
-						$m = ereg(OAuthUtil::$NVP, $params);
-						if ($m == 1) {
-							$keyValue = explode("=", $params);
-							$name = OAuthUtil::urlencodeRFC3986($keyValue[0]);
-							$value = OAuthUtil::urlencodeRFC3986(str_replace("\"", "", $keyValue[1]));
-							$into[$name] = $value;
-						}
-					}
-				}
-			}
-		}
-		return $into;
-	}
-}
\ No newline at end of file
+	public function requireParameters($names)
+	{
+		$present = $this->parameters;
+		$absent = array();
+		foreach ($names as $required) {
+			if (! in_array($required, $present)) {
+				$absent[] = $required;
+			}
+		}
+		if (count($absent) == 0) {
+			throw new OAuthProblemException("oauth_parameters_absent: " . OAuthUtil::urlencodeRFC3986($absent));
+		}
+	}
+
+	/**
+	 * Returns the normalized parameters of the request
+	 *
+	 * This will be all (except oauth_signature) parameters,
+	 * sorted first by key, and if duplicate keys, then by
+	 * value.
+	 *
+	 * The returned string will be all the key=value pairs
+	 * concated by &.
+	 *
+	 * @return string
+	 */
+	public function get_signable_parameters()
+	{
+		// Grab all parameters
+		$params = $this->parameters;
+		// Remove oauth_signature if present
+		if (isset($params['oauth_signature'])) {
+			unset($params['oauth_signature']);
+		}
+		// Urlencode both keys and values
+		$keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params));
+		$values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params));
+		$params = array_combine($keys, $values);
+		// Sort by keys (natsort)
+		uksort($params, 'strnatcmp');
+		// Generate key=value pairs
+		$pairs = array();
+		foreach ($params as $key => $value) {
+			if (is_array($value)) {
+				// If the value is an array, it's because there are multiple
+				// with the same key, sort them, then add all the pairs
+				natsort($value);
+				foreach ($value as $v2) {
+					$pairs[] = $key . '=' . $v2;
+				}
+			} else {
+				$pairs[] = $key . '=' . $value;
+			}
+		}
+		// Return the pairs, concated with &
+		return implode('&', $pairs);
+	}
+
+	/**
+	 * Returns the base string of this request
+	 *
+	 * The base string defined as the method, the url
+	 * and the parameters (normalized), each urlencoded
+	 * and the concated with &.
+	 */
+	public function get_signature_base_string()
+	{
+		$tmp = $this->parameters;
+		$parts = parse_url($this->http_url);
+		parse_str(@$parts['query'], $params);
+		foreach ($params as $key => $value) {
+			if ($key == "signOwner" || $key == "signViewer") {
+				continue;
+			}
+			$this->parameters[$key] = $value;
+		}
+		$parts = array($this->get_normalized_http_method(), $this->get_normalized_http_url(), 
+				$this->get_signable_parameters());
+		$parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts);
+		$this->parameters = $tmp;
+		return implode('&', $parts);
+	}
+
+	/**
+	 * just uppercases the http method
+	 */
+	public function get_normalized_http_method()
+	{
+		return strtoupper($this->http_method);
+	}
+
+	/**
+	 * parses the url and rebuilds it to be
+	 * scheme://host/path
+	 */
+	public function get_normalized_http_url()
+	{
+		$parts = parse_url($this->http_url);
+		// FIXME: port should handle according to http://groups.google.com/group/oauth/browse_thread/thread/1b203a51d9590226
+		$port = (isset($parts['port']) && $parts['port'] != '80') ? ':' . $parts['port'] : '';
+		$path = (isset($parts['path'])) ? $parts['path'] : '';
+		
+		return $parts['scheme'] . '://' . $parts['host'] . $port . $path;
+	}
+
+	/**
+	 * builds a url usable for a GET request
+	 */
+	public function to_url()
+	{
+		$out = $this->get_normalized_http_url() . "?";
+		$out .= $this->to_postdata();
+		$parts = parse_url($this->http_url);
+		$out .= "&" . @$parts['query'];
+		return $out;
+	}
+
+	public function get_url()
+	{
+		return $this->http_url;
+	}
+
+	/**
+	 * builds the data one would send in a POST request
+	 */
+	public function to_postdata()
+	{
+		$total = array();
+		foreach ($this->parameters as $k => $v) {
+			$total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v);
+		}
+		$out = implode("&", $total);
+		return $out;
+	}
+
+	/**
+	 * builds the Authorization: header
+	 */
+	public function to_header()
+	{
+		$out = '"Authorization: OAuth realm="",';
+		foreach ($this->parameters as $k => $v) {
+			if (substr($k, 0, 5) != "oauth")
+				continue;
+			$out .= ',' . OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '"';
+		}
+		return $out;
+	}
+
+	public function __toString()
+	{
+		return $this->to_url();
+	}
+
+	public function sign_request($signature_method, $consumer, $token)
+	{
+		$this->set_parameter("oauth_signature_method", $signature_method->get_name());
+		$signature = $this->build_signature($signature_method, $consumer, $token);
+		$this->set_parameter("oauth_signature", $signature);
+	}
+
+	public function build_signature($signature_method, $consumer, $token)
+	{
+		$signature = $signature_method->build_signature($this, $consumer, $token);
+		return $signature;
+	}
+
+	/**
+	 * util function: current timestamp
+	 */
+	private static function generate_timestamp()
+	{
+		return time();
+	}
+
+	/**
+	 * util function: current nonce
+	 */
+	public static function generate_nonce()
+	{
+		$mt = microtime();
+		$rand = mt_rand();
+		return md5($mt . $rand); // md5s look nicer than numbers
+	}
+
+	/**
+	 * util function for turning the Authorization: header into
+	 * parameters, has to do some unescaping
+	 */
+	private static function split_header($header)
+	{
+		// this should be a regex
+		// error cases: commas in parameter values
+		$parts = explode(",", $header);
+		$out = array();
+		foreach ($parts as $param) {
+			$param = ltrim($param);
+			// skip the "realm" param, nobody ever uses it anyway
+			if (substr($param, 0, 5) != "oauth")
+				continue;
+			$param_parts = explode("=", $param);
+			// rawurldecode() used because urldecode() will turn a "+" in the
+			// value into a space
+			$out[$param_parts[0]] = rawurldecode(substr($param_parts[1], 1, - 1));
+		}
+		return $out;
+	}
+
+	/**
+	 * helper to try to sort out headers for people who aren't running apache
+	 */
+	private static function get_headers()
+	{
+		if (function_exists('apache_request_headers')) {
+			// we need this to get the actual Authorization: header
+			// because apache tends to tell us it doesn't exist
+			return apache_request_headers();
+		}
+		// otherwise we don't have apache and are just going to have to hope
+		// that $_SERVER actually contains what we need
+		$out = array();
+		foreach ($_SERVER as $key => $value) {
+			if (substr($key, 0, 5) == "HTTP_") {
+				// this is chaos, basically it is just there to capitalize the first
+				// letter of every word that is not an initial HTTP and strip HTTP
+				// code from przemek
+				$key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))));
+				$out[$key] = $value;
+			}
+		}
+		return $out;
+	}
+}
+
+class OAuthUtil {
+	
+	public static $AUTH_SCHEME = "OAuth";
+	private static $AUTHORIZATION = "\ *[a-zA-Z0-9*]\ +(.*)";
+	private static $NVP = "(\\S*)\\s*\\=\\s*\"([^\"]*)\"";
+	
+	public static function getPostBodyString(Array $params)
+	{
+		$result = '';
+		$first = true;
+		foreach ($params as $key => $val) {
+			if ($first) {
+				$first = false;
+			} else {
+				$result .= '&';
+			}
+			$result .= OAuthUtil::urlencodeRFC3986($key) . "=" . OAuthUtil::urlencodeRFC3986($val);
+		}
+		return $result;
+	}
+
+	public static function urlencodeRFC3986($string)
+	{
+		return str_replace('%7E', '~', rawurlencode($string));
+	}
+
+	public static function urldecodeRFC3986($string)
+	{
+		return rawurldecode($string);
+	}
+
+	/** Return true if the given Content-Type header means FORM_ENCODED. */
+	public static function isFormEncoded($contentType)
+	{
+		if (! isset($contentType)) {
+			return false;
+		}
+		$semi = strpos($contentType, ";");
+		if ($semi >= 0) {
+			$contentType = substr($contentType, 0, $semi);
+		}
+		return strtolower(OAuth::$FORM_ENCODED) == strtolower(trim($contentType));
+	}
+
+	public static function addParameters($url, $oauthParams)
+	{
+		$url .= strchr($url, '?') === false ? '?' : '&';
+		foreach ($oauthParams as $key => $value) {
+			$url .= "$key=$value&";
+		}
+		return $url;
+	}
+
+	public static function decodeForm($form)
+	{
+		$parameters = array();
+		$explodedForm = explode("&", $form);
+		foreach ($explodedForm as $params) {
+			$value = explode("=", $params);
+			if (! empty($value[0]) && ! empty($value[1])) {
+				$parameters[OAuthUtil::urldecodeRFC3986($value[0])] = OAuthUtil::urldecodeRFC3986($value[1]);
+			}
+		}
+		return $parameters;
+	}
+
+	/**
+	 * Parse the parameters from an OAuth Authorization or WWW-Authenticate
+	 * header. The realm is included as a parameter. If the given header doesn't
+	 * start with "OAuth ", return an empty list.
+	 */
+	public static function decodeAuthorization($authorization)
+	{
+		$into = array();
+		if ($authorization != null) {
+			$m = ereg(self::$AUTHORIZATION, $authorization);
+			if ($m !== false) {
+				if (strpos($authorization, OAuthUtil::$AUTH_SCHEME) == 0) {
+					$authorization = str_replace("OAuth ", "", $authorization);
+					$authParams = explode(", ", $authorization);
+					foreach ($authParams as $params) {
+						$m = ereg(OAuthUtil::$NVP, $params);
+						if ($m == 1) {
+							$keyValue = explode("=", $params);
+							$name = OAuthUtil::urlencodeRFC3986($keyValue[0]);
+							$value = OAuthUtil::urlencodeRFC3986(str_replace("\"", "", $keyValue[1]));
+							$into[$name] = $value;
+						}
+					}
+				}
+			}
+		}
+		return $into;
+	}
+}