PageRenderTime 83ms CodeModel.GetById 16ms RepoModel.GetById 10ms app.codeStats 0ms

/mod/twitter_api/vendors/twitteroauth/OAuth.php

https://github.com/fragilbert/Elgg
PHP | 518 lines | 333 code | 79 blank | 106 comment | 51 complexity | e0bfa242b80c880e4be942dc54517026 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0
  1. <?php
  2. // vim: foldmethod=marker
  3. class OAuthConsumer {
  4. public $key;
  5. public $secret;
  6. function __construct($key, $secret, $callback_url=NULL) {
  7. $this->key = $key;
  8. $this->secret = $secret;
  9. $this->callback_url = $callback_url;
  10. }
  11. function __toString() {
  12. return "OAuthConsumer[key=$this->key,secret=$this->secret]";
  13. }
  14. }
  15. class OAuthToken {
  16. // access tokens and request tokens
  17. public $key;
  18. public $secret;
  19. /**
  20. * key = the token
  21. * secret = the token secret
  22. */
  23. function __construct($key, $secret) {
  24. $this->key = $key;
  25. $this->secret = $secret;
  26. }
  27. /**
  28. * generates the basic string serialization of a token that a server
  29. * would respond to request_token and access_token calls with
  30. */
  31. function to_string() {
  32. return "oauth_token=" .
  33. OAuthUtil::urlencode_rfc3986($this->key) .
  34. "&oauth_token_secret=" .
  35. OAuthUtil::urlencode_rfc3986($this->secret);
  36. }
  37. function __toString() {
  38. return $this->to_string();
  39. }
  40. }
  41. class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SHA1 {/*{{{*/
  42. function get_name() {/*{{{*/
  43. return "HMAC-SHA1";
  44. }/*}}}*/
  45. public function build_signature($request, $consumer, $token) {/*{{{*/
  46. $base_string = $request->get_signature_base_string();
  47. $request->base_string = $base_string;
  48. $key_parts = array(
  49. $consumer->secret,
  50. ($token) ? $token->secret : ""
  51. );
  52. $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
  53. $key = implode('&', $key_parts);
  54. return base64_encode( hash_hmac('sha1', $base_string, $key, true));
  55. }/*}}}*/
  56. public function check_signature(&$request, $consumer, $token, $signature) {
  57. $built = $this->build_signature($request, $consumer, $token);
  58. return $built == $signature;
  59. }
  60. }/*}}}*/
  61. class twitterOAuthRequest extends OAuthRequest {
  62. private $parameters;
  63. private $http_method;
  64. private $http_url;
  65. // for debug purposes
  66. public $base_string;
  67. public static $version = '1.0';
  68. public static $POST_INPUT = 'php://input';
  69. function __construct($http_method, $http_url, $parameters=NULL) {
  70. @$parameters or $parameters = array();
  71. $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
  72. $this->parameters = $parameters;
  73. $this->http_method = $http_method;
  74. $this->http_url = $http_url;
  75. }
  76. /**
  77. * attempt to build up a request from what was passed to the server
  78. */
  79. public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
  80. $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
  81. ? 'http'
  82. : 'https';
  83. @$http_url or $http_url = $scheme .
  84. '://' . $_SERVER['HTTP_HOST'] .
  85. ':' .
  86. $_SERVER['SERVER_PORT'] .
  87. $_SERVER['REQUEST_URI'];
  88. @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
  89. // We weren't handed any parameters, so let's find the ones relevant to
  90. // this request.
  91. // If you run XML-RPC or similar you should use this to provide your own
  92. // parsed parameter-list
  93. if (!$parameters) {
  94. // Find request headers
  95. $request_headers = OAuthUtil::get_headers();
  96. // Parse the query-string to find GET parameters
  97. $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
  98. // It's a POST request of the proper content-type, so parse POST
  99. // parameters and add those overriding any duplicates from GET
  100. if ($http_method == "POST"
  101. && @strstr($request_headers["Content-Type"],
  102. "application/x-www-form-urlencoded")
  103. ) {
  104. $post_data = OAuthUtil::parse_parameters(
  105. file_get_contents(self::$POST_INPUT)
  106. );
  107. $parameters = array_merge($parameters, $post_data);
  108. }
  109. // We have a Authorization-header with OAuth data. Parse the header
  110. // and add those overriding any duplicates from GET or POST
  111. if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
  112. $header_parameters = OAuthUtil::split_header(
  113. $request_headers['Authorization']
  114. );
  115. $parameters = array_merge($parameters, $header_parameters);
  116. }
  117. }
  118. return new twitterOAuthRequest($http_method, $http_url, $parameters);
  119. }
  120. /**
  121. * pretty much a helper function to set up the request
  122. */
  123. public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
  124. @$parameters or $parameters = array();
  125. $defaults = array("oauth_version" => twitterOAuthRequest::$version,
  126. "oauth_nonce" => twitterOAuthRequest::generate_nonce(),
  127. "oauth_timestamp" => twitterOAuthRequest::generate_timestamp(),
  128. "oauth_consumer_key" => $consumer->key);
  129. if ($token)
  130. $defaults['oauth_token'] = $token->key;
  131. $parameters = array_merge($defaults, $parameters);
  132. return new twitterOAuthRequest($http_method, $http_url, $parameters);
  133. }
  134. public function set_parameter($name, $value, $allow_duplicates = true) {
  135. if ($allow_duplicates && isset($this->parameters[$name])) {
  136. // We have already added parameter(s) with this name, so add to the list
  137. if (is_scalar($this->parameters[$name])) {
  138. // This is the first duplicate, so transform scalar (string)
  139. // into an array so we can add the duplicates
  140. $this->parameters[$name] = array($this->parameters[$name]);
  141. }
  142. $this->parameters[$name][] = $value;
  143. } else {
  144. $this->parameters[$name] = $value;
  145. }
  146. }
  147. public function get_parameter($name) {
  148. return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
  149. }
  150. public function get_parameters() {
  151. return $this->parameters;
  152. }
  153. public function unset_parameter($name) {
  154. unset($this->parameters[$name]);
  155. }
  156. /**
  157. * The request parameters, sorted and concatenated into a normalized string.
  158. * @return string
  159. */
  160. public function get_signable_parameters() {
  161. // Grab all parameters
  162. $params = $this->parameters;
  163. // Remove oauth_signature if present
  164. // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
  165. if (isset($params['oauth_signature'])) {
  166. unset($params['oauth_signature']);
  167. }
  168. return OAuthUtil::build_http_query($params);
  169. }
  170. /**
  171. * Returns the base string of this request
  172. *
  173. * The base string defined as the method, the url
  174. * and the parameters (normalized), each urlencoded
  175. * and the concated with &.
  176. */
  177. public function get_signature_base_string() {
  178. $parts = array(
  179. $this->get_normalized_http_method(),
  180. $this->get_normalized_http_url(),
  181. $this->get_signable_parameters()
  182. );
  183. $parts = OAuthUtil::urlencode_rfc3986($parts);
  184. return implode('&', $parts);
  185. }
  186. /**
  187. * just uppercases the http method
  188. */
  189. public function get_normalized_http_method() {
  190. return strtoupper($this->http_method);
  191. }
  192. /**
  193. * parses the url and rebuilds it to be
  194. * scheme://host/path
  195. */
  196. public function get_normalized_http_url() {
  197. $parts = parse_url($this->http_url);
  198. $port = @$parts['port'];
  199. $scheme = $parts['scheme'];
  200. $host = $parts['host'];
  201. $path = @$parts['path'];
  202. $port or $port = ($scheme == 'https') ? '443' : '80';
  203. if (($scheme == 'https' && $port != '443')
  204. || ($scheme == 'http' && $port != '80')) {
  205. $host = "$host:$port";
  206. }
  207. return "$scheme://$host$path";
  208. }
  209. /**
  210. * builds a url usable for a GET request
  211. */
  212. public function to_url() {
  213. $post_data = $this->to_postdata();
  214. $out = $this->get_normalized_http_url();
  215. if ($post_data) {
  216. $out .= '?'.$post_data;
  217. }
  218. return $out;
  219. }
  220. /**
  221. * builds the data one would send in a POST request
  222. */
  223. public function to_postdata() {
  224. return OAuthUtil::build_http_query($this->parameters);
  225. }
  226. /**
  227. * builds the Authorization: header
  228. */
  229. public function to_header($realm=null) {
  230. $first = true;
  231. if($realm) {
  232. $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
  233. $first = false;
  234. } else
  235. $out = 'Authorization: OAuth';
  236. $total = array();
  237. foreach ($this->parameters as $k => $v) {
  238. if (substr($k, 0, 5) != "oauth") continue;
  239. if (is_array($v)) {
  240. throw new OAuthException('Arrays not supported in headers');
  241. }
  242. $out .= ($first) ? ' ' : ',';
  243. $out .= OAuthUtil::urlencode_rfc3986($k) .
  244. '="' .
  245. OAuthUtil::urlencode_rfc3986($v) .
  246. '"';
  247. $first = false;
  248. }
  249. return $out;
  250. }
  251. public function __toString() {
  252. return $this->to_url();
  253. }
  254. public function sign_request($signature_method, $consumer, $token) {
  255. $this->set_parameter(
  256. "oauth_signature_method",
  257. $signature_method->get_name(),
  258. false
  259. );
  260. $signature = $this->build_signature($signature_method, $consumer, $token);
  261. $this->set_parameter("oauth_signature", $signature, false);
  262. }
  263. public function build_signature($signature_method, $consumer, $token) {
  264. $signature = $signature_method->build_signature($this, $consumer, $token);
  265. return $signature;
  266. }
  267. /**
  268. * util function: current timestamp
  269. */
  270. private static function generate_timestamp() {
  271. return time();
  272. }
  273. /**
  274. * util function: current nonce
  275. */
  276. private static function generate_nonce() {
  277. $mt = microtime();
  278. $rand = mt_rand();
  279. return md5($mt . $rand); // md5s look nicer than numbers
  280. }
  281. }
  282. class OAuthDataStore {
  283. function lookup_consumer($consumer_key) {
  284. // implement me
  285. }
  286. function lookup_token($consumer, $token_type, $token) {
  287. // implement me
  288. }
  289. function lookup_nonce($consumer, $token, $nonce, $timestamp) {
  290. // implement me
  291. }
  292. function new_request_token($consumer, $callback = null) {
  293. // return a new token attached to this consumer
  294. }
  295. function new_access_token($token, $consumer, $verifier = null) {
  296. // return a new access token attached to this consumer
  297. // for the user associated with this token if the request token
  298. // is authorized
  299. // should also invalidate the request token
  300. }
  301. }
  302. class OAuthUtil {
  303. public static function urlencode_rfc3986($input) {
  304. if (is_array($input)) {
  305. return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
  306. } else if (is_scalar($input)) {
  307. return str_replace(
  308. '+',
  309. ' ',
  310. str_replace('%7E', '~', rawurlencode($input))
  311. );
  312. } else {
  313. return '';
  314. }
  315. }
  316. // This decode function isn't taking into consideration the above
  317. // modifications to the encoding process. However, this method doesn't
  318. // seem to be used anywhere so leaving it as is.
  319. public static function urldecode_rfc3986($string) {
  320. return urldecode($string);
  321. }
  322. // Utility function for turning the Authorization: header into
  323. // parameters, has to do some unescaping
  324. // Can filter out any non-oauth parameters if needed (default behaviour)
  325. public static function split_header($header, $only_allow_oauth_parameters = true) {
  326. $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
  327. $offset = 0;
  328. $params = array();
  329. while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
  330. $match = $matches[0];
  331. $header_name = $matches[2][0];
  332. $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
  333. if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
  334. $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
  335. }
  336. $offset = $match[1] + strlen($match[0]);
  337. }
  338. if (isset($params['realm'])) {
  339. unset($params['realm']);
  340. }
  341. return $params;
  342. }
  343. // helper to try to sort out headers for people who aren't running apache
  344. public static function get_headers() {
  345. if (function_exists('apache_request_headers')) {
  346. // we need this to get the actual Authorization: header
  347. // because apache tends to tell us it doesn't exist
  348. $headers = apache_request_headers();
  349. // sanitize the output of apache_request_headers because
  350. // we always want the keys to be Cased-Like-This and arh()
  351. // returns the headers in the same case as they are in the
  352. // request
  353. $out = array();
  354. foreach( $headers AS $key => $value ) {
  355. $key = str_replace(
  356. " ",
  357. "-",
  358. ucwords(strtolower(str_replace("-", " ", $key)))
  359. );
  360. $out[$key] = $value;
  361. }
  362. } else {
  363. // otherwise we don't have apache and are just going to have to hope
  364. // that $_SERVER actually contains what we need
  365. $out = array();
  366. if( isset($_SERVER['CONTENT_TYPE']) )
  367. $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
  368. if( isset($_ENV['CONTENT_TYPE']) )
  369. $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
  370. foreach ($_SERVER as $key => $value) {
  371. if (substr($key, 0, 5) == "HTTP_") {
  372. // this is chaos, basically it is just there to capitalize the first
  373. // letter of every word that is not an initial HTTP and strip HTTP
  374. // code from przemek
  375. $key = str_replace(
  376. " ",
  377. "-",
  378. ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
  379. );
  380. $out[$key] = $value;
  381. }
  382. }
  383. }
  384. return $out;
  385. }
  386. // This function takes a input like a=b&a=c&d=e and returns the parsed
  387. // parameters like this
  388. // array('a' => array('b','c'), 'd' => 'e')
  389. public static function parse_parameters( $input ) {
  390. if (!isset($input) || !$input) return array();
  391. $pairs = explode('&', $input);
  392. $parsed_parameters = array();
  393. foreach ($pairs as $pair) {
  394. $split = explode('=', $pair, 2);
  395. $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
  396. $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
  397. if (isset($parsed_parameters[$parameter])) {
  398. // We have already recieved parameter(s) with this name, so add to the list
  399. // of parameters with this name
  400. if (is_scalar($parsed_parameters[$parameter])) {
  401. // This is the first duplicate, so transform scalar (string) into an array
  402. // so we can add the duplicates
  403. $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
  404. }
  405. $parsed_parameters[$parameter][] = $value;
  406. } else {
  407. $parsed_parameters[$parameter] = $value;
  408. }
  409. }
  410. return $parsed_parameters;
  411. }
  412. public static function build_http_query($params) {
  413. if (!$params) return '';
  414. // Urlencode both keys and values
  415. $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
  416. $values = OAuthUtil::urlencode_rfc3986(array_values($params));
  417. $params = array_combine($keys, $values);
  418. // Parameters are sorted by name, using lexicographical byte value ordering.
  419. // Ref: Spec: 9.1.1 (1)
  420. uksort($params, 'strcmp');
  421. $pairs = array();
  422. foreach ($params as $parameter => $value) {
  423. if (is_array($value)) {
  424. // If two or more parameters share the same name, they are sorted by their value
  425. // Ref: Spec: 9.1.1 (1)
  426. natsort($value);
  427. foreach ($value as $duplicate_value) {
  428. $pairs[] = $parameter . '=' . $duplicate_value;
  429. }
  430. } else {
  431. $pairs[] = $parameter . '=' . $value;
  432. }
  433. }
  434. // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
  435. // Each name-value pair is separated by an '&' character (ASCII code 38)
  436. return implode('&', $pairs);
  437. }
  438. }
  439. ?>