Untitled diff

Created Diff never expires
16 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
833 lines
24 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
841 lines
<?php
<?php


/*
/*
* Abraham Williams (abraham@abrah.am) http://abrah.am
* Abraham Williams (abraham@abrah.am) http://abrah.am
*
*
* The first PHP Library to support OAuth for Twitter's REST API.
* The first PHP Library to support OAuth for Twitter's REST API.
*/
*/


/* Load OAuth lib. You can find it at http://oauth.net */
/* Load OAuth lib. You can find it at http://oauth.net */


/* Generic exception class
/* Generic exception class
*/
*/
class pk_OAuthException extends Exception {
class pk_OAuthException extends Exception {
// pass
// pass
}
}


if( ! class_exists( 'OAuthConsumer' ) ) :
class OAuthConsumer {
class OAuthConsumer {
public $key;
public $key;
public $secret;
public $secret;


function __construct($key, $secret, $callback_url=NULL) {
function __construct($key, $secret, $callback_url=NULL) {
$this->key = $key;
$this->key = $key;
$this->secret = $secret;
$this->secret = $secret;
$this->callback_url = $callback_url;
$this->callback_url = $callback_url;
}
}


function __toString() {
function __toString() {
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
}
}
}
}
endif;


if( ! class_exists( 'OAuthToken' ) ) :
class OAuthToken {
class OAuthToken {
// access tokens and request tokens
// access tokens and request tokens
public $key;
public $key;
public $secret;
public $secret;


/**
/**
* key = the token
* key = the token
* secret = the token secret
* secret = the token secret
*/
*/
function __construct($key, $secret) {
function __construct($key, $secret) {
$this->key = $key;
$this->key = $key;
$this->secret = $secret;
$this->secret = $secret;
}
}



/**
/**
* generates the basic string serialization of a token that a server
* generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with
* would respond to request_token and access_token calls with
*/
*/
function to_string() {
function to_string() {
return "oauth_token=" .
return "oauth_token=" .
OAuthUtil::urlencode_rfc3986($this->key) .
OAuthUtil::urlencode_rfc3986($this->key) .
"&oauth_token_secret=" .
"&oauth_token_secret=" .
OAuthUtil::urlencode_rfc3986($this->secret);
OAuthUtil::urlencode_rfc3986($this->secret);
}
}


function __toString() {
function __toString() {
return $this->to_string();
return $this->to_string();
}
}
}
}
endif;


/**
/**
* A class for implementing a Signature Method
* A class for implementing a Signature Method
* See section 9 ("Signing Requests") in the spec
* See section 9 ("Signing Requests") in the spec
*/
*/
if( ! class_exists( 'OAuthSignatureMethod' ) ) :
abstract class OAuthSignatureMethod {
abstract class OAuthSignatureMethod {
/**
/**
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
* @return string
* @return string
*/
*/
abstract public function get_name();
abstract public function get_name();


/**
/**
* Build up the signature
* Build up the signature
* NOTE: The output of this function MUST NOT be urlencoded.
* NOTE: The output of this function MUST NOT be urlencoded.
* the encoding is handled in OAuthRequest when the final
* the encoding is handled in OAuthRequest when the final
* request is serialized
* request is serialized
* @param OAuthRequest $request
* @param OAuthRequest $request
* @param OAuthConsumer $consumer
* @param OAuthConsumer $consumer
* @param OAuthToken $token
* @param OAuthToken $token
* @return string
* @return string
*/
*/
abstract public function build_signature($request, $consumer, $token);
abstract public function build_signature($request, $consumer, $token);


/**
/**
* Verifies that a given signature is correct
* Verifies that a given signature is correct
* @param OAuthRequest $request
* @param OAuthRequest $request
* @param OAuthConsumer $consumer
* @param OAuthConsumer $consumer
* @param OAuthToken $token
* @param OAuthToken $token
* @param string $signature
* @param string $signature
* @return bool
* @return bool
*/
*/
public function check_signature($request, $consumer, $token, $signature) {
public function check_signature($request, $consumer, $token, $signature) {
$built = $this->build_signature($request, $consumer, $token);
$built = $this->build_signature($request, $consumer, $token);
return $built == $signature;
return $built == $signature;
}
}
}
}
endif;


/**
/**
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* character (ASCII code 38) even if empty.
* character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1")
* - Chapter 9.2 ("HMAC-SHA1")
*/
*/
if( ! class_exists( 'OAuthSignatureMethod_HMAC_SHA1' ) ) :
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
function get_name() {
function get_name() {
return "HMAC-SHA1";
return "HMAC-SHA1";
}
}


public function build_signature($request, $consumer, $token) {
public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
$request->base_string = $base_string;


$key_parts = array(
$key_parts = array(
$consumer->secret,
$consumer->secret,
($token) ? $token->secret : ""
($token) ? $token->secret : ""
);
);


$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
$key = implode('&', $key_parts);


return base64_encode(hash_hmac('sha1', $base_string, $key, true));
return base64_encode(hash_hmac('sha1', $base_string, $key, true));
}
}
}
}
endif;


/**
/**
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* over a secure channel such as HTTPS. It does not use the Signature Base String.
* over a secure channel such as HTTPS. It does not use the Signature Base String.
* - Chapter 9.4 ("PLAINTEXT")
* - Chapter 9.4 ("PLAINTEXT")
*/
*/
if( ! class_exists( 'OAuthSignatureMethod_PLAINTEXT' ) ) :
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
public function get_name() {
public function get_name() {
return "PLAINTEXT";
return "PLAINTEXT";
}
}


/**
/**
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* empty. The result MUST be encoded again.
* empty. The result MUST be encoded again.
* - Chapter 9.4.1 ("Generating Signatures")
* - Chapter 9.4.1 ("Generating Signatures")
*
*
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* OAuthRequest handles this!
* OAuthRequest handles this!
*/
*/
public function build_signature($request, $consumer, $token) {
public function build_signature($request, $consumer, $token) {
$key_parts = array(
$key_parts = array(
$consumer->secret,
$consumer->secret,
($token) ? $token->secret : ""
($token) ? $token->secret : ""
);
);


$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
$key = implode('&', $key_parts);
$request->base_string = $key;
$request->base_string = $key;


return $key;
return $key;
}
}
}
}
endif;


/**
/**
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* verified way to the Service Provider, in a manner which is beyond the scope of this
* verified way to the Service Provider, in a manner which is beyond the scope of this
* specification.
* specification.
* - Chapter 9.3 ("RSA-SHA1")
* - Chapter 9.3 ("RSA-SHA1")
*/
*/
if( ! class_exists( 'OAuthSignatureMethod_RSA_SHA1' ) ) :
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
public function get_name() {
public function get_name() {
return "RSA-SHA1";
return "RSA-SHA1";
}
}


// Up to the SP to implement this lookup of keys. Possible ideas are:
// Up to the SP to implement this lookup of keys. Possible ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
// (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
// (2) fetch via http using a url provided by the requester
// (3) some sort of specific discovery code based on request
// (3) some sort of specific discovery code based on request
//
//
// Either way should return a string representation of the certificate
// Either way should return a string representation of the certificate
protected abstract function fetch_public_cert(&$request);
protected abstract function fetch_public_cert(&$request);


// Up to the SP to implement this lookup of keys. Possible ideas are:
// Up to the SP to implement this lookup of keys. Possible ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
// (1) do a lookup in a table of trusted certs keyed off of consumer
//
//
// Either way should return a string representation of the certificate
// Either way should return a string representation of the certificate
protected abstract function fetch_private_cert(&$request);
protected abstract function fetch_private_cert(&$request);


public function build_signature($request, $consumer, $token) {
public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
$request->base_string = $base_string;


// Fetch the private key cert based on the request
// Fetch the private key cert based on the request
$cert = $this->fetch_private_cert($request);
$cert = $this->fetch_private_cert($request);


// Pull the private key ID from the certificate
// Pull the private key ID from the certificate
$privatekeyid = openssl_get_privatekey($cert);
$privatekeyid = openssl_get_privatekey($cert);


// Sign using the key
// Sign using the key
$ok = openssl_sign($base_string, $signature, $privatekeyid);
$ok = openssl_sign($base_string, $signature, $privatekeyid);


// Release the key resource
// Release the key resource
openssl_free_key($privatekeyid);
openssl_free_key($privatekeyid);


return base64_encode($signature);
return base64_encode($signature);
}
}



public function check_signature($request, $consumer, $token, $signature) {
public function check_signature($request, $consumer, $token, $signature) {
$decoded_sig = base64_decode($signature);
$decoded_sig = base64_decode($signature);


$base_string = $request->get_signature_base_string();
$base_string = $request->get_signature_base_string();


// Fetch the public key cert based on the request
// Fetch the public key cert based on the request
$cert = $this->fetch_public_cert($request);
$cert = $this->fetch_public_cert($request);


// Pull the public key ID from the certificate
// Pull the public key ID from the certificate
$publickeyid = openssl_get_publickey($cert);
$publickeyid = openssl_get_publickey($cert);


// Check the computed signature against the one passed in the query
// Check the computed signature against the one passed in the query
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);


// Release the key resource
// Release the key resource
openssl_free_key($publickeyid);
openssl_free_key($publickeyid);


return $ok == 1;
return $ok == 1;
}
}
}
}
endif;


