PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/EpiOAuth.php

https://github.com/faizanulhaqq/twitter-async
PHP | 353 lines | 288 code | 52 blank | 13 comment | 29 complexity | 0dfabb0e0b478c4f145cb7eb450360d3 MD5 | raw file
  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. protected $useSSL = false;
  15. protected $headers = array();
  16. protected $userAgent = 'EpiOAuth (http://github.com/jmathai/twitter-async/tree/)';
  17. protected $connectionTimeout = 5;
  18. protected $requestTimeout = 30;
  19. public function addHeader($header)
  20. {
  21. if(is_array($header) && !empty($header))
  22. $this->headers = array_merge($this->headers, $header);
  23. elseif(!empty($header))
  24. $this->headers[] = $header;
  25. }
  26. public function getAccessToken($params = null)
  27. {
  28. $resp = $this->httpRequest('GET', $this->getUrl($this->accessTokenUrl), $params);
  29. return new EpiOAuthResponse($resp);
  30. }
  31. public function getAuthenticateUrl($token = null, $params = null)
  32. {
  33. $token = $token ? $token : $this->getRequestToken($params);
  34. $addlParams = empty($params) ? '' : '&'.http_build_query($params, '', '&');
  35. return $this->getUrl($this->authenticateUrl) . '?oauth_token=' . $token->oauth_token . $addlParams;
  36. }
  37. public function getAuthorizeUrl($token = null, $params = null)
  38. {
  39. $token = $token ? $token : $this->getRequestToken($params);
  40. return $this->getUrl($this->authorizeUrl) . '?oauth_token=' . $token->oauth_token;
  41. }
  42. // DEPRECATED in favor of getAuthorizeUrl()
  43. public function getAuthorizationUrl($token = null)
  44. {
  45. return $this->getAuthorizeUrl($token);
  46. }
  47. public function getRequestToken($params = null)
  48. {
  49. $resp = $this->httpRequest('GET', $this->getUrl($this->requestTokenUrl), $params);
  50. return new EpiOAuthResponse($resp);
  51. }
  52. public function getUrl($url)
  53. {
  54. if($this->useSSL === true)
  55. return preg_replace('/^http:/', 'https:', $url);
  56. return $url;
  57. }
  58. public function httpRequest($method = null, $url = null, $params = null, $isMultipart = false)
  59. {
  60. if(empty($method) || empty($url))
  61. return false;
  62. if(empty($params['oauth_signature']))
  63. $params = $this->prepareParameters($method, $url, $params);
  64. switch($method)
  65. {
  66. case 'GET':
  67. return $this->httpGet($url, $params);
  68. break;
  69. case 'POST':
  70. return $this->httpPost($url, $params, $isMultipart);
  71. break;
  72. }
  73. }
  74. public function setTimeout($requestTimeout = null, $connectionTimeout = null)
  75. {
  76. if($requestTimeout !== null)
  77. $this->requestTimeout = floatval($requestTimeout);
  78. if($connectionTimeout !== null)
  79. $this->connectionTimeout = floatval($connectionTimeout);
  80. }
  81. public function setToken($token = null, $secret = null)
  82. {
  83. $this->token = $token;
  84. $this->tokenSecret = $secret;
  85. }
  86. public function useSSL($use = false)
  87. {
  88. $this->useSSL = (bool)$use;
  89. }
  90. protected function addDefaultHeaders($url, $oauthHeaders)
  91. {
  92. $_h = array('Expect:');
  93. $urlParts = parse_url($url);
  94. $oauth = 'Authorization: OAuth realm="' . $urlParts['scheme'] . '://' . $urlParts['host'] . $urlParts['path'] . '",';
  95. foreach($oauthHeaders as $name => $value)
  96. {
  97. $oauth .= "{$name}=\"{$value}\",";
  98. }
  99. $_h[] = substr($oauth, 0, -1);
  100. $_h[] = "User-Agent: {$this->userAgent}";
  101. $this->addHeader($_h);
  102. }
  103. protected function buildHttpQueryRaw($params)
  104. {
  105. $retval = '';
  106. foreach((array)$params as $key => $value)
  107. $retval .= "{$key}={$value}&";
  108. $retval = substr($retval, 0, -1);
  109. return $retval;
  110. }
  111. protected function curlInit($url)
  112. {
  113. $ch = curl_init($url);
  114. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  115. curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
  116. curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout);
  117. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
  118. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  119. if($this->useSSL === true)
  120. {
  121. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  122. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  123. }
  124. return $ch;
  125. }
  126. protected function emptyHeaders()
  127. {
  128. $this->headers = array();
  129. }
  130. protected function encode_rfc3986($string)
  131. {
  132. return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode(($string))));
  133. }
  134. protected function generateNonce()
  135. {
  136. if(isset($this->nonce)) // for unit testing
  137. return $this->nonce;
  138. return md5(uniqid(rand(), true));
  139. }
  140. // parameters should already have been passed through prepareParameters
  141. // no need to double encode
  142. protected function generateSignature($method = null, $url = null, $params = null)
  143. {
  144. if(empty($method) || empty($url))
  145. return false;
  146. // concatenating and encode
  147. $concatenatedParams = $this->encode_rfc3986($this->buildHttpQueryRaw($params));
  148. // normalize url
  149. $normalizedUrl = $this->encode_rfc3986($this->normalizeUrl($url));
  150. $method = $this->encode_rfc3986($method); // don't need this but why not?
  151. $signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}";
  152. return $this->signString($signatureBaseString);
  153. }
  154. protected function httpGet($url, $params = null)
  155. {
  156. if(count($params['request']) > 0)
  157. {
  158. $url .= '?';
  159. foreach($params['request'] as $k => $v)
  160. {
  161. $url .= "{$k}={$v}&";
  162. }
  163. $url = substr($url, 0, -1);
  164. }
  165. $this->addDefaultHeaders($url, $params['oauth']);
  166. $ch = $this->curlInit($url);
  167. $resp = $this->curl->addCurl($ch);
  168. $this->emptyHeaders();
  169. return $resp;
  170. }
  171. protected function httpPost($url, $params = null, $isMultipart)
  172. {
  173. $this->addDefaultHeaders($url, $params['oauth']);
  174. $ch = $this->curlInit($url);
  175. curl_setopt($ch, CURLOPT_POST, 1);
  176. // php's curl extension automatically sets the content type
  177. // based on whether the params are in string or array form
  178. if($isMultipart)
  179. curl_setopt($ch, CURLOPT_POSTFIELDS, $params['request']);
  180. else
  181. curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildHttpQueryRaw($params['request']));
  182. $resp = $this->curl->addCurl($ch);
  183. $this->emptyHeaders();
  184. return $resp;
  185. }
  186. protected function normalizeUrl($url = null)
  187. {
  188. $urlParts = parse_url($url);
  189. $scheme = strtolower($urlParts['scheme']);
  190. $host = strtolower($urlParts['host']);
  191. $port = isset($urlParts['port']) ? intval($urlParts['port']) : 0;
  192. $retval = strtolower($scheme) . '://' . strtolower($host);
  193. if(!empty($port) && (($scheme === 'http' && $port != 80) || ($scheme === 'https' && $port != 443)))
  194. $retval .= ":{$port}";
  195. $retval .= $urlParts['path'];
  196. if(!empty($urlParts['query']))
  197. {
  198. $retval .= "?{$urlParts['query']}";
  199. }
  200. return $retval;
  201. }
  202. protected function prepareParameters($method = null, $url = null, $params = null)
  203. {
  204. if(empty($method) || empty($url))
  205. return false;
  206. $oauth['oauth_consumer_key'] = $this->consumerKey;
  207. $oauth['oauth_token'] = $this->token;
  208. $oauth['oauth_nonce'] = $this->generateNonce();
  209. $oauth['oauth_timestamp'] = !isset($this->timestamp) ? time() : $this->timestamp; // for unit test
  210. $oauth['oauth_signature_method'] = $this->signatureMethod;
  211. $oauth['oauth_version'] = $this->version;
  212. // encode all oauth values
  213. foreach($oauth as $k => $v)
  214. $oauth[$k] = $this->encode_rfc3986($v);
  215. // encode all non '@' params
  216. // keep sigParams for signature generation (exclude '@' params)
  217. // rename '@key' to 'key'
  218. $sigParams = array();
  219. $hasFile = false;
  220. if(is_array($params))
  221. {
  222. foreach($params as $k => $v)
  223. {
  224. if(strncmp('@',$k,1) !== 0)
  225. {
  226. $sigParams[$k] = $this->encode_rfc3986($v);
  227. $params[$k] = $this->encode_rfc3986($v);
  228. }
  229. else
  230. {
  231. $params[substr($k, 1)] = $v;
  232. unset($params[$k]);
  233. $hasFile = true;
  234. }
  235. }
  236. if($hasFile === true)
  237. $sigParams = array();
  238. }
  239. $sigParams = array_merge($oauth, (array)$sigParams);
  240. // sorting
  241. ksort($sigParams);
  242. // signing
  243. $oauth['oauth_signature'] = $this->encode_rfc3986($this->generateSignature($method, $url, $sigParams));
  244. return array('request' => $params, 'oauth' => $oauth);
  245. }
  246. protected function signString($string = null)
  247. {
  248. $retval = false;
  249. switch($this->signatureMethod)
  250. {
  251. case 'HMAC-SHA1':
  252. $key = $this->encode_rfc3986($this->consumerSecret) . '&' . $this->encode_rfc3986($this->tokenSecret);
  253. $retval = base64_encode(hash_hmac('sha1', $string, $key, true));
  254. break;
  255. }
  256. return $retval;
  257. }
  258. public function __construct($consumerKey, $consumerSecret, $signatureMethod='HMAC-SHA1')
  259. {
  260. $this->consumerKey = $consumerKey;
  261. $this->consumerSecret = $consumerSecret;
  262. $this->signatureMethod = $signatureMethod;
  263. $this->curl = EpiCurl::getInstance();
  264. }
  265. }
  266. class EpiOAuthResponse
  267. {
  268. private $__resp;
  269. public function __construct($resp)
  270. {
  271. $this->__resp = $resp;
  272. }
  273. public function __get($name)
  274. {
  275. if($this->__resp->code != 200)
  276. EpiOAuthException::raise($this->__resp->data, $this->__resp->code);
  277. parse_str($this->__resp->data, $result);
  278. foreach($result as $k => $v)
  279. {
  280. $this->$k = $v;
  281. }
  282. return $result[$name];
  283. }
  284. }
  285. class EpiOAuthException extends Exception
  286. {
  287. public static function raise($message, $code)
  288. {
  289. switch($code)
  290. {
  291. case 400:
  292. throw new EpiOAuthBadRequestException($message, $code);
  293. case 401:
  294. throw new EpiOAuthUnauthorizedException($message, $code);
  295. default:
  296. throw new EpiOAuthException($message, $code);
  297. }
  298. }
  299. }
  300. class EpiOAuthBadRequestException extends EpiOAuthException{}
  301. class EpiOAuthUnauthorizedException extends EpiOAuthException{}