ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 
144  protected static $DROP_QUERY_PARAMS = array(
145  'code',
146  'state',
147  'signed_request',
148  );
149 
153  public static $DOMAIN_MAP = array(
154  'api' => 'https://api.facebook.com/',
155  'api_video' => 'https://api-video.facebook.com/',
156  'api_read' => 'https://api-read.facebook.com/',
157  'graph' => 'https://graph.facebook.com/',
158  'graph_video' => 'https://graph-video.facebook.com/',
159  'www' => 'https://www.facebook.com/',
160  );
161 
167  protected $appId;
168 
174  protected $appSecret;
175 
181  protected $user;
182 
186  protected $signedRequest;
187 
191  protected $state;
192 
199  protected $accessToken = null;
200 
206  protected $fileUploadSupport = false;
207 
213  protected $trustForwarded = false;
214 
225  public function __construct($config) {
226  $this->setAppId($config['appId']);
227  $this->setAppSecret($config['secret']);
228  if (isset($config['fileUpload'])) {
229  $this->setFileUploadSupport($config['fileUpload']);
230  }
231  if (isset($config['trustForwarded']) && $config['trustForwarded']) {
232  $this->trustForwarded = true;
233  }
234  $state = $this->getPersistentData('state');
235  if (!empty($state)) {
236  $this->state = $state;
237  }
238  }
239 
246  public function setAppId($appId) {
247  $this->appId = $appId;
248  return $this;
249  }
250 
256  public function getAppId() {
257  return $this->appId;
258  }
259 
267  public function setApiSecret($apiSecret) {
268  $this->setAppSecret($apiSecret);
269  return $this;
270  }
271 
278  public function setAppSecret($appSecret) {
279  $this->appSecret = $appSecret;
280  return $this;
281  }
282 
289  public function getApiSecret() {
290  return $this->getAppSecret();
291  }
292 
298  public function getAppSecret() {
299  return $this->appSecret;
300  }
301 
308  public function setFileUploadSupport($fileUploadSupport) {
309  $this->fileUploadSupport = $fileUploadSupport;
310  return $this;
311  }
312 
318  public function getFileUploadSupport() {
319  return $this->fileUploadSupport;
320  }
321 
329  public function useFileUploadSupport() {
330  return $this->getFileUploadSupport();
331  }
332 
341  public function setAccessToken($access_token) {
342  $this->accessToken = $access_token;
343  return $this;
344  }
345 
351  public function setExtendedAccessToken() {
352  try {
353  // need to circumvent json_decode by calling _oauthRequest
354  // directly, since response isn't JSON format
355  $access_token_response = $this->_oauthRequest(
356  $this->getUrl('graph', '/oauth/access_token'),
357  $params = array(
358  'client_id' => $this->getAppId(),
359  'client_secret' => $this->getAppSecret(),
360  'grant_type' => 'fb_exchange_token',
361  'fb_exchange_token' => $this->getAccessToken(),
362  )
363  );
364  }
365  catch (FacebookApiException $e) {
366  // most likely that user very recently revoked authorization
367  // In any event, we don't have an access token, so say so
368  return false;
369  }
370 
371  if (empty($access_token_response)) {
372  return false;
373  }
374 
375  $response_params = array();
376  parse_str($access_token_response, $response_params);
377 
378  if (!isset($response_params['access_token'])) {
379  return false;
380  }
381 
382  $this->destroySession();
383 
384  $this->setPersistentData(
385  'access_token', $response_params['access_token']
386  );
387  }
388 
398  public function getAccessToken() {
399  if ($this->accessToken !== null) {
400  // we've done this already and cached it. Just return.
401  return $this->accessToken;
402  }
403 
404  // first establish access token to be the application
405  // access token, in case we navigate to the /oauth/access_token
406  // endpoint, where SOME access token is required
407  $this->setAccessToken($this->getApplicationAccessToken());
408  $user_access_token = $this->getUserAccessToken();
409  if ($user_access_token) {
410  $this->setAccessToken($user_access_token);
411  }
412 
413  return $this->accessToken;
414  }
415 
426  protected function getUserAccessToken() {
427  // first, consider a signed request if it's supplied
428  // if there is a signed request, then it alone determines
429  // the access token
430  $signed_request = $this->getSignedRequest();
431  if ($signed_request) {
432  // apps.facebook.com hands the access_token in the signed_request
433  if (array_key_exists('oauth_token', $signed_request)) {
434  $access_token = $signed_request['oauth_token'];
435  $this->setPersistentData('access_token', $access_token);
436  return $access_token;
437  }
438 
439  // the JS SDK puts a code in with the redirect_uri of ''
440  if (array_key_exists('code', $signed_request)) {
441  $code = $signed_request['code'];
442  if ($code && $code == $this->getPersistentData('code')) {
443  // short-circuit if the code we have is the same as the one presented
444  return $this->getPersistentData('access_token');
445  }
446 
447  $access_token = $this->getAccessTokenFromCode($code, '');
448  if ($access_token) {
449  $this->setPersistentData('code', $code);
450  $this->setPersistentData('access_token', $access_token);
451  return $access_token;
452  }
453  }
454 
455  // signed request states there's no access token, so anything
456  // stored should be cleared
457  $this->clearAllPersistentData();
458  return false; // respect the signed request's data, even
459  // if there's an authorization code or something else
460  }
461 
462  $code = $this->getCode();
463  if ($code && $code != $this->getPersistentData('code')) {
464  $access_token = $this->getAccessTokenFromCode($code);
465  if ($access_token) {
466  $this->setPersistentData('code', $code);
467  $this->setPersistentData('access_token', $access_token);
468  return $access_token;
469  }
470 
471  // code was bogus, so everything based on it should be invalidated
472  $this->clearAllPersistentData();
473  return false;
474  }
475 
476  // as a fallback, just return whatever is in the persistent
477  // store, knowing nothing explicit (signed request, authorization
478  // code, etc.) was present to shadow it (or we saw a code in $_REQUEST,
479  // but it's the same as what's in the persistent store)
480  return $this->getPersistentData('access_token');
481  }
482 
489  public function getSignedRequest() {
490  if (!$this->signedRequest) {
491  if (!empty($_REQUEST['signed_request'])) {
492  $this->signedRequest = $this->parseSignedRequest(
493  $_REQUEST['signed_request']);
494  } else if (!empty($_COOKIE[$this->getSignedRequestCookieName()])) {
495  $this->signedRequest = $this->parseSignedRequest(
496  $_COOKIE[$this->getSignedRequestCookieName()]);
497  }
498  }
499  return $this->signedRequest;
500  }
501 
508  public function getUser() {
509  if ($this->user !== null) {
510  // we've already determined this and cached the value
511  return $this->user;
512  }
513 
514  return $this->user = $this->getUserFromAvailableData();
515  }
516 
525  protected function getUserFromAvailableData() {
526  // if a signed request is supplied, then it solely determines
527  // who the user is
528  $signed_request = $this->getSignedRequest();
529  if ($signed_request) {
530  if (array_key_exists('user_id', $signed_request)) {
531  $user = $signed_request['user_id'];
532 
533  if($user != $this->getPersistentData('user_id')){
534  $this->clearAllPersistentData();
535  }
536 
537  $this->setPersistentData('user_id', $signed_request['user_id']);
538  return $user;
539  }
540 
541  // if the signed request didn't present a user id, then invalidate
542  // all entries in any persistent store
543  $this->clearAllPersistentData();
544  return 0;
545  }
546 
547  $user = $this->getPersistentData('user_id', $default = 0);
548  $persisted_access_token = $this->getPersistentData('access_token');
549 
550  // use access_token to fetch user id if we have a user access_token, or if
551  // the cached access token has changed
552  $access_token = $this->getAccessToken();
553  if ($access_token &&
554  $access_token != $this->getApplicationAccessToken() &&
555  !($user && $persisted_access_token == $access_token)) {
556  $user = $this->getUserFromAccessToken();
557  if ($user) {
558  $this->setPersistentData('user_id', $user);
559  } else {
560  $this->clearAllPersistentData();
561  }
562  }
563 
564  return $user;
565  }
566 
579  public function getLoginUrl($params=array()) {
580  $this->establishCSRFTokenState();
581  $currentUrl = $this->getCurrentUrl();
582 
583  // if 'scope' is passed as an array, convert to comma separated list
584  $scopeParams = isset($params['scope']) ? $params['scope'] : null;
585  if ($scopeParams && is_array($scopeParams)) {
586  $params['scope'] = implode(',', $scopeParams);
587  }
588 
589  return $this->getUrl(
590  'www',
591  'dialog/oauth',
592  array_merge(array(
593  'client_id' => $this->getAppId(),
594  'redirect_uri' => $currentUrl, // possibly overwritten
595  'state' => $this->state),
596  $params));
597  }
598 
608  public function getLogoutUrl($params=array()) {
609  return $this->getUrl(
610  'www',
611  'logout.php',
612  array_merge(array(
613  'next' => $this->getCurrentUrl(),
614  'access_token' => $this->getUserAccessToken(),
615  ), $params)
616  );
617  }
618 
630  public function getLoginStatusUrl($params=array()) {
631  return $this->getUrl(
632  'www',
633  'extern/login_status.php',
634  array_merge(array(
635  'api_key' => $this->getAppId(),
636  'no_session' => $this->getCurrentUrl(),
637  'no_user' => $this->getCurrentUrl(),
638  'ok_session' => $this->getCurrentUrl(),
639  'session_version' => 3,
640  ), $params)
641  );
642  }
643 
649  public function api(/* polymorphic */) {
650  $args = func_get_args();
651  if (is_array($args[0])) {
652  return $this->_restserver($args[0]);
653  } else {
654  return call_user_func_array(array($this, '_graph'), $args);
655  }
656  }
657 
667  protected function getSignedRequestCookieName() {
668  return 'fbsr_'.$this->getAppId();
669  }
670 
678  protected function getMetadataCookieName() {
679  return 'fbm_'.$this->getAppId();
680  }
681 
690  protected function getCode() {
691  if (isset($_REQUEST['code'])) {
692  if ($this->state !== null &&
693  isset($_REQUEST['state']) &&
694  $this->state === $_REQUEST['state']) {
695 
696  // CSRF state has done its job, so clear it
697  $this->state = null;
698  $this->clearPersistentData('state');
699  return $_REQUEST['code'];
700  } else {
701  self::errorLog('CSRF state token does not match one provided. ' . $this->state . '!=' . $_REQUEST['state']);
702  return false;
703  }
704  }
705 
706  return false;
707  }
708 
719  protected function getUserFromAccessToken() {
720  try {
721  $user_info = $this->api('/me');
722  return $user_info['id'];
723  } catch (FacebookApiException $e) {
724  return 0;
725  }
726  }
727 
735  protected function getApplicationAccessToken() {
736  return $this->appId.'|'.$this->appSecret;
737  }
738 
744  protected function establishCSRFTokenState() {
745  if ($this->state === null) {
746  $this->state = md5(uniqid(mt_rand(), true));
747  $this->setPersistentData('state', $this->state);
748  }
749  }
750 
763  protected function getAccessTokenFromCode($code, $redirect_uri = null) {
764  if (empty($code)) {
765  return false;
766  }
767 
768  if ($redirect_uri === null) {
769  $redirect_uri = $this->getCurrentUrl();
770  }
771 
772  try {
773  // need to circumvent json_decode by calling _oauthRequest
774  // directly, since response isn't JSON format
775  $access_token_response =
776  $this->_oauthRequest(
777  $this->getUrl('graph', '/oauth/access_token'),
778  $params = array('client_id' => $this->getAppId(),
779  'client_secret' => $this->getAppSecret(),
780  'redirect_uri' => $redirect_uri,
781  'code' => $code));
782  } catch (FacebookApiException $e) {
783  // most likely that user very recently revoked authorization.
784  // In any event, we don't have an access token, so say so.
785  return false;
786  }
787 
788  if (empty($access_token_response)) {
789  return false;
790  }
791 
792  $response_params = json_decode($access_token_response, true);
793  if (!isset($response_params['access_token'])) {
794  return false;
795  }
796 
797  return $response_params['access_token'];
798  }
799 
808  protected function _restserver($params) {
809  // generic application level parameters
810  $params['api_key'] = $this->getAppId();
811  $params['format'] = 'json-strings';
812 
813  $result = json_decode($this->_oauthRequest(
814  $this->getApiUrl($params['method']),
815  $params
816  ), true);
817 
818  // results are returned, errors are thrown
819  if (is_array($result) && isset($result['error_code'])) {
820  $this->throwAPIException($result);
821  // @codeCoverageIgnoreStart
822  }
823  // @codeCoverageIgnoreEnd
824 
825  $method = strtolower($params['method']);
826  if ($method === 'auth.expiresession' ||
827  $method === 'auth.revokeauthorization') {
828  $this->destroySession();
829  }
830 
831  return $result;
832  }
833 
842  protected function isVideoPost($path, $method = 'GET') {
843  if ($method == 'POST' && preg_match("/^(\/)(.+)(\/)(videos)$/", $path)) {
844  return true;
845  }
846  return false;
847  }
848 
859  protected function _graph($path, $method = 'GET', $params = array()) {
860  if (is_array($method) && empty($params)) {
861  $params = $method;
862  $method = 'GET';
863  }
864  $params['method'] = $method; // method override as we always do a POST
865 
866  if ($this->isVideoPost($path, $method)) {
867  $domainKey = 'graph_video';
868  } else {
869  $domainKey = 'graph';
870  }
871 
872  $result = json_decode($this->_oauthRequest(
873  $this->getUrl($domainKey, $path),
874  $params
875  ), true);
876 
877  // results are returned, errors are thrown
878  if (is_array($result) && isset($result['error'])) {
879  $this->throwAPIException($result);
880  // @codeCoverageIgnoreStart
881  }
882  // @codeCoverageIgnoreEnd
883 
884  return $result;
885  }
886 
896  protected function _oauthRequest($url, $params) {
897  if (!isset($params['access_token'])) {
898  $params['access_token'] = $this->getAccessToken();
899  }
900 
901  // json_encode all params values that are not strings
902  foreach ($params as $key => $value) {
903  if (!is_string($value)) {
904  $params[$key] = json_encode($value);
905  }
906  }
907 
908  return $this->makeRequest($url, $params);
909  }
910 
922  protected function makeRequest($url, $params, $ch=null) {
923  if (!$ch) {
924  $ch = curl_init();
925  }
926 
927  $opts = self::$CURL_OPTS;
928  if ($this->getFileUploadSupport()) {
929  $opts[CURLOPT_POSTFIELDS] = $params;
930  } else {
931  $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&');
932  }
933  $opts[CURLOPT_URL] = $url;
934 
935  // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
936  // for 2 seconds if the server does not support this header
937  if (isset($opts[CURLOPT_HTTPHEADER])) {
938  $existing_headers = $opts[CURLOPT_HTTPHEADER];
939  $existing_headers[] = 'Expect:';
940  $opts[CURLOPT_HTTPHEADER] = $existing_headers;
941  } else {
942  $opts[CURLOPT_HTTPHEADER] = array('Expect:');
943  }
944 
945  curl_setopt_array($ch, $opts);
946  $result = curl_exec($ch);
947 
948  if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT
949  self::errorLog('Invalid or no certificate authority found, '.
950  'using bundled information');
951  curl_setopt($ch, CURLOPT_CAINFO,
952  dirname(__FILE__) . '/fb_ca_chain_bundle.crt');
953  $result = curl_exec($ch);
954  }
955 
956  // With dual stacked DNS responses, it's possible for a server to
957  // have IPv6 enabled but not have IPv6 connectivity. If this is
958  // the case, curl will try IPv4 first and if that fails, then it will
959  // fall back to IPv6 and the error EHOSTUNREACH is returned by the
960  // operating system
961  if ($result === false && empty($opts[CURLOPT_IPRESOLVE])) {
962  $matches = array();
963  $regex = '/Failed to connect to ([^:].*): Network is unreachable/';
964  if (preg_match($regex, curl_error($ch), $matches)) {
965  if (strlen(@inet_pton($matches[1])) === 16) {
966  self::errorLog('Invalid IPv6 configuration on server, '.
967  'Please disable or get native IPv6 on your server.');
968  self::$CURL_OPTS[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
969  curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
970  $result = curl_exec($ch);
971  }
972  }
973  }
974 
975  if ($result === false) {
976  $e = new FacebookApiException(array(
977  'error_code' => curl_errno($ch),
978  'error' => array(
979  'message' => curl_error($ch),
980  'type' => 'CurlException',
981  ),
982  ));
983  curl_close($ch);
984  throw $e;
985  }
986  curl_close($ch);
987  return $result;
988  }
989 
996  protected function parseSignedRequest($signed_request) {
997  list($encoded_sig, $payload) = explode('.', $signed_request, 2);
998 
999  // decode the data
1000  $sig = self::base64UrlDecode($encoded_sig);
1001  $data = json_decode(self::base64UrlDecode($payload), true);
1002 
1003  if (strtoupper($data['algorithm']) !== self::SIGNED_REQUEST_ALGORITHM) {
1004  self::errorLog(
1005  'Unknown algorithm. Expected ' . self::SIGNED_REQUEST_ALGORITHM);
1006  return null;
1007  }
1008 
1009  // check sig
1010  $expected_sig = hash_hmac('sha256', $payload,
1011  $this->getAppSecret(), $raw = true);
1012  if ($sig !== $expected_sig) {
1013  self::errorLog('Bad Signed JSON signature!');
1014  return null;
1015  }
1016 
1017  return $data;
1018  }
1019 
1026  protected function makeSignedRequest($data) {
1027  if (!is_array($data)) {
1028  throw new InvalidArgumentException(
1029  'makeSignedRequest expects an array. Got: ' . print_r($data, true));
1030  }
1031  $data['algorithm'] = self::SIGNED_REQUEST_ALGORITHM;
1032  $data['issued_at'] = time();
1033  $json = json_encode($data);
1034  $b64 = self::base64UrlEncode($json);
1035  $raw_sig = hash_hmac('sha256', $b64, $this->getAppSecret(), $raw = true);
1036  $sig = self::base64UrlEncode($raw_sig);
1037  return $sig.'.'.$b64;
1038  }
1039 
1046  protected function getApiUrl($method) {
1047  static $READ_ONLY_CALLS =
1048  array('admin.getallocation' => 1,
1049  'admin.getappproperties' => 1,
1050  'admin.getbannedusers' => 1,
1051  'admin.getlivestreamvialink' => 1,
1052  'admin.getmetrics' => 1,
1053  'admin.getrestrictioninfo' => 1,
1054  'application.getpublicinfo' => 1,
1055  'auth.getapppublickey' => 1,
1056  'auth.getsession' => 1,
1057  'auth.getsignedpublicsessiondata' => 1,
1058  'comments.get' => 1,
1059  'connect.getunconnectedfriendscount' => 1,
1060  'dashboard.getactivity' => 1,
1061  'dashboard.getcount' => 1,
1062  'dashboard.getglobalnews' => 1,
1063  'dashboard.getnews' => 1,
1064  'dashboard.multigetcount' => 1,
1065  'dashboard.multigetnews' => 1,
1066  'data.getcookies' => 1,
1067  'events.get' => 1,
1068  'events.getmembers' => 1,
1069  'fbml.getcustomtags' => 1,
1070  'feed.getappfriendstories' => 1,
1071  'feed.getregisteredtemplatebundlebyid' => 1,
1072  'feed.getregisteredtemplatebundles' => 1,
1073  'fql.multiquery' => 1,
1074  'fql.query' => 1,
1075  'friends.arefriends' => 1,
1076  'friends.get' => 1,
1077  'friends.getappusers' => 1,
1078  'friends.getlists' => 1,
1079  'friends.getmutualfriends' => 1,
1080  'gifts.get' => 1,
1081  'groups.get' => 1,
1082  'groups.getmembers' => 1,
1083  'intl.gettranslations' => 1,
1084  'links.get' => 1,
1085  'notes.get' => 1,
1086  'notifications.get' => 1,
1087  'pages.getinfo' => 1,
1088  'pages.isadmin' => 1,
1089  'pages.isappadded' => 1,
1090  'pages.isfan' => 1,
1091  'permissions.checkavailableapiaccess' => 1,
1092  'permissions.checkgrantedapiaccess' => 1,
1093  'photos.get' => 1,
1094  'photos.getalbums' => 1,
1095  'photos.gettags' => 1,
1096  'profile.getinfo' => 1,
1097  'profile.getinfooptions' => 1,
1098  'stream.get' => 1,
1099  'stream.getcomments' => 1,
1100  'stream.getfilters' => 1,
1101  'users.getinfo' => 1,
1102  'users.getloggedinuser' => 1,
1103  'users.getstandardinfo' => 1,
1104  'users.hasapppermission' => 1,
1105  'users.isappuser' => 1,
1106  'users.isverified' => 1,
1107  'video.getuploadlimits' => 1);
1108  $name = 'api';
1109  if (isset($READ_ONLY_CALLS[strtolower($method)])) {
1110  $name = 'api_read';
1111  } else if (strtolower($method) == 'video.upload') {
1112  $name = 'api_video';
1113  }
1114  return self::getUrl($name, 'restserver.php');
1115  }
1116 
1126  protected function getUrl($name, $path='', $params=array()) {
1127  $url = self::$DOMAIN_MAP[$name];
1128  if ($path) {
1129  if ($path[0] === '/') {
1130  $path = substr($path, 1);
1131  }
1132  $url .= $path;
1133  }
1134  if ($params) {
1135  $url .= '?' . http_build_query($params, null, '&');
1136  }
1137 
1138  return $url;
1139  }
1140 
1141  protected function getHttpHost() {
1142  if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
1143  return $_SERVER['HTTP_X_FORWARDED_HOST'];
1144  }
1145  return $_SERVER['HTTP_HOST'];
1146  }
1147 
1148  protected function getHttpProtocol() {
1149  if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
1150  if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
1151  return 'https';
1152  }
1153  return 'http';
1154  }
1155  /*apache + variants specific way of checking for https*/
1156  if (isset($_SERVER['HTTPS']) &&
1157  ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {
1158  return 'https';
1159  }
1160  /*nginx way of checking for https*/
1161  if (isset($_SERVER['SERVER_PORT']) &&
1162  ($_SERVER['SERVER_PORT'] === '443')) {
1163  return 'https';
1164  }
1165  return 'http';
1166  }
1167 
1171  protected function getBaseDomain() {
1172  // The base domain is stored in the metadata cookie if not we fallback
1173  // to the current hostname
1174  $metadata = $this->getMetadataCookie();
1175  if (array_key_exists('base_domain', $metadata) &&
1176  !empty($metadata['base_domain'])) {
1177  return trim($metadata['base_domain'], '.');
1178  }
1179  return $this->getHttpHost();
1180  }
1181 
1188  protected function getCurrentUrl() {
1189  $protocol = $this->getHttpProtocol() . '://';
1190  $host = $this->getHttpHost();
1191  $currentUrl = $protocol.$host.$_SERVER['REQUEST_URI'];
1192  $parts = parse_url($currentUrl);
1193 
1194  $query = '';
1195  if (!empty($parts['query'])) {
1196  // drop known fb params
1197  $params = explode('&', $parts['query']);
1198  $retained_params = array();
1199  foreach ($params as $param) {
1200  if ($this->shouldRetainParam($param)) {
1201  $retained_params[] = $param;
1202  }
1203  }
1204 
1205  if (!empty($retained_params)) {
1206  $query = '?'.implode($retained_params, '&');
1207  }
1208  }
1209 
1210  // use port if non default
1211  $port =
1212  isset($parts['port']) &&
1213  (($protocol === 'http://' && $parts['port'] !== 80) ||
1214  ($protocol === 'https://' && $parts['port'] !== 443))
1215  ? ':' . $parts['port'] : '';
1216 
1217  // rebuild
1218  return $protocol . $parts['host'] . $port . $parts['path'] . $query;
1219  }
1220 
1232  protected function shouldRetainParam($param) {
1233  foreach (self::$DROP_QUERY_PARAMS as $drop_query_param) {
1234  if (strpos($param, $drop_query_param.'=') === 0) {
1235  return false;
1236  }
1237  }
1238 
1239  return true;
1240  }
1241 
1250  protected function throwAPIException($result) {
1251  $e = new FacebookApiException($result);
1252  switch ($e->getType()) {
1253  // OAuth 2.0 Draft 00 style
1254  case 'OAuthException':
1255  // OAuth 2.0 Draft 10 style
1256  case 'invalid_token':
1257  // REST server errors are just Exceptions
1258  case 'Exception':
1259  $message = $e->getMessage();
1260  if ((strpos($message, 'Error validating access token') !== false) ||
1261  (strpos($message, 'Invalid OAuth access token') !== false) ||
1262  (strpos($message, 'An active access token must be used') !== false)
1263  ) {
1264  $this->destroySession();
1265  }
1266  break;
1267  }
1268 
1269  throw $e;
1270  }
1271 
1272 
1278  protected static function errorLog($msg) {
1279  // disable error log if we are running in a CLI environment
1280  // @codeCoverageIgnoreStart
1281  if (php_sapi_name() != 'cli') {
1282  error_log($msg);
1283  }
1284  // @codeCoverageIgnoreEnd
1285  }
1286 
1297  protected static function base64UrlDecode($input) {
1298  return base64_decode(strtr($input, '-_', '+/'));
1299  }
1300 
1310  protected static function base64UrlEncode($input) {
1311  $str = strtr(base64_encode($input), '+/', '-_');
1312  $str = str_replace('=', '', $str);
1313  return $str;
1314  }
1315 
1319  public function destroySession() {
1320  $this->accessToken = null;
1321  $this->signedRequest = null;
1322  $this->user = null;
1323  $this->clearAllPersistentData();
1324 
1325  // Javascript sets a cookie that will be used in getSignedRequest that we
1326  // need to clear if we can
1327  $cookie_name = $this->getSignedRequestCookieName();
1328  if (array_key_exists($cookie_name, $_COOKIE)) {
1329  unset($_COOKIE[$cookie_name]);
1330  if (!headers_sent()) {
1331  $base_domain = $this->getBaseDomain();
1332  setcookie($cookie_name, '', 1, '/', '.'.$base_domain);
1333  } else {
1334  // @codeCoverageIgnoreStart
1335  self::errorLog(
1336  'There exists a cookie that we wanted to clear that we couldn\'t '.
1337  'clear because headers was already sent. Make sure to do the first '.
1338  'API call before outputing anything.'
1339  );
1340  // @codeCoverageIgnoreEnd
1341  }
1342  }
1343  }
1344 
1350  protected function getMetadataCookie() {
1351  $cookie_name = $this->getMetadataCookieName();
1352  if (!array_key_exists($cookie_name, $_COOKIE)) {
1353  return array();
1354  }
1355 
1356  // The cookie value can be wrapped in "-characters so remove them
1357  $cookie_value = trim($_COOKIE[$cookie_name], '"');
1358 
1359  if (empty($cookie_value)) {
1360  return array();
1361  }
1362 
1363  $parts = explode('&', $cookie_value);
1364  $metadata = array();
1365  foreach ($parts as $part) {
1366  $pair = explode('=', $part, 2);
1367  if (!empty($pair[0])) {
1368  $metadata[urldecode($pair[0])] =
1369  (count($pair) > 1) ? urldecode($pair[1]) : '';
1370  }
1371  }
1372 
1373  return $metadata;
1374  }
1375 
1376  protected static function isAllowedDomain($big, $small) {
1377  if ($big === $small) {
1378  return true;
1379  }
1380  return self::endsWith($big, '.'.$small);
1381  }
1382 
1383  protected static function endsWith($big, $small) {
1384  $len = strlen($small);
1385  if ($len === 0) {
1386  return true;
1387  }
1388  return substr($big, -$len) === $small;
1389  }
1390 
1410  abstract protected function setPersistentData($key, $value);
1411 
1420  abstract protected function getPersistentData($key, $default = false);
1421 
1428  abstract protected function clearPersistentData($key);
1429 
1435  abstract protected function clearAllPersistentData();
1436 }
static endsWith($big, $small)
$params
Definition: disable.php:11
getBaseDomain()
Get the base domain used for the cookie.
$_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.
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.
shouldRetainParam($param)
Returns true if and only if the key or key/value pair should be retained as part of the query string...
$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
$error
Definition: Error.php:17
destroySession()
Destroy the current session.
if($format !==null) $name
Definition: metadata.php:146
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.
$query
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...
Create styles array
The data for the language used.
api()
Make an API call.
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.
$accessToken
Definition: demo.php:45
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
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
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 ...
Copyright 2011 Facebook, Inc.