Advertisement
Guest User

OAuth.php

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