if( ! class_exists( 'OAuthRequest' ) ) :
class OAuthRequest {
class OAuthRequest {
private $parameters;
private $parameters;
private $http_method;
private $http_method;
private $http_url;
private $http_url;
// for debug purposes
// for debug purposes
public $base_string;
public $base_string;
public static $version = '1.0';
public static $version = '1.0';
public static $POST_INPUT = 'php://input';
public static $POST_INPUT = 'php://input';


function __construct($http_method, $http_url, $parameters=NULL) {
function __construct($http_method, $http_url, $parameters=NULL) {
@$parameters or $parameters = array();
@$parameters or $parameters = array();
$parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
$parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
$this->parameters = $parameters;
$this->parameters = $parameters;
$this->http_method = $http_method;
$this->http_method = $http_method;
$this->http_url = $http_url;
$this->http_url = $http_url;
}
}




/**
/**
* attempt to build up a request from what was passed to the server
* 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) {
public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
? 'http'
? 'http'
: 'https';
: 'https';
@$http_url or $http_url = $scheme .
@$http_url or $http_url = $scheme .
'://' . $_SERVER['HTTP_HOST'] .
'://' . $_SERVER['HTTP_HOST'] .
':' .
':' .
$_SERVER['SERVER_PORT'] .
$_SERVER['SERVER_PORT'] .
$_SERVER['REQUEST_URI'];
$_SERVER['REQUEST_URI'];
@$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
@$http_method or $http_method = $_SERVER['REQUEST_METHOD'];


// We weren't handed any parameters, so let's find the ones relevant to
// We weren't handed any parameters, so let's find the ones relevant to
// this request.
// this request.
// If you run XML-RPC or similar you should use this to provide your own
// If you run XML-RPC or similar you should use this to provide your own
// parsed parameter-list
// parsed parameter-list
if (!$parameters) {
if (!$parameters) {
// Find request headers
// Find request headers
$request_headers = OAuthUtil::get_headers();
$request_headers = OAuthUtil::get_headers();


// Parse the query-string to find GET parameters
// Parse the query-string to find GET parameters
$parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
$parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);


// It's a POST request of the proper content-type, so parse POST
// It's a POST request of the proper content-type, so parse POST
// parameters and add those overriding any duplicates from GET
// parameters and add those overriding any duplicates from GET
if ($http_method == "POST"
if ($http_method == "POST"
&& @strstr($request_headers["Content-Type"],
&& @strstr($request_headers["Content-Type"],
"application/x-www-form-urlencoded")
"application/x-www-form-urlencoded")
) {
) {
$post_data = OAuthUtil::parse_parameters(
$post_data = OAuthUtil::parse_parameters(
file_get_contents(self::$POST_INPUT)
file_get_contents(self::$POST_INPUT)
);
);
$parameters = array_merge($parameters, $post_data);
$parameters = array_merge($parameters, $post_data);
}
}


// We have a Authorization-header with OAuth data. Parse the header
// We have a Authorization-header with OAuth data. Parse the header
// and add those overriding any duplicates from GET or POST
// and add those overriding any duplicates from GET or POST
if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
$header_parameters = OAuthUtil::split_header(
$header_parameters = OAuthUtil::split_header(
$request_headers['Authorization']
$request_headers['Authorization']
);
);
$parameters = array_merge($parameters, $header_parameters);
$parameters = array_merge($parameters, $header_parameters);
}
}


}
}


return new OAuthRequest($http_method, $http_url, $parameters);
return new OAuthRequest($http_method, $http_url, $parameters);
}
}


/**
/**
* pretty much a helper function to set up the request
* 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) {
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
@$parameters or $parameters = array();
@$parameters or $parameters = array();
$defaults = array("oauth_version" => OAuthRequest::$version,
$defaults = array("oauth_version" => OAuthRequest::$version,
"oauth_nonce" => OAuthRequest::generate_nonce(),
"oauth_nonce" => OAuthRequest::generate_nonce(),
"oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_consumer_key" => $consumer->key);
"oauth_consumer_key" => $consumer->key);
if ($token)
if ($token)
$defaults['oauth_token'] = $token->key;
$defaults['oauth_token'] = $token->key;


$parameters = array_merge($defaults, $parameters);
$parameters = array_merge($defaults, $parameters);


return new OAuthRequest($http_method, $http_url, $parameters);
return new OAuthRequest($http_method, $http_url, $parameters);
}
}


public function set_parameter($name, $value, $allow_duplicates = true) {
public function set_parameter($name, $value, $allow_duplicates = true) {
if ($allow_duplicates && isset($this->parameters[$name])) {
if ($allow_duplicates && isset($this->parameters[$name])) {
// We have already added parameter(s) with this name, so add to the list
// We have already added parameter(s) with this name, so add to the list
if (is_scalar($this->parameters[$name])) {
if (is_scalar($this->parameters[$name])) {
// This is the first duplicate, so transform scalar (string)
// This is the first duplicate, so transform scalar (string)
// into an array so we can add the duplicates
// into an array so we can add the duplicates
$this->parameters[$name] = array($this->parameters[$name]);
$this->parameters[$name] = array($this->parameters[$name]);
}
}


$this->parameters[$name][] = $value;
$this->parameters[$name][] = $value;
} else {
} else {
$this->parameters[$name] = $value;
$this->parameters[$name] = $value;
}
}
}
}


public function get_parameter($name) {
public function get_parameter($name) {
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
}
}


public function get_parameters() {
public function get_parameters() {
return $this->parameters;
return $this->parameters;
}
}


public function unset_parameter($name) {
public function unset_parameter($name) {
unset($this->parameters[$name]);
unset($this->parameters[$name]);
}
}


/**
/**
* The request parameters, sorted and concatenated into a normalized string.
* The request parameters, sorted and concatenated into a normalized string.
* @return string
* @return string
*/
*/
public function get_signable_parameters() {
public function get_signable_parameters() {
// Grab all parameters
// Grab all parameters
$params = $this->parameters;
$params = $this->parameters;


// Remove oauth_signature if present
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
if (isset($params['oauth_signature'])) {
if (isset($params['oauth_signature'])) {
unset($params['oauth_signature']);
unset($params['oauth_signature']);
}
}


return OAuthUtil::build_http_query($params);
return OAuthUtil::build_http_query($params);
}
}


/**
/**
* Returns the base string of this request
* Returns the base string of this request
*
*
* The base string defined as the method, the url
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the parameters (normalized), each urlencoded
* and the concated with &.
* and the concated with &.
*/
*/
public function get_signature_base_string() {
public function get_signature_base_string() {
$parts = array(
$parts = array(
$this->get_normalized_http_method(),
$this->get_normalized_http_method(),
$this->get_normalized_http_url(),
$this->get_normalized_http_url(),
$this->get_signable_parameters()
$this->get_signable_parameters()
);
);


$parts = OAuthUtil::urlencode_rfc3986($parts);
$parts = OAuthUtil::urlencode_rfc3986($parts);


return implode('&', $parts);
return implode('&', $parts);
}
}


/**
/**
* just uppercases the http method
* just uppercases the http method
*/
*/
public function get_normalized_http_method() {
public function get_normalized_http_method() {
return strtoupper($this->http_method);
return strtoupper($this->http_method);
}
}


/**
/**
* parses the url and rebuilds it to be
* parses the url and rebuilds it to be
* scheme://host/path
* scheme://host/path
*/
*/
public function get_normalized_http_url() {
public function get_normalized_http_url() {
$parts = parse_url($this->http_url);
$parts = parse_url($this->http_url);


$port = @$parts['port'];
$port = @$parts['port'];
$scheme = $parts['scheme'];
$scheme = $parts['scheme'];
$host = $parts['host'];
$host = $parts['host'];
$path = @$parts['path'];
$path = @$parts['path'];


$port or $port = ($scheme == 'https') ? '443' : '80';
$port or $port = ($scheme == 'https') ? '443' : '80';


if (($scheme == 'https' && $port != '443')
if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) {
|| ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
$host = "$host:$port";
}
}
return "$scheme://$host$path";
return "$scheme://$host$path";
}
}


/**
/**
* builds a url usable for a GET request
* builds a url usable for a GET request
*/
*/
public function to_url() {
public function to_url() {
$post_data = $this->to_postdata();
$post_data = $this->to_postdata();
$out = $this->get_normalized_http_url();
$out = $this->get_normalized_http_url();
if ($post_data) {
if ($post_data) {
$out .= '?'.$post_data;
$out .= '?'.$post_data;
}
}
return $out;
return $out;
}
}


/**
/**
* builds the data one would send in a POST request
* builds the data one would send in a POST request
*/
*/
public function to_postdata() {
public function to_postdata() {
return OAuthUtil::build_http_query($this->parameters);
return OAuthUtil::build_http_query($this->parameters);
}
}


/**
/**
* builds the Authorization: header
* builds the Authorization: header
*/
*/
public function to_header($realm=null) {
public function to_header($realm=null) {
$first = true;
$first = true;
if($realm) {
if($realm) {
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
$first = false;
$first = false;
} else
} else
$out = 'Authorization: OAuth';
$out = 'Authorization: OAuth';


$total = array();
$total = array();
foreach ($this->parameters as $k => $v) {
foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != "oauth") continue;
if (substr($k, 0, 5) != "oauth") continue;
if (is_array($v)) {
if (is_array($v)) {
throw new pk_OAuthException('Arrays not supported in headers');
throw new pk_OAuthException('Arrays not supported in headers');
}
}
$out .= ($first) ? ' ' : ',';
$out .= ($first) ? ' ' : ',';
$out .= OAuthUtil::urlencode_rfc3986($k) .
$out .= OAuthUtil::urlencode_rfc3986($k) .
'="' .
'="' .
OAuthUtil::urlencode_rfc3986($v) .
OAuthUtil::urlencode_rfc3986($v) .
'"';
'"';
$first = false;
$first = false;
}
}
return $out;
return $out;
}
}


