PageRenderTime 91ms CodeModel.GetById 41ms RepoModel.GetById 8ms app.codeStats 0ms

/trunk/lib/facebook/fbauthentication.php

https://bitbucket.org/pooshonk/esw
PHP | 934 lines | 505 code | 85 blank | 344 comment | 65 complexity | 98adbfe0d9f5176159c99c1900cc7fed MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. if (!function_exists('curl_init')) {
  3. throw new Exception('Facebook needs the CURL PHP extension.');
  4. }
  5. if (!function_exists('json_decode')) {
  6. throw new Exception('Facebook needs the JSON PHP extension.');
  7. }
  8. /**
  9. * Thrown when an API call returns an exception.
  10. *
  11. * @author Naitik Shah <naitik@facebook.com>
  12. */
  13. class FacebookApiException extends Exception
  14. {
  15. /**
  16. * The result from the API server that represents the exception information.
  17. */
  18. protected $result;
  19. /**
  20. * Make a new API Exception with the given result.
  21. *
  22. * @param Array $result the result from the API server
  23. */
  24. public function __construct($result) {
  25. $this->result = $result;
  26. $code = isset($result['error_code']) ? $result['error_code'] : 0;
  27. $msg = isset($result['error'])
  28. ? $result['error']['message'] : $result['error_msg'];
  29. parent::__construct($msg, $code);
  30. }
  31. /**
  32. * Return the associated result object returned by the API server.
  33. *
  34. * @returns Array the result from the API server
  35. */
  36. public function getResult() {
  37. return $this->result;
  38. }
  39. /**
  40. * Returns the associated type for the error. This will default to
  41. * 'Exception' when a type is not available.
  42. *
  43. * @return String
  44. */
  45. public function getType() {
  46. return
  47. isset($this->result['error']) && isset($this->result['error']['type'])
  48. ? $this->result['error']['type']
  49. : 'Exception';
  50. }
  51. /**
  52. * To make debugging easier.
  53. *
  54. * @returns String the string representation of the error
  55. */
  56. public function __toString() {
  57. $str = $this->getType() . ': ';
  58. if ($this->code != 0) {
  59. $str .= $this->code . ': ';
  60. }
  61. return $str . $this->message;
  62. }
  63. }
  64. /**
  65. * Provides access to the Facebook Platform.
  66. *
  67. * @author Naitik Shah <naitik@facebook.com>
  68. */
  69. class Facebook
  70. {
  71. /**
  72. * Version.
  73. */
  74. const VERSION = '2.1.1';
  75. /**
  76. * Default options for curl.
  77. */
  78. public static $CURL_OPTS = array(
  79. CURLOPT_CONNECTTIMEOUT => 10,
  80. CURLOPT_RETURNTRANSFER => true,
  81. CURLOPT_TIMEOUT => 60,
  82. CURLOPT_USERAGENT => 'facebook-php-2.0',
  83. CURLOPT_SSL_VERIFYPEER => false,
  84. CURLOPT_SSL_VERIFYHOST => 2
  85. );
  86. /**
  87. * List of query parameters that get automatically dropped when rebuilding
  88. * the current URL.
  89. */
  90. protected static $DROP_QUERY_PARAMS = array(
  91. 'session',
  92. 'signed_request',
  93. );
  94. /**
  95. * Maps aliases to Facebook domains.
  96. */
  97. public static $DOMAIN_MAP = array(
  98. 'api' => 'https://api.facebook.com/',
  99. 'api_read' => 'https://api-read.facebook.com/',
  100. 'graph' => 'https://graph.facebook.com/',
  101. 'www' => 'https://www.facebook.com/',
  102. );
  103. /**
  104. * The Application ID.
  105. */
  106. protected $appId;
  107. /**
  108. * The Application API Secret.
  109. */
  110. protected $apiSecret;
  111. /**
  112. * The active user session, if one is available.
  113. */
  114. protected $session;
  115. /**
  116. * The data from the signed_request token.
  117. */
  118. protected $signedRequest;
  119. /**
  120. * Indicates that we already loaded the session as best as we could.
  121. */
  122. protected $sessionLoaded = false;
  123. /**
  124. * Indicates if Cookie support should be enabled.
  125. */
  126. protected $cookieSupport = false;
  127. /**
  128. * Base domain for the Cookie.
  129. */
  130. protected $baseDomain = '';
  131. /**
  132. * Indicates if the CURL based @ syntax for file uploads is enabled.
  133. */
  134. protected $fileUploadSupport = false;
  135. /**
  136. * Initialize a Facebook Application.
  137. *
  138. * The configuration:
  139. * - appId: the application ID
  140. * - secret: the application secret
  141. * - cookie: (optional) boolean true to enable cookie support
  142. * - domain: (optional) domain for the cookie
  143. * - fileUpload: (optional) boolean indicating if file uploads are enabled
  144. *
  145. * @param Array $config the application configuration
  146. */
  147. public function __construct($config) {
  148. $this->setAppId($config['appId']);
  149. $this->setApiSecret($config['secret']);
  150. if (isset($config['cookie'])) {
  151. $this->setCookieSupport($config['cookie']);
  152. }
  153. if (isset($config['domain'])) {
  154. $this->setBaseDomain($config['domain']);
  155. }
  156. if (isset($config['fileUpload'])) {
  157. $this->setFileUploadSupport($config['fileUpload']);
  158. }
  159. }
  160. /**
  161. * Set the Application ID.
  162. *
  163. * @param String $appId the Application ID
  164. */
  165. public function setAppId($appId) {
  166. $this->appId = $appId;
  167. return $this;
  168. }
  169. /**
  170. * Get the Application ID.
  171. *
  172. * @return String the Application ID
  173. */
  174. public function getAppId() {
  175. return $this->appId;
  176. }
  177. /**
  178. * Set the API Secret.
  179. *
  180. * @param String $appId the API Secret
  181. */
  182. public function setApiSecret($apiSecret) {
  183. $this->apiSecret = $apiSecret;
  184. return $this;
  185. }
  186. /**
  187. * Get the API Secret.
  188. *
  189. * @return String the API Secret
  190. */
  191. public function getApiSecret() {
  192. return $this->apiSecret;
  193. }
  194. /**
  195. * Set the Cookie Support status.
  196. *
  197. * @param Boolean $cookieSupport the Cookie Support status
  198. */
  199. public function setCookieSupport($cookieSupport) {
  200. $this->cookieSupport = $cookieSupport;
  201. return $this;
  202. }
  203. /**
  204. * Get the Cookie Support status.
  205. *
  206. * @return Boolean the Cookie Support status
  207. */
  208. public function useCookieSupport() {
  209. return $this->cookieSupport;
  210. }
  211. /**
  212. * Set the base domain for the Cookie.
  213. *
  214. * @param String $domain the base domain
  215. */
  216. public function setBaseDomain($domain) {
  217. $this->baseDomain = $domain;
  218. return $this;
  219. }
  220. /**
  221. * Get the base domain for the Cookie.
  222. *
  223. * @return String the base domain
  224. */
  225. public function getBaseDomain() {
  226. return $this->baseDomain;
  227. }
  228. /**
  229. * Set the file upload support status.
  230. *
  231. * @param String $domain the base domain
  232. */
  233. public function setFileUploadSupport($fileUploadSupport) {
  234. $this->fileUploadSupport = $fileUploadSupport;
  235. return $this;
  236. }
  237. /**
  238. * Get the file upload support status.
  239. *
  240. * @return String the base domain
  241. */
  242. public function useFileUploadSupport() {
  243. return $this->fileUploadSupport;
  244. }
  245. /**
  246. * Get the data from a signed_request token
  247. *
  248. * @return String the base domain
  249. */
  250. public function getSignedRequest() {
  251. if (!$this->signedRequest) {
  252. if (isset($_REQUEST['signed_request'])) {
  253. $this->signedRequest = $this->parseSignedRequest(
  254. $_REQUEST['signed_request']);
  255. }
  256. }
  257. return $this->signedRequest;
  258. }
  259. /**
  260. * Set the Session.
  261. *
  262. * @param Array $session the session
  263. * @param Boolean $write_cookie indicate if a cookie should be written. this
  264. * value is ignored if cookie support has been disabled.
  265. */
  266. public function setSession($session=null, $write_cookie=true) {
  267. $session = $this->validateSessionObject($session);
  268. $this->sessionLoaded = true;
  269. $this->session = $session;
  270. if ($write_cookie) {
  271. $this->setCookieFromSession($session);
  272. }
  273. return $this;
  274. }
  275. /**
  276. * Get the session object. This will automatically look for a signed session
  277. * sent via the signed_request, Cookie or Query Parameters if needed.
  278. *
  279. * @return Array the session
  280. */
  281. public function getSession() {
  282. if (!$this->sessionLoaded) {
  283. $session = null;
  284. $write_cookie = true;
  285. // try loading session from signed_request in $_REQUEST
  286. $signedRequest = $this->getSignedRequest();
  287. if ($signedRequest) {
  288. // sig is good, use the signedRequest
  289. $session = $this->createSessionFromSignedRequest($signedRequest);
  290. }
  291. // try loading session from $_REQUEST
  292. if (!$session && isset($_REQUEST['session'])) {
  293. $session = json_decode(
  294. get_magic_quotes_gpc()
  295. ? stripslashes($_REQUEST['session'])
  296. : $_REQUEST['session'],
  297. true
  298. );
  299. $session = $this->validateSessionObject($session);
  300. }
  301. // try loading session from cookie if necessary
  302. if (!$session && $this->useCookieSupport()) {
  303. $cookieName = $this->getSessionCookieName();
  304. if (isset($_COOKIE[$cookieName])) {
  305. $session = array();
  306. parse_str(trim(
  307. get_magic_quotes_gpc()
  308. ? stripslashes($_COOKIE[$cookieName])
  309. : $_COOKIE[$cookieName],
  310. '"'
  311. ), $session);
  312. $session = $this->validateSessionObject($session);
  313. // write only if we need to delete a invalid session cookie
  314. $write_cookie = empty($session);
  315. }
  316. }
  317. $this->setSession($session, $write_cookie);
  318. }
  319. return $this->session;
  320. }
  321. /**
  322. * Get the UID from the session.
  323. *
  324. * @return String the UID if available
  325. */
  326. public function getUser() {
  327. $session = $this->getSession();
  328. return $session ? $session['uid'] : null;
  329. }
  330. /**
  331. * Gets a OAuth access token.
  332. *
  333. * @return String the access token
  334. */
  335. public function getAccessToken() {
  336. $session = $this->getSession();
  337. // either user session signed, or app signed
  338. if ($session) {
  339. return $session['access_token'];
  340. } else {
  341. return $this->getAppId() .'|'. $this->getApiSecret();
  342. }
  343. }
  344. /**
  345. * Get a Login URL for use with redirects. By default, full page redirect is
  346. * assumed. If you are using the generated URL with a window.open() call in
  347. * JavaScript, you can pass in display=popup as part of the $params.
  348. *
  349. * The parameters:
  350. * - next: the url to go to after a successful login
  351. * - cancel_url: the url to go to after the user cancels
  352. * - req_perms: comma separated list of requested extended perms
  353. * - display: can be "page" (default, full page) or "popup"
  354. *
  355. * @param Array $params provide custom parameters
  356. * @return String the URL for the login flow
  357. */
  358. public function getLoginUrl($params=array()) {
  359. $currentUrl = $this->getCurrentUrl();
  360. return $this->getUrl(
  361. 'www',
  362. 'login.php',
  363. array_merge(array(
  364. 'api_key' => $this->getAppId(),
  365. 'cancel_url' => $currentUrl,
  366. 'display' => 'page',
  367. 'fbconnect' => 1,
  368. 'next' => $currentUrl,
  369. 'return_session' => 1,
  370. 'session_version' => 3,
  371. 'v' => '1.0',
  372. ), $params)
  373. );
  374. }
  375. /**
  376. * Get a Logout URL suitable for use with redirects.
  377. *
  378. * The parameters:
  379. * - next: the url to go to after a successful logout
  380. *
  381. * @param Array $params provide custom parameters
  382. * @return String the URL for the logout flow
  383. */
  384. public function getLogoutUrl($params=array()) {
  385. return $this->getUrl(
  386. 'www',
  387. 'logout.php',
  388. array_merge(array(
  389. 'next' => $this->getCurrentUrl(),
  390. 'access_token' => $this->getAccessToken(),
  391. ), $params)
  392. );
  393. }
  394. /**
  395. * Get a login status URL to fetch the status from facebook.
  396. *
  397. * The parameters:
  398. * - ok_session: the URL to go to if a session is found
  399. * - no_session: the URL to go to if the user is not connected
  400. * - no_user: the URL to go to if the user is not signed into facebook
  401. *
  402. * @param Array $params provide custom parameters
  403. * @return String the URL for the logout flow
  404. */
  405. public function getLoginStatusUrl($params=array()) {
  406. return $this->getUrl(
  407. 'www',
  408. 'extern/login_status.php',
  409. array_merge(array(
  410. 'api_key' => $this->getAppId(),
  411. 'no_session' => $this->getCurrentUrl(),
  412. 'no_user' => $this->getCurrentUrl(),
  413. 'ok_session' => $this->getCurrentUrl(),
  414. 'session_version' => 3,
  415. ), $params)
  416. );
  417. }
  418. /**
  419. * Make an API call.
  420. *
  421. * @param Array $params the API call parameters
  422. * @return the decoded response
  423. */
  424. public function api(/* polymorphic */) {
  425. $args = func_get_args();
  426. if (is_array($args[0])) {
  427. return $this->_restserver($args[0]);
  428. } else {
  429. return call_user_func_array(array($this, '_graph'), $args);
  430. }
  431. }
  432. /**
  433. * Invoke the old restserver.php endpoint.
  434. *
  435. * @param Array $params method call object
  436. * @return the decoded response object
  437. * @throws FacebookApiException
  438. */
  439. protected function _restserver($params) {
  440. // generic application level parameters
  441. $params['api_key'] = $this->getAppId();
  442. $params['format'] = 'json-strings';
  443. $result = json_decode($this->_oauthRequest(
  444. $this->getApiUrl($params['method']),
  445. $params
  446. ), true);
  447. // results are returned, errors are thrown
  448. if (is_array($result) && isset($result['error_code'])) {
  449. throw new FacebookApiException($result);
  450. }
  451. return $result;
  452. }
  453. /**
  454. * Invoke the Graph API.
  455. *
  456. * @param String $path the path (required)
  457. * @param String $method the http method (default 'GET')
  458. * @param Array $params the query/post data
  459. * @return the decoded response object
  460. * @throws FacebookApiException
  461. */
  462. protected function _graph($path, $method='GET', $params=array()) {
  463. if (is_array($method) && empty($params)) {
  464. $params = $method;
  465. $method = 'GET';
  466. }
  467. $params['method'] = $method; // method override as we always do a POST
  468. $result = json_decode($this->_oauthRequest(
  469. $this->getUrl('graph', $path),
  470. $params
  471. ), true);
  472. // results are returned, errors are thrown
  473. if (is_array($result) && isset($result['error'])) {
  474. $e = new FacebookApiException($result);
  475. if ($e->getType() === 'OAuthException') {
  476. $this->setSession(null);
  477. }
  478. throw $e;
  479. }
  480. return $result;
  481. }
  482. /**
  483. * Make a OAuth Request
  484. *
  485. * @param String $path the path (required)
  486. * @param Array $params the query/post data
  487. * @return the decoded response object
  488. * @throws FacebookApiException
  489. */
  490. protected function _oauthRequest($url, $params) {
  491. if (!isset($params['access_token'])) {
  492. $params['access_token'] = $this->getAccessToken();
  493. }
  494. // json_encode all params values that are not strings
  495. foreach ($params as $key => $value) {
  496. if (!is_string($value)) {
  497. $params[$key] = json_encode($value);
  498. }
  499. }
  500. return $this->makeRequest($url, $params);
  501. }
  502. /**
  503. * Makes an HTTP request. This method can be overriden by subclasses if
  504. * developers want to do fancier things or use something other than curl to
  505. * make the request.
  506. *
  507. * @param String $url the URL to make the request to
  508. * @param Array $params the parameters to use for the POST body
  509. * @param CurlHandler $ch optional initialized curl handle
  510. * @return String the response text
  511. */
  512. protected function makeRequest($url, $params, $ch=null) {
  513. if (!$ch) {
  514. $ch = curl_init();
  515. }
  516. $opts = self::$CURL_OPTS;
  517. if ($this->useFileUploadSupport()) {
  518. $opts[CURLOPT_POSTFIELDS] = $params;
  519. } else {
  520. $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&');
  521. }
  522. $opts[CURLOPT_URL] = $url;
  523. // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
  524. // for 2 seconds if the server does not support this header.
  525. if (isset($opts[CURLOPT_HTTPHEADER])) {
  526. $existing_headers = $opts[CURLOPT_HTTPHEADER];
  527. $existing_headers[] = 'Expect:';
  528. $opts[CURLOPT_HTTPHEADER] = $existing_headers;
  529. } else {
  530. $opts[CURLOPT_HTTPHEADER] = array('Expect:');
  531. }
  532. curl_setopt_array($ch, $opts);
  533. $result = curl_exec($ch);
  534. if ($result === false) {
  535. $e = new FacebookApiException(array(
  536. 'error_code' => curl_errno($ch),
  537. 'error' => array(
  538. 'message' => curl_error($ch),
  539. 'type' => 'CurlException',
  540. ),
  541. ));
  542. curl_close($ch);
  543. throw $e;
  544. }
  545. curl_close($ch);
  546. return $result;
  547. }
  548. /**
  549. * The name of the Cookie that contains the session.
  550. *
  551. * @return String the cookie name
  552. */
  553. protected function getSessionCookieName() {
  554. return 'fbs_' . $this->getAppId();
  555. }
  556. /**
  557. * Set a JS Cookie based on the _passed in_ session. It does not use the
  558. * currently stored session -- you need to explicitly pass it in.
  559. *
  560. * @param Array $session the session to use for setting the cookie
  561. */
  562. protected function setCookieFromSession($session=null) {
  563. if (!$this->useCookieSupport()) {
  564. return;
  565. }
  566. $cookieName = $this->getSessionCookieName();
  567. $value = 'deleted';
  568. $expires = time() - 3600;
  569. $domain = $this->getBaseDomain();
  570. if ($session) {
  571. $value = '"' . http_build_query($session, null, '&') . '"';
  572. if (isset($session['base_domain'])) {
  573. $domain = $session['base_domain'];
  574. }
  575. $expires = $session['expires'];
  576. }
  577. // prepend dot if a domain is found
  578. if ($domain) {
  579. $domain = '.' . $domain;
  580. }
  581. // if an existing cookie is not set, we dont need to delete it
  582. if ($value == 'deleted' && empty($_COOKIE[$cookieName])) {
  583. return;
  584. }
  585. if (headers_sent()) {
  586. self::errorLog('Could not set cookie. Headers already sent.');
  587. // ignore for code coverage as we will never be able to setcookie in a CLI
  588. // environment
  589. // @codeCoverageIgnoreStart
  590. } else {
  591. setcookie($cookieName, $value, $expires, '/', $domain);
  592. }
  593. // @codeCoverageIgnoreEnd
  594. }
  595. /**
  596. * Validates a session_version=3 style session object.
  597. *
  598. * @param Array $session the session object
  599. * @return Array the session object if it validates, null otherwise
  600. */
  601. protected function validateSessionObject($session) {
  602. // make sure some essential fields exist
  603. if (is_array($session) &&
  604. isset($session['uid']) &&
  605. isset($session['access_token']) &&
  606. isset($session['sig'])) {
  607. // validate the signature
  608. $session_without_sig = $session;
  609. unset($session_without_sig['sig']);
  610. $expected_sig = self::generateSignature(
  611. $session_without_sig,
  612. $this->getApiSecret()
  613. );
  614. if ($session['sig'] != $expected_sig) {
  615. self::errorLog('Got invalid session signature in cookie.');
  616. $session = null;
  617. }
  618. // check expiry time
  619. } else {
  620. $session = null;
  621. }
  622. return $session;
  623. }
  624. /**
  625. * Returns something that looks like our JS session object from the
  626. * signed token's data
  627. *
  628. * TODO: Nuke this once the login flow uses OAuth2
  629. *
  630. * @param Array the output of getSignedRequest
  631. * @return Array Something that will work as a session
  632. */
  633. protected function createSessionFromSignedRequest($data) {
  634. if (!isset($data['oauth_token'])) {
  635. return null;
  636. }
  637. $session = array(
  638. 'uid' => $data['user_id'],
  639. 'access_token' => $data['oauth_token'],
  640. 'expires' => $data['expires'],
  641. );
  642. // put a real sig, so that validateSignature works
  643. $session['sig'] = self::generateSignature(
  644. $session,
  645. $this->getApiSecret()
  646. );
  647. return $session;
  648. }
  649. /**
  650. * Parses a signed_request and validates the signature.
  651. * Then saves it in $this->signed_data
  652. *
  653. * @param String A signed token
  654. * @param Boolean Should we remove the parts of the payload that
  655. * are used by the algorithm?
  656. * @return Array the payload inside it or null if the sig is wrong
  657. */
  658. protected function parseSignedRequest($signed_request) {
  659. list($encoded_sig, $payload) = explode('.', $signed_request, 2);
  660. // decode the data
  661. $sig = self::base64UrlDecode($encoded_sig);
  662. $data = json_decode(self::base64UrlDecode($payload), true);
  663. if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
  664. self::errorLog('Unknown algorithm. Expected HMAC-SHA256');
  665. return null;
  666. }
  667. // check sig
  668. $expected_sig = hash_hmac('sha256', $payload,
  669. $this->getApiSecret(), $raw = true);
  670. if ($sig !== $expected_sig) {
  671. self::errorLog('Bad Signed JSON signature!');
  672. return null;
  673. }
  674. return $data;
  675. }
  676. /**
  677. * Build the URL for api given parameters.
  678. *
  679. * @param $method String the method name.
  680. * @return String the URL for the given parameters
  681. */
  682. protected function getApiUrl($method) {
  683. static $READ_ONLY_CALLS =
  684. array('admin.getallocation' => 1,
  685. 'admin.getappproperties' => 1,
  686. 'admin.getbannedusers' => 1,
  687. 'admin.getlivestreamvialink' => 1,
  688. 'admin.getmetrics' => 1,
  689. 'admin.getrestrictioninfo' => 1,
  690. 'application.getpublicinfo' => 1,
  691. 'auth.getapppublickey' => 1,
  692. 'auth.getsession' => 1,
  693. 'auth.getsignedpublicsessiondata' => 1,
  694. 'comments.get' => 1,
  695. 'connect.getunconnectedfriendscount' => 1,
  696. 'dashboard.getactivity' => 1,
  697. 'dashboard.getcount' => 1,
  698. 'dashboard.getglobalnews' => 1,
  699. 'dashboard.getnews' => 1,
  700. 'dashboard.multigetcount' => 1,
  701. 'dashboard.multigetnews' => 1,
  702. 'data.getcookies' => 1,
  703. 'events.get' => 1,
  704. 'events.getmembers' => 1,
  705. 'fbml.getcustomtags' => 1,
  706. 'feed.getappfriendstories' => 1,
  707. 'feed.getregisteredtemplatebundlebyid' => 1,
  708. 'feed.getregisteredtemplatebundles' => 1,
  709. 'fql.multiquery' => 1,
  710. 'fql.query' => 1,
  711. 'friends.arefriends' => 1,
  712. 'friends.get' => 1,
  713. 'friends.getappusers' => 1,
  714. 'friends.getlists' => 1,
  715. 'friends.getmutualfriends' => 1,
  716. 'gifts.get' => 1,
  717. 'groups.get' => 1,
  718. 'groups.getmembers' => 1,
  719. 'intl.gettranslations' => 1,
  720. 'links.get' => 1,
  721. 'notes.get' => 1,
  722. 'notifications.get' => 1,
  723. 'pages.getinfo' => 1,
  724. 'pages.isadmin' => 1,
  725. 'pages.isappadded' => 1,
  726. 'pages.isfan' => 1,
  727. 'permissions.checkavailableapiaccess' => 1,
  728. 'permissions.checkgrantedapiaccess' => 1,
  729. 'photos.get' => 1,
  730. 'photos.getalbums' => 1,
  731. 'photos.gettags' => 1,
  732. 'profile.getinfo' => 1,
  733. 'profile.getinfooptions' => 1,
  734. 'stream.get' => 1,
  735. 'stream.getcomments' => 1,
  736. 'stream.getfilters' => 1,
  737. 'users.getinfo' => 1,
  738. 'users.getloggedinuser' => 1,
  739. 'users.getstandardinfo' => 1,
  740. 'users.hasapppermission' => 1,
  741. 'users.isappuser' => 1,
  742. 'users.isverified' => 1,
  743. 'video.getuploadlimits' => 1);
  744. $name = 'api';
  745. if (isset($READ_ONLY_CALLS[strtolower($method)])) {
  746. $name = 'api_read';
  747. }
  748. return self::getUrl($name, 'restserver.php');
  749. }
  750. /**
  751. * Build the URL for given domain alias, path and parameters.
  752. *
  753. * @param $name String the name of the domain
  754. * @param $path String optional path (without a leading slash)
  755. * @param $params Array optional query parameters
  756. * @return String the URL for the given parameters
  757. */
  758. protected function getUrl($name, $path='', $params=array()) {
  759. $url = self::$DOMAIN_MAP[$name];
  760. if ($path) {
  761. if ($path[0] === '/') {
  762. $path = substr($path, 1);
  763. }
  764. $url .= $path;
  765. }
  766. if ($params) {
  767. $url .= '?' . http_build_query($params, null, '&');
  768. }
  769. return $url;
  770. }
  771. /**
  772. * Returns the Current URL, stripping it of known FB parameters that should
  773. * not persist.
  774. *
  775. * @return String the current URL
  776. */
  777. protected function getCurrentUrl() {
  778. $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'
  779. ? 'https://'
  780. : 'http://';
  781. $currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  782. $parts = parse_url($currentUrl);
  783. // drop known fb params
  784. $query = '';
  785. if (!empty($parts['query'])) {
  786. $params = array();
  787. parse_str($parts['query'], $params);
  788. foreach(self::$DROP_QUERY_PARAMS as $key) {
  789. unset($params[$key]);
  790. }
  791. if (!empty($params)) {
  792. $query = '?' . http_build_query($params, null, '&');
  793. }
  794. }
  795. // use port if non default
  796. $port =
  797. isset($parts['port']) &&
  798. (($protocol === 'http://' && $parts['port'] !== 80) ||
  799. ($protocol === 'https://' && $parts['port'] !== 443))
  800. ? ':' . $parts['port'] : '';
  801. // rebuild
  802. return $protocol . $parts['host'] . $port . $parts['path'] . $query;
  803. }
  804. /**
  805. * Generate a signature for the given params and secret.
  806. *
  807. * @param Array $params the parameters to sign
  808. * @param String $secret the secret to sign with
  809. * @return String the generated signature
  810. */
  811. protected static function generateSignature($params, $secret) {
  812. // work with sorted data
  813. ksort($params);
  814. // generate the base string
  815. $base_string = '';
  816. foreach($params as $key => $value) {
  817. $base_string .= $key . '=' . $value;
  818. }
  819. $base_string .= $secret;
  820. return md5($base_string);
  821. }
  822. /**
  823. * Prints to the error log if you aren't in command line mode.
  824. *
  825. * @param String log message
  826. */
  827. protected static function errorLog($msg) {
  828. // disable error log if we are running in a CLI environment
  829. // @codeCoverageIgnoreStart
  830. if (php_sapi_name() != 'cli') {
  831. error_log($msg);
  832. }
  833. // uncomment this if you want to see the errors on the page
  834. // print 'error_log: '.$msg."\n";
  835. // @codeCoverageIgnoreEnd
  836. }
  837. /**
  838. * Base64 encoding that doesn't need to be urlencode()ed.
  839. * Exactly the same as base64_encode except it uses
  840. * - instead of +
  841. * _ instead of /
  842. *
  843. * @param String base64UrlEncodeded string
  844. */
  845. protected static function base64UrlDecode($input) {
  846. return base64_decode(strtr($input, '-_', '+/'));
  847. }
  848. }
  849. ?>