<?php
/**
* @version $Id: EpiOAuth.php 1404 2013-03-25 10:09:12Z datahell $
* @package Elxis
* @subpackage OAuth / Authentication
* @copyright Copyright (c) 2006-2013 Elxis CMS (http://www.elxis.org). All rights reserved.
* @license Elxis Public License ( http://www.elxis.org/elxis-public-license.html )
* @author Elxis Team ( http://www.elxis.org )
* @description Elxis CMS is free software. Read the license for copyright notices and details
*/
defined('_ELXIS_') or die ('Direct access to this location is not allowed');
class EpiOAuth {
public $version = '1.0';
protected $requestTokenUrl;
protected $accessTokenUrl;
protected $authorizeUrl;
protected $consumerKey;
protected $consumerSecret;
protected $token;
protected $tokenSecret;
protected $signatureMethod;
public function __construct($consumerKey, $consumerSecret, $signatureMethod='HMAC-SHA1') {
$this->consumerKey = $consumerKey;
$this->consumerSecret = $consumerSecret;
$this->signatureMethod = $signatureMethod;
$this->curl = EpiCurl::getInstance();
}
public function getAccessToken() {
$resp = $this->httpRequest('GET', $this->accessTokenUrl);
return new EpiOAuthResponse($resp);
}
public function getAuthorizationUrl() {
$retval = "{$this->authorizeUrl}?";
$token = $this->getRequestToken();
return $this->authorizeUrl.'?oauth_token='.$token->oauth_token;
}
public function getRequestToken() {
$resp = $this->httpRequest('GET', $this->requestTokenUrl);
return new EpiOAuthResponse($resp);
}
public function httpRequest($method = null, $url = null, $params = null) {
if (empty($method) || empty($url)) { return false; }
if (empty($params['oauth_signature'])) { $params = $this->prepareParameters($method, $url, $params); }
switch($method) {
case 'GET':
return $this->httpGet($url, $params);
break;
case 'POST':
return $this->httpPost($url, $params);
break;
}
}
public function setToken($token = null, $secret = null) {
$params = func_get_args();
$this->token = $token;
$this->tokenSecret = $secret;
}
public function encode($string) {
return rawurlencode(utf8_encode($string));
}
protected function addOAuthHeaders(&$ch, $url, $oauthHeaders) {
$_h = array('Expect:');
$urlParts = parse_url($url);
$oauth = 'Authorization: OAuth realm="'.$urlParts['path'].'",';
foreach($oauthHeaders as $name => $value) {
$oauth .= "{$name}=\"{$value}\",";
}
$_h[] = substr($oauth, 0, -1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $_h);
}
protected function generateNonce() {
if (isset($this->nonce)) { // for unit testing
return $this->nonce;
}
return md5(uniqid(rand(), true));
}
protected function generateSignature($method = null, $url = null, $params = null) {
if(empty($method) || empty($url)) { return false; }
//concatenating
$concatenatedParams = '';
foreach($params as $k => $v) {
$v = $this->encode($v);
$concatenatedParams .= "{$k}={$v}&";
}
$concatenatedParams = $this->encode(substr($concatenatedParams, 0, -1));
//normalize url
$normalizedUrl = $this->encode($this->normalizeUrl($url));
$method = $this->encode($method); // don't need this but why not?
$signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}";
return $this->signString($signatureBaseString);
}
protected function httpGet($url, $params = null) {
if (count($params['request']) > 0) {
$url .= '?';
foreach ($params['request'] as $k => $v) { $url .= "{$k}={$v}&"; }
$url = substr($url, 0, -1);
}
$ch = curl_init($url);
$this->addOAuthHeaders($ch, $url, $params['oauth']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$resp = $this->curl->addCurl($ch);
return $resp;
}
protected function httpPost($url, $params = null) {
$ch = curl_init($url);
$this->addOAuthHeaders($ch, $url, $params['oauth']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params['request']));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$resp = $this->curl->addCurl($ch);
return $resp;
}
protected function normalizeUrl($url = null) {
$urlParts = parse_url($url);
$scheme = strtolower($urlParts['scheme']);
$host = strtolower($urlParts['host']);
$port = (isset($urlParts['port'])) ? (int)$urlParts['port'] : ($scheme == 'https') ? 443 : 80;
$retval = "{$scheme}://{$host}";
if ($port > 0 && ($scheme === 'http' && $port !== 80) || ($scheme === 'https' && $port !== 443)) {
$retval .= ":{$port}";
}
$retval .= $urlParts['path'];
if (!empty($urlParts['query'])) {
$retval .= "?{$urlParts['query']}";
}
return $retval;
}
protected function prepareParameters($method = null, $url = null, $params = null) {
if(empty($method) || empty($url)) { return false; }
$oauth = array();
$oauth['oauth_consumer_key'] = $this->consumerKey;
$oauth['oauth_token'] = $this->token;
$oauth['oauth_nonce'] = $this->generateNonce();
$oauth['oauth_timestamp'] = !isset($this->timestamp) ? time() : $this->timestamp; // for unit test
$oauth['oauth_signature_method'] = $this->signatureMethod;
$oauth['oauth_version'] = $this->version;
//encoding
array_walk($oauth, array($this, 'encode'));
if (is_array($params)) {
array_walk($params, array($this, 'encode'));
}
$encodedParams = array_merge($oauth, (array)$params);
//sorting
ksort($encodedParams);
//signing
$oauth['oauth_signature'] = $this->encode($this->generateSignature($method, $url, $encodedParams));
return array('request' => $params, 'oauth' => $oauth);
}
protected function signString($string = null) {
$retval = false;
switch($this->signatureMethod) {
case 'HMAC-SHA1':
$key = $this->encode($this->consumerSecret) . '&' . $this->encode($this->tokenSecret);
$retval = base64_encode(hash_hmac('sha1', $string, $key, true));
break;
}
return $retval;
}
}
class EpiOAuthResponse {
private $__resp;
public function __construct($resp) {
$this->__resp = $resp;
}
public function __get($name) {
if ($this->__resp->code < 200 || $this->__resp->code > 299) {
return false;
}
parse_str($this->__resp->data, $result);
foreach($result as $k => $v) {
$this->$k = $v;
}
return $result[$name];
}
}
class EpiCurl {
const timeout = 3;
static $inst = null;
static $singleton = 0;
private $mc;
private $msgs;
private $running;
private $requests = array();
private $responses = array();
private $properties = array();
public function __construct() {
if (self::$singleton == 0) {
throw new Exception('This class cannot be instantiated by the new keyword. You must instantiate it using: $obj = EpiCurl::getInstance();');
}
$this->mc = curl_multi_init();
$this->properties = array(
'code' => CURLINFO_HTTP_CODE,
'time' => CURLINFO_TOTAL_TIME,
'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD,
'type' => CURLINFO_CONTENT_TYPE
);
}
public function addCurl($ch) {
$key = (string)$ch;
$this->requests[$key] = $ch;
$res = curl_multi_add_handle($this->mc, $ch);
if ($res === CURLM_OK || $res === CURLM_CALL_MULTI_PERFORM) {
do {
$mrc = curl_multi_exec($this->mc, $active);
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
return new EpiCurlManager($key);
} else {
return $res;
}
}
public function getResult($key = null) {
if ($key != null) {
if (isset($this->responses[$key])) {
return $this->responses[$key];
}
$running = null;
do {
$resp = curl_multi_exec($this->mc, $runningCurrent);
if ($running !== null && $runningCurrent != $running) {
$this->storeResponses($key);
if (isset($this->responses[$key])) {
return $this->responses[$key];
}
}
$running = $runningCurrent;
} while($runningCurrent > 0);
}
return false;
}
private function storeResponses() {
while($done = curl_multi_info_read($this->mc)) {
$key = (string)$done['handle'];
$this->responses[$key]['data'] = curl_multi_getcontent($done['handle']);
foreach($this->properties as $name => $const) {
$this->responses[$key][$name] = curl_getinfo($done['handle'], $const);
curl_multi_remove_handle($this->mc, $done['handle']);
}
}
}
static function getInstance() {
if (self::$inst == null) {
self::$singleton = 1;
self::$inst = new EpiCurl();
}
return self::$inst;
}
}
class EpiCurlManager {
private $key;
private $epiCurl;
public function __construct($key) {
$this->key = $key;
$this->epiCurl = EpiCurl::getInstance();
}
public function __get($name) {
$responses = $this->epiCurl->getResult($this->key);
return (isset($responses[$name])) ? $responses[$name] : null;
}
}
?>