public function __toString() {
public function __toString() {
return $this->to_url();
return $this->to_url();
}
}




public function sign_request($signature_method, $consumer, $token) {
public function sign_request($signature_method, $consumer, $token) {
$this->set_parameter(
$this->set_parameter(
"oauth_signature_method",
"oauth_signature_method",
$signature_method->get_name(),
$signature_method->get_name(),
false
false
);
);
$signature = $this->build_signature($signature_method, $consumer, $token);
$signature = $this->build_signature($signature_method, $consumer, $token);
$this->set_parameter("oauth_signature", $signature, false);
$this->set_parameter("oauth_signature", $signature, false);
}
}


public function build_signature($signature_method, $consumer, $token) {
public function build_signature($signature_method, $consumer, $token) {
$signature = $signature_method->build_signature($this, $consumer, $token);
$signature = $signature_method->build_signature($this, $consumer, $token);
return $signature;
return $signature;
}
}


/**
/**
* util function: current timestamp
* util function: current timestamp
*/
*/
private static function generate_timestamp() {
private static function generate_timestamp() {
return time();
return time();
}
}


/**
/**
* util function: current nonce
* util function: current nonce
*/
*/
private static function generate_nonce() {
private static function generate_nonce() {
$mt = microtime();
$mt = microtime();
$rand = mt_rand();
$rand = mt_rand();


return md5($mt . $rand); // md5s look nicer than numbers
return md5($mt . $rand); // md5s look nicer than numbers
}
}
}
}
endif;


