ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
base_facebook.php
Go to the documentation of this file.
1 <?php
18 if (!function_exists('curl_init')) {
19  throw new Exception('Facebook needs the CURL PHP extension.');
20 }
21 if (!function_exists('json_decode')) {
22  throw new Exception('Facebook needs the JSON PHP extension.');
23 }
24 
31 {
35  protected $result;
36 
42  public function __construct($result) {
43  $this->result = $result;
44 
45  $code = isset($result['error_code']) ? $result['error_code'] : 0;
46 
47  if (isset($result['error_description'])) {
48  // OAuth 2.0 Draft 10 style
49  $msg = $result['error_description'];
50  } else if (isset($result['error']) && is_array($result['error'])) {
51  // OAuth 2.0 Draft 00 style
52  $msg = $result['error']['message'];
53  } else if (isset($result['error_msg'])) {
54  // Rest server style
55  $msg = $result['error_msg'];
56  } else {
57  $msg = 'Unknown Error. Check getResult()';
58  }
59 
60  parent::__construct($msg, $code);
61  }
62 
68  public function getResult() {
69  return $this->result;
70  }
71 
78  public function getType() {
79  if (isset($this->result['error'])) {
80  $error = $this->result['error'];
81  if (is_string($error)) {
82  // OAuth 2.0 Draft 10 style
83  return $error;
84  } else if (is_array($error)) {
85  // OAuth 2.0 Draft 00 style
86  if (isset($error['type'])) {
87  return $error['type'];
88  }
89  }
90  }
91 
92  return 'Exception';
93  }
94 
100  public function __toString() {
101  $str = $this->getType() . ': ';
102  if ($this->code != 0) {
103  $str .= $this->code . ': ';
104  }
105  return $str . $this->message;
106  }
107 }
108 
118 abstract class BaseFacebook
119 {
123  const VERSION = '3.2.2';
124 
128  const SIGNED_REQUEST_ALGORITHM = 'HMAC-SHA256';
129 
133  public static $CURL_OPTS = array(
134  CURLOPT_CONNECTTIMEOUT => 10,
135  CURLOPT_RETURNTRANSFER => true,
136  CURLOPT_TIMEOUT => 60,
137  CURLOPT_USERAGENT => 'facebook-php-3.2',
138  );
139 
143  public static $DOMAIN_MAP = array(
144  'api' => 'https://api.facebook.com/',
145  'api_video' => 'https://api-video.facebook.com/',
146  'api_read' => 'https://api-read.facebook.com/',
147  'graph' => 'https://graph.facebook.com/',
148  'graph_video' => 'https://graph-video.facebook.com/',
149  'www' => 'https://www.facebook.com/',
150  );
151 
157  protected $appId;
158 
164  protected $appSecret;
165 
171  protected $user;
172 
176  protected $signedRequest;
177 
181  protected $state;
182 
189  protected $accessToken = null;
190 
196  protected $fileUploadSupport = false;
197 
203  protected $trustForwarded = false;
204 
215  public function __construct($config) {
216  $this->setAppId($config['appId']);
217  $this->setAppSecret($config['secret']);
218  if (isset($config['fileUpload'])) {
219  $this->setFileUploadSupport($config['fileUpload']);
220  }
221  if (isset($config['trustForwarded']) && $config['trustForwarded']) {
222  $this->trustForwarded = true;
223  }
224  $state = $this->getPersistentData('state');
225  if (!empty($state)) {
226  $this->state = $state;
227  }
228  }
229 
236  public function setAppId($appId) {
237  $this->appId = $appId;
238  return $this;
239  }
240 
246  public function getAppId() {
247  return $this->appId;
248  }
249 
257  public function setApiSecret($apiSecret) {
258  $this->setAppSecret($apiSecret);
259  return $this;
260  }
261 
268  public function setAppSecret($appSecret) {
269  $this->appSecret = $appSecret;
270  return $this;
271  }
272 
279  public function getApiSecret() {
280  return $this->getAppSecret();
281  }
282 
288  public function getAppSecret() {
289  return $this->appSecret;
290  }
291 
298  public function setFileUploadSupport($fileUploadSupport) {
299  $this->fileUploadSupport = $fileUploadSupport;
300  return $this;
301  }
302 
308  public function getFileUploadSupport() {
309  return $this->fileUploadSupport;
310  }
311 
319  public function useFileUploadSupport() {
320  return $this->getFileUploadSupport();
321  }
322 
331  public function setAccessToken($access_token) {
332  $this->accessToken = $access_token;
333  return $this;
334  }
335 
341  public function setExtendedAccessToken() {
342  try {
343  // need to circumvent json_decode by calling _oauthRequest
344  // directly, since response isn't JSON format
345  $access_token_response = $this->_oauthRequest(
346  $this->getUrl('graph', '/oauth/access_token'),
347  $params = array(
348  'client_id' => $this->getAppId(),
349  'client_secret' => $this->getAppSecret(),
350  'grant_type' => 'fb_exchange_token',
351  'fb_exchange_token' => $this->getAccessToken(),
352  )
353  );
354  }
355  catch (FacebookApiException $e) {
356  // most likely that user very recently revoked authorization
357  // In any event, we don't have an access token, so say so
358  return false;
359  }
360 
361  if (empty($access_token_response)) {
362  return false;
363  }
364 
365  $response_params = array();
366  parse_str($access_token_response, $response_params);
367 
368  if (!isset($response_params['access_token'])) {
369  return false;
370  }
371 
372  $this->destroySession();
373 
374  $this->setPersistentData(
375  'access_token', $response_params['access_token']
376  );
377  }
378 
388  public function getAccessToken() {
389  if ($this->accessToken !== null) {
390  // we've done this already and cached it. Just return.
391  return $this->accessToken;
392  }
393 
394  // first establish access token to be the application
395  // access token, in case we navigate to the /oauth/access_token
396  // endpoint, where SOME access token is required
397  $this->setAccessToken($this->getApplicationAccessToken());
398  $user_access_token = $this->getUserAccessToken();
399  if ($user_access_token) {
400  $this->setAccessToken($user_access_token);
401  }
402 
403  return $this->accessToken;
404  }
405 
416  protected function getUserAccessToken() {
417  // first, consider a signed request if it's supplied
418  // if there is a signed request, then it alone determines
419  // the access token
420  $signed_request = $this->getSignedRequest();
421  if ($signed_request) {
422  // apps.facebook.com hands the access_token in the signed_request
423  if (array_key_exists('oauth_token', $signed_request)) {
424  $access_token = $signed_request['oauth_token'];
425  $this->setPersistentData('access_token', $access_token);
426  return $access_token;
427  }
428 
429  // the JS SDK puts a code in with the redirect_uri of ''
430  if (array_key_exists('code', $signed_request)) {
431  $code = $signed_request['code'];
432  if ($code && $code == $this->getPersistentData('code')) {
433  // short-circuit if the code we have is the same as the one presented
434  return $this->getPersistentData('access_token');
435  }
436 
437  $access_token = $this->getAccessTokenFromCode($code, '');
438  if ($access_token) {
439  $this->setPersistentData('code', $code);
440  $this->setPersistentData('access_token', $access_token);
441  return $access_token;
442  }
443  }
444 
445  // signed request states there's no access token, so anything
446  // stored should be cleared
447  $this->clearAllPersistentData();
448  return false; // respect the signed request's data, even
449  // if there's an authorization code or something else
450  }
451 
452  $code = $this->getCode();
453  if ($code && $code != $this->getPersistentData('code')) {
454  $access_token = $this->getAccessTokenFromCode($code);
455  if ($access_token) {
456  $this->setPersistentData('code', $code);
457  $this->setPersistentData('access_token', $access_token);
458  return $access_token;
459  }
460 
461  // code was bogus, so everything based on it should be invalidated
462  $this->clearAllPersistentData();
463  return false;
464  }
465 
466  // as a fallback, just return whatever is in the persistent
467  // store, knowing nothing explicit (signed request, authorization
468  // code, etc.) was present to shadow it (or we saw a code in $_REQUEST,
469  // but it's the same as what's in the persistent store)
470  return $this->getPersistentData('access_token');
471  }
472 
479  public function getSignedRequest() {
480  if (!$this->signedRequest) {
481  if (!empty($_REQUEST['signed_request'])) {
482  $this->signedRequest = $this->parseSignedRequest(
483  $_REQUEST['signed_request']);
484  } else if (!empty($_COOKIE[$this->getSignedRequestCookieName()])) {
485  $this->signedRequest = $this->parseSignedRequest(
486  $_COOKIE[$this->getSignedRequestCookieName()]);
487  }
488  }
489  return $this->signedRequest;
490  }
491 
498  public function getUser() {
499  if ($this->user !== null) {
500  // we've already determined this and cached the value
501  return $this->user;
502  }
503 
504  return $this->user = $this->getUserFromAvailableData();
505  }
506 
515  protected function getUserFromAvailableData() {
516  // if a signed request is supplied, then it solely determines
517  // who the user is
518  $signed_request = $this->getSignedRequest();
519  if ($signed_request) {
520  if (array_key_exists('user_id', $signed_request)) {
521  $user = $signed_request['user_id'];
522 
523  if($user != $this->getPersistentData('user_id')){
524  $this->clearAllPersistentData();
525  }
526 
527  $this->setPersistentData('user_id', $signed_request['user_id']);
528  return $user;
529  }
530 
531  // if the signed request didn't present a user id, then invalidate
532  // all entries in any persistent store
533  $this->clearAllPersistentData();
534  return 0;
535  }
536 
537  $user = $this->getPersistentData('user_id', $default = 0);
538  $persisted_access_token = $this->getPersistentData('access_token');
539 
540  // use access_token to fetch user id if we have a user access_token, or if
541  // the cached access token has changed
542  $access_token = $this->getAccessToken();
543  if ($access_token &&
544  $access_token != $this->getApplicationAccessToken() &&
545  !($user && $persisted_access_token == $access_token)) {
546  $user = $this->getUserFromAccessToken();
547  if ($user) {
548  $this->setPersistentData('user_id', $user);
549  } else {
550  $this->clearAllPersistentData();
551  }
552  }
553 
554  return $user;
555  }
556 
569  public function getLoginUrl($params=array()) {
570  $this->establishCSRFTokenState();
571  $currentUrl = $this->getCurrentUrl();
572 
573  // if 'scope' is passed as an array, convert to comma separated list
574  $scopeParams = isset($params['scope']) ? $params['scope'] : null;
575  if ($scopeParams && is_array($scopeParams)) {
576  $params['scope'] = implode(',', $scopeParams);
577  }
578 
579  return $this->getUrl(
580  'www',
581  'dialog/oauth',
582  array_merge(array(
583  'client_id' => $this->getAppId(),
584  'redirect_uri' => $currentUrl, // possibly overwritten
585  'state' => $this->state),
586  $params));
587  }
588 
598  public function getLogoutUrl($params=array()) {
599  return $this->getUrl(
600  'www',
601  'logout.php',
602  array_merge(array(
603  'next' => $this->getCurrentUrl(),
604  'access_token' => $this->getUserAccessToken(),
605  ), $params)
606  );
607  }
608 
620  public function getLoginStatusUrl($params=array()) {
621  return $this->getUrl(
622  'www',
623  'extern/login_status.php',
624  array_merge(array(
625  'api_key' => $this->getAppId(),
626  'no_session' => $this->getCurrentUrl(),
627  'no_user' => $this->getCurrentUrl(),
628  'ok_session' => $this->getCurrentUrl(),
629  'session_version' => 3,
630  ), $params)
631  );
632  }
633 
639  public function api(/* polymorphic */) {
640  $args = func_get_args();
641  if (is_array($args[0])) {
642  return $this->_restserver($args[0]);
643  } else {
644  return call_user_func_array(array($this, '_graph'), $args);
645  }
646  }
647 
657  protected function getSignedRequestCookieName() {
658  return 'fbsr_'.$this->getAppId();
659  }
660 
668  protected function getMetadataCookieName() {
669  return 'fbm_'.$this->getAppId();
670  }
671 
680  protected function getCode() {
681  if (isset($_REQUEST['code'])) {
682  if ($this->state !== null &&
683  isset($_REQUEST['state']) &&
684  $this->state === $_REQUEST['state']) {
685 
686  // CSRF state has done its job, so clear it
687  $this->state = null;
688  $this->clearPersistentData('state');
689  return $_REQUEST['code'];
690  } else {
691  self::errorLog('CSRF state token does not match one provided. ' . $this->state . '!=' . $_REQUEST['state']);
692  return false;
693  }
694  }
695 
696  return false;
697  }
698 
709  protected function getUserFromAccessToken() {
710  try {
711  $user_info = $this->api('/me');
712  return $user_info['id'];
713  } catch (FacebookApiException $e) {
714  return 0;
715  }
716  }
717 
725  protected function getApplicationAccessToken() {
726  return $this->appId.'|'.$this->appSecret;
727  }
728 
734  protected function establishCSRFTokenState() {
735  if ($this->state === null) {
736  $this->state = md5(uniqid(mt_rand(), true));
737  $this->setPersistentData('state', $this->state);
738  }
739  }
740 
753  protected function getAccessTokenFromCode($code, $redirect_uri = null) {
754  if (empty($code)) {
755  return false;
756  }
757 
758  if ($redirect_uri === null) {
759  $redirect_uri = $this->getCurrentUrl();
760  }
761 
762  try {
763  // need to circumvent json_decode by calling _oauthRequest
764  // directly, since response isn't JSON format
765  $access_token_response =
766  $this->_oauthRequest(
767  $this->getUrl('graph', '/oauth/access_token'),
768  $params = array('client_id' => $this->getAppId(),
769  'client_secret' => $this->getAppSecret(),
770  'redirect_uri' => $redirect_uri,
771  'code' => $code));
772  } catch (FacebookApiException $e) {
773  // most likely that user very recently revoked authorization.
774  // In any event, we don't have an access token, so say so.
775  self::errorLog($e->getMessage());
776  return false;
777  }
778 
779  if (empty($access_token_response)) {
780  self::errorlog('No access token response');
781  return false;
782  }
783 
784  $response_params = json_decode($access_token_response, true);
785  if (!isset($response_params['access_token'])) {
786  self::errorlog('No access token in response. ' . $access_token_response);
787  return false;
788  }
789 
790  return $response_params['access_token'];
791  }
792 
801  protected function _restserver($params) {
802  // generic application level parameters
803  $params['api_key'] = $this->getAppId();
804  $params['format'] = 'json-strings';
805 
806  $result = json_decode($this->_oauthRequest(
807  $this->getApiUrl($params['method']),
808  $params
809  ), true);
810 
811  // results are returned, errors are thrown
812  if (is_array($result) && isset($result['error_code'])) {
813  $this->throwAPIException($result);
814  // @codeCoverageIgnoreStart
815  }
816  // @codeCoverageIgnoreEnd
817 
818  $method = strtolower($params['method']);
819  if ($method === 'auth.expiresession' ||
820  $method === 'auth.revokeauthorization') {
821  $this->destroySession();
822  }
823 
824  return $result;
825  }
826 
835  protected function isVideoPost($path, $method = 'GET') {
836  if ($method == 'POST' && preg_match("/^(\/)(.+)(\/)(videos)$/", $path)) {
837  return true;
838  }
839  return false;
840  }
841 
852  protected function _graph($path, $method = 'GET', $params = array()) {
853  if (is_array($method) && empty($params)) {
854  $params = $method;
855  $method = 'GET';
856  }
857  $params['method'] = $method; // method override as we always do a POST
858 
859  if ($this->isVideoPost($path, $method)) {
860  $domainKey = 'graph_video';
861  } else {
862  $domainKey = 'graph';
863  }
864 
865  $result = json_decode($this->_oauthRequest(
866  $this->getUrl($domainKey, $path),
867  $params
868  ), true);
869 
870  // results are returned, errors are thrown
871  if (is_array($result) && isset($result['error'])) {
872  $this->throwAPIException($result);
873  // @codeCoverageIgnoreStart
874  }
875  // @codeCoverageIgnoreEnd
876 
877  return $result;
878  }
879 
889  protected function _oauthRequest($url, $params) {
890  if (!isset($params['access_token'])) {
891  $params['access_token'] = $this->getAccessToken();
892  }
893 
894  // json_encode all params values that are not strings
895  foreach ($params as $key => $value) {
896  if (!is_string($value)) {
897  $params[$key] = json_encode($value);
898  }
899  }
900 
901  return $this->makeRequest($url, $params);
902  }
903 
915  protected function makeRequest($url, $params, $ch=null) {
916  if (!$ch) {
917  $ch = curl_init();
918  }
919 
920  $opts = self::$CURL_OPTS;
921  if ($this->getFileUploadSupport()) {
922  $opts[CURLOPT_POSTFIELDS] = $params;
923  } else {
924  $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&');
925  }
926  $opts[CURLOPT_URL] = $url;
927 
928  // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
929  // for 2 seconds if the server does not support this header
930  if (isset($opts[CURLOPT_HTTPHEADER])) {
931  $existing_headers = $opts[CURLOPT_HTTPHEADER];
932  $existing_headers[] = 'Expect:';
933  $opts[CURLOPT_HTTPHEADER] = $existing_headers;
934  } else {
935  $opts[CURLOPT_HTTPHEADER] = array('Expect:');
936  }
937 
938  curl_setopt_array($ch, $opts);
939  $result = curl_exec($ch);
940 
941  if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT
942  self::errorLog('Invalid or no certificate authority found, '.
943  'using bundled information');
944  curl_setopt($ch, CURLOPT_CAINFO,
945  dirname(__FILE__) . '/fb_ca_chain_bundle.crt');
946  $result = curl_exec($ch);
947  }
948 
949  // With dual stacked DNS responses, it's possible for a server to
950  // have IPv6 enabled but not have IPv6 connectivity. If this is
951  // the case, curl will try IPv4 first and if that fails, then it will
952  // fall back to IPv6 and the error EHOSTUNREACH is returned by the
953  // operating system
954  if ($result === false && empty($opts[CURLOPT_IPRESOLVE])) {
955  $matches = array();
956  $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
957  if (preg_match($regex, curl_error($ch), $matches)) {
958  if (strlen(@inet_pton($matches[1])) === 16) {
959  self::errorLog('Invalid IPv6 configuration on server, '.
960  'Please disable or get native IPv6 on your server.');
961  self::$CURL_OPTS[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
962  curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
963  $result = curl_exec($ch);
964  }
965  }
966  }
967 
968  if ($result === false) {
969  $e = new FacebookApiException(array(
970  'error_code' => curl_errno($ch),
971  'error' => array(
972  'message' => curl_error($ch),
973  'type' => 'CurlException',
974  ),
975  ));
976  curl_close($ch);
977  throw $e;
978  }
979  curl_close($ch);
980  return $result;
981  }
982 
989  protected function parseSignedRequest($signed_request) {
990  list($encoded_sig, $payload) = explode('.', $signed_request, 2);
991 
992  // decode the data
993  $sig = self::base64UrlDecode($encoded_sig);
994  $data = json_decode(self::base64UrlDecode($payload), true);
995 
996  if (strtoupper($data['algorithm']) !== self::SIGNED_REQUEST_ALGORITHM) {
997  self::errorLog(
998  'Unknown algorithm. Expected ' . self::SIGNED_REQUEST_ALGORITHM);
999  return null;
1000  }
1001 
1002  // check sig
1003  $expected_sig = hash_hmac('sha256', $payload,
1004  $this->getAppSecret(), $raw = true);
1005  if ($sig !== $expected_sig) {
1006  self::errorLog('Bad Signed JSON signature!');
1007  return null;
1008  }
1009 
1010  return $data;
1011  }
1012 
1019  protected function makeSignedRequest($data) {
1020  if (!is_array($data)) {
1021  throw new InvalidArgumentException(
1022  'makeSignedRequest expects an array. Got: ' . print_r($data, true));
1023  }
1024  $data['algorithm'] = self::SIGNED_REQUEST_ALGORITHM;
1025  $data['issued_at'] = time();
1026  $json = json_encode($data);
1027  $b64 = self::base64UrlEncode($json);
1028  $raw_sig = hash_hmac('sha256', $b64, $this->getAppSecret(), $raw = true);
1029  $sig = self::base64UrlEncode($raw_sig);
1030  return $sig.'.'.$b64;
1031  }
1032 
1039  protected function getApiUrl($method) {
1040  static $READ_ONLY_CALLS =
1041  array('admin.getallocation' => 1,
1042  'admin.getappproperties' => 1,
1043  'admin.getbannedusers' => 1,
1044  'admin.getlivestreamvialink' => 1,
1045  'admin.getmetrics' => 1,
1046  'admin.getrestrictioninfo' => 1,
1047  'application.getpublicinfo' => 1,
1048  'auth.getapppublickey' => 1,
1049  'auth.getsession' => 1,
1050  'auth.getsignedpublicsessiondata' => 1,
1051  'comments.get' => 1,
1052  'connect.getunconnectedfriendscount' => 1,
1053  'dashboard.getactivity' => 1,
1054  'dashboard.getcount' => 1,
1055  'dashboard.getglobalnews' => 1,
1056  'dashboard.getnews' => 1,
1057  'dashboard.multigetcount' => 1,
1058  'dashboard.multigetnews' => 1,
1059  'data.getcookies' => 1,
1060  'events.get' => 1,
1061  'events.getmembers' => 1,
1062  'fbml.getcustomtags' => 1,
1063  'feed.getappfriendstories' => 1,
1064  'feed.getregisteredtemplatebundlebyid' => 1,
1065  'feed.getregisteredtemplatebundles' => 1,
1066  'fql.multiquery' => 1,
1067  'fql.query' => 1,
1068  'friends.arefriends' => 1,
1069  'friends.get' => 1,
1070  'friends.getappusers' => 1,
1071  'friends.getlists' => 1,
1072  'friends.getmutualfriends' => 1,
1073  'gifts.get' => 1,
1074  'groups.get' => 1,
1075  'groups.getmembers' => 1,
1076  'intl.gettranslations' => 1,
1077  'links.get' => 1,
1078  'notes.get' => 1,
1079  'notifications.get' => 1,
1080  'pages.getinfo' => 1,
1081  'pages.isadmin' => 1,
1082  'pages.isappadded' => 1,
1083  'pages.isfan' => 1,
1084  'permissions.checkavailableapiaccess' => 1,
1085  'permissions.checkgrantedapiaccess' => 1,
1086  'photos.get' => 1,
1087  'photos.getalbums' => 1,
1088  'photos.gettags' => 1,
1089  'profile.getinfo' => 1,
1090  'profile.getinfooptions' => 1,
1091  'stream.get' => 1,
1092  'stream.getcomments' => 1,
1093  'stream.getfilters' => 1,
1094  'users.getinfo' => 1,
1095  'users.getloggedinuser' => 1,
1096  'users.getstandardinfo' => 1,
1097  'users.hasapppermission' => 1,
1098  'users.isappuser' => 1,
1099  'users.isverified' => 1,
1100  'video.getuploadlimits' => 1);
1101  $name = 'api';
1102  if (isset($READ_ONLY_CALLS[strtolower($method)])) {
1103  $name = 'api_read';
1104  } else if (strtolower($method) == 'video.upload') {
1105  $name = 'api_video';
1106  }
1107  return self::getUrl($name, 'restserver.php');
1108  }
1109 
1119  protected function getUrl($name, $path='', $params=array()) {
1120  $url = self::$DOMAIN_MAP[$name];
1121  if ($path) {
1122  if ($path[0] === '/') {
1123  $path = substr($path, 1);
1124  }
1125  $url .= $path;
1126  }
1127  if ($params) {
1128  $url .= '?' . http_build_query($params, null, '&');
1129  }
1130 
1131  return $url;
1132  }
1133 
1134  protected function getHttpHost() {
1135  if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
1136  return $_SERVER['HTTP_X_FORWARDED_HOST'];
1137  }
1138  return $_SERVER['HTTP_HOST'];
1139  }
1140 
1141  protected function getHttpProtocol() {
1142  if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
1143  if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
1144  return 'https';
1145  }
1146  return 'http';
1147  }
1148  /*apache + variants specific way of checking for https*/
1149  if (isset($_SERVER['HTTPS']) &&
1150  ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {
1151  return 'https';
1152  }
1153  /*nginx way of checking for https*/
1154  if (isset($_SERVER['SERVER_PORT']) &&
1155  ($_SERVER['SERVER_PORT'] === '443')) {
1156  return 'https';
1157  }
1158  return 'http';
1159  }
1160 
1164  protected function getBaseDomain() {
1165  // The base domain is stored in the metadata cookie if not we fallback
1166  // to the current hostname
1167  $metadata = $this->getMetadataCookie();
1168  if (array_key_exists('base_domain', $metadata) &&
1169  !empty($metadata['base_domain'])) {
1170  return trim($metadata['base_domain'], '.');
1171  }
1172  return $this->getHttpHost();
1173  }
1174 
1181  protected function getCurrentUrl() {
1182  $protocol = $this->getHttpProtocol() . '://';
1183  $host = $this->getHttpHost();
1184  $currentUrl = $protocol.$host.$_SERVER['REQUEST_URI'];
1185  $parts = parse_url($currentUrl);
1186 
1187  // use port if non default
1188  $port =
1189  isset($parts['port']) &&
1190  (($protocol === 'http://' && $parts['port'] !== 80) ||
1191  ($protocol === 'https://' && $parts['port'] !== 443))
1192  ? ':' . $parts['port'] : '';
1193 
1194  // rebuild
1195  return $protocol . $parts['host'] . $port . $parts['path'];
1196  }
1197 
1206  protected function throwAPIException($result) {
1207  $e = new FacebookApiException($result);
1208  switch ($e->getType()) {
1209  // OAuth 2.0 Draft 00 style
1210  case 'OAuthException':
1211  // OAuth 2.0 Draft 10 style
1212  case 'invalid_token':
1213  // REST server errors are just Exceptions
1214  case 'Exception':
1215  $message = $e->getMessage();
1216  if ((strpos($message, 'Error validating access token') !== false) ||
1217  (strpos($message, 'Invalid OAuth access token') !== false) ||
1218  (strpos($message, 'An active access token must be used') !== false)
1219  ) {
1220  $this->destroySession();
1221  }
1222  break;
1223  }
1224 
1225  throw $e;
1226  }
1227 
1228 
1234  protected static function errorLog($msg) {
1235  // disable error log if we are running in a CLI environment
1236  // @codeCoverageIgnoreStart
1237  if (php_sapi_name() != 'cli') {
1238  error_log($msg);
1239  }
1240  // @codeCoverageIgnoreEnd
1241  }
1242 
1253  protected static function base64UrlDecode($input) {
1254  return base64_decode(strtr($input, '-_', '+/'));
1255  }
1256 
1266  protected static function base64UrlEncode($input) {
1267  $str = strtr(base64_encode($input), '+/', '-_');
1268  $str = str_replace('=', '', $str);
1269  return $str;
1270  }
1271 
1275  public function destroySession() {
1276  $this->accessToken = null;
1277  $this->signedRequest = null;
1278  $this->user = null;
1279  $this->clearAllPersistentData();
1280 
1281  // Javascript sets a cookie that will be used in getSignedRequest that we
1282  // need to clear if we can
1283  $cookie_name = $this->getSignedRequestCookieName();
1284  if (array_key_exists($cookie_name, $_COOKIE)) {
1285  unset($_COOKIE[$cookie_name]);
1286  if (!headers_sent()) {
1287  $base_domain = $this->getBaseDomain();
1288  setcookie($cookie_name, '', 1, '/', '.'.$base_domain);
1289  } else {
1290  // @codeCoverageIgnoreStart
1291  self::errorLog(
1292  'There exists a cookie that we wanted to clear that we couldn\'t '.
1293  'clear because headers was already sent. Make sure to do the first '.
1294  'API call before outputing anything.'
1295  );
1296  // @codeCoverageIgnoreEnd
1297  }
1298  }
1299  }
1300 
1306  protected function getMetadataCookie() {
1307  $cookie_name = $this->getMetadataCookieName();
1308  if (!array_key_exists($cookie_name, $_COOKIE)) {
1309  return array();
1310  }
1311 
1312  // The cookie value can be wrapped in "-characters so remove them
1313  $cookie_value = trim($_COOKIE[$cookie_name], '"');
1314 
1315  if (empty($cookie_value)) {
1316  return array();
1317  }
1318 
1319  $parts = explode('&', $cookie_value);
1320  $metadata = array();
1321  foreach ($parts as $part) {
1322  $pair = explode('=', $part, 2);
1323  if (!empty($pair[0])) {
1324  $metadata[urldecode($pair[0])] =
1325  (count($pair) > 1) ? urldecode($pair[1]) : '';
1326  }
1327  }
1328 
1329  return $metadata;
1330  }
1331 
1332  protected static function isAllowedDomain($big, $small) {
1333  if ($big === $small) {
1334  return true;
1335  }
1336  return self::endsWith($big, '.'.$small);
1337  }
1338 
1339  protected static function endsWith($big, $small) {
1340  $len = strlen($small);
1341  if ($len === 0) {
1342  return true;
1343  }
1344  return substr($big, -$len) === $small;
1345  }
1346 
1366  abstract protected function setPersistentData($key, $value);
1367 
1376  abstract protected function getPersistentData($key, $default = false);
1377 
1384  abstract protected function clearPersistentData($key);
1385 
1391  abstract protected function clearAllPersistentData();
1392 }
static endsWith($big, $small)
getBaseDomain()
Get the base domain used for the cookie.
$path
Definition: aliased.php:25
$_COOKIE['client_id']
Definition: server.php:9
getLoginStatusUrl($params=array())
Get a login status URL to fetch the status from Facebook.
$signedRequest
The data from the signed_request token.
__construct($result)
Make a new API Exception with the given result.
setAppId($appId)
Set the Application ID.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
getMetadataCookie()
Parses the metadata cookie that our Javascript API set.
getApiUrl($method)
Build the URL for api given parameters.
$config
Definition: bootstrap.php:15
getApplicationAccessToken()
Returns the access token that should be used for logged out users when no authorization code is avail...
getUserFromAccessToken()
Retrieves the UID with the understanding that $this->accessToken has already been set and is seemingl...
getUserAccessToken()
Determines and returns the user access token, first using the signed request if present, and then falling back on the authorization code if present.
$code
Definition: example_050.php:99
throwAPIException($result)
Analyzes the supplied result to see if it was thrown because the access token is no longer valid...
static errorLog($msg)
Prints to the error log if you aren&#39;t in command line mode.
getCurrentUrl()
Returns the Current URL, stripping it of known FB parameters that should not persist.
__toString()
To make debugging easier.
setAccessToken($access_token)
Sets the access token for api calls.
__construct($config)
Initialize a Facebook Application.
getLogoutUrl($params=array())
Get a Logout URL suitable for use with redirects.
static isAllowedDomain($big, $small)
setAppSecret($appSecret)
Set the App Secret.
$metadata['__DYNAMIC:1__']
getType()
Returns the associated type for the error.
user()
Definition: user.php:4
getAccessTokenFromCode($code, $redirect_uri=null)
Retrieves an access token for the given authorization code (previously generated from www...
getApiSecret()
Get the App Secret.
getMetadataCookieName()
Constructs and returns the name of the coookie that potentially contain metadata. ...
getAccessToken()
Determines the access token that should be used for API calls.
getUserFromAvailableData()
Determines the connected user by first examining any signed requests, then considering an authorizati...
$result
The result from the API server that represents the exception information.
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
destroySession()
Destroy the current session.
getUrl($name, $path='', $params=array())
Build the URL for given domain alias, path and parameters.
catch(Exception $e) $message
_oauthRequest($url, $params)
Make a OAuth Request.
makeRequest($url, $params, $ch=null)
Makes an HTTP request.
getUser()
Get the UID of the connected user, or 0 if the Facebook user is not connected.
$state
A CSRF state variable to assist in the defense against CSRF attacks.
setApiSecret($apiSecret)
Set the App Secret.
getFileUploadSupport()
Get the file upload support status.
getCode()
Get the authorization code from the query parameters, if it exists, and otherwise return false to sig...
$user
Definition: migrateto20.php:57
api()
Make an API call.
$default
Definition: build.php:20
parseSignedRequest($signed_request)
Parses a signed_request and validates the signature.
getSignedRequest()
Retrieve the signed request, either from a request parameter or, if not present, from a cookie...
setFileUploadSupport($fileUploadSupport)
Set the file upload support status.
_restserver($params)
Invoke the old restserver.php endpoint.
getResult()
Return the associated result object returned by the API server.
_graph($path, $method='GET', $params=array())
Invoke the Graph API.
getAppId()
Get the Application ID.
static base64UrlDecode($input)
Base64 encoding that doesn&#39;t need to be urlencode()ed.
getLoginUrl($params=array())
Get a Login URL for use with redirects.
$url
isVideoPost($path, $method='GET')
Return true if this is video post.
setExtendedAccessToken()
Extend an access token, while removing the short-lived token that might have been generated via clien...
static base64UrlEncode($input)
Base64 encoding that doesn&#39;t need to be urlencode()ed.
$key
Definition: croninfo.php:18
Provides access to the Facebook Platform.
establishCSRFTokenState()
Lays down a CSRF state token for this process.
getAppSecret()
Get the App Secret.
useFileUploadSupport()
DEPRECATED! Please use getFileUploadSupport instead.
makeSignedRequest($data)
Makes a signed_request blob using the given data.
getSignedRequestCookieName()
Constructs and returns the name of the cookie that potentially houses the signed request for the app ...
$data
Definition: bench.php:6
Copyright 2011 Facebook, Inc.