Advertisement
freejinger

oauth

Oct 23rd, 2014
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.51 KB | None | 0 0
  1. <?php
  2. /* Generic exception class*/
  3. class OAuthException extends Exception { }
  4. class OAuthConsumer {
  5. public $key;
  6. public $secret;
  7. function __construct($key, $secret, $callback_url=NULL) {
  8. $this->key = $key;
  9. $this->secret = $secret;
  10. $this->callback_url = $callback_url;
  11. }
  12. function __toString() { return "OAuthConsumer[key=$this->key,secret=$this->secret]"; }
  13. }
  14. class OAuthToken {
  15. public $key; // access tokens and request tokens
  16. public $secret;
  17. /** * key = the token * secret = the token secret */
  18. function __construct($key, $secret) {
  19. $this->key = $key;
  20. $this->secret = $secret;
  21. }
  22. /**
  23. * generates the basic string serialization of a token that a server
  24. * would respond to request_token and access_token calls with
  25. */
  26. function to_string() {
  27. return "oauth_token=".OAuthUtil::urlencode_rfc3986($this->key)."&oauth_token_secret=".OAuthUtil::urlencode_rfc3986($this->secret); }
  28. function __toString() { return $this->to_string(); }
  29. }
  30. /**
  31. * A class for implementing a Signature Method
  32. * See section 9 ("Signing Requests") in the spec
  33. */
  34. abstract class OAuthSignatureMethod {
  35. /**
  36. * Needs to return the name of the Signature Method (ie HMAC-SHA1)
  37. * @return string
  38. */
  39. abstract public function get_name();
  40. /**
  41. * Build up the signature
  42. * NOTE: The output of this function MUST NOT be urlencoded.
  43. * the encoding is handled in OAuthRequest when the final
  44. * request is serialized
  45. * @param OAuthRequest $request
  46. * @param OAuthConsumer $consumer
  47. * @param OAuthToken $token
  48. * @return string
  49. */
  50. abstract public function build_signature($request, $consumer, $token);
  51. /**
  52. * Verifies that a given signature is correct
  53. * @param OAuthRequest $request
  54. * @param OAuthConsumer $consumer
  55. * @param OAuthToken $token
  56. * @param string $signature
  57. * @return bool
  58. */
  59. public function check_signature($request, $consumer, $token, $signature) {
  60. $built = $this->build_signature($request, $consumer, $token);
  61. return $built == $signature;
  62. }
  63. }
  64. /**
  65. * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
  66. * where the Signature Base String is the text and the key is the concatenated values (each first
  67. * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
  68. * character (ASCII code 38) even if empty.
  69. * - Chapter 9.2 ("HMAC-SHA1")
  70. */
  71. class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
  72. function get_name() { return "HMAC-SHA1"; }
  73. public function build_signature($request, $consumer, $token) {
  74. $base_string = $request->get_signature_base_string();
  75. $request->base_string = $base_string;
  76. $key_parts = array( $consumer->secret,($token) ? $token->secret : "" );
  77. $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
  78. $key = implode('&', $key_parts);
  79. return base64_encode(hash_hmac('sha1', $base_string, $key, true));
  80. }
  81. }
  82.  
  83. /**
  84. * The PLAINTEXT method does not provide any security protection and SHOULD only be used
  85. * over a secure channel such as HTTPS. It does not use the Signature Base String.
  86. * - Chapter 9.4 ("PLAINTEXT")
  87. */
  88. class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
  89. public function get_name() { return "PLAINTEXT"; }
  90. /**
  91. * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
  92. * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
  93. * empty. The result MUST be encoded again.
  94. * - Chapter 9.4.1 ("Generating Signatures")
  95. *
  96. * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
  97. * OAuthRequest handles this!
  98. */
  99. public function build_signature($request, $consumer, $token) {
  100. $key_parts = array( $consumer->secret, ($token) ? $token->secret : "" );
  101. $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
  102. $key = implode('&', $key_parts);
  103. $request->base_string = $key;
  104. return $key;
  105. }
  106. }
  107. /**
  108. * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
  109. * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
  110. * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
  111. * verified way to the Service Provider, in a manner which is beyond the scope of this
  112. * specification.
  113. * - Chapter 9.3 ("RSA-SHA1")
  114. */
  115. abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
  116. public function get_name() { return "RSA-SHA1"; }
  117. // Up to the SP to implement this lookup of keys. Possible ideas are:
  118. // (1) do a lookup in a table of trusted certs keyed off of consumer
  119. // (2) fetch via http using a url provided by the requester
  120. // (3) some sort of specific discovery code based on request
  121. // Either way should return a string representation of the certificate
  122. protected abstract function fetch_public_cert(&$request);
  123. // Up to the SP to implement this lookup of keys. Possible ideas are:
  124. // (1) do a lookup in a table of trusted certs keyed off of consumer
  125. // Either way should return a string representation of the certificate
  126. protected abstract function fetch_private_cert(&$request);
  127. public function build_signature($request, $consumer, $token) {
  128. $base_string = $request->get_signature_base_string();
  129. $request->base_string = $base_string;
  130. // Fetch the private key cert based on the request
  131. $cert = $this->fetch_private_cert($request);
  132. // Pull the private key ID from the certificate
  133. $privatekeyid = openssl_get_privatekey($cert);
  134. // Sign using the key
  135. $ok = openssl_sign($base_string, $signature, $privatekeyid);
  136. // Release the key resource
  137. openssl_free_key($privatekeyid);
  138. return base64_encode($signature);
  139. }
  140. public function check_signature($request, $consumer, $token, $signature) {
  141. $decoded_sig = base64_decode($signature);
  142. $base_string = $request->get_signature_base_string();
  143. // Fetch the public key cert based on the request
  144. $cert = $this->fetch_public_cert($request);
  145. // Pull the public key ID from the certificate
  146. $publickeyid = openssl_get_publickey($cert);
  147. // Check the computed signature against the one passed in the query
  148. $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
  149. // Release the key resource
  150. openssl_free_key($publickeyid);
  151. return $ok == 1;
  152. }
  153. }
  154. class OAuthRequest {
  155. private $parameters;
  156. private $http_method;
  157. private $http_url;
  158. // for debug purposes
  159. public $base_string;
  160. public static $version = '1.0';
  161. public static $POST_INPUT = 'php://input';
  162. function __construct($http_method, $http_url, $parameters=NULL) {
  163. @$parameters or $parameters = array();
  164. $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
  165. $this->parameters = $parameters;
  166. $this->http_method = $http_method;
  167. $this->http_url = $http_url;
  168. }
  169. /** * attempt to build up a request from what was passed to the server */
  170. public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
  171. $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
  172. ? 'http'
  173. : 'https';
  174. @$http_url or $http_url = $scheme .
  175. '://' . $_SERVER['HTTP_HOST'] .
  176. ':' .
  177. $_SERVER['SERVER_PORT'] .
  178. $_SERVER['REQUEST_URI'];
  179. @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
  180.  
  181. // We weren't handed any parameters, so let's find the ones relevant to
  182. // this request.
  183. // If you run XML-RPC or similar you should use this to provide your own
  184. // parsed parameter-list
  185. if (!$parameters) {
  186. // Find request headers
  187. $request_headers = OAuthUtil::get_headers();
  188.  
  189. // Parse the query-string to find GET parameters
  190. $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
  191.  
  192. // It's a POST request of the proper content-type, so parse POST
  193. // parameters and add those overriding any duplicates from GET
  194. if ($http_method == "POST"
  195. && @strstr($request_headers["Content-Type"],
  196. "application/x-www-form-urlencoded")
  197. ) {
  198. $post_data = OAuthUtil::parse_parameters(
  199. file_get_contents(self::$POST_INPUT)
  200. );
  201. $parameters = array_merge($parameters, $post_data);
  202. }
  203.  
  204. // We have a Authorization-header with OAuth data. Parse the header
  205. // and add those overriding any duplicates from GET or POST
  206. if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
  207. $header_parameters = OAuthUtil::split_header(
  208. $request_headers['Authorization']
  209. );
  210. $parameters = array_merge($parameters, $header_parameters);
  211. }
  212.  
  213. }
  214.  
  215. return new OAuthRequest($http_method, $http_url, $parameters);
  216. }
  217.  
  218. /**
  219. * pretty much a helper function to set up the request
  220. */
  221. public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
  222. @$parameters or $parameters = array();
  223. $defaults = array("oauth_version" => OAuthRequest::$version,
  224. "oauth_nonce" => OAuthRequest::generate_nonce(),
  225. "oauth_timestamp" => OAuthRequest::generate_timestamp(),
  226. "oauth_consumer_key" => $consumer->key);
  227. if ($token)
  228. $defaults['oauth_token'] = $token->key;
  229.  
  230. $parameters = array_merge($defaults, $parameters);
  231.  
  232. return new OAuthRequest($http_method, $http_url, $parameters);
  233. }
  234.  
  235. public function set_parameter($name, $value, $allow_duplicates = true) {
  236. if ($allow_duplicates && isset($this->parameters[$name])) {
  237. // We have already added parameter(s) with this name, so add to the list
  238. if (is_scalar($this->parameters[$name])) {
  239. // This is the first duplicate, so transform scalar (string)
  240. // into an array so we can add the duplicates
  241. $this->parameters[$name] = array($this->parameters[$name]);
  242. }
  243.  
  244. $this->parameters[$name][] = $value;
  245. } else {
  246. $this->parameters[$name] = $value;
  247. }
  248. }
  249.  
  250. public function get_parameter($name) {
  251. return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
  252. }
  253.  
  254. public function get_parameters() {
  255. return $this->parameters;
  256. }
  257.  
  258. public function unset_parameter($name) {
  259. unset($this->parameters[$name]);
  260. }
  261.  
  262. /**
  263. * The request parameters, sorted and concatenated into a normalized string.
  264. * @return string
  265. */
  266. public function get_signable_parameters() {
  267. // Grab all parameters
  268. $params = $this->parameters;
  269.  
  270. // Remove oauth_signature if present
  271. // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
  272. if (isset($params['oauth_signature'])) {
  273. unset($params['oauth_signature']);
  274. }
  275.  
  276. return OAuthUtil::build_http_query($params);
  277. }
  278.  
  279. /**
  280. * Returns the base string of this request
  281. *
  282. * The base string defined as the method, the url
  283. * and the parameters (normalized), each urlencoded
  284. * and the concated with &.
  285. */
  286. public function get_signature_base_string() {
  287. $parts = array(
  288. $this->get_normalized_http_method(),
  289. $this->get_normalized_http_url(),
  290. $this->get_signable_parameters()
  291. );
  292.  
  293. $parts = OAuthUtil::urlencode_rfc3986($parts);
  294.  
  295. return implode('&', $parts);
  296. }
  297.  
  298. /**
  299. * just uppercases the http method
  300. */
  301. public function get_normalized_http_method() {
  302. return strtoupper($this->http_method);
  303. }
  304.  
  305. /**
  306. * parses the url and rebuilds it to be
  307. * scheme://host/path
  308. */
  309. public function get_normalized_http_url() {
  310. $parts = parse_url($this->http_url);
  311.  
  312. $port = @$parts['port'];
  313. $scheme = $parts['scheme'];
  314. $host = $parts['host'];
  315. $path = @$parts['path'];
  316.  
  317. $port or $port = ($scheme == 'https') ? '443' : '80';
  318.  
  319. if (($scheme == 'https' && $port != '443')
  320. || ($scheme == 'http' && $port != '80')) {
  321. $host = "$host:$port";
  322. }
  323. return "$scheme://$host$path";
  324. }
  325.  
  326. /**
  327. * builds a url usable for a GET request
  328. */
  329. public function to_url() {
  330. $post_data = $this->to_postdata();
  331. $out = $this->get_normalized_http_url();
  332. if ($post_data) {
  333. $out .= '?'.$post_data;
  334. }
  335. return $out;
  336. }
  337.  
  338. /**
  339. * builds the data one would send in a POST request
  340. */
  341. public function to_postdata() {
  342. return OAuthUtil::build_http_query($this->parameters);
  343. }
  344.  
  345. /**
  346. * builds the Authorization: header
  347. */
  348. public function to_header($realm=null) {
  349. $first = true;
  350. if($realm) {
  351. $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
  352. $first = false;
  353. } else
  354. $out = 'Authorization: OAuth';
  355.  
  356. $total = array();
  357. foreach ($this->parameters as $k => $v) {
  358. if (substr($k, 0, 5) != "oauth") continue;
  359. if (is_array($v)) {
  360. throw new OAuthException('Arrays not supported in headers');
  361. }
  362. $out .= ($first) ? ' ' : ',';
  363. $out .= OAuthUtil::urlencode_rfc3986($k) .
  364. '="' .
  365. OAuthUtil::urlencode_rfc3986($v) .
  366. '"';
  367. $first = false;
  368. }
  369. return $out;
  370. }
  371.  
  372. public function __toString() {
  373. return $this->to_url();
  374. }
  375.  
  376.  
  377. public function sign_request($signature_method, $consumer, $token) {
  378. $this->set_parameter(
  379. "oauth_signature_method",
  380. $signature_method->get_name(),
  381. false
  382. );
  383. $signature = $this->build_signature($signature_method, $consumer, $token);
  384. $this->set_parameter("oauth_signature", $signature, false);
  385. }
  386.  
  387. public function build_signature($signature_method, $consumer, $token) {
  388. $signature = $signature_method->build_signature($this, $consumer, $token);
  389. return $signature;
  390. }
  391.  
  392. /**
  393. * util function: current timestamp
  394. */
  395. private static function generate_timestamp() {
  396. return time();
  397. }
  398.  
  399. /**
  400. * util function: current nonce
  401. */
  402. private static function generate_nonce() {
  403. $mt = microtime();
  404. $rand = mt_rand();
  405.  
  406. return md5($mt . $rand); // md5s look nicer than numbers
  407. }
  408. }
  409.  
  410. class OAuthServer {
  411. protected $timestamp_threshold = 300; // in seconds, five minutes
  412. protected $version = '1.0'; // hi blaine
  413. protected $signature_methods = array();
  414.  
  415. protected $data_store;
  416.  
  417. function __construct($data_store) {
  418. $this->data_store = $data_store;
  419. }
  420.  
  421. public function add_signature_method($signature_method) {
  422. $this->signature_methods[$signature_method->get_name()] =
  423. $signature_method;
  424. }
  425.  
  426. // high level functions
  427.  
  428. /**
  429. * process a request_token request
  430. * returns the request token on success
  431. */
  432. public function fetch_request_token(&$request) {
  433. $this->get_version($request);
  434.  
  435. $consumer = $this->get_consumer($request);
  436.  
  437. // no token required for the initial token request
  438. $token = NULL;
  439.  
  440. $this->check_signature($request, $consumer, $token);
  441.  
  442. // Rev A change
  443. $callback = $request->get_parameter('oauth_callback');
  444. $new_token = $this->data_store->new_request_token($consumer, $callback);
  445.  
  446. return $new_token;
  447. }
  448.  
  449. /**
  450. * process an access_token request
  451. * returns the access token on success
  452. */
  453. public function fetch_access_token(&$request) {
  454. $this->get_version($request);
  455.  
  456. $consumer = $this->get_consumer($request);
  457.  
  458. // requires authorized request token
  459. $token = $this->get_token($request, $consumer, "request");
  460.  
  461. $this->check_signature($request, $consumer, $token);
  462.  
  463. // Rev A change
  464. $verifier = $request->get_parameter('oauth_verifier');
  465. $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
  466.  
  467. return $new_token;
  468. }
  469.  
  470. /**
  471. * verify an api call, checks all the parameters
  472. */
  473. public function verify_request(&$request) {
  474. $this->get_version($request);
  475. $consumer = $this->get_consumer($request);
  476. $token = $this->get_token($request, $consumer, "access");
  477. $this->check_signature($request, $consumer, $token);
  478. return array($consumer, $token);
  479. }
  480.  
  481. // Internals from here
  482. /**
  483. * version 1
  484. */
  485. private function get_version(&$request) {
  486. $version = $request->get_parameter("oauth_version");
  487. if (!$version) {
  488. // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
  489. // Chapter 7.0 ("Accessing Protected Ressources")
  490. $version = '1.0';
  491. }
  492. if ($version !== $this->version) {
  493. throw new OAuthException("OAuth version '$version' not supported");
  494. }
  495. return $version;
  496. }
  497.  
  498. /**
  499. * figure out the signature with some defaults
  500. */
  501. private function get_signature_method(&$request) {
  502. $signature_method =
  503. @$request->get_parameter("oauth_signature_method");
  504.  
  505. if (!$signature_method) {
  506. // According to chapter 7 ("Accessing Protected Ressources") the signature-method
  507. // parameter is required, and we can't just fallback to PLAINTEXT
  508. throw new OAuthException('No signature method parameter. This parameter is required');
  509. }
  510.  
  511. if (!in_array($signature_method,
  512. array_keys($this->signature_methods))) {
  513. throw new OAuthException(
  514. "Signature method '$signature_method' not supported " .
  515. "try one of the following: " .
  516. implode(", ", array_keys($this->signature_methods))
  517. );
  518. }
  519. return $this->signature_methods[$signature_method];
  520. }
  521.  
  522. /**
  523. * try to find the consumer for the provided request's consumer key
  524. */
  525. private function get_consumer(&$request) {
  526. $consumer_key = @$request->get_parameter("oauth_consumer_key");
  527. if (!$consumer_key) {
  528. throw new OAuthException("Invalid consumer key");
  529. }
  530.  
  531. $consumer = $this->data_store->lookup_consumer($consumer_key);
  532. if (!$consumer) {
  533. throw new OAuthException("Invalid consumer");
  534. }
  535.  
  536. return $consumer;
  537. }
  538.  
  539. /**
  540. * try to find the token for the provided request's token key
  541. */
  542. private function get_token(&$request, $consumer, $token_type="access") {
  543. $token_field = @$request->get_parameter('oauth_token');
  544. $token = $this->data_store->lookup_token(
  545. $consumer, $token_type, $token_field
  546. );
  547. if (!$token) {
  548. throw new OAuthException("Invalid $token_type token: $token_field");
  549. }
  550. return $token;
  551. }
  552.  
  553. /**
  554. * all-in-one function to check the signature on a request
  555. * should guess the signature method appropriately
  556. */
  557. private function check_signature(&$request, $consumer, $token) {
  558. // this should probably be in a different method
  559. $timestamp = @$request->get_parameter('oauth_timestamp');
  560. $nonce = @$request->get_parameter('oauth_nonce');
  561.  
  562. $this->check_timestamp($timestamp);
  563. $this->check_nonce($consumer, $token, $nonce, $timestamp);
  564.  
  565. $signature_method = $this->get_signature_method($request);
  566.  
  567. $signature = $request->get_parameter('oauth_signature');
  568. $valid_sig = $signature_method->check_signature(
  569. $request,
  570. $consumer,
  571. $token,
  572. $signature
  573. );
  574.  
  575. if (!$valid_sig) {
  576. throw new OAuthException("Invalid signature");
  577. }
  578. }
  579.  
  580. /**
  581. * check that the timestamp is new enough
  582. */
  583. private function check_timestamp($timestamp) {
  584. if( ! $timestamp )
  585. throw new OAuthException(
  586. 'Missing timestamp parameter. The parameter is required'
  587. );
  588.  
  589. // verify that timestamp is recentish
  590. $now = time();
  591. if (abs($now - $timestamp) > $this->timestamp_threshold) {
  592. throw new OAuthException( "Expired timestamp, yours $timestamp, ours $now" );
  593. }
  594. }
  595.  
  596. /**
  597. * check that the nonce is not repeated
  598. */
  599. private function check_nonce($consumer, $token, $nonce, $timestamp) {
  600. if( ! $nonce )
  601. throw new OAuthException(
  602. 'Missing nonce parameter. The parameter is required'
  603. );
  604.  
  605. // verify that the nonce is uniqueish
  606. $found = $this->data_store->lookup_nonce(
  607. $consumer,
  608. $token,
  609. $nonce,
  610. $timestamp
  611. );
  612. if ($found) {
  613. throw new OAuthException("Nonce already used: $nonce");
  614. }
  615. }
  616.  
  617. }
  618.  
  619. class OAuthDataStore {
  620. function lookup_consumer($consumer_key) {
  621. // implement me
  622. }
  623.  
  624. function lookup_token($consumer, $token_type, $token) {
  625. // implement me
  626. }
  627.  
  628. function lookup_nonce($consumer, $token, $nonce, $timestamp) {
  629. // implement me
  630. }
  631.  
  632. function new_request_token($consumer, $callback = null) {
  633. // return a new token attached to this consumer
  634. }
  635.  
  636. function new_access_token($token, $consumer, $verifier = null) {
  637. // return a new access token attached to this consumer
  638. // for the user associated with this token if the request token
  639. // is authorized
  640. // should also invalidate the request token
  641. }
  642.  
  643. }
  644.  
  645. class OAuthUtil {
  646. public static function urlencode_rfc3986($input) {
  647. if (is_array($input)) {
  648. return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
  649. } else if (is_scalar($input)) {
  650. return str_replace(
  651. '+',
  652. ' ',
  653. str_replace('%7E', '~', rawurlencode($input))
  654. );
  655. } else {
  656. return '';
  657. }
  658. }
  659.  
  660.  
  661. // This decode function isn't taking into consideration the above
  662. // modifications to the encoding process. However, this method doesn't
  663. // seem to be used anywhere so leaving it as is.
  664. public static function urldecode_rfc3986($string) {
  665. return urldecode($string);
  666. }
  667.  
  668. // Utility function for turning the Authorization: header into
  669. // parameters, has to do some unescaping
  670. // Can filter out any non-oauth parameters if needed (default behaviour)
  671. public static function split_header($header, $only_allow_oauth_parameters = true) {
  672. $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
  673. $offset = 0;
  674. $params = array();
  675. while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
  676. $match = $matches[0];
  677. $header_name = $matches[2][0];
  678. $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
  679. if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
  680. $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
  681. }
  682. $offset = $match[1] + strlen($match[0]);
  683. }
  684.  
  685. if (isset($params['realm'])) {
  686. unset($params['realm']);
  687. }
  688.  
  689. return $params;
  690. }
  691.  
  692. // helper to try to sort out headers for people who aren't running apache
  693. public static function get_headers() {
  694. if (function_exists('apache_request_headers')) {
  695. // we need this to get the actual Authorization: header
  696. // because apache tends to tell us it doesn't exist
  697. $headers = apache_request_headers();
  698.  
  699. // sanitize the output of apache_request_headers because
  700. // we always want the keys to be Cased-Like-This and arh()
  701. // returns the headers in the same case as they are in the
  702. // request
  703. $out = array();
  704. foreach( $headers AS $key => $value ) {
  705. $key = str_replace(
  706. " ",
  707. "-",
  708. ucwords(strtolower(str_replace("-", " ", $key)))
  709. );
  710. $out[$key] = $value;
  711. }
  712. } else {
  713. // otherwise we don't have apache and are just going to have to hope
  714. // that $_SERVER actually contains what we need
  715. $out = array();
  716. if( isset($_SERVER['CONTENT_TYPE']) )
  717. $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
  718. if( isset($_ENV['CONTENT_TYPE']) )
  719. $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
  720.  
  721. foreach ($_SERVER as $key => $value) {
  722. if (substr($key, 0, 5) == "HTTP_") {
  723. // this is chaos, basically it is just there to capitalize the first
  724. // letter of every word that is not an initial HTTP and strip HTTP
  725. // code from przemek
  726. $key = str_replace(
  727. " ",
  728. "-",
  729. ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
  730. );
  731. $out[$key] = $value;
  732. }
  733. }
  734. }
  735. return $out;
  736. }
  737.  
  738. // This function takes a input like a=b&a=c&d=e and returns the parsed
  739. // parameters like this
  740. // array('a' => array('b','c'), 'd' => 'e')
  741. public static function parse_parameters( $input ) {
  742. if (!isset($input) || !$input) return array();
  743.  
  744. $pairs = explode('&', $input);
  745.  
  746. $parsed_parameters = array();
  747. foreach ($pairs as $pair) {
  748. $split = explode('=', $pair, 2);
  749. $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
  750. $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
  751.  
  752. if (isset($parsed_parameters[$parameter])) {
  753. // We have already recieved parameter(s) with this name, so add to the list
  754. // of parameters with this name
  755.  
  756. if (is_scalar($parsed_parameters[$parameter])) {
  757. // This is the first duplicate, so transform scalar (string) into an array
  758. // so we can add the duplicates
  759. $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
  760. }
  761.  
  762. $parsed_parameters[$parameter][] = $value;
  763. } else {
  764. $parsed_parameters[$parameter] = $value;
  765. }
  766. }
  767. return $parsed_parameters;
  768. }
  769.  
  770. public static function build_http_query($params) {
  771. if (!$params) return '';
  772.  
  773. // Urlencode both keys and values
  774. $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
  775. $values = OAuthUtil::urlencode_rfc3986(array_values($params));
  776. $params = array_combine($keys, $values);
  777.  
  778. // Parameters are sorted by name, using lexicographical byte value ordering.
  779. // Ref: Spec: 9.1.1 (1)
  780. uksort($params, 'strcmp');
  781.  
  782. $pairs = array();
  783. foreach ($params as $parameter => $value) {
  784. if (is_array($value)) {
  785. // If two or more parameters share the same name, they are sorted by their value
  786. // Ref: Spec: 9.1.1 (1)
  787. natsort($value);
  788. foreach ($value as $duplicate_value) {
  789. $pairs[] = $parameter . '=' . $duplicate_value;
  790. }
  791. } else {
  792. $pairs[] = $parameter . '=' . $value;
  793. }
  794. }
  795. // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
  796. // Each name-value pair is separated by an '&' character (ASCII code 38)
  797. return implode('&', $pairs);
  798. }
  799. }
  800.  
  801.  
  802.  
  803.  
  804.  
  805.  
  806.  
  807.  
  808.  
  809.  
  810.  
  811.  
  812.  
  813.  
  814.  
  815.  
  816.  
  817. /**
  818. * Twitter OAuth class
  819. */
  820. class TwitterOAuth {
  821. /* Contains the last HTTP status code returned. */
  822. public $http_code;
  823. /* Contains the last API call. */
  824. public $url;
  825. /* Set up the API root URL. */
  826. public $host = "https://api.twitter.com/1/";
  827. /* Set timeout default. */
  828. public $timeout = 30;
  829. /* Set connect timeout. */
  830. public $connecttimeout = 30;
  831. /* Verify SSL Cert. */
  832. public $ssl_verifypeer = FALSE;
  833. /* Respons format. */
  834. public $format = 'json';
  835. /* Decode returned json data. */
  836. public $decode_json = TRUE;
  837. /* Contains the last HTTP headers returned. */
  838. public $http_info;
  839. /* Set the useragnet. */
  840. public $useragent = 'TwitterOAuth v0.2.0-beta2';
  841. /* Immediately retry the API call if the response was not successful. */
  842. //public $retry = TRUE;
  843.  
  844.  
  845.  
  846.  
  847. /**
  848. * Set API URLS
  849. */
  850. function accessTokenURL() { return 'https://api.twitter.com/oauth/access_token'; }
  851. function authenticateURL() { return 'https://api.twitter.com/oauth/authenticate'; }
  852. function authorizeURL() { return 'https://api.twitter.com/oauth/authorize'; }
  853. function requestTokenURL() { return 'https://api.twitter.com/oauth/request_token'; }
  854.  
  855. /**
  856. * Debug helpers
  857. */
  858. function lastStatusCode() { return $this->http_status; }
  859. function lastAPICall() { return $this->last_api_call; }
  860.  
  861. /**
  862. * construct TwitterOAuth object
  863. */
  864. function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
  865. $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
  866. $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
  867. if (!empty($oauth_token) && !empty($oauth_token_secret)) {
  868. $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
  869. } else {
  870. $this->token = NULL;
  871. }
  872. }
  873.  
  874.  
  875. /**
  876. * Get a request_token from Twitter
  877. *
  878. * @returns a key/value array containing oauth_token and oauth_token_secret
  879. */
  880. function getRequestToken($oauth_callback = NULL) {
  881. $parameters = array();
  882. if (!empty($oauth_callback)) {
  883. $parameters['oauth_callback'] = $oauth_callback;
  884. }
  885. $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
  886. $token = OAuthUtil::parse_parameters($request);
  887. $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
  888. return $token;
  889. }
  890.  
  891. /**
  892. * Get the authorize URL
  893. *
  894. * @returns a string
  895. */
  896. function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) {
  897. if (is_array($token)) {
  898. $token = $token['oauth_token'];
  899. }
  900. if (empty($sign_in_with_twitter)) {
  901. return $this->authorizeURL() . "?oauth_token={$token}";
  902. } else {
  903. return $this->authenticateURL() . "?oauth_token={$token}";
  904. }
  905. }
  906.  
  907. /**
  908. * Exchange request token and secret for an access token and
  909. * secret, to sign API calls.
  910. *
  911. * @returns array("oauth_token" => "the-access-token",
  912. * "oauth_token_secret" => "the-access-secret",
  913. * "user_id" => "9436992",
  914. * "screen_name" => "abraham")
  915. */
  916. function getAccessToken($oauth_verifier = FALSE) {
  917. $parameters = array();
  918. if (!empty($oauth_verifier)) {
  919. $parameters['oauth_verifier'] = $oauth_verifier;
  920. }
  921. $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
  922. $token = OAuthUtil::parse_parameters($request);
  923. $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
  924. return $token;
  925. }
  926.  
  927. /**
  928. * One time exchange of username and password for access token and secret.
  929. *
  930. * @returns array("oauth_token" => "the-access-token",
  931. * "oauth_token_secret" => "the-access-secret",
  932. * "user_id" => "9436992",
  933. * "screen_name" => "abraham",
  934. * "x_auth_expires" => "0")
  935. */
  936. function getXAuthToken($username, $password) {
  937. $parameters = array();
  938. $parameters['x_auth_username'] = $username;
  939. $parameters['x_auth_password'] = $password;
  940. $parameters['x_auth_mode'] = 'client_auth';
  941. $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters);
  942. $token = OAuthUtil::parse_parameters($request);
  943. $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
  944. return $token;
  945. }
  946.  
  947. /**
  948. * GET wrapper for oAuthRequest.
  949. */
  950. function get($url, $parameters = array()) {
  951. $response = $this->oAuthRequest($url, 'GET', $parameters);
  952. if ($this->format === 'json' && $this->decode_json) {
  953. return json_decode($response);
  954. }
  955. return $response;
  956. }
  957.  
  958. /**
  959. * POST wrapper for oAuthRequest.
  960. */
  961. function post($url, $parameters = array()) {
  962. $response = $this->oAuthRequest($url, 'POST', $parameters);
  963. if ($this->format === 'json' && $this->decode_json) {
  964. return json_decode($response);
  965. }
  966. return $response;
  967. }
  968.  
  969. /**
  970. * DELETE wrapper for oAuthReqeust.
  971. */
  972. function delete($url, $parameters = array()) {
  973. $response = $this->oAuthRequest($url, 'DELETE', $parameters);
  974. if ($this->format === 'json' && $this->decode_json) {
  975. return json_decode($response);
  976. }
  977. return $response;
  978. }
  979.  
  980. /**
  981. * Format and sign an OAuth / API request
  982. */
  983. function oAuthRequest($url, $method, $parameters) {
  984. if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
  985. $url = "{$this->host}{$url}.{$this->format}";
  986. }
  987. $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
  988. $request->sign_request($this->sha1_method, $this->consumer, $this->token);
  989. switch ($method) {
  990. case 'GET':
  991. return $this->http($request->to_url(), 'GET');
  992. default:
  993. return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata());
  994. }
  995. }
  996.  
  997. /**
  998. * Make an HTTP request
  999. *
  1000. * @return API results
  1001. */
  1002. function http($url, $method, $postfields = NULL) {
  1003. $this->http_info = array();
  1004. $ci = curl_init();
  1005. /* Curl settings */
  1006. curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
  1007. curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
  1008. curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
  1009. curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
  1010. curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));
  1011. curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
  1012. curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
  1013. curl_setopt($ci, CURLOPT_HEADER, FALSE);
  1014.  
  1015. switch ($method) {
  1016. case 'POST':
  1017. curl_setopt($ci, CURLOPT_POST, TRUE);
  1018. if (!empty($postfields)) {
  1019. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  1020. }
  1021. break;
  1022. case 'DELETE':
  1023. curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
  1024. if (!empty($postfields)) {
  1025. $url = "{$url}?{$postfields}";
  1026. }
  1027. }
  1028.  
  1029. curl_setopt($ci, CURLOPT_URL, $url);
  1030. $response = curl_exec($ci);
  1031. $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
  1032. $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
  1033. $this->url = $url;
  1034. curl_close ($ci);
  1035. return $response;
  1036. }
  1037.  
  1038. /**
  1039. * Get the header info to store.
  1040. */
  1041. function getHeader($ch, $header) {
  1042. $i = strpos($header, ':');
  1043. if (!empty($i)) {
  1044. $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
  1045. $value = trim(substr($header, $i + 2));
  1046. $this->http_header[$key] = $value;
  1047. }
  1048. return strlen($header);
  1049. }
  1050. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement