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

/wp-content/themes/lifeline/framework/helpers/codebird.php

https://bitbucket.org/acipriani/madeinapulia.com
PHP | 1025 lines | 831 code | 41 blank | 153 comment | 44 complexity | 72db7a7d4ecfce5c6918e71bfc8af6d3 MD5 | raw file
Possible License(s): GPL-3.0, MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0, Apache-2.0
  1. <?php
  2. //namespace Codebird;
  3. /**
  4. * A Twitter library in PHP.
  5. *
  6. * @package codebird
  7. * @version 2.4.1
  8. * @author J.M. <me@mynetx.net>
  9. * @copyright 2010-2013 J.M. <me@mynetx.net>
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. /**
  25. * Define constants
  26. */
  27. $constants = explode(' ', 'OBJECT ARRAY JSON');
  28. foreach ($constants as $i => $id) {
  29. $id = 'CODEBIRD_RETURNFORMAT_' . $id;
  30. defined($id) or define($id, $i);
  31. }
  32. $constants = array(
  33. 'CURLE_SSL_CERTPROBLEM' => 58,
  34. 'CURLE_SSL_CACERT' => 60,
  35. 'CURLE_SSL_CACERT_BADFILE' => 77,
  36. 'CURLE_SSL_CRL_BADFILE' => 82,
  37. 'CURLE_SSL_ISSUER_ERROR' => 83
  38. );
  39. foreach ($constants as $id => $i) {
  40. defined($id) or define($id, $i);
  41. }
  42. unset($constants);
  43. unset($i);
  44. unset($id);
  45. /**
  46. * A Twitter library in PHP.
  47. *
  48. * @package codebird
  49. * @subpackage codebird-php
  50. */
  51. class SH_Codebird
  52. {
  53. /**
  54. * The current singleton instance
  55. */
  56. private static $_instance = null;
  57. /**
  58. * The OAuth consumer key of your registered app
  59. */
  60. protected static $_oauth_consumer_key = null;
  61. /**
  62. * The corresponding consumer secret
  63. */
  64. protected static $_oauth_consumer_secret = null;
  65. /**
  66. * The app-only bearer token. Used to authorize app-only requests
  67. */
  68. protected static $_oauth_bearer_token = null;
  69. /**
  70. * The API endpoint to use
  71. */
  72. protected static $_endpoint = 'https://api.twitter.com/1.1/';
  73. /**
  74. * The API endpoint to use for OAuth requests
  75. */
  76. protected static $_endpoint_oauth = 'https://api.twitter.com/';
  77. /**
  78. * The Request or access token. Used to sign requests
  79. */
  80. protected $_oauth_token = null;
  81. /**
  82. * The corresponding request or access token secret
  83. */
  84. protected $_oauth_token_secret = null;
  85. /**
  86. * The format of data to return from API calls
  87. */
  88. protected $_return_format = CODEBIRD_RETURNFORMAT_OBJECT;
  89. /**
  90. * The file formats that Twitter accepts as image uploads
  91. */
  92. protected $_supported_media_files = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
  93. /**
  94. * The current Codebird version
  95. */
  96. protected $_version = '2.4.1';
  97. /**
  98. * Returns singleton class instance
  99. * Always use this method unless you're working with multiple authenticated users at once
  100. *
  101. * @return Codebird The instance
  102. */
  103. public static function getInstance()
  104. {
  105. if (self::$_instance == null) {
  106. self::$_instance = new self;
  107. }
  108. return self::$_instance;
  109. }
  110. /**
  111. * Sets the OAuth consumer key and secret (App key)
  112. *
  113. * @param string $key OAuth consumer key
  114. * @param string $secret OAuth consumer secret
  115. *
  116. * @return void
  117. */
  118. public static function setConsumerKey($key, $secret)
  119. {
  120. self::$_oauth_consumer_key = $key;
  121. self::$_oauth_consumer_secret = $secret;
  122. }
  123. /**
  124. * Sets the OAuth2 app-only auth bearer token
  125. *
  126. * @param string $token OAuth2 bearer token
  127. *
  128. * @return void
  129. */
  130. public static function setBearerToken($token)
  131. {
  132. self::$_oauth_bearer_token = $token;
  133. }
  134. /**
  135. * Gets the current Codebird version
  136. *
  137. * @return string The version number
  138. */
  139. public function getVersion()
  140. {
  141. return $this->_version;
  142. }
  143. /**
  144. * Sets the OAuth request or access token and secret (User key)
  145. *
  146. * @param string $token OAuth request or access token
  147. * @param string $secret OAuth request or access token secret
  148. *
  149. * @return void
  150. */
  151. public function setToken($token, $secret)
  152. {
  153. $this->_oauth_token = $token;
  154. $this->_oauth_token_secret = $secret;
  155. }
  156. /**
  157. * Sets the format for API replies
  158. *
  159. * @param int $return_format One of these:
  160. * CODEBIRD_RETURNFORMAT_OBJECT (default)
  161. * CODEBIRD_RETURNFORMAT_ARRAY
  162. *
  163. * @return void
  164. */
  165. public function setReturnFormat($return_format)
  166. {
  167. $this->_return_format = $return_format;
  168. }
  169. /**
  170. * Main API handler working on any requests you issue
  171. *
  172. * @param string $fn The member function you called
  173. * @param array $params The parameters you sent along
  174. *
  175. * @return mixed The API reply encoded in the set return_format
  176. */
  177. public function __call($fn, $params)
  178. {
  179. // parse parameters
  180. $apiparams = array();
  181. if (count($params) > 0) {
  182. if (is_array($params[0])) {
  183. $apiparams = $params[0];
  184. } else {
  185. parse_str($params[0], $apiparams);
  186. // remove auto-added slashes if on magic quotes steroids
  187. if (get_magic_quotes_gpc()) {
  188. foreach($apiparams as $key => $value) {
  189. if (is_array($value)) {
  190. $apiparams[$key] = array_map('stripslashes', $value);
  191. } else {
  192. $apiparams[$key] = stripslashes($value);
  193. }
  194. }
  195. }
  196. }
  197. }
  198. // stringify null and boolean parameters
  199. foreach ($apiparams as $key => $value) {
  200. if (! is_scalar($value)) {
  201. continue;
  202. }
  203. if (is_null($value)) {
  204. $apiparams[$key] = 'null';
  205. } elseif (is_bool($value)) {
  206. $apiparams[$key] = $value ? 'true' : 'false';
  207. }
  208. }
  209. $app_only_auth = false;
  210. if (count($params) > 1) {
  211. $app_only_auth = !! $params[1];
  212. }
  213. // map function name to API method
  214. $method = '';
  215. // replace _ by /
  216. $path = explode('_', $fn);
  217. for ($i = 0; $i < count($path); $i++) {
  218. if ($i > 0) {
  219. $method .= '/';
  220. }
  221. $method .= $path[$i];
  222. }
  223. // undo replacement for URL parameters
  224. $url_parameters_with_underscore = array('screen_name');
  225. foreach ($url_parameters_with_underscore as $param) {
  226. $param = strtoupper($param);
  227. $replacement_was = str_replace('_', '/', $param);
  228. $method = str_replace($replacement_was, $param, $method);
  229. }
  230. // replace AA by URL parameters
  231. $method_template = $method;
  232. $match = array();
  233. if (preg_match('/[A-Z_]{2,}/', $method, $match)) {
  234. foreach ($match as $param) {
  235. $param_l = strtolower($param);
  236. $method_template = str_replace($param, ':' . $param_l, $method_template);
  237. if (!isset($apiparams[$param_l])) {
  238. for ($i = 0; $i < 26; $i++) {
  239. $method_template = str_replace(chr(65 + $i), '_' . chr(97 + $i), $method_template);
  240. }
  241. throw new \Exception(
  242. 'To call the templated method "' . $method_template
  243. . '", specify the parameter value for "' . $param_l . '".'
  244. );
  245. }
  246. $method = str_replace($param, $apiparams[$param_l], $method);
  247. unset($apiparams[$param_l]);
  248. }
  249. }
  250. // replace A-Z by _a-z
  251. for ($i = 0; $i < 26; $i++) {
  252. $method = str_replace(chr(65 + $i), '_' . chr(97 + $i), $method);
  253. $method_template = str_replace(chr(65 + $i), '_' . chr(97 + $i), $method_template);
  254. }
  255. $httpmethod = $this->_detectMethod($method_template, $apiparams);
  256. $multipart = $this->_detectMultipart($method_template);
  257. return $this->_callApi(
  258. $httpmethod,
  259. $method,
  260. $method_template,
  261. $apiparams,
  262. $multipart,
  263. $app_only_auth
  264. );
  265. }
  266. /**
  267. * Uncommon API methods
  268. */
  269. /**
  270. * Gets the OAuth authenticate URL for the current request token
  271. *
  272. * @return string The OAuth authenticate URL
  273. */
  274. public function oauth_authenticate($force_login = NULL, $screen_name = NULL)
  275. {
  276. if ($this->_oauth_token == null) {
  277. throw new \Exception('To get the authenticate URL, the OAuth token must be set.');
  278. }
  279. $url = self::$_endpoint_oauth . 'oauth/authenticate?oauth_token=' . $this->_url($this->_oauth_token);
  280. if ($force_login) {
  281. $url .= "&force_login=1";
  282. }
  283. if ($screen_name) {
  284. $url .= "&screen_name=" . $screen_name;
  285. }
  286. return $url;
  287. }
  288. /**
  289. * Gets the OAuth authorize URL for the current request token
  290. *
  291. * @return string The OAuth authorize URL
  292. */
  293. public function oauth_authorize($force_login = NULL, $screen_name = NULL)
  294. {
  295. if ($this->_oauth_token == null) {
  296. throw new \Exception('To get the authorize URL, the OAuth token must be set.');
  297. }
  298. $url = self::$_endpoint_oauth . 'oauth/authorize?oauth_token=' . $this->_url($this->_oauth_token);
  299. if ($force_login) {
  300. $url .= "&force_login=1";
  301. }
  302. if ($screen_name) {
  303. $url .= "&screen_name=" . $screen_name;
  304. }
  305. return $url;
  306. }
  307. /**
  308. * Gets the OAuth bearer token
  309. *
  310. * @return string The OAuth bearer token
  311. */
  312. public function oauth2_token()
  313. {
  314. if (! function_exists('curl_init')) {
  315. throw new \Exception('To make API requests, the PHP curl extension must be available.');
  316. }
  317. if (self::$_oauth_consumer_key == null) {
  318. throw new \Exception('To obtain a bearer token, the consumer key must be set.');
  319. }
  320. $ch = false;
  321. $post_fields = array(
  322. 'grant_type' => 'client_credentials'
  323. );
  324. $url = self::$_endpoint_oauth . 'oauth2/token';
  325. $ch = curl_init($url);
  326. curl_setopt($ch, CURLOPT_POST, 1);
  327. curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
  328. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  329. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
  330. curl_setopt($ch, CURLOPT_HEADER, 1);
  331. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  332. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  333. curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');
  334. curl_setopt($ch, CURLOPT_USERPWD, self::$_oauth_consumer_key . ':' . self::$_oauth_consumer_secret);
  335. curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  336. 'Expect:'
  337. ));
  338. $reply = curl_exec($ch);
  339. // certificate validation results
  340. $validation_result = curl_errno($ch);
  341. if (in_array(
  342. $validation_result,
  343. array(
  344. CURLE_SSL_CERTPROBLEM,
  345. CURLE_SSL_CACERT,
  346. CURLE_SSL_CACERT_BADFILE,
  347. CURLE_SSL_CRL_BADFILE,
  348. CURLE_SSL_ISSUER_ERROR
  349. )
  350. )
  351. ) {
  352. throw new \Exception('Error ' . $validation_result . ' while validating the Twitter API certificate.');
  353. }
  354. $httpstatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  355. $reply = $this->_parseApiReply('oauth2/token', $reply);
  356. switch ($this->_return_format) {
  357. case CODEBIRD_RETURNFORMAT_ARRAY:
  358. $reply['httpstatus'] = $httpstatus;
  359. if ($httpstatus == 200) {
  360. self::setBearerToken($reply['access_token']);
  361. }
  362. break;
  363. case CODEBIRD_RETURNFORMAT_JSON:
  364. if ($httpstatus == 200) {
  365. $parsed = json_decode($reply);
  366. self::setBearerToken($parsed->access_token);
  367. }
  368. break;
  369. case CODEBIRD_RETURNFORMAT_OBJECT:
  370. $reply->httpstatus = $httpstatus;
  371. if ($httpstatus == 200) {
  372. self::setBearerToken($reply->access_token);
  373. }
  374. break;
  375. }
  376. return $reply;
  377. }
  378. /**
  379. * Signing helpers
  380. */
  381. /**
  382. * URL-encodes the given data
  383. *
  384. * @param mixed $data
  385. *
  386. * @return mixed The encoded data
  387. */
  388. private function _url($data)
  389. {
  390. if (is_array($data)) {
  391. return array_map(array(
  392. $this,
  393. '_url'
  394. ), $data);
  395. } elseif (is_scalar($data)) {
  396. return str_replace(array(
  397. '+',
  398. '!',
  399. '*',
  400. "'",
  401. '(',
  402. ')'
  403. ), array(
  404. ' ',
  405. '%21',
  406. '%2A',
  407. '%27',
  408. '%28',
  409. '%29'
  410. ), rawurlencode($data));
  411. } else {
  412. return '';
  413. }
  414. }
  415. /**
  416. * Gets the base64-encoded SHA1 hash for the given data
  417. *
  418. * @param string $data The data to calculate the hash from
  419. *
  420. * @return string The hash
  421. */
  422. private function _sha1($data)
  423. {
  424. if (self::$_oauth_consumer_secret == null) {
  425. throw new \Exception('To generate a hash, the consumer secret must be set.');
  426. }
  427. if (!function_exists('hash_hmac')) {
  428. throw new \Exception('To generate a hash, the PHP hash extension must be available.');
  429. }
  430. return base64_encode(hash_hmac('sha1', $data, self::$_oauth_consumer_secret . '&'
  431. . ($this->_oauth_token_secret != null ? $this->_oauth_token_secret : ''), true));
  432. }
  433. /**
  434. * Generates a (hopefully) unique random string
  435. *
  436. * @param int optional $length The length of the string to generate
  437. *
  438. * @return string The random string
  439. */
  440. protected function _nonce($length = 8)
  441. {
  442. if ($length < 1) {
  443. throw new \Exception('Invalid nonce length.');
  444. }
  445. return substr(md5(microtime(true)), 0, $length);
  446. }
  447. /**
  448. * Generates an OAuth signature
  449. *
  450. * @param string $httpmethod Usually either 'GET' or 'POST' or 'DELETE'
  451. * @param string $method The API method to call
  452. * @param array optional $params The API call parameters, associative
  453. *
  454. * @return string Authorization HTTP header
  455. */
  456. protected function _sign($httpmethod, $method, $params = array())
  457. {
  458. if (self::$_oauth_consumer_key == null) {
  459. throw new \Exception('To generate a signature, the consumer key must be set.');
  460. }
  461. $sign_params = array(
  462. 'consumer_key' => self::$_oauth_consumer_key,
  463. 'version' => '1.0',
  464. 'timestamp' => time(),
  465. 'nonce' => $this->_nonce(),
  466. 'signature_method' => 'HMAC-SHA1'
  467. );
  468. $sign_base_params = array();
  469. foreach ($sign_params as $key => $value) {
  470. $sign_base_params['oauth_' . $key] = $this->_url($value);
  471. }
  472. if ($this->_oauth_token != null) {
  473. $sign_base_params['oauth_token'] = $this->_url($this->_oauth_token);
  474. }
  475. $oauth_params = $sign_base_params;
  476. foreach ($params as $key => $value) {
  477. $sign_base_params[$key] = $this->_url($value);
  478. }
  479. ksort($sign_base_params);
  480. $sign_base_string = '';
  481. foreach ($sign_base_params as $key => $value) {
  482. $sign_base_string .= $key . '=' . $value . '&';
  483. }
  484. $sign_base_string = substr($sign_base_string, 0, -1);
  485. $signature = $this->_sha1($httpmethod . '&' . $this->_url($method) . '&' . $this->_url($sign_base_string));
  486. $params = array_merge($oauth_params, array(
  487. 'oauth_signature' => $signature
  488. ));
  489. ksort($params);
  490. $authorization = 'Authorization: OAuth ';
  491. foreach ($params as $key => $value) {
  492. $authorization .= $key . '="' . $this->_url($value) . '", ';
  493. }
  494. return substr($authorization, 0, -2);
  495. }
  496. /**
  497. * Detects HTTP method to use for API call
  498. *
  499. * @param string $method The API method to call
  500. * @param array $params The parameters to send along
  501. *
  502. * @return string The HTTP method that should be used
  503. */
  504. protected function _detectMethod($method, $params)
  505. {
  506. // multi-HTTP method endpoints
  507. switch($method) {
  508. case 'account/settings':
  509. $method = count($params) > 0 ? $method . '__post' : $method;
  510. break;
  511. }
  512. $httpmethods = array();
  513. $httpmethods['GET'] = array(
  514. // Timelines
  515. 'statuses/mentions_timeline',
  516. 'statuses/user_timeline',
  517. 'statuses/home_timeline',
  518. 'statuses/retweets_of_me',
  519. // Tweets
  520. 'statuses/retweets/:id',
  521. 'statuses/show/:id',
  522. 'statuses/oembed',
  523. // Search
  524. 'search/tweets',
  525. // Direct Messages
  526. 'direct_messages',
  527. 'direct_messages/sent',
  528. 'direct_messages/show',
  529. // Friends & Followers
  530. 'friendships/no_retweets/ids',
  531. 'friends/ids',
  532. 'followers/ids',
  533. 'friendships/lookup',
  534. 'friendships/incoming',
  535. 'friendships/outgoing',
  536. 'friendships/show',
  537. 'friends/list',
  538. 'followers/list',
  539. // Users
  540. 'account/settings',
  541. 'account/verify_credentials',
  542. 'blocks/list',
  543. 'blocks/ids',
  544. 'users/lookup',
  545. 'users/show',
  546. 'users/search',
  547. 'users/contributees',
  548. 'users/contributors',
  549. 'users/profile_banner',
  550. // Suggested Users
  551. 'users/suggestions/:slug',
  552. 'users/suggestions',
  553. 'users/suggestions/:slug/members',
  554. // Favorites
  555. 'favorites/list',
  556. // Lists
  557. 'lists/list',
  558. 'lists/statuses',
  559. 'lists/memberships',
  560. 'lists/subscribers',
  561. 'lists/subscribers/show',
  562. 'lists/members/show',
  563. 'lists/members',
  564. 'lists/show',
  565. 'lists/subscriptions',
  566. // Saved searches
  567. 'saved_searches/list',
  568. 'saved_searches/show/:id',
  569. // Places & Geo
  570. 'geo/id/:place_id',
  571. 'geo/reverse_geocode',
  572. 'geo/search',
  573. 'geo/similar_places',
  574. // Trends
  575. 'trends/place',
  576. 'trends/available',
  577. 'trends/closest',
  578. // OAuth
  579. 'oauth/authenticate',
  580. 'oauth/authorize',
  581. // Help
  582. 'help/configuration',
  583. 'help/languages',
  584. 'help/privacy',
  585. 'help/tos',
  586. 'application/rate_limit_status'
  587. );
  588. $httpmethods['POST'] = array(
  589. // Tweets
  590. 'statuses/destroy/:id',
  591. 'statuses/update',
  592. 'statuses/retweet/:id',
  593. 'statuses/update_with_media',
  594. // Direct Messages
  595. 'direct_messages/destroy',
  596. 'direct_messages/new',
  597. // Friends & Followers
  598. 'friendships/create',
  599. 'friendships/destroy',
  600. 'friendships/update',
  601. // Users
  602. 'account/settings__post',
  603. 'account/update_delivery_device',
  604. 'account/update_profile',
  605. 'account/update_profile_background_image',
  606. 'account/update_profile_colors',
  607. 'account/update_profile_image',
  608. 'blocks/create',
  609. 'blocks/destroy',
  610. 'account/update_profile_banner',
  611. 'account/remove_profile_banner',
  612. // Favorites
  613. 'favorites/destroy',
  614. 'favorites/create',
  615. // Lists
  616. 'lists/members/destroy',
  617. 'lists/subscribers/create',
  618. 'lists/subscribers/destroy',
  619. 'lists/members/create_all',
  620. 'lists/members/create',
  621. 'lists/destroy',
  622. 'lists/update',
  623. 'lists/create',
  624. 'lists/members/destroy_all',
  625. // Saved Searches
  626. 'saved_searches/create',
  627. 'saved_searches/destroy/:id',
  628. // Places & Geo
  629. 'geo/place',
  630. // Spam Reporting
  631. 'users/report_spam',
  632. // OAuth
  633. 'oauth/access_token',
  634. 'oauth/request_token',
  635. 'oauth2/token',
  636. 'oauth2/invalidate_token'
  637. );
  638. foreach ($httpmethods as $httpmethod => $methods) {
  639. if (in_array($method, $methods)) {
  640. return $httpmethod;
  641. }
  642. }
  643. throw new \Exception('Can\'t find HTTP method to use for "' . $method . '".');
  644. }
  645. /**
  646. * Detects if API call should use multipart/form-data
  647. *
  648. * @param string $method The API method to call
  649. *
  650. * @return bool Whether the method should be sent as multipart
  651. */
  652. protected function _detectMultipart($method)
  653. {
  654. $multiparts = array(
  655. // Tweets
  656. 'statuses/update_with_media',
  657. // Users
  658. 'account/update_profile_background_image',
  659. 'account/update_profile_image',
  660. 'account/update_profile_banner'
  661. );
  662. return in_array($method, $multiparts);
  663. }
  664. /**
  665. * Detect filenames in upload parameters,
  666. * build multipart request from upload params
  667. *
  668. * @param string $method The API method to call
  669. * @param array $params The parameters to send along
  670. *
  671. * @return void
  672. */
  673. protected function _buildMultipart($method, $params)
  674. {
  675. // well, files will only work in multipart methods
  676. if (! $this->_detectMultipart($method)) {
  677. return;
  678. }
  679. // only check specific parameters
  680. $possible_files = array(
  681. // Tweets
  682. 'statuses/update_with_media' => 'media[]',
  683. // Accounts
  684. 'account/update_profile_background_image' => 'image',
  685. 'account/update_profile_image' => 'image',
  686. 'account/update_profile_banner' => 'banner'
  687. );
  688. // method might have files?
  689. if (! in_array($method, array_keys($possible_files))) {
  690. return;
  691. }
  692. $possible_files = explode(' ', $possible_files[$method]);
  693. $multipart_border = '--------------------' . $this->_nonce();
  694. $multipart_request = '';
  695. foreach ($params as $key => $value) {
  696. // is it an array?
  697. if (is_array($value)) {
  698. throw new \Exception('Using URL-encoded parameters is not supported for uploading media.');
  699. continue;
  700. }
  701. $multipart_request .=
  702. '--' . $multipart_border . "\r\n"
  703. . 'Content-Disposition: form-data; name="' . $key . '"';
  704. // check for filenames
  705. if (in_array($key, $possible_files)) {
  706. if (// is it a file, a readable one?
  707. @file_exists($value)
  708. && @is_readable($value)
  709. // is it a valid image?
  710. && $data = @getimagesize($value)
  711. ) {
  712. if (// is it a supported image format?
  713. in_array($data[2], $this->_supported_media_files)
  714. ) {
  715. // try to read the file
  716. ob_start();
  717. readfile($value);
  718. $data = ob_get_contents();
  719. ob_end_clean();
  720. if (strlen($data) == 0) {
  721. continue;
  722. }
  723. $value = $data;
  724. }
  725. }
  726. /*
  727. $multipart_request .=
  728. "\r\nContent-Transfer-Encoding: base64";
  729. $value = base64_encode($value);
  730. */
  731. }
  732. $multipart_request .=
  733. "\r\n\r\n" . $value . "\r\n";
  734. }
  735. $multipart_request .= '--' . $multipart_border . '--';
  736. return $multipart_request;
  737. }
  738. /**
  739. * Builds the complete API endpoint url
  740. *
  741. * @param string $method The API method to call
  742. * @param string $method_template The API method template to call
  743. *
  744. * @return string The URL to send the request to
  745. */
  746. protected function _getEndpoint($method, $method_template)
  747. {
  748. if (substr($method, 0, 5) == 'oauth') {
  749. $url = self::$_endpoint_oauth . $method;
  750. } else {
  751. $url = self::$_endpoint . $method . '.json';
  752. }
  753. return $url;
  754. }
  755. /**
  756. * Calls the API using cURL
  757. *
  758. * @param string $httpmethod The HTTP method to use for making the request
  759. * @param string $method The API method to call
  760. * @param string $method_template The templated API method to call
  761. * @param array optional $params The parameters to send along
  762. * @param bool optional $multipart Whether to use multipart/form-data
  763. * @param bool optional $app_only_auth Whether to use app-only bearer authentication
  764. *
  765. * @return mixed The API reply, encoded in the set return_format
  766. */
  767. protected function _callApi($httpmethod, $method, $method_template, $params = array(), $multipart = false, $app_only_auth = false)
  768. {
  769. if (! function_exists('curl_init')) {
  770. throw new \Exception('To make API requests, the PHP curl extension must be available.');
  771. }
  772. $url = $this->_getEndpoint($method, $method_template);
  773. $ch = false;
  774. if ($httpmethod == 'GET') {
  775. $url_with_params = $url;
  776. if (count($params) > 0) {
  777. $url_with_params .= '?' . http_build_query($params);
  778. }
  779. $authorization = $this->_sign($httpmethod, $url, $params);
  780. $ch = curl_init($url_with_params);
  781. } else {
  782. if ($multipart) {
  783. $authorization = $this->_sign($httpmethod, $url, array());
  784. $params = $this->_buildMultipart($method_template, $params);
  785. } else {
  786. $authorization = $this->_sign($httpmethod, $url, $params);
  787. $params = http_build_query($params);
  788. }
  789. $ch = curl_init($url);
  790. curl_setopt($ch, CURLOPT_POST, 1);
  791. curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  792. }
  793. if ($app_only_auth) {
  794. if (self::$_oauth_consumer_key == null) {
  795. throw new \Exception('To make an app-only auth API request, the consumer key must be set.');
  796. }
  797. // automatically fetch bearer token, if necessary
  798. if (self::$_oauth_bearer_token == null) {
  799. $this->oauth2_token();
  800. }
  801. $authorization = 'Authorization: Bearer ' . self::$_oauth_bearer_token;
  802. }
  803. $request_headers = array();
  804. if (isset($authorization)) {
  805. $request_headers[] = $authorization;
  806. $request_headers[] = 'Expect:';
  807. }
  808. if ($multipart) {
  809. $first_newline = strpos($params, "\r\n");
  810. $multipart_boundary = substr($params, 2, $first_newline - 2);
  811. $request_headers[] = 'Content-Length: ' . strlen($params);
  812. $request_headers[] = 'Content-Type: multipart/form-data; boundary='
  813. . $multipart_boundary;
  814. }
  815. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  816. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
  817. curl_setopt($ch, CURLOPT_HEADER, 1);
  818. curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
  819. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  820. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  821. curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');
  822. $reply = curl_exec($ch);
  823. // certificate validation results
  824. $validation_result = curl_errno($ch);
  825. if (in_array(
  826. $validation_result,
  827. array(
  828. CURLE_SSL_CERTPROBLEM,
  829. CURLE_SSL_CACERT,
  830. CURLE_SSL_CACERT_BADFILE,
  831. CURLE_SSL_CRL_BADFILE,
  832. CURLE_SSL_ISSUER_ERROR
  833. )
  834. )
  835. ) {
  836. throw new \Exception('Error ' . $validation_result . ' while validating the Twitter API certificate.');
  837. }
  838. $httpstatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  839. $reply = $this->_parseApiReply($method_template, $reply);
  840. if ($this->_return_format == CODEBIRD_RETURNFORMAT_OBJECT) {
  841. $reply->httpstatus = $httpstatus;
  842. } elseif ($this->_return_format == CODEBIRD_RETURNFORMAT_ARRAY) {
  843. $reply['httpstatus'] = $httpstatus;
  844. }
  845. return $reply;
  846. }
  847. /**
  848. * Parses the API reply to encode it in the set return_format
  849. *
  850. * @param string $method The method that has been called
  851. * @param string $reply The actual reply, JSON-encoded or URL-encoded
  852. *
  853. * @return array|object The parsed reply
  854. */
  855. protected function _parseApiReply($method, $reply)
  856. {
  857. // split headers and body
  858. $headers = array();
  859. $reply = explode("\r\n\r\n", $reply, 4);
  860. // check if using proxy
  861. if (substr($reply[0], 0, 35) === 'HTTP/1.1 200 Connection Established') {
  862. array_shift($reply);
  863. } elseif (count($reply) > 2) {
  864. $headers = array_shift($reply);
  865. $reply = array(
  866. $headers,
  867. implode("\r\n", $reply)
  868. );
  869. }
  870. $headers_array = explode("\r\n", $reply[0]);
  871. foreach ($headers_array as $header) {
  872. $header_array = explode(': ', $header, 2);
  873. $key = $header_array[0];
  874. $value = '';
  875. if (count($header_array) > 1) {
  876. $value = $header_array[1];
  877. }
  878. $headers[$key] = $value;
  879. }
  880. if (count($reply) > 1) {
  881. $reply = $reply[1];
  882. } else {
  883. $reply = '';
  884. }
  885. $need_array = $this->_return_format == CODEBIRD_RETURNFORMAT_ARRAY;
  886. if ($reply == '[]') {
  887. switch ($this->_return_format) {
  888. case CODEBIRD_RETURNFORMAT_ARRAY:
  889. return array();
  890. case CODEBIRD_RETURNFORMAT_JSON:
  891. return '{}';
  892. case CODEBIRD_RETURNFORMAT_OBJECT:
  893. return new \stdClass;
  894. }
  895. }
  896. $parsed = array();
  897. if (! $parsed = json_decode($reply, $need_array)) {
  898. if ($reply) {
  899. if (stripos($reply, '<' . '?xml version="1.0" encoding="UTF-8"?' . '>') === 0) {
  900. // we received XML./ since this only happens for errors,
  901. // don't perform a full decoding
  902. preg_match('/<request>(.*)<\/request>/', $reply, $request);
  903. preg_match('/<error>(.*)<\/error>/', $reply, $error);
  904. $parsed['request'] = htmlspecialchars_decode($request[1]);
  905. $parsed['error'] = htmlspecialchars_decode($error[1]);
  906. } else {
  907. // assume query format
  908. $reply = explode('&', $reply);
  909. foreach ($reply as $element) {
  910. if (stristr($element, '=')) {
  911. list($key, $value) = explode('=', $element);
  912. $parsed[$key] = $value;
  913. } else {
  914. $parsed['message'] = $element;
  915. }
  916. }
  917. }
  918. }
  919. $reply = json_encode($parsed);
  920. }
  921. switch ($this->_return_format) {
  922. case CODEBIRD_RETURNFORMAT_ARRAY:
  923. return $parsed;
  924. case CODEBIRD_RETURNFORMAT_JSON:
  925. return $reply;
  926. case CODEBIRD_RETURNFORMAT_OBJECT:
  927. return (object) $parsed;
  928. }
  929. return $parsed;
  930. }
  931. }
  932. ?>