PageRenderTime 25ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/apps/frontend/lib/EpiOAuth.class.php

https://github.com/mootools/mootools-forge
PHP | 441 lines | 368 code | 60 blank | 13 comment | 45 complexity | 0543c22561e0165f36fafc2ada4e5200 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 $callback;
  14. protected $signatureMethod;
  15. protected $debug = false;
  16. protected $useSSL = false;
  17. protected $followLocation = false;
  18. protected $headers = array();
  19. protected $userAgent = 'EpiOAuth';
  20. protected $connectionTimeout = 5;
  21. protected $requestTimeout = 30;
  22. public function addHeader($header)
  23. {
  24. if(is_array($header) && !empty($header))
  25. $this->headers = array_merge($this->headers, $header);
  26. elseif(!empty($header))
  27. $this->headers[] = $header;
  28. }
  29. public function getAccessToken($params = null)
  30. {
  31. if (isset($_GET['oauth_verifier']) && !isset($params['oauth_verifier']))
  32. {
  33. $params['oauth_verifier'] = $_GET['oauth_verifier'];
  34. }
  35. $resp = $this->httpRequest('POST', $this->getUrl($this->accessTokenUrl), $params);
  36. return new EpiOAuthResponse($resp);
  37. }
  38. public function getAuthenticateUrl($token = null, $params = null)
  39. {
  40. $token = $token ? $token : $this->getRequestToken($params);
  41. if (is_object($token)) $token = $token->oauth_token;
  42. $addlParams = empty($params) ? '' : '&'.http_build_query($params, '', '&');
  43. return $this->getUrl($this->authenticateUrl) . '?oauth_token=' . $token . $addlParams;
  44. }
  45. public function getAuthorizeUrl($token = null, $params = null)
  46. {
  47. $token = $token ? $token : $this->getRequestToken($params);
  48. if (is_object($token)) $token = $token->oauth_token;
  49. return $this->getUrl($this->authorizeUrl) . '?oauth_token=' . $token;
  50. }
  51. public function getRequestToken($params = null)
  52. {
  53. if (isset($this->callback) && !isset($params['oauth_callback']))
  54. {
  55. $params['oauth_callback'] = $this->callback;
  56. }
  57. $resp = $this->httpRequest('POST', $this->getUrl($this->requestTokenUrl), $params);
  58. return new EpiOAuthResponse($resp);
  59. }
  60. public function getUrl($url)
  61. {
  62. if($this->useSSL === true)
  63. return preg_replace('/^http:/', 'https:', $url);
  64. return $url;
  65. }
  66. public function httpRequest($method = null, $url = null, $params = null, $isMultipart = false)
  67. {
  68. if(empty($method) || empty($url))
  69. return false;
  70. if(empty($params['oauth_signature']))
  71. $params = $this->prepareParameters($method, $url, $params);
  72. switch($method)
  73. {
  74. case 'GET':
  75. return $this->httpGet($url, $params);
  76. break;
  77. case 'POST':
  78. return $this->httpPost($url, $params, $isMultipart);
  79. break;
  80. case 'DELETE':
  81. return $this->httpDelete($url, $params);
  82. break;
  83. }
  84. }
  85. public function setDebug($bool=false)
  86. {
  87. $this->debug = (bool)$bool;
  88. }
  89. public function setFollowLocation($bool)
  90. {
  91. $this->followLocation = (bool)$bool;
  92. }
  93. public function setTimeout($requestTimeout = null, $connectionTimeout = null)
  94. {
  95. if($requestTimeout !== null)
  96. $this->requestTimeout = floatval($requestTimeout);
  97. if($connectionTimeout !== null)
  98. $this->connectionTimeout = floatval($connectionTimeout);
  99. }
  100. public function setToken($token = null, $secret = null)
  101. {
  102. $this->token = $token;
  103. $this->tokenSecret = $secret;
  104. }
  105. public function setCallback($callback = null)
  106. {
  107. $this->callback = $callback;
  108. }
  109. public function useSSL($use = true)
  110. {
  111. $this->useSSL = (bool)$use;
  112. }
  113. protected function addDefaultHeaders($url, $oauthHeaders)
  114. {
  115. $_h = array('Expect:');
  116. $urlParts = parse_url($url);
  117. $oauth = 'Authorization: OAuth realm="' . $urlParts['scheme'] . '://' . $urlParts['host'] . $urlParts['path'] . '",';
  118. foreach($oauthHeaders as $name => $value)
  119. {
  120. $oauth .= "{$name}=\"{$value}\",";
  121. }
  122. $_h[] = substr($oauth, 0, -1);
  123. $_h[] = "User-Agent: {$this->userAgent}";
  124. $this->addHeader($_h);
  125. }
  126. protected function buildHttpQueryRaw($params)
  127. {
  128. $retval = '';
  129. foreach((array)$params as $key => $value)
  130. $retval .= "{$key}={$value}&";
  131. $retval = substr($retval, 0, -1);
  132. return $retval;
  133. }
  134. protected function curlInit($url)
  135. {
  136. $ch = curl_init($url);
  137. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  138. curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
  139. curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout);
  140. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
  141. curl_setopt($ch, CURLOPT_ENCODING, '');
  142. if($this->followLocation)
  143. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  144. if(isset($_SERVER ['SERVER_ADDR']) && !empty($_SERVER['SERVER_ADDR']) && $_SERVER['SERVER_ADDR'] != '127.0.0.1')
  145. curl_setopt($ch, CURLOPT_INTERFACE, $_SERVER ['SERVER_ADDR']);
  146. // if the certificate exists then use it, else bypass ssl checks
  147. if(file_exists($cert = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ca-bundle.crt'))
  148. {
  149. curl_setopt($ch, CURLOPT_CAINFO, $cert);
  150. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
  151. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  152. }
  153. else
  154. {
  155. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  156. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  157. }
  158. return $ch;
  159. }
  160. protected function emptyHeaders()
  161. {
  162. $this->headers = array();
  163. }
  164. protected function encode_rfc3986($string)
  165. {
  166. return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode(($string))));
  167. }
  168. protected function generateNonce()
  169. {
  170. if(isset($this->nonce)) // for unit testing
  171. return $this->nonce;
  172. return md5(uniqid(rand(), true));
  173. }
  174. // parameters should already have been passed through prepareParameters
  175. // no need to double encode
  176. protected function generateSignature($method = null, $url = null, $params = null)
  177. {
  178. if(empty($method) || empty($url))
  179. return false;
  180. // concatenating and encode
  181. $concatenatedParams = $this->encode_rfc3986($this->buildHttpQueryRaw($params));
  182. // normalize url
  183. $normalizedUrl = $this->encode_rfc3986($this->normalizeUrl($url));
  184. $method = $this->encode_rfc3986($method); // don't need this but why not?
  185. $signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}";
  186. return $this->signString($signatureBaseString);
  187. }
  188. protected function executeCurl($ch)
  189. {
  190. if($this->isAsynchronous)
  191. return $this->curl->addCurl($ch);
  192. else
  193. return $this->curl->addEasyCurl($ch);
  194. }
  195. protected function httpDelete($url, $params) {
  196. $this->addDefaultHeaders($url, $params['oauth']);
  197. $ch = $this->curlInit($url);
  198. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
  199. curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildHttpQueryRaw($params['request']));
  200. $resp = $this->executeCurl($ch);
  201. $this->emptyHeaders();
  202. return $resp;
  203. }
  204. protected function httpGet($url, $params = null)
  205. {
  206. if(count($params['request']) > 0)
  207. {
  208. $url .= '?';
  209. foreach($params['request'] as $k => $v)
  210. {
  211. $url .= "{$k}={$v}&";
  212. }
  213. $url = substr($url, 0, -1);
  214. }
  215. $this->addDefaultHeaders($url, $params['oauth']);
  216. $ch = $this->curlInit($url);
  217. $resp = $this->executeCurl($ch);
  218. $this->emptyHeaders();
  219. return $resp;
  220. }
  221. protected function httpPost($url, $params = null, $isMultipart)
  222. {
  223. $this->addDefaultHeaders($url, $params['oauth']);
  224. $ch = $this->curlInit($url);
  225. curl_setopt($ch, CURLOPT_POST, 1);
  226. // php's curl extension automatically sets the content type
  227. // based on whether the params are in string or array form
  228. if($isMultipart) {
  229. $params['request']['status'] = urldecode($params['request']['status']);
  230. curl_setopt($ch, CURLOPT_POSTFILEDS, $params['request']);
  231. }
  232. else
  233. curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildHttpQueryRaw($params['request']));
  234. $resp = $this->executeCurl($ch);
  235. $this->emptyHeaders();
  236. return $resp;
  237. }
  238. protected function normalizeUrl($url = null)
  239. {
  240. $urlParts = parse_url($url);
  241. $scheme = strtolower($urlParts['scheme']);
  242. $host = strtolower($urlParts['host']);
  243. $port = isset($urlParts['port']) ? intval($urlParts['port']) : 0;
  244. $retval = strtolower($scheme) . '://' . strtolower($host);
  245. if(!empty($port) && (($scheme === 'http' && $port != 80) || ($scheme === 'https' && $port != 443)))
  246. $retval .= ":{$port}";
  247. $retval .= $urlParts['path'];
  248. if(!empty($urlParts['query']))
  249. {
  250. $retval .= "?{$urlParts['query']}";
  251. }
  252. return $retval;
  253. }
  254. protected function isMultipart($params = null)
  255. {
  256. if($params)
  257. {
  258. foreach($params as $k => $v)
  259. {
  260. if(strncmp('@',$k,1) === 0)
  261. return true;
  262. }
  263. }
  264. return false;
  265. }
  266. protected function prepareParameters($method = null, $url = null, $params = null)
  267. {
  268. if(empty($method) || empty($url))
  269. return false;
  270. $oauth['oauth_consumer_key'] = $this->consumerKey;
  271. $oauth['oauth_token'] = $this->token;
  272. $oauth['oauth_nonce'] = $this->generateNonce();
  273. $oauth['oauth_timestamp'] = !isset($this->timestamp) ? time() : $this->timestamp; // for unit test
  274. $oauth['oauth_signature_method'] = $this->signatureMethod;
  275. if(isset($params['oauth_verifier']))
  276. {
  277. $oauth['oauth_verifier'] = $params['oauth_verifier'];
  278. unset($params['oauth_verifier']);
  279. }
  280. if(isset($params['oauth_callback']))
  281. {
  282. $oauth['oauth_callback'] = $params['oauth_callback'];
  283. unset($params['oauth_callback']);
  284. }
  285. $oauth['oauth_version'] = $this->version;
  286. // encode all oauth values
  287. foreach($oauth as $k => $v)
  288. $oauth[$k] = $this->encode_rfc3986($v);
  289. // encode all non '@' params
  290. // keep sigParams for signature generation (exclude '@' params)
  291. // rename '@key' to 'key'
  292. $sigParams = array();
  293. $hasFile = false;
  294. if(is_array($params))
  295. {
  296. foreach($params as $k => $v)
  297. {
  298. if(strncmp('@',$k,1) !== 0)
  299. {
  300. $sigParams[$k] = $this->encode_rfc3986($v);
  301. $params[$k] = $this->encode_rfc3986($v);
  302. }
  303. else
  304. {
  305. $params[substr($k, 1)] = $v;
  306. unset($params[$k]);
  307. $hasFile = true;
  308. }
  309. }
  310. if($hasFile === true)
  311. $sigParams = array();
  312. }
  313. $sigParams = array_merge($oauth, (array)$sigParams);
  314. // sorting
  315. ksort($sigParams);
  316. // signing
  317. $oauth['oauth_signature'] = $this->encode_rfc3986($this->generateSignature($method, $url, $sigParams));
  318. return array('request' => $params, 'oauth' => $oauth);
  319. }
  320. protected function signString($string = null)
  321. {
  322. $retval = false;
  323. switch($this->signatureMethod)
  324. {
  325. case 'HMAC-SHA1':
  326. $key = $this->encode_rfc3986($this->consumerSecret) . '&' . $this->encode_rfc3986($this->tokenSecret);
  327. $retval = base64_encode(hash_hmac('sha1', $string, $key, true));
  328. break;
  329. }
  330. return $retval;
  331. }
  332. public function __construct($consumerKey, $consumerSecret, $signatureMethod='HMAC-SHA1')
  333. {
  334. $this->consumerKey = $consumerKey;
  335. $this->consumerSecret = $consumerSecret;
  336. $this->signatureMethod = $signatureMethod;
  337. $this->curl = EpiCurl::getInstance();
  338. }
  339. }
  340. class EpiOAuthResponse
  341. {
  342. private $__resp;
  343. protected $debug = false;
  344. public function __construct($resp)
  345. {
  346. $this->__resp = $resp;
  347. }
  348. public function __get($name)
  349. {
  350. if($this->__resp->code != 200)
  351. EpiOAuthException::raise($this->__resp, $this->debug);
  352. parse_str($this->__resp->data, $result);
  353. foreach($result as $k => $v)
  354. {
  355. $this->$k = $v;
  356. }
  357. return isset($result[$name]) ? $result[$name] : null;
  358. }
  359. public function __toString()
  360. {
  361. return $this->__resp->data;
  362. }
  363. }
  364. class EpiOAuthException extends Exception
  365. {
  366. public static function raise($response, $debug)
  367. {
  368. $message = $response->responseText;
  369. switch($response->code)
  370. {
  371. case 400:
  372. throw new EpiOAuthBadRequestException($message, $response->code);
  373. case 401:
  374. throw new EpiOAuthUnauthorizedException($message, $response->code);
  375. default:
  376. throw new EpiOAuthException($message, $response->code);
  377. }
  378. }
  379. }
  380. class EpiOAuthBadRequestException extends EpiOAuthException{}
  381. class EpiOAuthUnauthorizedException extends EpiOAuthException{}