PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/lib/twitter/EpiOAuth.php

https://bitbucket.org/pooshonk/esw
PHP | 258 lines | 213 code | 40 blank | 5 comment | 18 complexity | 7eae007c282fbe300c4d101c1b5e25bf MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. class EpiOAuth
  3. {
  4. public $version = '1.0';
  5. protected $requestTokenUrl;
  6. protected $accessTokenUrl;
  7. protected $authenticateUrl;
  8. protected $authorizeUrl;
  9. protected $consumerKey;
  10. protected $consumerSecret;
  11. protected $token;
  12. protected $tokenSecret;
  13. protected $signatureMethod;
  14. public function getAccessToken()
  15. {
  16. $resp = $this->httpRequest('GET', $this->accessTokenUrl);
  17. return new EpiOAuthResponse($resp);
  18. }
  19. public function getAuthenticateUrl()
  20. {
  21. $token = $this->getRequestToken();
  22. return $this->authenticateUrl . '?oauth_token=' . $token->oauth_token;
  23. }
  24. public function getAuthorizationUrl()
  25. {
  26. $token = $this->getRequestToken();
  27. return $this->authorizeUrl . '?oauth_token=' . $token->oauth_token;
  28. }
  29. public function getRequestToken()
  30. {
  31. $resp = $this->httpRequest('GET', $this->requestTokenUrl);
  32. return new EpiOAuthResponse($resp);
  33. }
  34. public function httpRequest($method = null, $url = null, $params = null)
  35. {
  36. if(empty($method) || empty($url))
  37. return false;
  38. if(empty($params['oauth_signature']))
  39. $params = $this->prepareParameters($method, $url, $params);
  40. switch($method)
  41. {
  42. case 'GET':
  43. return $this->httpGet($url, $params);
  44. break;
  45. case 'POST':
  46. return $this->httpPost($url, $params);
  47. break;
  48. }
  49. }
  50. public function setToken($token = null, $secret = null)
  51. {
  52. $params = func_get_args();
  53. $this->token = $token;
  54. $this->tokenSecret = $secret;
  55. }
  56. protected function encode_rfc3986($string)
  57. {
  58. return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode(($string))));
  59. }
  60. protected function addOAuthHeaders(&$ch, $url, $oauthHeaders)
  61. {
  62. $_h = array('Expect:');
  63. $urlParts = parse_url($url);
  64. $oauth = 'Authorization: OAuth realm="' . $urlParts['path'] . '",';
  65. foreach($oauthHeaders as $name => $value)
  66. {
  67. $oauth .= "{$name}=\"{$value}\",";
  68. }
  69. $_h[] = substr($oauth, 0, -1);
  70. curl_setopt($ch, CURLOPT_HTTPHEADER, $_h);
  71. }
  72. protected function generateNonce()
  73. {
  74. if(isset($this->nonce)) // for unit testing
  75. return $this->nonce;
  76. return md5(uniqid(rand(), true));
  77. }
  78. protected function generateSignature($method = null, $url = null, $params = null)
  79. {
  80. if(empty($method) || empty($url))
  81. return false;
  82. // concatenating
  83. $concatenatedParams = '';
  84. foreach($params as $k => $v)
  85. {
  86. $v = $this->encode_rfc3986($v);
  87. $concatenatedParams .= "{$k}={$v}&";
  88. }
  89. $concatenatedParams = $this->encode_rfc3986(substr($concatenatedParams, 0, -1));
  90. // normalize url
  91. $normalizedUrl = $this->encode_rfc3986($this->normalizeUrl($url));
  92. $method = $this->encode_rfc3986($method); // don't need this but why not?
  93. $signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}";
  94. return $this->signString($signatureBaseString);
  95. }
  96. protected function httpGet($url, $params = null)
  97. {
  98. if(count($params['request']) > 0)
  99. {
  100. $url .= '?';
  101. foreach($params['request'] as $k => $v)
  102. {
  103. $url .= "{$k}={$v}&";
  104. }
  105. $url = substr($url, 0, -1);
  106. }
  107. $ch = curl_init($url);
  108. $this->addOAuthHeaders($ch, $url, $params['oauth']);
  109. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  110. $resp = $this->curl->addCurl($ch);
  111. return $resp;
  112. }
  113. protected function httpPost($url, $params = null)
  114. {
  115. $ch = curl_init($url);
  116. $this->addOAuthHeaders($ch, $url, $params['oauth']);
  117. curl_setopt($ch, CURLOPT_POST, 1);
  118. curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params['request']));
  119. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  120. $resp = $this->curl->addCurl($ch);
  121. return $resp;
  122. }
  123. protected function normalizeUrl($url = null)
  124. {
  125. $urlParts = parse_url($url);
  126. $scheme = strtolower($urlParts['scheme']);
  127. $host = strtolower($urlParts['host']);
  128. $port = isset( $urlParts['port'] ) ? intval($urlParts['port']) : 80;
  129. $retval = "{$scheme}://{$host}";
  130. if($port > 0 && ($scheme === 'http' && $port !== 80) || ($scheme === 'https' && $port !== 443))
  131. {
  132. $retval .= ":{$port}";
  133. }
  134. $retval .= $urlParts['path'];
  135. if(!empty($urlParts['query']))
  136. {
  137. $retval .= "?{$urlParts['query']}";
  138. }
  139. return $retval;
  140. }
  141. protected function prepareParameters($method = null, $url = null, $params = null)
  142. {
  143. if(empty($method) || empty($url))
  144. return false;
  145. $oauth['oauth_consumer_key'] = $this->consumerKey;
  146. $oauth['oauth_token'] = $this->token;
  147. $oauth['oauth_nonce'] = $this->generateNonce();
  148. $oauth['oauth_timestamp'] = !isset($this->timestamp) ? time() : $this->timestamp; // for unit test
  149. $oauth['oauth_signature_method'] = $this->signatureMethod;
  150. $oauth['oauth_version'] = $this->version;
  151. // encoding
  152. array_walk($oauth, array($this, 'encode_rfc3986'));
  153. if(is_array($params))
  154. array_walk($params, array($this, 'encode_rfc3986'));
  155. $encodedParams = array_merge($oauth, (array)$params);
  156. // sorting
  157. ksort($encodedParams);
  158. // signing
  159. $oauth['oauth_signature'] = $this->encode_rfc3986($this->generateSignature($method, $url, $encodedParams));
  160. return array('request' => $params, 'oauth' => $oauth);
  161. }
  162. protected function signString($string = null)
  163. {
  164. $retval = false;
  165. switch($this->signatureMethod)
  166. {
  167. case 'HMAC-SHA1':
  168. $key = $this->encode_rfc3986($this->consumerSecret) . '&' . $this->encode_rfc3986($this->tokenSecret);
  169. $retval = base64_encode(hash_hmac('sha1', $string, $key, true));
  170. break;
  171. }
  172. return $retval;
  173. }
  174. public function __construct($consumerKey, $consumerSecret, $signatureMethod='HMAC-SHA1')
  175. {
  176. $this->consumerKey = $consumerKey;
  177. $this->consumerSecret = $consumerSecret;
  178. $this->signatureMethod = $signatureMethod;
  179. $this->curl = EpiCurl::getInstance();
  180. }
  181. }
  182. class EpiOAuthResponse
  183. {
  184. private $__resp;
  185. public function __construct($resp)
  186. {
  187. $this->__resp = $resp;
  188. }
  189. public function __get($name)
  190. {
  191. if($this->__resp->code != 200)
  192. EpiOAuthException::raise($this->__resp->data, $this->__resp->code);
  193. $result = null;
  194. parse_str($this->__resp->data, $result);
  195. foreach($result as $k => $v)
  196. {
  197. $this->$k = $v;
  198. }
  199. return $result[$name];
  200. }
  201. }
  202. class EpiOAuthException extends Exception
  203. {
  204. public static function raise($message, $code)
  205. {
  206. switch($code)
  207. {
  208. case 400:
  209. throw new EpiOAuthBadRequestException($message, $code);
  210. case 401:
  211. throw new EpiOAuthUnauthorizedException($message, $code);
  212. default:
  213. throw new EpiOAuthException($message, $code);
  214. }
  215. }
  216. }
  217. class EpiOAuthBadRequestException extends EpiOAuthException{}
  218. class EpiOAuthUnauthorizedException extends EpiOAuthException{}