ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
base_facebook.php
Go to the documentation of this file.
1<?php
18if (!function_exists('curl_init')) {
19 throw new Exception('Facebook needs the CURL PHP extension.');
20}
21if (!function_exists('json_decode')) {
22 throw new Exception('Facebook needs the JSON PHP extension.');
23}
24
30class FacebookApiException extends Exception
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
118abstract 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
299 $this->fileUploadSupport = $fileUploadSupport;
300 return $this;
301 }
302
308 public function getFileUploadSupport() {
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
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(
487 }
488 }
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()) {
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'])) {
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'])) {
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) {
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 }
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
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}
$result
user()
Definition: user.php:4
$_COOKIE['client_id']
Definition: server.php:9
$metadata['__DYNAMIC:1__']
$path
Definition: aliased.php:25
$default
Definition: build.php:20
Provides access to the Facebook Platform.
_graph($path, $method='GET', $params=array())
Invoke the Graph API.
parseSignedRequest($signed_request)
Parses a signed_request and validates the signature.
static $DOMAIN_MAP
Maps aliases to Facebook domains.
getApplicationAccessToken()
Returns the access token that should be used for logged out users when no authorization code is avail...
getAppId()
Get the Application ID.
makeSignedRequest($data)
Makes a signed_request blob using the given data.
getAccessToken()
Determines the access token that should be used for API calls.
useFileUploadSupport()
DEPRECATED! Please use getFileUploadSupport instead.
api()
Make an API call.
setPersistentData($key, $value)
Each of the following four methods should be overridden in a concrete subclass, as they are in the pr...
clearAllPersistentData()
Clear all data from the persistent storage.
_restserver($params)
Invoke the old restserver.php endpoint.
getFileUploadSupport()
Get the file upload support status.
setAccessToken($access_token)
Sets the access token for api calls.
getApiUrl($method)
Build the URL for api given parameters.
getLoginStatusUrl($params=array())
Get a login status URL to fetch the status from Facebook.
clearPersistentData($key)
Clear the data with $key from the persistent storage.
static endsWith($big, $small)
getUrl($name, $path='', $params=array())
Build the URL for given domain alias, path and parameters.
destroySession()
Destroy the current session.
establishCSRFTokenState()
Lays down a CSRF state token for this process.
setAppId($appId)
Set the Application ID.
getCurrentUrl()
Returns the Current URL, stripping it of known FB parameters that should not persist.
getAccessTokenFromCode($code, $redirect_uri=null)
Retrieves an access token for the given authorization code (previously generated from www....
getCode()
Get the authorization code from the query parameters, if it exists, and otherwise return false to sig...
getBaseDomain()
Get the base domain used for the cookie.
getSignedRequestCookieName()
Constructs and returns the name of the cookie that potentially houses the signed request for the app ...
getLogoutUrl($params=array())
Get a Logout URL suitable for use with redirects.
throwAPIException($result)
Analyzes the supplied result to see if it was thrown because the access token is no longer valid.
_oauthRequest($url, $params)
Make a OAuth 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.
static base64UrlDecode($input)
Base64 encoding that doesn't need to be urlencode()ed.
static errorLog($msg)
Prints to the error log if you aren't in command line mode.
$signedRequest
The data from the signed_request token.
const SIGNED_REQUEST_ALGORITHM
Signed Request Algorithm.
getMetadataCookie()
Parses the metadata cookie that our Javascript API set.
isVideoPost($path, $method='GET')
Return true if this is video post.
setFileUploadSupport($fileUploadSupport)
Set the file upload support status.
static base64UrlEncode($input)
Base64 encoding that doesn't need to be urlencode()ed.
getApiSecret()
Get the App Secret.
setAppSecret($appSecret)
Set the App Secret.
getUserFromAccessToken()
Retrieves the UID with the understanding that $this->accessToken has already been set and is seemingl...
getMetadataCookieName()
Constructs and returns the name of the coookie that potentially contain metadata.
makeRequest($url, $params, $ch=null)
Makes an HTTP request.
getPersistentData($key, $default=false)
Get the data for $key, persisted by BaseFacebook::setPersistentData()
getUserFromAvailableData()
Determines the connected user by first examining any signed requests, then considering an authorizati...
static $CURL_OPTS
Default options for curl.
getLoginUrl($params=array())
Get a Login URL for use with redirects.
getSignedRequest()
Retrieve the signed request, either from a request parameter or, if not present, from a cookie.
const VERSION
Version.
setExtendedAccessToken()
Extend an access token, while removing the short-lived token that might have been generated via clien...
getAppSecret()
Get the App Secret.
__construct($config)
Initialize a Facebook Application.
static isAllowedDomain($big, $small)
setApiSecret($apiSecret)
Set the App Secret.
getUserAccessToken()
Determines and returns the user access token, first using the signed request if present,...
An exception for terminatinating execution or to throw for unit testing.
Copyright 2011 Facebook, Inc.
getType()
Returns the associated type for the error.
$result
The result from the API server that represents the exception information.
__toString()
To make debugging easier.
__construct($result)
Make a new API Exception with the given result.
getResult()
Return the associated result object returned by the API server.
$key
Definition: croninfo.php:18
$code
Definition: example_050.php:99
$config
Definition: bootstrap.php:15
catch(Exception $e) $message
$url
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$data
Definition: bench.php:6