if( ! class_exists( 'OAuthServer' ) ) :
class OAuthServer {
class OAuthServer {
protected $timestamp_threshold = 300; // in seconds, five minutes
protected $timestamp_threshold = 300; // in seconds, five minutes
protected $version = '1.0'; // hi blaine
protected $version = '1.0'; // hi blaine
protected $signature_methods = array();
protected $signature_methods = array();


protected $data_store;
protected $data_store;


function __construct($data_store) {
function __construct($data_store) {
$this->data_store = $data_store;
$this->data_store = $data_store;
}
}


public function add_signature_method($signature_method) {
public function add_signature_method($signature_method) {
$this->signature_methods[$signature_method->get_name()] =
$this->signature_methods[$signature_method->get_name()] =
$signature_method;
$signature_method;
}
}


// high level functions
// high level functions


/**
/**
* process a request_token request
* process a request_token request
* returns the request token on success
* returns the request token on success
*/
*/
public function fetch_request_token(&$request) {
public function fetch_request_token(&$request) {
$this->get_version($request);
$this->get_version($request);


$consumer = $this->get_consumer($request);
$consumer = $this->get_consumer($request);


// no token required for the initial token request
// no token required for the initial token request
$token = NULL;
$token = NULL;


$this->check_signature($request, $consumer, $token);
$this->check_signature($request, $consumer, $token);


// Rev A change
// Rev A change
$callback = $request->get_parameter('oauth_callback');
$callback = $request->get_parameter('oauth_callback');
$new_token = $this->data_store->new_request_token($consumer, $callback);
$new_token = $this->data_store->new_request_token($consumer, $callback);


return $new_token;
return $new_token;
}
}


/**
/**
* process an access_token request
* process an access_token request
* returns the access token on success
* returns the access token on success
*/
*/
public function fetch_access_token(&$request) {
public function fetch_access_token(&$request) {
$this->get_version($request);
$this->get_version($request);


$consumer = $this->get_consumer($request);
$consumer = $this->get_consumer($request);


// requires authorized request token
// requires authorized request token
$token = $this->get_token($request, $consumer, "request");
$token = $this->get_token($request, $consumer, "request");


$this->check_signature($request, $consumer, $token);
$this->check_signature($request, $consumer, $token);


// Rev A change
// Rev A change
$verifier = $request->get_parameter('oauth_verifier');
$verifier = $request->get_parameter('oauth_verifier');
$new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
$new_token = $this->data_store->new_access_token($token, $consumer, $verifier);


return $new_token;
return $new_token;
}
}


/**
/**
* verify an api call, checks all the parameters
* verify an api call, checks all the parameters
*/
*/
public function verify_request(&$request) {
public function verify_request(&$request) {
$this->get_version($request);
$this->get_version($request);
$consumer = $this->get_consumer($request);
$consumer = $this->get_consumer($request);
$token = $this->get_token($request, $consumer, "access");
$token = $this->get_token($request, $consumer, "access");
$this->check_signature($request, $consumer, $token);
$this->check_signature($request, $consumer, $token);
return array($consumer, $token);
return array($consumer, $token);
}
}


// Internals from here
// Internals from here
/**
/**
* version 1
* version 1
*/
*/
private function get_version(&$request) {
private function get_version(&$request) {
$version = $request->get_parameter("oauth_version");
$version = $request->get_parameter("oauth_version");
if (!$version) {
if (!$version) {
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Chapter 7.0 ("Accessing Protected Ressources")
// Chapter 7.0 ("Accessing Protected Ressources")
$version = '1.0';
$version = '1.0';
}
}
if ($version !== $this->version) {
if ($version !== $this->version) {
throw new pk_OAuthException("OAuth version '$version' not supported");
throw new pk_OAuthException("OAuth version '$version' not supported");
}
}
return $version;
return $version;
}
}


/**
/**
* figure out the signature with some defaults
* figure out the signature with some defaults
*/
*/
private function get_signature_method(&$request) {
private function get_signature_method(&$request) {
$signature_method =
$signature_method =
@$request->get_parameter("oauth_signature_method");
@$request->get_parameter("oauth_signature_method");


if (!$signature_method) {
if (!$signature_method) {
// According to chapter 7 ("Accessing Protected Ressources") the signature-method
// According to chapter 7 ("Accessing Protected Ressources") the signature-method
// parameter is required, and we can't just fallback to PLAINTEXT
// parameter is required, and we can't just fallback to PLAINTEXT
throw new pk_OAuthException('No signature method parameter. This parameter is required');
throw new pk_OAuthException('No signature method parameter. This parameter is required');
}
}


if (!in_array($signature_method,
if (!in_array($signature_method,
array_keys($this->signature_methods))) {
array_keys($this->signature_methods))) {
throw new pk_OAuthException(
throw new pk_OAuthException(
"Signature method '$signature_method' not supported " .
"Signature method '$signature_method' not supported " .
"try one of the following: " .
"try one of the following: " .
implode(", ", array_keys($this->signature_methods))
implode(", ", array_keys($this->signature_methods))
);
);
}
}
return $this->signature_methods[$signature_method];
return $this->signature_methods[$signature_method];
}
}


/**
/**
* try to find the consumer for the provided request's consumer key
* try to find the consumer for the provided request's consumer key
*/
*/
private function get_consumer(&$request) {
private function get_consumer(&$request) {
$consumer_key = @$request->get_parameter("oauth_consumer_key");
$consumer_key = @$request->get_parameter("oauth_consumer_key");
if (!$consumer_key) {
if (!$consumer_key) {
throw new pk_OAuthException("Invalid consumer key");
throw new pk_OAuthException("Invalid consumer key");
}
}


$consumer = $this->data_store->lookup_consumer($consumer_key);
$consumer = $this->data_store->lookup_consumer($consumer_key);
if (!$consumer) {
if (!$consumer) {
throw new pk_OAuthException("Invalid consumer");
throw new pk_OAuthException("Invalid consumer");
}
}


return $consumer;
return $consumer;
}
}


/**
/**
* try to find the token for the provided request's token key
* try to find the token for the provided request's token key
*/
*/
private function get_token(&$request, $consumer, $token_type="access") {
private function get_token(&$request, $consumer, $token_type="access") {
$token_field = @$request->get_parameter('oauth_token');
$token_field = @$request->get_parameter('oauth_token');
$token = $this->data_store->lookup_token(
$token = $this->data_store->lookup_token(
$consumer, $token_type, $token_field
$consumer, $token_type, $token_field
);
);
if (!$token) {
if (!$token) {
throw new pk_OAuthException("Invalid $token_type token: $token_field");
throw new pk_OAuthException("Invalid $token_type token: $token_field");
}
}
return $token;
return $token;
}
}


/**
/**
* all-in-one function to check the signature on a request
* all-in-one function to check the signature on a request
* should guess the signature method appropriately
* should guess the signature method appropriately
*/
*/
private function check_signature(&$request, $consumer, $token) {
private function check_signature(&$request, $consumer, $token) {
// this should probably be in a different method
// this should probably be in a different method
$timestamp = @$request->get_parameter('oauth_timestamp');
$timestamp = @$request->get_parameter('oauth_timestamp');
$nonce = @$request->get_parameter('oauth_nonce');
$nonce = @$request->get_parameter('oauth_nonce');


$this->check_timestamp($timestamp);
$this->check_timestamp($timestamp);
$this->check_nonce($consumer, $token, $nonce, $timestamp);
$this->check_nonce($consumer, $token, $nonce, $timestamp);


$signature_method = $this->get_signature_method($request);
$signature_method = $this->get_signature_method($request);


$signature = $request->get_parameter('oauth_signature');
$signature = $request->get_parameter('oauth_signature');
$valid_sig = $signature_method->check_signature(
$valid_sig = $signature_method->check_signature(
$request,
$request,
$consumer,
$consumer,
$token,
$token,
$signature
$signature
);
);


if (!$valid_sig) {
if (!$valid_sig) {
throw new pk_OAuthException("Invalid signature");
throw new pk_OAuthException("Invalid signature");
}
}
}
}


/**
/**
* check that the timestamp is new enough
* check that the timestamp is new enough
*/
*/
private function check_timestamp($timestamp) {
private function check_timestamp($timestamp) {
if( ! $timestamp )
if( ! $timestamp )
throw new pk_OAuthException(
throw new pk_OAuthException(
'Missing timestamp parameter. The parameter is required'
'Missing timestamp parameter. The parameter is required'
);
);
// verify that timestamp is recentish
// verify that timestamp is recentish
$now = time();
$now = time();
if (abs($now - $timestamp) > $this->timestamp_threshold) {
if (abs($now - $timestamp) > $this->timestamp_threshold) {
throw new pk_OAuthException(
throw new pk_OAuthException(
"Expired timestamp, yours $timestamp, ours $now"
"Expired timestamp, yours $timestamp, ours $now"
);
);
}
}
}
}


/**
/**
* check that the nonce is not repeated
* check that the nonce is not repeated
*/
*/
private function check_nonce($consumer, $token, $nonce, $timestamp) {
private function check_nonce($consumer, $token, $nonce, $timestamp) {
if( ! $nonce )
if( ! $nonce )
throw new pk_OAuthException(
throw new pk_OAuthException(
'Missing nonce parameter. The parameter is required'
'Missing nonce parameter. The parameter is required'
);
);


// verify that the nonce is uniqueish
// verify that the nonce is uniqueish
$found = $this->data_store->lookup_nonce(
$found = $this->data_store->lookup_nonce(
$consumer,
$consumer,
$token,
$token,
$nonce,
$nonce,
$timestamp
$timestamp
);
);
if ($found) {
if ($found) {
throw new pk_OAuthException("Nonce already used: $nonce");
throw new pk_OAuthException("Nonce already used: $nonce");
}
}
}
}


}
}
endif;



