PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/administrator/components/com_zoo/libraries/twitter/OAuth.php

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