From 399669b1fb955d2d8c18098a7b551184d534a94c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 6 Mar 2009 16:26:28 -0800 Subject: [PATCH] Upstream changes to OAuth.php --- extlib/OAuth.php | 135 ++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/extlib/OAuth.php b/extlib/OAuth.php index 6dc6b3f356..029166175c 100644 --- a/extlib/OAuth.php +++ b/extlib/OAuth.php @@ -16,6 +16,10 @@ class OAuthConsumer {/*{{{*/ $this->secret = $secret; $this->callback_url = $callback_url; }/*}}}*/ + + function __toString() {/*{{{*/ + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + }/*}}}*/ }/*}}}*/ class OAuthToken {/*{{{*/ @@ -37,8 +41,8 @@ class OAuthToken {/*{{{*/ * 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); + return "oauth_token=" . OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . OAuthUtil::urlencode_rfc3986($this->secret); }/*}}}*/ function __toString() {/*{{{*/ @@ -67,7 +71,7 @@ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {/*{{{*/ ($token) ? $token->secret : "" ); - $key_parts = array_map(array('OAuthUtil','urlencodeRFC3986'), $key_parts); + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); $key = implode('&', $key_parts); return base64_encode( hash_hmac('sha1', $base_string, $key, true)); @@ -81,11 +85,11 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {/*{{{*/ public function build_signature($request, $consumer, $token) {/*{{{*/ $sig = array( - OAuthUtil::urlencodeRFC3986($consumer->secret) + OAuthUtil::urlencode_rfc3986($consumer->secret) ); if ($token) { - array_push($sig, OAuthUtil::urlencodeRFC3986($token->secret)); + array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret)); } else { array_push($sig, ''); } @@ -94,7 +98,7 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {/*{{{*/ // for debug purposes $request->base_string = $raw; - return OAuthUtil::urlencodeRFC3986($raw); + return OAuthUtil::urlencode_rfc3986($raw); }/*}}}*/ }/*}}}*/ @@ -182,7 +186,7 @@ class OAuthRequest {/*{{{*/ */ 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_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; $request_headers = OAuthRequest::get_headers(); @@ -192,27 +196,23 @@ class OAuthRequest {/*{{{*/ // do this if ($parameters) { $req = new OAuthRequest($http_method, $http_url, $parameters); + } else { + // collect request parameters from query string (GET) and post-data (POST) if appropriate (note: POST vars have priority) + $req_parameters = $_GET; + if ($http_method == "POST" && @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) { + $req_parameters = array_merge($req_parameters, $_POST); + } + + // 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 + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthRequest::split_header($request_headers['Authorization']); + $parameters = array_merge($req_parameters, $header_parameters); + $req = new OAuthRequest($http_method, $http_url, $parameters); + } else $req = new OAuthRequest($http_method, $http_url, $req_parameters); } - // 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 - else if (@substr($request_headers['Authorization'], 0, 5) == "OAuth") { - $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); - } - else if ($http_method == "GET") { - $req = new OAuthRequest($http_method, $http_url, $_GET); - } - else if ($http_method == "POST") { - $req = new OAuthRequest($http_method, $http_url, $_POST); - } + return $req; }/*}}}*/ @@ -238,7 +238,7 @@ class OAuthRequest {/*{{{*/ }/*}}}*/ public function get_parameter($name) {/*{{{*/ - return $this->parameters[$name]; + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; }/*}}}*/ public function get_parameters() {/*{{{*/ @@ -267,12 +267,12 @@ class OAuthRequest {/*{{{*/ } // Urlencode both keys and values - $keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params)); - $values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params)); + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); $params = array_combine($keys, $values); // Sort by keys (natsort) - uksort($params, 'strnatcmp'); + uksort($params, 'strcmp'); // Generate key=value pairs $pairs = array(); @@ -307,7 +307,7 @@ class OAuthRequest {/*{{{*/ $this->get_signable_parameters() ); - $parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts); + $parts = OAuthUtil::urlencode_rfc3986($parts); return implode('&', $parts); }/*}}}*/ @@ -351,11 +351,21 @@ class OAuthRequest {/*{{{*/ /** * builds the data one would send in a POST request + * + * TODO(morten.fangel): + * this function might be easily replaced with http_build_query() + * and corrections for rfc3986 compatibility.. but not sure */ public function to_postdata() {/*{{{*/ $total = array(); foreach ($this->parameters as $k => $v) { - $total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v); + if (is_array($v)) { + foreach ($v as $va) { + $total[] = OAuthUtil::urlencode_rfc3986($k) . "[]=" . OAuthUtil::urlencode_rfc3986($va); + } + } else { + $total[] = OAuthUtil::urlencode_rfc3986($k) . "=" . OAuthUtil::urlencode_rfc3986($v); + } } $out = implode("&", $total); return $out; @@ -364,12 +374,13 @@ class OAuthRequest {/*{{{*/ /** * builds the Authorization: header */ - public function to_header($realm="") {/*{{{*/ - $out ='"Authorization: OAuth realm="' . $realm . '",'; + public function to_header() {/*{{{*/ + $out ='Authorization: OAuth realm=""'; $total = array(); foreach ($this->parameters as $k => $v) { if (substr($k, 0, 5) != "oauth") continue; - $out .= ',' . OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '"'; + if (is_array($v)) throw new OAuthException('Arrays not supported in headers'); + $out .= ',' . OAuthUtil::urlencode_rfc3986($k) . '="' . OAuthUtil::urlencode_rfc3986($v) . '"'; } return $out; }/*}}}*/ @@ -412,24 +423,22 @@ class OAuthRequest {/*{{{*/ * parameters, has to do some unescaping */ private static function split_header($header) {/*{{{*/ - // remove 'OAuth ' at the start of a header - $header = substr($header, 6); - - // 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)); + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + $params[$header_name] = OAuthUtil::urldecode_rfc3986( $header_content ); + $offset = $match[1] + strlen($match[0]); } - return $out; + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; }/*}}}*/ /** @@ -506,6 +515,7 @@ class OAuthServer {/*{{{*/ // requires authorized request token $token = $this->get_token($request, $consumer, "request"); + $this->check_signature($request, $consumer, $token); $new_token = $this->data_store->new_access_token($token, $consumer); @@ -654,11 +664,11 @@ class OAuthDataStore {/*{{{*/ // implement me }/*}}}*/ - function fetch_request_token($consumer) {/*{{{*/ + function new_request_token($consumer) {/*{{{*/ // return a new token attached to this consumer }/*}}}*/ - function fetch_access_token($token, $consumer) {/*{{{*/ + function new_access_token($token, $consumer) {/*{{{*/ // return a new access token attached to this consumer // for the user associated with this token if the request token // is authorized @@ -737,17 +747,22 @@ class SimpleOAuthDataStore extends OAuthDataStore {/*{{{*/ }/*}}}*/ class OAuthUtil {/*{{{*/ - public static function urlencodeRFC3986($string) {/*{{{*/ - return str_replace('+', ' ', - str_replace('%7E', '~', rawurlencode($string))); - + public static function urlencode_rfc3986($input) {/*{{{*/ + if (is_array($input)) { + return array_map(array('OAuthUtil','urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace('+', ' ', + str_replace('%7E', '~', rawurlencode($input))); + } else { + return ''; + } }/*}}}*/ // This decode function isn't taking into consideration the above // modifications to the encoding process. However, this method doesn't // seem to be used anywhere so leaving it as is. - public static function urldecodeRFC3986($string) {/*{{{*/ + public static function urldecode_rfc3986($string) {/*{{{*/ return rawurldecode($string); }/*}}}*/ }/*}}}*/