if( ! class_exists( 'OAuthDataStore' ) ) :
class OAuthDataStore {
class OAuthDataStore {
function lookup_consumer($consumer_key) {
function lookup_consumer($consumer_key) {
// implement me
// implement me
}
}


function lookup_token($consumer, $token_type, $token) {
function lookup_token($consumer, $token_type, $token) {
// implement me
// implement me
}
}


function lookup_nonce($consumer, $token, $nonce, $timestamp) {
function lookup_nonce($consumer, $token, $nonce, $timestamp) {
// implement me
// implement me
}
}


function new_request_token($consumer, $callback = null) {
function new_request_token($consumer, $callback = null) {
// return a new token attached to this consumer
// return a new token attached to this consumer
}
}


function new_access_token($token, $consumer, $verifier = null) {
function new_access_token($token, $consumer, $verifier = null) {
// return a new access token attached to this consumer
// return a new access token attached to this consumer
// for the user associated with this token if the request token
// for the user associated with this token if the request token
// is authorized
// is authorized
// should also invalidate the request token
// should also invalidate the request token
}
}


}
}
endif;


if( ! class_exists( 'OAuthUtil' ) ) :
class OAuthUtil {
class OAuthUtil {
public static function urlencode_rfc3986($input) {
public static function urlencode_rfc3986($input) {
if (is_array($input)) {
if (is_array($input)) {
return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
} else if (is_scalar($input)) {
} else if (is_scalar($input)) {
return str_replace(
return str_replace(
'+',
'+',
' ',
' ',
str_replace('%7E', '~', rawurlencode($input))
str_replace('%7E', '~', rawurlencode($input))
);
);
} else {
} else {
return '';
return '';
}
}
}
}





// This decode function isn't taking into consideration the above
// This decode function isn't taking into consideration the above
// modifications to the encoding process. However, this method doesn't
// modifications to the encoding process. However, this method doesn't
// seem to be used anywhere so leaving it as is.
// seem to be used anywhere so leaving it as is.
public static function urldecode_rfc3986($string) {
public static function urldecode_rfc3986($string) {
return urldecode($string);
return urldecode($string);
}
}


// Utility function for turning the Authorization: header into
// Utility function for turning the Authorization: header into
// parameters, has to do some unescaping
// parameters, has to do some unescaping
// Can filter out any non-oauth parameters if needed (default behaviour)
// Can filter out any non-oauth parameters if needed (default behaviour)
public static function split_header($header, $only_allow_oauth_parameters = true) {
public static function split_header($header, $only_allow_oauth_parameters = true) {
$pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
$pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
$offset = 0;
$offset = 0;
$params = array();
$params = array();
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
$match = $matches[0];
$match = $matches[0];
$header_name = $matches[2][0];
$header_name = $matches[2][0];
$header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
$header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
$params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
$params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
}
}
$offset = $match[1] + strlen($match[0]);
$offset = $match[1] + strlen($match[0]);
}
}


if (isset($params['realm'])) {
if (isset($params['realm'])) {
unset($params['realm']);
unset($params['realm']);
}
}


return $params;
return $params;
}
}


// helper to try to sort out headers for people who aren't running apache
// helper to try to sort out headers for people who aren't running apache
public static function get_headers() {
public static function get_headers() {
if (function_exists('apache_request_headers')) {
if (function_exists('apache_request_headers')) {
// we need this to get the actual Authorization: header
// we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist
// because apache tends to tell us it doesn't exist
$headers = apache_request_headers();
$headers = apache_request_headers();


// sanitize the output of apache_request_headers because
// sanitize the output of apache_request_headers because
// we always want the keys to be Cased-Like-This and arh()
// we always want the keys to be Cased-Like-This and arh()
// returns the headers in the same case as they are in the
// returns the headers in the same case as they are in the
// request
// request
$out = array();
$out = array();
foreach( $headers AS $key => $value ) {
foreach( $headers AS $key => $value ) {
$key = str_replace(
$key = str_replace(
" ",
" ",
"-",
"-",
ucwords(strtolower(str_replace("-", " ", $key)))
ucwords(strtolower(str_replace("-", " ", $key)))
);
);
$out[$key] = $value;
$out[$key] = $value;
}
}
} else {
} else {
// otherwise we don't have apache and are just going to have to hope
// otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need
// that $_SERVER actually contains what we need
$out = array();
$out = array();
if( isset($_SERVER['CONTENT_TYPE']) )
if( isset($_SERVER['CONTENT_TYPE']) )
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
if( isset($_ENV['CONTENT_TYPE']) )
if( isset($_ENV['CONTENT_TYPE']) )
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];


foreach ($_SERVER as $key => $value) {
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == "HTTP_") {
if (substr($key, 0, 5) == "HTTP_") {
// this is chaos, basically it is just there to capitalize the first
// 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
// letter of every word that is not an initial HTTP and strip HTTP
// code from przemek
// code from przemek
$key = str_replace(
$key = str_replace(
" ",
" ",
"-",
"-",
ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
);
);
$out[$key] = $value;
$out[$key] = $value;
}
}
}
}
}
}
return $out;
return $out;
}
}


// This function takes a input like a=b&a=c&d=e and returns the parsed
// This function takes a input like a=b&a=c&d=e and returns th
// parameters like this
// array('a' => array('b','c'), 'd' => 'e')
public static function parse_parameters( $input ) {
if (!isset($input) || !$input) return array();

$pairs = explode('&', $input);

$parsed_parameters = array();
foreach ($pairs as $pair) {
$split = explode('=', $pair, 2);
$parameter = OAuthUtil::urldecode_rfc3986($split[0]);
$value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';

if (isset($parsed_parameters[$parameter])) {
// We have already recieved parameter(s) with this