diff --git a/config-templates/authsources.php b/config-templates/authsources.php index 89a86a4de55de59ca6c79f6c3dcdd8079c0e60f5..38848f39032d22fd60222462274b944c6dedc39b 100644 --- a/config-templates/authsources.php +++ b/config-templates/authsources.php @@ -129,8 +129,14 @@ $config = array( /* 'facebook' => array( 'authfacebook:Facebook', + // Register your Facebook application on http://www.facebook.com/developers + // App ID or API key (requests with App ID should be faster; https://github.com/facebook/php-sdk/issues/214) 'api_key' => 'xxxxxxxxxxxxxxxx', + // App Secret 'secret' => 'xxxxxxxxxxxxxxxx', + // which additional data permissions to request from user + // see http://developers.facebook.com/docs/authentication/permissions/ for the full list + // 'req_perms' => 'email,user_birthday', ), */ diff --git a/modules/authfacebook/docs/authfacebook.txt b/modules/authfacebook/docs/authfacebook.txt index e5b59143653f932a6bcc27464b90a3070514a4cf..20e943e4275d3b2c095e549e42546aeec6d5627e 100644 --- a/modules/authfacebook/docs/authfacebook.txt +++ b/modules/authfacebook/docs/authfacebook.txt @@ -1,18 +1,13 @@ Using the Facebook authentication source with simpleSAMLphp =========================================================== -Remember to configure `authsources.php`, with both API key and secret. +Remember to configure `authsources.php`, with both App ID (or API Key) and App Secret. -To get an API key and a secret, register the application at: +To get an App ID/API Key and secret, register the application at: * <http://www.facebook.com/developers/> -Set the callback URL to be: - - * `http://idp.example.org/simplesaml/module.php/authfacebook/linkback.php?next=` - -Replace `idp.example.org` with your hostname. - - +Note: requests with App ID should be faster <https://github.com/facebook/php-sdk/issues/214>. +This module needs the CURL and JSON PHP extensions. diff --git a/modules/authfacebook/extlibinc/facebook.php b/modules/authfacebook/extlibinc/facebook.php index fd381eca1de27e02e5235012b2b7a4982944c036..b9b064b75b2cb1e7e48a99e94a0e2885776994cb 100644 --- a/modules/authfacebook/extlibinc/facebook.php +++ b/modules/authfacebook/extlibinc/facebook.php @@ -1,348 +1,982 @@ <?php -// Copyright 2004-2008 Facebook. All Rights Reserved. -// -// +---------------------------------------------------------------------------+ -// | Facebook Platform PHP5 client | -// +---------------------------------------------------------------------------+ -// | Copyright (c) 2007 Facebook, Inc. | -// | All rights reserved. | -// | | -// | Redistribution and use in source and binary forms, with or without | -// | modification, are permitted provided that the following conditions | -// | are met: | -// | | -// | 1. Redistributions of source code must retain the above copyright | -// | notice, this list of conditions and the following disclaimer. | -// | 2. Redistributions in binary form must reproduce the above copyright | -// | notice, this list of conditions and the following disclaimer in the | -// | documentation and/or other materials provided with the distribution. | -// | | -// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | -// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | -// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | -// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | -// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | -// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | -// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | -// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | -// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | -// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | -// +---------------------------------------------------------------------------+ -// | For help with this library, contact developers-help@facebook.com | -// +---------------------------------------------------------------------------+ -// - -include_once 'facebookapi_php5_restlib.php'; - -define('FACEBOOK_API_VALIDATION_ERROR', 1); -class Facebook { - public $api_client; - - public $api_key; - public $secret; - public $generate_session_secret; - public $session_expires; - - public $fb_params; - public $user; - - public function __construct($api_key, $secret, $generate_session_secret=false) { - $this->api_key = $api_key; - $this->secret = $secret; - $this->generate_session_secret = $generate_session_secret; - $this->api_client = new FacebookRestClient($api_key, $secret); - - $this->validate_fb_params(); - if (isset($this->fb_params['friends'])) { - $this->api_client->friends_list = explode(',', $this->fb_params['friends']); - } - if (isset($this->fb_params['added'])) { - $this->api_client->added = $this->fb_params['added']; - } - } - - public function validate_fb_params($resolve_auth_token=true) { - $this->fb_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_sig'); - if (!$this->fb_params) { - $this->fb_params = $this->get_valid_fb_params($_GET, 48*3600, 'fb_sig'); - } - if ($this->fb_params) { - // If we got any fb_params passed in at all, then either: - // - they included an fb_user / fb_session_key, which we should assume to be correct - // - they didn't include an fb_user / fb_session_key, which means the user doesn't have a - // valid session and if we want to get one we'll need to use require_login(). (Calling - // set_user with null values for user/session_key will work properly.) - // Note that we should *not* use our cookies in this scenario, since they may be referring to - // the wrong user. - $user = isset($this->fb_params['user']) ? $this->fb_params['user'] : null; - $session_key = isset($this->fb_params['session_key']) ? $this->fb_params['session_key'] : null; - $expires = isset($this->fb_params['expires']) ? $this->fb_params['expires'] : null; - $this->set_user($user, $session_key, $expires); - } else if (!empty($_COOKIE) && $cookies = $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) { - // use $api_key . '_' as a prefix for the cookies in case there are - // multiple facebook clients on the same domain. - $expires = isset($cookies['expires']) ? $cookies['expires'] : null; - $this->set_user($cookies['user'], $cookies['session_key'], $expires); - } else if (isset($_GET['auth_token']) && $resolve_auth_token && - $session = $this->do_get_session($_GET['auth_token'])) { - $session_secret = ($this->generate_session_secret && !empty($session['secret'])) ? $session['secret'] : null; - $this->set_user($session['uid'], $session['session_key'], $session['expires'], $session_secret); - } - - return !empty($this->fb_params); - } - - // Store a temporary session secret for the current session - // for use with the JS client library - public function promote_session() { - try { - $session_secret = $this->api_client->auth_promoteSession(); - if (!$this->in_fb_canvas()) { - $this->set_cookies($this->user, $this->api_client->session_key, $this->session_expires, $session_secret); - } - return $session_secret; - } catch (FacebookRestClientException $e) { - // API_EC_PARAM means we don't have a logged in user, otherwise who - // knows what it means, so just throw it. - if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) { - throw $e; - } +/** + * + * Copyright 2011 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +if (!function_exists('curl_init')) { + throw new Exception('Facebook needs the CURL PHP extension.'); +} +if (!function_exists('json_decode')) { + throw new Exception('Facebook needs the JSON PHP extension.'); +} + +/** + * Thrown when an API call returns an exception. + * + * @author Naitik Shah <naitik@facebook.com> + */ +class FacebookApiException extends Exception +{ + /** + * The result from the API server that represents the exception information. + */ + protected $result; + + /** + * Make a new API Exception with the given result. + * + * @param Array $result the result from the API server + */ + public function __construct($result) { + $this->result = $result; + + $code = isset($result['error_code']) ? $result['error_code'] : 0; + + if (isset($result['error_description'])) { + // OAuth 2.0 Draft 10 style + $msg = $result['error_description']; + } else if (isset($result['error']) && is_array($result['error'])) { + // OAuth 2.0 Draft 00 style + $msg = $result['error']['message']; + } else if (isset($result['error_msg'])) { + // Rest server style + $msg = $result['error_msg']; + } else { + $msg = 'Unknown Error. Check getResult()'; } + + parent::__construct($msg, $code); } - public function do_get_session($auth_token) { - try { - return $this->api_client->auth_getSession($auth_token, $this->generate_session_secret); - } catch (FacebookRestClientException $e) { - // API_EC_PARAM means we don't have a logged in user, otherwise who - // knows what it means, so just throw it. - if ($e->getCode() != FacebookAPIErrorCodes::API_EC_PARAM) { - throw $e; - } - } + /** + * Return the associated result object returned by the API server. + * + * @returns Array the result from the API server + */ + public function getResult() { + return $this->result; } - // Invalidate the session currently being used, and clear any state associated with it - public function expire_session() { - if ($this->api_client->auth_expireSession()) { - if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) { - $cookies = array('user', 'session_key', 'expires', 'ss'); - foreach ($cookies as $name) { - setcookie($this->api_key . '_' . $name, false, time() - 3600); - unset($_COOKIE[$this->api_key . '_' . $name]); + /** + * Returns the associated type for the error. This will default to + * 'Exception' when a type is not available. + * + * @return String + */ + public function getType() { + if (isset($this->result['error'])) { + $error = $this->result['error']; + if (is_string($error)) { + // OAuth 2.0 Draft 10 style + return $error; + } else if (is_array($error)) { + // OAuth 2.0 Draft 00 style + if (isset($error['type'])) { + return $error['type']; } - setcookie($this->api_key, false, time() - 3600); - unset($_COOKIE[$this->api_key]); } + } + return 'Exception'; + } - // now, clear the rest of the stored state - $this->user = 0; - $this->api_client->session_key = 0; - return true; - } else { - return false; + /** + * To make debugging easier. + * + * @returns String the string representation of the error + */ + public function __toString() { + $str = $this->getType() . ': '; + if ($this->code != 0) { + $str .= $this->code . ': '; } + return $str . $this->message; } +} - public function redirect($url) { - if ($this->in_fb_canvas()) { - echo '<fb:redirect url="' . $url . '"/>'; - } else if (preg_match('/^https?:\/\/([^\/]*\.)?facebook\.com(:\d+)?/i', $url)) { - // make sure facebook.com url's load in the full frame so that we don't - // get a frame within a frame. - echo "<script type=\"text/javascript\">\ntop.location.href = \"$url\";\n</script>"; - } else { - header('Location: ' . $url); +/** + * Provides access to the Facebook Platform. + * + * @author Naitik Shah <naitik@facebook.com> + */ +class Facebook +{ + /** + * Version. + */ + const VERSION = '2.1.2'; + + /** + * Default options for curl. + */ + public static $CURL_OPTS = array( + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 60, + CURLOPT_USERAGENT => 'facebook-php-2.0', + ); + + /** + * List of query parameters that get automatically dropped when rebuilding + * the current URL. + */ + protected static $DROP_QUERY_PARAMS = array( + 'session', + 'signed_request', + ); + + /** + * Maps aliases to Facebook domains. + */ + public static $DOMAIN_MAP = array( + 'api' => 'https://api.facebook.com/', + 'api_video' => 'https://api-video.facebook.com/', + 'api_read' => 'https://api-read.facebook.com/', + 'graph' => 'https://graph.facebook.com/', + 'www' => 'https://www.facebook.com/', + ); + + /** + * The Application ID. + */ + protected $appId; + + /** + * The Application API Secret. + */ + protected $apiSecret; + + /** + * The active user session, if one is available. + */ + protected $session; + + /** + * The data from the signed_request token. + */ + protected $signedRequest; + + /** + * Indicates that we already loaded the session as best as we could. + */ + protected $sessionLoaded = false; + + /** + * Indicates if Cookie support should be enabled. + */ + protected $cookieSupport = false; + + /** + * Base domain for the Cookie. + */ + protected $baseDomain = ''; + + /** + * Indicates if the CURL based @ syntax for file uploads is enabled. + */ + protected $fileUploadSupport = false; + + /** + * Initialize a Facebook Application. + * + * The configuration: + * - appId: the application ID + * - secret: the application secret + * - cookie: (optional) boolean true to enable cookie support + * - domain: (optional) domain for the cookie + * - fileUpload: (optional) boolean indicating if file uploads are enabled + * + * @param Array $config the application configuration + */ + public function __construct($config) { + $this->setAppId($config['appId']); + $this->setApiSecret($config['secret']); + if (isset($config['cookie'])) { + $this->setCookieSupport($config['cookie']); + } + if (isset($config['domain'])) { + $this->setBaseDomain($config['domain']); + } + if (isset($config['fileUpload'])) { + $this->setFileUploadSupport($config['fileUpload']); } - exit; } - public function in_frame() { - return isset($this->fb_params['in_canvas']) || isset($this->fb_params['in_iframe']); + /** + * Set the Application ID. + * + * @param String $appId the Application ID + */ + public function setAppId($appId) { + $this->appId = $appId; + return $this; } - public function in_fb_canvas() { - return isset($this->fb_params['in_canvas']); + + /** + * Get the Application ID. + * + * @return String the Application ID + */ + public function getAppId() { + return $this->appId; } - public function get_loggedin_user() { - return $this->user; + /** + * Set the API Secret. + * + * @param String $appId the API Secret + */ + public function setApiSecret($apiSecret) { + $this->apiSecret = $apiSecret; + return $this; } - public static function current_url() { - return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '?poot=bar'; + /** + * Get the API Secret. + * + * @return String the API Secret + */ + public function getApiSecret() { + return $this->apiSecret; } - public function require_login($next = NULL) { - if ($next === NULL) $next = self::current_url(); - if ($user = $this->get_loggedin_user()) { - return $user; - } - $this->redirect($this->get_login_url($next, $this->in_frame())); + /** + * Set the Cookie Support status. + * + * @param Boolean $cookieSupport the Cookie Support status + */ + public function setCookieSupport($cookieSupport) { + $this->cookieSupport = $cookieSupport; + return $this; } - public function require_install() { - // this was renamed, keeping for compatibility's sake - return $this->require_add(); + /** + * Get the Cookie Support status. + * + * @return Boolean the Cookie Support status + */ + public function useCookieSupport() { + return $this->cookieSupport; } - public function require_add() { - if ($user = $this->get_loggedin_user()) { - if ($this->fb_params['added']) { - return $user; + /** + * Set the base domain for the Cookie. + * + * @param String $domain the base domain + */ + public function setBaseDomain($domain) { + $this->baseDomain = $domain; + return $this; + } + + /** + * Get the base domain for the Cookie. + * + * @return String the base domain + */ + public function getBaseDomain() { + return $this->baseDomain; + } + + /** + * Set the file upload support status. + * + * @param String $domain the base domain + */ + public function setFileUploadSupport($fileUploadSupport) { + $this->fileUploadSupport = $fileUploadSupport; + return $this; + } + + /** + * Get the file upload support status. + * + * @return String the base domain + */ + public function useFileUploadSupport() { + return $this->fileUploadSupport; + } + + /** + * Get the data from a signed_request token + * + * @return String the base domain + */ + public function getSignedRequest() { + if (!$this->signedRequest) { + if (isset($_REQUEST['signed_request'])) { + $this->signedRequest = $this->parseSignedRequest( + $_REQUEST['signed_request']); } } - $this->redirect($this->get_add_url(self::current_url())); + return $this->signedRequest; } - public function require_frame() { - if (!$this->in_frame()) { - $this->redirect($this->get_login_url(self::current_url(), true)); + /** + * Set the Session. + * + * @param Array $session the session + * @param Boolean $write_cookie indicate if a cookie should be written. this + * value is ignored if cookie support has been disabled. + */ + public function setSession($session=null, $write_cookie=true) { + $session = $this->validateSessionObject($session); + $this->sessionLoaded = true; + $this->session = $session; + if ($write_cookie) { + $this->setCookieFromSession($session); + } + return $this; + } + + /** + * Get the session object. This will automatically look for a signed session + * sent via the signed_request, Cookie or Query Parameters if needed. + * + * @return Array the session + */ + public function getSession() { + if (!$this->sessionLoaded) { + $session = null; + $write_cookie = true; + + // try loading session from signed_request in $_REQUEST + $signedRequest = $this->getSignedRequest(); + if ($signedRequest) { + // sig is good, use the signedRequest + $session = $this->createSessionFromSignedRequest($signedRequest); + } + + // try loading session from $_REQUEST + if (!$session && isset($_REQUEST['session'])) { + $session = json_decode( + get_magic_quotes_gpc() + ? stripslashes($_REQUEST['session']) + : $_REQUEST['session'], + true + ); + $session = $this->validateSessionObject($session); + } + + // try loading session from cookie if necessary + if (!$session && $this->useCookieSupport()) { + $cookieName = $this->getSessionCookieName(); + if (isset($_COOKIE[$cookieName])) { + $session = array(); + parse_str(trim( + get_magic_quotes_gpc() + ? stripslashes($_COOKIE[$cookieName]) + : $_COOKIE[$cookieName], + '"' + ), $session); + $session = $this->validateSessionObject($session); + // write only if we need to delete a invalid session cookie + $write_cookie = empty($session); + } + } + + $this->setSession($session, $write_cookie); } + + return $this->session; } - public static function get_facebook_url($subdomain='www') { - return 'http://' . $subdomain . '.facebook.com'; + /** + * Get the UID from the session. + * + * @return String the UID if available + */ + public function getUser() { + $session = $this->getSession(); + return $session ? $session['uid'] : null; } - public function get_install_url($next=null) { - // this was renamed, keeping for compatibility's sake - return $this->get_add_url($next); + /** + * Gets a OAuth access token. + * + * @return String the access token + */ + public function getAccessToken() { + $session = $this->getSession(); + // either user session signed, or app signed + if ($session) { + return $session['access_token']; + } else { + return $this->getAppId() .'|'. $this->getApiSecret(); + } } - public function get_add_url($next=null) { - return self::get_facebook_url().'/add.php?api_key='.$this->api_key . - ($next ? '&next=' . urlencode($next) : ''); + /** + * Get a Login URL for use with redirects. By default, full page redirect is + * assumed. If you are using the generated URL with a window.open() call in + * JavaScript, you can pass in display=popup as part of the $params. + * + * The parameters: + * - next: the url to go to after a successful login + * - cancel_url: the url to go to after the user cancels + * - req_perms: comma separated list of requested extended perms + * - display: can be "page" (default, full page) or "popup" + * + * @param Array $params provide custom parameters + * @return String the URL for the login flow + */ + public function getLoginUrl($params=array()) { + $currentUrl = $this->getCurrentUrl(); + return $this->getUrl( + 'www', + 'login.php', + array_merge(array( + 'api_key' => $this->getAppId(), + 'cancel_url' => $currentUrl, + 'display' => 'page', + 'fbconnect' => 1, + 'next' => $currentUrl, + 'return_session' => 1, + 'session_version' => 3, + 'v' => '1.0', + ), $params) + ); } - public function get_login_url($next, $canvas) { - return self::get_facebook_url().'/login.php?v=1.0&api_key=' . $this->api_key . - ($next ? '&next=' . urlencode($next) : '') . - '&req_perms=email' . - ($canvas ? '&canvas' : ''); + /** + * Get a Logout URL suitable for use with redirects. + * + * The parameters: + * - next: the url to go to after a successful logout + * + * @param Array $params provide custom parameters + * @return String the URL for the logout flow + */ + public function getLogoutUrl($params=array()) { + return $this->getUrl( + 'www', + 'logout.php', + array_merge(array( + 'next' => $this->getCurrentUrl(), + 'access_token' => $this->getAccessToken(), + ), $params) + ); } - public static function generate_sig($params_array, $secret) { - $str = ''; + /** + * Get a login status URL to fetch the status from facebook. + * + * The parameters: + * - ok_session: the URL to go to if a session is found + * - no_session: the URL to go to if the user is not connected + * - no_user: the URL to go to if the user is not signed into facebook + * + * @param Array $params provide custom parameters + * @return String the URL for the logout flow + */ + public function getLoginStatusUrl($params=array()) { + return $this->getUrl( + 'www', + 'extern/login_status.php', + array_merge(array( + 'api_key' => $this->getAppId(), + 'no_session' => $this->getCurrentUrl(), + 'no_user' => $this->getCurrentUrl(), + 'ok_session' => $this->getCurrentUrl(), + 'session_version' => 3, + ), $params) + ); + } - ksort($params_array); - // Note: make sure that the signature parameter is not already included in - // $params_array. - foreach ($params_array as $k=>$v) { - $str .= "$k=$v"; + /** + * Make an API call. + * + * @param Array $params the API call parameters + * @return the decoded response + */ + public function api(/* polymorphic */) { + $args = func_get_args(); + if (is_array($args[0])) { + return $this->_restserver($args[0]); + } else { + return call_user_func_array(array($this, '_graph'), $args); } - $str .= $secret; - - return md5($str); } - public function set_user($user, $session_key, $expires=null, $session_secret=null) { - if (!$this->in_fb_canvas() && (!isset($_COOKIE[$this->api_key . '_user']) - || $_COOKIE[$this->api_key . '_user'] != $user)) { - $this->set_cookies($user, $session_key, $expires, $session_secret); + /** + * Invoke the old restserver.php endpoint. + * + * @param Array $params method call object + * @return the decoded response object + * @throws FacebookApiException + */ + protected function _restserver($params) { + // generic application level parameters + $params['api_key'] = $this->getAppId(); + $params['format'] = 'json-strings'; + + $result = json_decode($this->_oauthRequest( + $this->getApiUrl($params['method']), + $params + ), true); + + // results are returned, errors are thrown + if (is_array($result) && isset($result['error_code'])) { + throw new FacebookApiException($result); } - $this->user = $user; - $this->api_client->session_key = $session_key; - $this->session_expires = $expires; + return $result; } - public function set_cookies($user, $session_key, $expires=null, $session_secret=null) { - $cookies = array(); - $cookies['user'] = $user; - $cookies['session_key'] = $session_key; - if ($expires != null) { - $cookies['expires'] = $expires; + /** + * Invoke the Graph API. + * + * @param String $path the path (required) + * @param String $method the http method (default 'GET') + * @param Array $params the query/post data + * @return the decoded response object + * @throws FacebookApiException + */ + protected function _graph($path, $method='GET', $params=array()) { + if (is_array($method) && empty($params)) { + $params = $method; + $method = 'GET'; + } + $params['method'] = $method; // method override as we always do a POST + + $result = json_decode($this->_oauthRequest( + $this->getUrl('graph', $path), + $params + ), true); + + // results are returned, errors are thrown + if (is_array($result) && isset($result['error'])) { + $e = new FacebookApiException($result); + switch ($e->getType()) { + // OAuth 2.0 Draft 00 style + case 'OAuthException': + // OAuth 2.0 Draft 10 style + case 'invalid_token': + $this->setSession(null); + } + throw $e; } - if ($session_secret != null) { - $cookies['ss'] = $session_secret; + return $result; + } + + /** + * Make a OAuth Request + * + * @param String $path the path (required) + * @param Array $params the query/post data + * @return the decoded response object + * @throws FacebookApiException + */ + protected function _oauthRequest($url, $params) { + if (!isset($params['access_token'])) { + $params['access_token'] = $this->getAccessToken(); } - foreach ($cookies as $name => $val) { - setcookie($this->api_key . '_' . $name, $val, (int)$expires); - $_COOKIE[$this->api_key . '_' . $name] = $val; + + // json_encode all params values that are not strings + foreach ($params as $key => $value) { + if (!is_string($value)) { + $params[$key] = json_encode($value); + } } - $sig = self::generate_sig($cookies, $this->secret); - setcookie($this->api_key, $sig, (int)$expires); - $_COOKIE[$this->api_key] = $sig; + return $this->makeRequest($url, $params); } /** - * Tries to undo the badness of magic quotes as best we can - * @param string $val Should come directly from $_GET, $_POST, etc. - * @return string val without added slashes + * Makes an HTTP request. This method can be overriden by subclasses if + * developers want to do fancier things or use something other than curl to + * make the request. + * + * @param String $url the URL to make the request to + * @param Array $params the parameters to use for the POST body + * @param CurlHandler $ch optional initialized curl handle + * @return String the response text */ - public static function no_magic_quotes($val) { - if (get_magic_quotes_gpc()) { - return stripslashes($val); + protected function makeRequest($url, $params, $ch=null) { + if (!$ch) { + $ch = curl_init(); + } + + $opts = self::$CURL_OPTS; + if ($this->useFileUploadSupport()) { + $opts[CURLOPT_POSTFIELDS] = $params; + } else { + $opts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&'); + } + $opts[CURLOPT_URL] = $url; + + // disable the 'Expect: 100-continue' behaviour. This causes CURL to wait + // for 2 seconds if the server does not support this header. + if (isset($opts[CURLOPT_HTTPHEADER])) { + $existing_headers = $opts[CURLOPT_HTTPHEADER]; + $existing_headers[] = 'Expect:'; + $opts[CURLOPT_HTTPHEADER] = $existing_headers; } else { - return $val; + $opts[CURLOPT_HTTPHEADER] = array('Expect:'); + } + + curl_setopt_array($ch, $opts); + $result = curl_exec($ch); + + if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT + self::errorLog('Invalid or no certificate authority found, using bundled information'); + curl_setopt($ch, CURLOPT_CAINFO, + dirname(__FILE__) . '/fb_ca_chain_bundle.crt'); + $result = curl_exec($ch); } + + if ($result === false) { + $e = new FacebookApiException(array( + 'error_code' => curl_errno($ch), + 'error' => array( + 'message' => curl_error($ch), + 'type' => 'CurlException', + ), + )); + curl_close($ch); + throw $e; + } + curl_close($ch); + return $result; } - public function get_valid_fb_params($params, $timeout=null, $namespace='fb_sig') { - $prefix = $namespace . '_'; - $prefix_len = strlen($prefix); - $fb_params = array(); - foreach ($params as $name => $val) { - if (strpos($name, $prefix) === 0) { - $fb_params[substr($name, $prefix_len)] = self::no_magic_quotes($val); + /** + * The name of the Cookie that contains the session. + * + * @return String the cookie name + */ + protected function getSessionCookieName() { + return 'fbs_' . $this->getAppId(); + } + + /** + * Set a JS Cookie based on the _passed in_ session. It does not use the + * currently stored session -- you need to explicitly pass it in. + * + * @param Array $session the session to use for setting the cookie + */ + protected function setCookieFromSession($session=null) { + if (!$this->useCookieSupport()) { + return; + } + + $cookieName = $this->getSessionCookieName(); + $value = 'deleted'; + $expires = time() - 3600; + $domain = $this->getBaseDomain(); + if ($session) { + $value = '"' . http_build_query($session, null, '&') . '"'; + if (isset($session['base_domain'])) { + $domain = $session['base_domain']; } + $expires = $session['expires']; } - if ($timeout && (!isset($fb_params['time']) || time() - $fb_params['time'] > $timeout)) { - return array(); + + // prepend dot if a domain is found + if ($domain) { + $domain = '.' . $domain; } - if (!isset($params[$namespace]) || (!$this->verify_signature($fb_params, $params[$namespace]))) { - return array(); + + // if an existing cookie is not set, we dont need to delete it + if ($value == 'deleted' && empty($_COOKIE[$cookieName])) { + return; } - return $fb_params; + + if (headers_sent()) { + self::errorLog('Could not set cookie. Headers already sent.'); + + // ignore for code coverage as we will never be able to setcookie in a CLI + // environment + // @codeCoverageIgnoreStart + } else { + setcookie($cookieName, $value, $expires, '/', $domain); + } + // @codeCoverageIgnoreEnd } - public function verify_signature($fb_params, $expected_sig) { - return self::generate_sig($fb_params, $this->secret) == $expected_sig; + /** + * Validates a session_version=3 style session object. + * + * @param Array $session the session object + * @return Array the session object if it validates, null otherwise + */ + protected function validateSessionObject($session) { + // make sure some essential fields exist + if (is_array($session) && + isset($session['uid']) && + isset($session['access_token']) && + isset($session['sig'])) { + // validate the signature + $session_without_sig = $session; + unset($session_without_sig['sig']); + $expected_sig = self::generateSignature( + $session_without_sig, + $this->getApiSecret() + ); + if ($session['sig'] != $expected_sig) { + self::errorLog('Got invalid session signature in cookie.'); + $session = null; + } + // check expiry time + } else { + $session = null; + } + return $session; } - public function encode_validationError($summary, $message) { - return json_encode( - array('errorCode' => FACEBOOK_API_VALIDATION_ERROR, - 'errorTitle' => $summary, - 'errorMessage' => $message)); + /** + * Returns something that looks like our JS session object from the + * signed token's data + * + * TODO: Nuke this once the login flow uses OAuth2 + * + * @param Array the output of getSignedRequest + * @return Array Something that will work as a session + */ + protected function createSessionFromSignedRequest($data) { + if (!isset($data['oauth_token'])) { + return null; + } + + $session = array( + 'uid' => $data['user_id'], + 'access_token' => $data['oauth_token'], + 'expires' => $data['expires'], + ); + + // put a real sig, so that validateSignature works + $session['sig'] = self::generateSignature( + $session, + $this->getApiSecret() + ); + + return $session; } - public function encode_multiFeedStory($feed, $next) { - return json_encode( - array('method' => 'multiFeedStory', - 'content' => - array('next' => $next, - 'feed' => $feed))); + /** + * Parses a signed_request and validates the signature. + * Then saves it in $this->signed_data + * + * @param String A signed token + * @param Boolean Should we remove the parts of the payload that + * are used by the algorithm? + * @return Array the payload inside it or null if the sig is wrong + */ + protected function parseSignedRequest($signed_request) { + list($encoded_sig, $payload) = explode('.', $signed_request, 2); + + // decode the data + $sig = self::base64UrlDecode($encoded_sig); + $data = json_decode(self::base64UrlDecode($payload), true); + + if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') { + self::errorLog('Unknown algorithm. Expected HMAC-SHA256'); + return null; + } + + // check sig + $expected_sig = hash_hmac('sha256', $payload, + $this->getApiSecret(), $raw = true); + if ($sig !== $expected_sig) { + self::errorLog('Bad Signed JSON signature!'); + return null; + } + + return $data; } - public function encode_feedStory($feed, $next) { - return json_encode( - array('method' => 'feedStory', - 'content' => - array('next' => $next, - 'feed' => $feed))); + /** + * Build the URL for api given parameters. + * + * @param $method String the method name. + * @return String the URL for the given parameters + */ + protected function getApiUrl($method) { + static $READ_ONLY_CALLS = + array('admin.getallocation' => 1, + 'admin.getappproperties' => 1, + 'admin.getbannedusers' => 1, + 'admin.getlivestreamvialink' => 1, + 'admin.getmetrics' => 1, + 'admin.getrestrictioninfo' => 1, + 'application.getpublicinfo' => 1, + 'auth.getapppublickey' => 1, + 'auth.getsession' => 1, + 'auth.getsignedpublicsessiondata' => 1, + 'comments.get' => 1, + 'connect.getunconnectedfriendscount' => 1, + 'dashboard.getactivity' => 1, + 'dashboard.getcount' => 1, + 'dashboard.getglobalnews' => 1, + 'dashboard.getnews' => 1, + 'dashboard.multigetcount' => 1, + 'dashboard.multigetnews' => 1, + 'data.getcookies' => 1, + 'events.get' => 1, + 'events.getmembers' => 1, + 'fbml.getcustomtags' => 1, + 'feed.getappfriendstories' => 1, + 'feed.getregisteredtemplatebundlebyid' => 1, + 'feed.getregisteredtemplatebundles' => 1, + 'fql.multiquery' => 1, + 'fql.query' => 1, + 'friends.arefriends' => 1, + 'friends.get' => 1, + 'friends.getappusers' => 1, + 'friends.getlists' => 1, + 'friends.getmutualfriends' => 1, + 'gifts.get' => 1, + 'groups.get' => 1, + 'groups.getmembers' => 1, + 'intl.gettranslations' => 1, + 'links.get' => 1, + 'notes.get' => 1, + 'notifications.get' => 1, + 'pages.getinfo' => 1, + 'pages.isadmin' => 1, + 'pages.isappadded' => 1, + 'pages.isfan' => 1, + 'permissions.checkavailableapiaccess' => 1, + 'permissions.checkgrantedapiaccess' => 1, + 'photos.get' => 1, + 'photos.getalbums' => 1, + 'photos.gettags' => 1, + 'profile.getinfo' => 1, + 'profile.getinfooptions' => 1, + 'stream.get' => 1, + 'stream.getcomments' => 1, + 'stream.getfilters' => 1, + 'users.getinfo' => 1, + 'users.getloggedinuser' => 1, + 'users.getstandardinfo' => 1, + 'users.hasapppermission' => 1, + 'users.isappuser' => 1, + 'users.isverified' => 1, + 'video.getuploadlimits' => 1); + $name = 'api'; + if (isset($READ_ONLY_CALLS[strtolower($method)])) { + $name = 'api_read'; + } else if (strtolower($method) == 'video.upload') { + $name = 'api_video'; + } + return self::getUrl($name, 'restserver.php'); } - public function create_templatizedFeedStory($title_template, $title_data=array(), - $body_template='', $body_data = array(), $body_general=null, - $image_1=null, $image_1_link=null, - $image_2=null, $image_2_link=null, - $image_3=null, $image_3_link=null, - $image_4=null, $image_4_link=null) { - return array('title_template'=> $title_template, - 'title_data' => $title_data, - 'body_template'=> $body_template, - 'body_data' => $body_data, - 'body_general' => $body_general, - 'image_1' => $image_1, - 'image_1_link' => $image_1_link, - 'image_2' => $image_2, - 'image_2_link' => $image_2_link, - 'image_3' => $image_3, - 'image_3_link' => $image_3_link, - 'image_4' => $image_4, - 'image_4_link' => $image_4_link); + /** + * Build the URL for given domain alias, path and parameters. + * + * @param $name String the name of the domain + * @param $path String optional path (without a leading slash) + * @param $params Array optional query parameters + * @return String the URL for the given parameters + */ + protected function getUrl($name, $path='', $params=array()) { + $url = self::$DOMAIN_MAP[$name]; + if ($path) { + if ($path[0] === '/') { + $path = substr($path, 1); + } + $url .= $path; + } + if ($params) { + $url .= '?' . http_build_query($params, null, '&'); + } + return $url; } + /** + * Returns the Current URL, stripping it of known FB parameters that should + * not persist. + * + * @return String the current URL + */ + protected function getCurrentUrl() { + $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' + ? 'https://' + : 'http://'; + $currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $parts = parse_url($currentUrl); + + // drop known fb params + $query = ''; + if (!empty($parts['query'])) { + $params = array(); + parse_str($parts['query'], $params); + foreach(self::$DROP_QUERY_PARAMS as $key) { + unset($params[$key]); + } + if (!empty($params)) { + $query = '?' . http_build_query($params, null, '&'); + } + } -} + // use port if non default + $port = + isset($parts['port']) && + (($protocol === 'http://' && $parts['port'] !== 80) || + ($protocol === 'https://' && $parts['port'] !== 443)) + ? ':' . $parts['port'] : ''; + + // rebuild + return $protocol . $parts['host'] . $port . $parts['path'] . $query; + } + + /** + * Generate a signature for the given params and secret. + * + * @param Array $params the parameters to sign + * @param String $secret the secret to sign with + * @return String the generated signature + */ + protected static function generateSignature($params, $secret) { + // work with sorted data + ksort($params); + + // generate the base string + $base_string = ''; + foreach($params as $key => $value) { + $base_string .= $key . '=' . $value; + } + $base_string .= $secret; + + return md5($base_string); + } + /** + * Prints to the error log if you aren't in command line mode. + * + * @param String log message + */ + protected static function errorLog($msg) { + // disable error log if we are running in a CLI environment + // @codeCoverageIgnoreStart + if (php_sapi_name() != 'cli') { + error_log($msg); + } + // uncomment this if you want to see the errors on the page + // print 'error_log: '.$msg."\n"; + // @codeCoverageIgnoreEnd + } + + /** + * Base64 encoding that doesn't need to be urlencode()ed. + * Exactly the same as base64_encode except it uses + * - instead of + + * _ instead of / + * + * @param String base64UrlEncodeded string + */ + protected static function base64UrlDecode($input) { + return base64_decode(strtr($input, '-_', '+/')); + } +} diff --git a/modules/authfacebook/extlibinc/facebookapi_php5_restlib.php b/modules/authfacebook/extlibinc/facebookapi_php5_restlib.php deleted file mode 100755 index ab75935ad07858b989199526c46603a8cf8197dc..0000000000000000000000000000000000000000 --- a/modules/authfacebook/extlibinc/facebookapi_php5_restlib.php +++ /dev/null @@ -1,1890 +0,0 @@ -<?php -// Copyright 2004-2008 Facebook. All Rights Reserved. -// -// +---------------------------------------------------------------------------+ -// | Facebook Platform PHP5 client | -// +---------------------------------------------------------------------------+ -// | Copyright (c) 2007-2008 Facebook, Inc. | -// | All rights reserved. | -// | | -// | Redistribution and use in source and binary forms, with or without | -// | modification, are permitted provided that the following conditions | -// | are met: | -// | | -// | 1. Redistributions of source code must retain the above copyright | -// | notice, this list of conditions and the following disclaimer. | -// | 2. Redistributions in binary form must reproduce the above copyright | -// | notice, this list of conditions and the following disclaimer in the | -// | documentation and/or other materials provided with the distribution. | -// | | -// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | -// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | -// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | -// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | -// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | -// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | -// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | -// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | -// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | -// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | -// +---------------------------------------------------------------------------+ -// | For help with this library, contact developers-help@facebook.com | -// +---------------------------------------------------------------------------+ -// - -class FacebookRestClient { - public $secret; - public $session_key; - public $api_key; - public $friends_list; // to save making the friends.get api call, this will get prepopulated on canvas pages - public $added; // to save making the users.isAppAdded api call, this will get prepopulated on canvas pages - public $batch_mode; - private $batch_queue; - private $call_as_apikey; - - const BATCH_MODE_DEFAULT = 0; - const BATCH_MODE_SERVER_PARALLEL = 0; - const BATCH_MODE_SERIAL_ONLY = 2; - - /** - * Create the client. - * @param string $session_key if you haven't gotten a session key yet, leave - * this as null and then set it later by just - * directly accessing the $session_key member - * variable. - */ - public function __construct($api_key, $secret, $session_key=null) { - $this->secret = $secret; - $this->session_key = $session_key; - $this->api_key = $api_key; - $this->batch_mode = FacebookRestClient::BATCH_MODE_DEFAULT; - $this->last_call_id = 0; - $this->call_as_apikey = ''; - $this->server_addr = Facebook::get_facebook_url('api') . '/restserver.php'; - if (!empty($GLOBALS['facebook_config']['debug'])) { - $this->cur_id = 0; - ?> -<script type="text/javascript"> -var types = ['params', 'xml', 'php', 'sxml']; -function getStyle(elem, style) { - if (elem.getStyle) { - return elem.getStyle(style); - } else { - return elem.style[style]; - } -} -function setStyle(elem, style, value) { - if (elem.setStyle) { - elem.setStyle(style, value); - } else { - elem.style[style] = value; - } -} -function toggleDisplay(id, type) { - for (var i = 0; i < types.length; i++) { - var t = types[i]; - var pre = document.getElementById(t + id); - if (pre) { - if (t != type || getStyle(pre, 'display') == 'block') { - setStyle(pre, 'display', 'none'); - } else { - setStyle(pre, 'display', 'block'); - } - } - } - return false; -} -</script> -<?php - } - } - - - /** - * Start a batch operation. - */ - public function begin_batch() { - if($this->batch_queue !== null) - { - throw new FacebookRestClientException(FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED, - FacebookAPIErrorCodes::$api_error_descriptions[FacebookAPIErrorCodes::API_EC_BATCH_ALREADY_STARTED]); - } - - $this->batch_queue = array(); - } - - /* - * End current batch operation - */ - public function end_batch() { - if($this->batch_queue === null) { - throw new FacebookRestClientException(FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED, - FacebookAPIErrorCodes::$api_error_descriptions[FacebookAPIErrorCodes::API_EC_BATCH_NOT_STARTED]); - } - - $this->execute_server_side_batch(); - - $this->batch_queue = null; - } - - - private function execute_server_side_batch() { - - - $item_count = count($this->batch_queue); - $method_feed = array(); - foreach($this->batch_queue as $batch_item) { - $method_feed[] = $this->create_post_string($batch_item['m'], $batch_item['p']); - } - - $method_feed_json = json_encode($method_feed); - - $serial_only = $this->batch_mode == FacebookRestClient::BATCH_MODE_SERIAL_ONLY ; - $params = array('method_feed' => $method_feed_json, 'serial_only' => $serial_only); - if ($this->call_as_apikey) { - $params['call_as_apikey'] = $this->call_as_apikey; - } - - $xml = $this->post_request('batch.run', $params); - - $result = $this->convert_xml_to_result($xml, 'batch.run', $params); - - - if (is_array($result) && isset($result['error_code'])) { - throw new FacebookRestClientException($result['error_msg'], $result['error_code']); - } - - for($i = 0; $i < $item_count; $i++) { - $batch_item = $this->batch_queue[$i]; - $batch_item_result_xml = $result[$i]; - $batch_item_result = $this->convert_xml_to_result($batch_item_result_xml, $batch_item['m'], $batch_item['p']); - - if (is_array($batch_item_result) && isset($batch_item_result['error_code'])) { - throw new FacebookRestClientException($batch_item_result['error_msg'], $batch_item_result['error_code']); - } - $batch_item['r'] = $batch_item_result; - } - } - - public function begin_permissions_mode($permissions_apikey) { - $this->call_as_apikey = $permissions_apikey; - } - - public function end_permissions_mode() { - $this->call_as_apikey = ''; - } - - /** - * Returns the session information available after current user logs in. - * @param string $auth_token the token returned by auth_createToken or - * passed back to your callback_url. - * @param bool $generate_session_secret whether the session returned should include a session secret - * - * @return assoc array containing session_key, uid - */ - public function auth_getSession($auth_token, $generate_session_secret=false) { - //Check if we are in batch mode - if($this->batch_queue === null) { - $result = $this->call_method('facebook.auth.getSession', - array('auth_token' => $auth_token, 'generate_session_secret' => $generate_session_secret)); - $this->session_key = $result['session_key']; - - if (!empty($result['secret']) && !$generate_session_secret) { - // desktop apps have a special secret - $this->secret = $result['secret']; - } - return $result; - } - } - - /** - * Generates a session specific secret. This is for integration with client-side API calls, such as the - * JS library. - * @error API_EC_PARAM_SESSION_KEY - * API_EC_PARAM_UNKNOWN - * @return session secret for the current promoted session - */ - public function auth_promoteSession() { - return $this->call_method('facebook.auth.promoteSession', array()); - } - - /** - * Expires the session that is currently being used. If this call is successful, no further calls to the - * API (which require a session) can be made until a valid session is created. - * - * @return bool true if session expiration was successful, false otherwise - */ - public function auth_expireSession() { - return $this->call_method('facebook.auth.expireSession', array()); - } - - /** - * Returns events according to the filters specified. - * @param int $uid Optional: User associated with events. - * A null parameter will default to the session user. - * @param array $eids Optional: Filter by these event ids. - * A null parameter will get all events for the user. - * @param int $start_time Optional: Filter with this UTC as lower bound. - * A null or zero parameter indicates no lower bound. - * @param int $end_time Optional: Filter with this UTC as upper bound. - * A null or zero parameter indicates no upper bound. - * @param string $rsvp_status Optional: Only show events where the given uid - * has this rsvp status. This only works if you have specified a value for - * $uid. Values are as in events.getMembers. Null indicates to ignore - * rsvp status when filtering. - * @return array of events - */ - public function &events_get($uid, $eids, $start_time, $end_time, $rsvp_status) { - return $this->call_method('facebook.events.get', - array( - 'uid' => $uid, - 'eids' => $eids, - 'start_time' => $start_time, - 'end_time' => $end_time, - 'rsvp_status' => $rsvp_status)); - } - - /** - * Returns membership list data associated with an event - * @param int $eid : event id - * @return assoc array of four membership lists, with keys 'attending', - * 'unsure', 'declined', and 'not_replied' - */ - public function &events_getMembers($eid) { - return $this->call_method('facebook.events.getMembers', - array('eid' => $eid)); - } - - /** - * Makes an FQL query. This is a generalized way of accessing all the data - * in the API, as an alternative to most of the other method calls. More - * info at http://developers.facebook.com/documentation.php?v=1.0&doc=fql - * @param string $query the query to evaluate - * @return generalized array representing the results - */ - public function &fql_query($query) { - return $this->call_method('facebook.fql.query', - array('query' => $query)); - } - - public function &feed_publishStoryToUser($title, $body, - $image_1=null, $image_1_link=null, - $image_2=null, $image_2_link=null, - $image_3=null, $image_3_link=null, - $image_4=null, $image_4_link=null) { - return $this->call_method('facebook.feed.publishStoryToUser', - array('title' => $title, - 'body' => $body, - 'image_1' => $image_1, - 'image_1_link' => $image_1_link, - 'image_2' => $image_2, - 'image_2_link' => $image_2_link, - 'image_3' => $image_3, - 'image_3_link' => $image_3_link, - 'image_4' => $image_4, - 'image_4_link' => $image_4_link)); - } - - public function &feed_publishActionOfUser($title, $body, - $image_1=null, $image_1_link=null, - $image_2=null, $image_2_link=null, - $image_3=null, $image_3_link=null, - $image_4=null, $image_4_link=null) { - return $this->call_method('facebook.feed.publishActionOfUser', - array('title' => $title, - 'body' => $body, - 'image_1' => $image_1, - 'image_1_link' => $image_1_link, - 'image_2' => $image_2, - 'image_2_link' => $image_2_link, - 'image_3' => $image_3, - 'image_3_link' => $image_3_link, - 'image_4' => $image_4, - 'image_4_link' => $image_4_link)); - } - - public function &feed_publishTemplatizedAction($title_template, $title_data, - $body_template, $body_data, $body_general, - $image_1=null, $image_1_link=null, - $image_2=null, $image_2_link=null, - $image_3=null, $image_3_link=null, - $image_4=null, $image_4_link=null, - $target_ids='', $page_actor_id=null) { - return $this->call_method('facebook.feed.publishTemplatizedAction', - array('title_template' => $title_template, - 'title_data' => $title_data, - 'body_template' => $body_template, - 'body_data' => $body_data, - 'body_general' => $body_general, - 'image_1' => $image_1, - 'image_1_link' => $image_1_link, - 'image_2' => $image_2, - 'image_2_link' => $image_2_link, - 'image_3' => $image_3, - 'image_3_link' => $image_3_link, - 'image_4' => $image_4, - 'image_4_link' => $image_4_link, - 'target_ids' => $target_ids, - 'page_actor_id' => $page_actor_id)); - } - - /** - * Returns whether or not pairs of users are friends. - * Note that the Facebook friend relationship is symmetric. - * @param array $uids1: array of ids (id_1, id_2,...) of some length X - * @param array $uids2: array of ids (id_A, id_B,...) of SAME length X - * @return array of uid pairs with bool, true if pair are friends, e.g. - * array( 0 => array('uid1' => id_1, 'uid2' => id_A, 'are_friends' => 1), - * 1 => array('uid1' => id_2, 'uid2' => id_B, 'are_friends' => 0) - * ...) - */ - public function &friends_areFriends($uids1, $uids2) { - return $this->call_method('facebook.friends.areFriends', - array('uids1'=>$uids1, 'uids2'=>$uids2)); - } - - /** - * Returns the friends of the current session user. - * @return array of friends - */ - public function &friends_get() { - if (isset($this->friends_list)) { - return $this->friends_list; - } - return $this->call_method('facebook.friends.get', array()); - } - - /** - * Returns the friends of the session user, who are also users - * of the calling application. - * @return array of friends - */ - public function &friends_getAppUsers() { - return $this->call_method('facebook.friends.getAppUsers', array()); - } - - /** - * Returns groups according to the filters specified. - * @param int $uid Optional: User associated with groups. - * A null parameter will default to the session user. - * @param array $gids Optional: group ids to query. - * A null parameter will get all groups for the user. - * @return array of groups - */ - public function &groups_get($uid, $gids) { - return $this->call_method('facebook.groups.get', - array( - 'uid' => $uid, - 'gids' => $gids)); - } - - /** - * Returns the membership list of a group - * @param int $gid : Group id - * @return assoc array of four membership lists, with keys - * 'members', 'admins', 'officers', and 'not_replied' - */ - public function &groups_getMembers($gid) { - return $this->call_method('facebook.groups.getMembers', - array('gid' => $gid)); - } - - /** - * Returns cookies according to the filters specified. - * @param int $uid Required: User for which the cookies are needed. - * @param string $name Optional: - * A null parameter will get all cookies for the user. - * @return array of cookies - */ - public function data_getCookies($uid, $name) { - return $this->call_method('facebook.data.getCookies', - array( - 'uid' => $uid, - 'name' => $name)); - } - - /** - * Sets cookies according to the params specified. - * @param int $uid Required: User for which the cookies are needed. - * @param string $name Required: name of the cookie - * @param string $value Optional if expires specified and is in the past - * @param int$expires Optional - * @param string $path Optional - * - * @return bool - */ - public function data_setCookie($uid, $name, $value, $expires, $path) { - return $this->call_method('facebook.data.setCookie', - array( - 'uid' => $uid, - 'name' => $name, - 'value' => $value, - 'expires' => $expires, - 'path' => $path)); - } - - /** - * Permissions API - */ - - /** - * Checks API-access granted by self to the specified application - * @param string $permissions_apikey: Required - * - * @return array: API methods/namespaces which are allowed access - */ - public function permissions_checkGrantedApiAccess($permissions_apikey) { - return $this->call_method('facebook.permissions.checkGrantedApiAccess', - array( - 'permissions_apikey' => $permissions_apikey)); - } - - /** - * Checks API-access granted to self by the specified application - * @param string $permissions_apikey: Required - * - * @return array: API methods/namespaces which are allowed access - */ - public function permissions_checkAvailableApiAccess($permissions_apikey) { - return $this->call_method('facebook.permissions.checkAvailableApiAccess', - array( - 'permissions_apikey' => $permissions_apikey)); - } - - /** - * Grant API-access to the specified methods/namespaces to the specified application - * @param string $permissions_apikey: Required - * @param array(string) : Optional: API methods/namespaces to be allowed - * - * @return array: API methods/namespaces which are allowed access - */ - public function permissions_grantApiAccess($permissions_apikey, $method_arr) { - return $this->call_method('facebook.permissions.grantApiAccess', - array( - 'permissions_apikey' => $permissions_apikey, - 'method_arr' => $method_arr)); - } - - /** - * Revoke API-access granted to the specified application - * @param string $permissions_apikey: Required - * - * @return bool - */ - public function permissions_revokeApiAccess($permissions_apikey) { - return $this->call_method('facebook.permissions.revokeApiAccess', - array( - 'permissions_apikey' => $permissions_apikey)); - } - - /** - * Returns the outstanding notifications for the session user. - * @return assoc array of - * notification count objects for 'messages', 'pokes' and 'shares', - * a uid list of 'friend_requests', a gid list of 'group_invites', - * and an eid list of 'event_invites' - */ - public function ¬ifications_get() { - return $this->call_method('facebook.notifications.get', array()); - } - - /** - * Sends a notification to the specified users. - * @return (nothing) - */ - public function ¬ifications_send($to_ids, $notification) { - return $this->call_method('facebook.notifications.send', - array('to_ids' => $to_ids, 'notification' => $notification)); - } - - /** - * Sends an email to the specified user of the application. - * @param array $recipients : id of the recipients - * @param string $subject : subject of the email - * @param string $text : (plain text) body of the email - * @param string $fbml : fbml markup if you want an html version of the email - * @return comma separated list of successful recipients - */ - public function ¬ifications_sendEmail($recipients, $subject, $text, $fbml) { - return $this->call_method('facebook.notifications.sendEmail', - array('recipients' => $recipients, - 'subject' => $subject, - 'text' => $text, - 'fbml' => $fbml)); - } - - /** - * Returns the requested info fields for the requested set of pages - * @param array $page_ids an array of page ids - * @param array $fields an array of strings describing the info fields desired - * @param int $uid Optionally, limit results to pages of which this user is a fan. - * @param string type limits results to a particular type of page. - * @return array of pages - */ - public function &pages_getInfo($page_ids, $fields, $uid, $type) { - return $this->call_method('facebook.pages.getInfo', array('page_ids' => $page_ids, 'fields' => $fields, 'uid' => $uid, 'type' => $type)); - } - - /** - * Returns true if logged in user is an admin for the passed page - * @param int $page_id target page id - * @return boolean - */ - public function &pages_isAdmin($page_id) { - return $this->call_method('facebook.pages.isAdmin', array('page_id' => $page_id)); - } - - /** - * Returns whether or not the page corresponding to the current session object has the app installed - * @return boolean - */ - public function &pages_isAppAdded() { - if (isset($this->added)) { - return $this->added; - } - return $this->call_method('facebook.pages.isAppAdded', array()); - } - - /** - * Returns true if logged in user is a fan for the passed page - * @param int $page_id target page id - * @param int $uid user to compare. If empty, the logged in user. - * @return bool - */ - public function &pages_isFan($page_id, $uid) { - return $this->call_method('facebook.pages.isFan', array('page_id' => $page_id, 'uid' => $uid)); - } - - /** - * Returns photos according to the filters specified. - * @param int $subj_id Optional: Filter by uid of user tagged in the photos. - * @param int $aid Optional: Filter by an album, as returned by - * photos_getAlbums. - * @param array $pids Optional: Restrict to a list of pids - * Note that at least one of these parameters needs to be specified, or an - * error is returned. - * @return array of photo objects. - */ - public function &photos_get($subj_id, $aid, $pids) { - return $this->call_method('facebook.photos.get', - array('subj_id' => $subj_id, 'aid' => $aid, 'pids' => $pids)); - } - - /** - * Returns the albums created by the given user. - * @param int $uid Optional: the uid of the user whose albums you want. - * A null value will return the albums of the session user. - * @param array $aids Optional: a list of aids to restrict the query. - * Note that at least one of the (uid, aids) parameters must be specified. - * @returns an array of album objects. - */ - public function &photos_getAlbums($uid, $aids) { - return $this->call_method('facebook.photos.getAlbums', - array('uid' => $uid, - 'aids' => $aids)); - } - - /** - * Returns the tags on all photos specified. - * @param string $pids : a list of pids to query - * @return array of photo tag objects, with include pid, subject uid, - * and two floating-point numbers (xcoord, ycoord) for tag pixel location - */ - public function &photos_getTags($pids) { - return $this->call_method('facebook.photos.getTags', - array('pids' => $pids)); - } - - - /** - * Returns the requested info fields for the requested set of users - * @param array $uids an array of user ids - * @param array $fields an array of strings describing the info fields desired - * @return array of users - */ - public function &users_getInfo($uids, $fields) { - return $this->call_method('facebook.users.getInfo', array('uids' => $uids, 'fields' => $fields)); - } - - /** - * Returns the user corresponding to the current session object. - * @return integer uid - */ - public function &users_getLoggedInUser() { - return $this->call_method('facebook.users.getLoggedInUser', array()); - } - - /** - * Returns whether or not the user corresponding to the current session object has the app installed - * @return boolean - */ - public function &users_isAppAdded($uid=null) { - if (isset($this->added)) { - return $this->added; - } - return $this->call_method('facebook.users.isAppAdded', array('uid' => $uid)); - } - - /** - * Sets the FBML for the profile of the user attached to this session - * @param string $markup The FBML that describes the profile presence of this app for the user - * @param int $uid The user - * @param string $profile Profile FBML - * @param string $profile_action Profile action FBML - * @param string $mobile_profile Mobile profile FBML - * @return array A list of strings describing any compile errors for the submitted FBML - */ - function profile_setFBML($markup, $uid = null, $profile='', $profile_action='', $mobile_profile='') { - return $this->call_method('facebook.profile.setFBML', array('markup' => $markup, - 'uid' => $uid, - 'profile' => $profile, - 'profile_action' => $profile_action, - 'mobile_profile' => $mobile_profile)); - } - - public function &profile_getFBML($uid) { - return $this->call_method('facebook.profile.getFBML', array('uid' => $uid)); - } - - public function &fbml_refreshImgSrc($url) { - return $this->call_method('facebook.fbml.refreshImgSrc', array('url' => $url)); - } - - public function &fbml_refreshRefUrl($url) { - return $this->call_method('facebook.fbml.refreshRefUrl', array('url' => $url)); - } - - public function &fbml_setRefHandle($handle, $fbml) { - return $this->call_method('facebook.fbml.setRefHandle', array('handle' => $handle, 'fbml' => $fbml)); - } - - /** - * Get all the marketplace categories - * - * @return array A list of category names - */ - function marketplace_getCategories() { - return $this->call_method('facebook.marketplace.getCategories', array()); - } - - /** - * Get all the marketplace subcategories for a particular category - * - * @param category The category for which we are pulling subcategories - * @return array A list of subcategory names - */ - function marketplace_getSubCategories($category) { - return $this->call_method('facebook.marketplace.getSubCategories', array('category' => $category)); - } - - /** - * Get listings by either listing_id or user - * - * @param listing_ids An array of listing_ids (optional) - * @param uids An array of user ids (optional) - * @return array The data for matched listings - */ - function marketplace_getListings($listing_ids, $uids) { - return $this->call_method('facebook.marketplace.getListings', array('listing_ids' => $listing_ids, 'uids' => $uids)); - } - - /** - * Search for Marketplace listings. All arguments are optional, though at least - * one must be filled out to retrieve results. - * - * @param category The category in which to search (optional) - * @param subcategory The subcategory in which to search (optional) - * @param query A query string (optional) - * @return array The data for matched listings - */ - function marketplace_search($category, $subcategory, $query) { - return $this->call_method('facebook.marketplace.search', array('category' => $category, 'subcategory' => $subcategory, 'query' => $query)); - } - - /** - * Remove a listing from Marketplace - * - * @param listing_id The id of the listing to be removed - * @param status 'SUCCESS', 'NOT_SUCCESS', or 'DEFAULT' - * @return bool True on success - */ - function marketplace_removeListing($listing_id, $status='DEFAULT', $uid=null) { - return $this->call_method('facebook.marketplace.removeListing', - array('listing_id'=>$listing_id, - 'status'=>$status, - 'uid' => $uid)); - } - - /** - * Create/modify a Marketplace listing for the loggedinuser - * - * @param int listing_id The id of a listing to be modified, 0 for a new listing. - * @param show_on_profile bool Should we show this listing on the user's profile - * @param listing_attrs array An array of the listing data - * @return int The listing_id (unchanged if modifying an existing listing) - */ - function marketplace_createListing($listing_id, $show_on_profile, $attrs, $uid=null) { - return $this->call_method('facebook.marketplace.createListing', - array('listing_id'=>$listing_id, - 'show_on_profile'=>$show_on_profile, - 'listing_attrs'=>json_encode($attrs), - 'uid' => $uid)); - } - - - ///////////////////////////////////////////////////////////////////////////// - // Data Store API - - /** - * Set a user preference. - * - * @param pref_id preference identifier (0-200) - * @param value preferece's value - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setUserPreference($pref_id, $value) { - return $this->call_method - ('facebook.data.setUserPreference', - array('pref_id' => $pref_id, - 'value' => $value)); - } - - /** - * Set a user's all preferences for this application. - * - * @param values preferece values in an associative arrays - * @param replace whether to replace all existing preferences or - * merge into them. - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setUserPreferences($values, $replace = false) { - return $this->call_method - ('facebook.data.setUserPreferences', - array('values' => json_encode($values), - 'replace' => $replace)); - } - - /** - * Get a user preference. - * - * @param pref_id preference identifier (0-200) - * @return preference's value - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getUserPreference($pref_id) { - return $this->call_method - ('facebook.data.getUserPreference', - array('pref_id' => $pref_id)); - } - - /** - * Get a user preference. - * - * @return preference values - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getUserPreferences() { - return $this->call_method - ('facebook.data.getUserPreferences', - array()); - } - - /** - * Create a new object type. - * - * @param name object type's name - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_createObjectType($name) { - return $this->call_method - ('facebook.data.createObjectType', - array('name' => $name)); - } - - /** - * Delete an object type. - * - * @param obj_type object type's name - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_dropObjectType($obj_type) { - return $this->call_method - ('facebook.data.dropObjectType', - array('obj_type' => $obj_type)); - } - - /** - * Rename an object type. - * - * @param obj_type object type's name - * @param new_name new object type's name - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_renameObjectType($obj_type, $new_name) { - return $this->call_method - ('facebook.data.renameObjectType', - array('obj_type' => $obj_type, - 'new_name' => $new_name)); - } - - /** - * Add a new property to an object type. - * - * @param obj_type object type's name - * @param prop_name name of the property to add - * @param prop_type 1: integer; 2: string; 3: text blob - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_defineObjectProperty($obj_type, $prop_name, $prop_type) { - return $this->call_method - ('facebook.data.defineObjectProperty', - array('obj_type' => $obj_type, - 'prop_name' => $prop_name, - 'prop_type' => $prop_type)); - } - - /** - * Remove a previously defined property from an object type. - * - * @param obj_type object type's name - * @param prop_name name of the property to remove - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_undefineObjectProperty($obj_type, $prop_name) { - return $this->call_method - ('facebook.data.undefineObjectProperty', - array('obj_type' => $obj_type, - 'prop_name' => $prop_name)); - } - - /** - * Rename a previously defined property of an object type. - * - * @param obj_type object type's name - * @param prop_name name of the property to rename - * @param new_name new name to use - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_renameObjectProperty($obj_type, $prop_name, - $new_name) { - return $this->call_method - ('facebook.data.renameObjectProperty', - array('obj_type' => $obj_type, - 'prop_name' => $prop_name, - 'new_name' => $new_name)); - } - - /** - * Retrieve a list of all object types that have defined for the application. - * - * @return a list of object type names - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getObjectTypes() { - return $this->call_method - ('facebook.data.getObjectTypes', - array()); - } - - /** - * Get definitions of all properties of an object type. - * - * @param obj_type object type's name - * @return pairs of property name and property types - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getObjectType($obj_type) { - return $this->call_method - ('facebook.data.getObjectType', - array('obj_type' => $obj_type)); - } - - /** - * Create a new object. - * - * @param obj_type object type's name - * @param properties (optional) properties to set initially - * @return newly created object's id - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_createObject($obj_type, $properties = null) { - return $this->call_method - ('facebook.data.createObject', - array('obj_type' => $obj_type, - 'properties' => json_encode($properties))); - } - - /** - * Update an existing object. - * - * @param obj_id object's id - * @param properties new properties - * @param replace true for replacing existing properties; false for merging - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_updateObject($obj_id, $properties, $replace = false) { - return $this->call_method - ('facebook.data.updateObject', - array('obj_id' => $obj_id, - 'properties' => json_encode($properties), - 'replace' => $replace)); - } - - /** - * Delete an existing object. - * - * @param obj_id object's id - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_deleteObject($obj_id) { - return $this->call_method - ('facebook.data.deleteObject', - array('obj_id' => $obj_id)); - } - - /** - * Delete a list of objects. - * - * @param obj_ids objects to delete - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_deleteObjects($obj_ids) { - return $this->call_method - ('facebook.data.deleteObjects', - array('obj_ids' => json_encode($obj_ids))); - } - - /** - * Get a single property value of an object. - * - * @param obj_id object's id - * @param prop_name individual property's name - * @return individual property's value - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getObjectProperty($obj_id, $prop_name) { - return $this->call_method - ('facebook.data.getObjectProperty', - array('obj_id' => $obj_id, - 'prop_name' => $prop_name)); - } - - /** - * Get properties of an object. - * - * @param obj_id object's id - * @param prop_names (optional) properties to return; null for all. - * @return specified properties of an object - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getObject($obj_id, $prop_names = null) { - return $this->call_method - ('facebook.data.getObject', - array('obj_id' => $obj_id, - 'prop_names' => json_encode($prop_names))); - } - - /** - * Get properties of a list of objects. - * - * @param obj_ids object ids - * @param prop_names (optional) properties to return; null for all. - * @return specified properties of an object - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getObjects($obj_ids, $prop_names = null) { - return $this->call_method - ('facebook.data.getObjects', - array('obj_ids' => json_encode($obj_ids), - 'prop_names' => json_encode($prop_names))); - } - - /** - * Set a single property value of an object. - * - * @param obj_id object's id - * @param prop_name individual property's name - * @param prop_value new value to set - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setObjectProperty($obj_id, $prop_name, - $prop_value) { - return $this->call_method - ('facebook.data.setObjectProperty', - array('obj_id' => $obj_id, - 'prop_name' => $prop_name, - 'prop_value' => $prop_value)); - } - - /** - * Read hash value by key. - * - * @param obj_type object type's name - * @param key hash key - * @param prop_name (optional) individual property's name - * @return hash value - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getHashValue($obj_type, $key, $prop_name = null) { - return $this->call_method - ('facebook.data.getHashValue', - array('obj_type' => $obj_type, - 'key' => $key, - 'prop_name' => $prop_name)); - } - - /** - * Write hash value by key. - * - * @param obj_type object type's name - * @param key hash key - * @param value hash value - * @param prop_name (optional) individual property's name - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setHashValue($obj_type, $key, $value, $prop_name = null) { - return $this->call_method - ('facebook.data.setHashValue', - array('obj_type' => $obj_type, - 'key' => $key, - 'value' => $value, - 'prop_name' => $prop_name)); - } - - /** - * Increase a hash value by specified increment atomically. - * - * @param obj_type object type's name - * @param key hash key - * @param prop_name individual property's name - * @param increment (optional) default is 1 - * @return incremented hash value - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_incHashValue($obj_type, $key, $prop_name, $increment = 1) { - return $this->call_method - ('facebook.data.incHashValue', - array('obj_type' => $obj_type, - 'key' => $key, - 'prop_name' => $prop_name, - 'increment' => $increment)); - } - - /** - * Remove a hash key and its values. - * - * @param obj_type object type's name - * @param key hash key - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_removeHashKey($obj_type, $key) { - return $this->call_method - ('facebook.data.removeHashKey', - array('obj_type' => $obj_type, - 'key' => $key)); - } - - /** - * Remove hash keys and their values. - * - * @param obj_type object type's name - * @param keys hash keys - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_removeHashKeys($obj_type, $keys) { - return $this->call_method - ('facebook.data.removeHashKeys', - array('obj_type' => $obj_type, - 'keys' => json_encode($keys))); - } - - - /** - * Define an object association. - * - * @param name name of this association - * @param assoc_type 1: one-way 2: two-way symmetric 3: two-way asymmetric - * @param assoc_info1 needed info about first object type - * @param assoc_info2 needed info about second object type - * @param inverse (optional) name of reverse association - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_defineAssociation($name, $assoc_type, $assoc_info1, - $assoc_info2, $inverse = null) { - return $this->call_method - ('facebook.data.defineAssociation', - array('name' => $name, - 'assoc_type' => $assoc_type, - 'assoc_info1' => json_encode($assoc_info1), - 'assoc_info2' => json_encode($assoc_info2), - 'inverse' => $inverse)); - } - - /** - * Undefine an object association. - * - * @param name name of this association - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_undefineAssociation($name) { - return $this->call_method - ('facebook.data.undefineAssociation', - array('name' => $name)); - } - - /** - * Rename an object association or aliases. - * - * @param name name of this association - * @param new_name (optional) new name of this association - * @param new_alias1 (optional) new alias for object type 1 - * @param new_alias2 (optional) new alias for object type 2 - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_ALREADY_EXISTS - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_renameAssociation($name, $new_name, $new_alias1 = null, - $new_alias2 = null) { - return $this->call_method - ('facebook.data.renameAssociation', - array('name' => $name, - 'new_name' => $new_name, - 'new_alias1' => $new_alias1, - 'new_alias2' => $new_alias2)); - } - - /** - * Get definition of an object association. - * - * @param name name of this association - * @return specified association - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociationDefinition($name) { - return $this->call_method - ('facebook.data.getAssociationDefinition', - array('name' => $name)); - } - - /** - * Get definition of all associations. - * - * @return all defined associations - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociationDefinitions() { - return $this->call_method - ('facebook.data.getAssociationDefinitions', - array()); - } - - /** - * Create or modify an association between two objects. - * - * @param name name of association - * @param obj_id1 id of first object - * @param obj_id2 id of second object - * @param data (optional) extra string data to store - * @param assoc_time (optional) extra time data; default to creation time - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setAssociation($name, $obj_id1, $obj_id2, $data = null, - $assoc_time = null) { - return $this->call_method - ('facebook.data.setAssociation', - array('name' => $name, - 'obj_id1' => $obj_id1, - 'obj_id2' => $obj_id2, - 'data' => $data, - 'assoc_time' => $assoc_time)); - } - - /** - * Create or modify associations between objects. - * - * @param assocs associations to set - * @param name (optional) name of association - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_setAssociations($assocs, $name = null) { - return $this->call_method - ('facebook.data.setAssociations', - array('assocs' => json_encode($assocs), - 'name' => $name)); - } - - /** - * Remove an association between two objects. - * - * @param name name of association - * @param obj_id1 id of first object - * @param obj_id2 id of second object - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_removeAssociation($name, $obj_id1, $obj_id2) { - return $this->call_method - ('facebook.data.removeAssociation', - array('name' => $name, - 'obj_id1' => $obj_id1, - 'obj_id2' => $obj_id2)); - } - - /** - * Remove associations between objects by specifying pairs of object ids. - * - * @param assocs associations to remove - * @param name (optional) name of association - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_removeAssociations($assocs, $name = null) { - return $this->call_method - ('facebook.data.removeAssociations', - array('assocs' => json_encode($assocs), - 'name' => $name)); - } - - /** - * Remove associations between objects by specifying one object id. - * - * @param name name of association - * @param obj_id who's association to remove - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_removeAssociatedObjects($name, $obj_id) { - return $this->call_method - ('facebook.data.removeAssociatedObjects', - array('name' => $name, - 'obj_id' => $obj_id)); - } - - /** - * Retrieve a list of associated objects. - * - * @param name name of association - * @param obj_id who's association to retrieve - * @param no_data only return object ids - * @return associated objects - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociatedObjects($name, $obj_id, $no_data = true) { - return $this->call_method - ('facebook.data.getAssociatedObjects', - array('name' => $name, - 'obj_id' => $obj_id, - 'no_data' => $no_data)); - } - - /** - * Count associated objects. - * - * @param name name of association - * @param obj_id who's association to retrieve - * @return associated object's count - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociatedObjectCount($name, $obj_id) { - return $this->call_method - ('facebook.data.getAssociatedObjectCount', - array('name' => $name, - 'obj_id' => $obj_id)); - } - - /** - * Get a list of associated object counts. - * - * @param name name of association - * @param obj_ids whose association to retrieve - * @return associated object counts - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_DATA_OBJECT_NOT_FOUND - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_INVALID_OPERATION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociatedObjectCounts($name, $obj_ids) { - return $this->call_method - ('facebook.data.getAssociatedObjectCounts', - array('name' => $name, - 'obj_ids' => json_encode($obj_ids))); - } - - /** - * Find all associations between two objects. - * - * @param obj_id1 id of first object - * @param obj_id2 id of second object - * @param no_data only return association names without data - * @return all associations between objects - * @error - * API_EC_DATA_DATABASE_ERROR - * API_EC_PARAM - * API_EC_PERMISSION - * API_EC_DATA_QUOTA_EXCEEDED - * API_EC_DATA_UNKNOWN_ERROR - */ - public function &data_getAssociations($obj_id1, $obj_id2, $no_data = true) { - return $this->call_method - ('facebook.data.getAssociations', - array('obj_id1' => $obj_id1, - 'obj_id2' => $obj_id2, - 'no_data' => $no_data)); - } - - /** - * Get the properties that you have set for an app. - * - * @param properties list of properties names to fetch - * @return a map from property name to value - */ - public function admin_getAppProperties($properties) { - return json_decode($this->call_method - ('facebook.admin.getAppProperties', - array('properties' => json_encode($properties))), true); - } - - /** - * Set properties for an app. - * - * @param properties a map from property names to values - * @return true on success - */ - public function admin_setAppProperties($properties) { - return $this->call_method - ('facebook.admin.setAppProperties', - array('properties' => json_encode($properties))); - } - - /** - * Returns the allocation limit value for a specified integration point name - * Integration point names are defined in lib/api/karma/constants.php in the limit_map - * @param string $integration_point_name - * @return integration point allocation value - */ - public function &admin_getAllocation($integration_point_name) { - return $this->call_method('facebook.admin.getAllocation', array('integration_point_name' => $integration_point_name)); - } - - /** - * Returns values for the specified daily metrics for the current - * application, in the given date range (inclusive). - * - * @param start_date unix time for the start of the date range - * @param end_date unix time for the end of the date range - * @param metrics list of daily metrics to look up - * @return a list of the values for those metrics - */ - public function &admin_getDailyMetrics($start_date, $end_date, $metrics) { - return $this->call_method('admin.getDailyMetrics', - array('start_date' => $start_date, - 'end_date' => $end_date, - 'metrics' => json_encode($metrics))); - } - - /** - * Returns values for the specified metrics for the current - * application, in the given time range. The metrics are collected - * for fixed-length periods, and the times represent midnight at - * the end of each period. - * - * @param start_time unix time for the start of the range - * @param end_time unix time for the end of the range - * @param period number of seconds in the desired period - * @param metrics list of metrics to look up - * @return a list of the values for those metrics - */ - public function &admin_getMetrics($start_time, $end_time, $period, $metrics) { - return $this->call_method('admin.getMetrics', - array('start_time' => $start_time, - 'end_time' => $end_time, - 'period' => $period, - 'metrics' => json_encode($metrics))); - } - - /** - * Sets application targeting info - * - * @param targeting_info - * @return bool - */ - public function admin_setTargetingInfo($targeting_info = null) { - $targeting_str = null; - if (!empty($targeting_info)) { - $targeting_str = json_encode($targeting_info); - } - return $this->call_method('admin.setTargetingInfo', - array('targeting_str' => $targeting_str)); - } - - /** - * Gets application targeting info - * - * @return bool - */ - public function admin_getTargetingInfo() { - return json_decode($this->call_method( - 'admin.getTargetingInfo', - array()), - true); - } - - /* UTILITY FUNCTIONS */ - - public function & call_method($method, $params) { - - //Check if we are in batch mode - if($this->batch_queue === null) { - if ($this->call_as_apikey) { - $params['call_as_apikey'] = $this->call_as_apikey; - } - $xml = $this->post_request($method, $params); - $result = $this->convert_xml_to_result($xml, $method, $params); - if (is_array($result) && isset($result['error_code'])) { - throw new FacebookRestClientException($result['error_msg'], $result['error_code']); - } - } - else { - $result = null; - $batch_item = array('m' => $method, 'p' => $params, 'r' => & $result); - $this->batch_queue[] = $batch_item; - } - - return $result; - } - - private function convert_xml_to_result($xml, $method, $params) { - $sxml = simplexml_load_string($xml); - $result = self::convert_simplexml_to_array($sxml); - - - if (!empty($GLOBALS['facebook_config']['debug'])) { - // output the raw xml and its corresponding php object, for debugging: - print '<div style="margin: 10px 30px; padding: 5px; border: 2px solid black; background: gray; color: white; font-size: 12px; font-weight: bold;">'; - $this->cur_id++; - print $this->cur_id . ': Called ' . $method . ', show ' . - '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'params\');">Params</a> | '. - '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'xml\');">XML</a> | '. - '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'sxml\');">SXML</a> | '. - '<a href=# onclick="return toggleDisplay(' . $this->cur_id . ', \'php\');">PHP</a>'; - print '<pre id="params'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($params, true).'</pre>'; - print '<pre id="xml'.$this->cur_id.'" style="display: none; overflow: auto;">'.htmlspecialchars($xml).'</pre>'; - print '<pre id="php'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($result, true).'</pre>'; - print '<pre id="sxml'.$this->cur_id.'" style="display: none; overflow: auto;">'.print_r($sxml, true).'</pre>'; - print '</div>'; - } - return $result; - } - - - - private function create_post_string($method, $params) { - $params['method'] = $method; - $params['session_key'] = $this->session_key; - $params['api_key'] = $this->api_key; - $params['call_id'] = microtime(true); - if ($params['call_id'] <= $this->last_call_id) { - $params['call_id'] = $this->last_call_id + 0.001; - } - $this->last_call_id = $params['call_id']; - if (!isset($params['v'])) { - $params['v'] = '1.0'; - } - $post_params = array(); - foreach ($params as $key => &$val) { - if (is_array($val)) $val = implode(',', $val); - $post_params[] = $key.'='.urlencode($val); - } - $secret = $this->secret; - $post_params[] = 'sig='.Facebook::generate_sig($params, $secret); - return implode('&', $post_params); - } - - public function post_request($method, $params) { - - $post_string = $this->create_post_string($method, $params); - - if (function_exists('curl_init')) { - // Use CURL if installed... - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $this->server_addr); - curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion()); - $result = curl_exec($ch); - curl_close($ch); - } else { - // Non-CURL based version... - $context = - array('http' => - array('method' => 'POST', - 'header' => 'Content-type: application/x-www-form-urlencoded'."\r\n". - 'User-Agent: Facebook API PHP5 Client 1.1 (non-curl) '.phpversion()."\r\n". - 'Content-length: ' . strlen($post_string), - 'content' => $post_string)); - $contextid=stream_context_create($context); - $sock=fopen($this->server_addr, 'r', false, $contextid); - if ($sock) { - $result=''; - while (!feof($sock)) - $result.=fgets($sock, 4096); - - fclose($sock); - } - } - return $result; - } - - public static function convert_simplexml_to_array($sxml) { - $arr = array(); - if ($sxml) { - foreach ($sxml as $k => $v) { - if ($sxml['list']) { - $arr[] = self::convert_simplexml_to_array($v); - } else { - $arr[$k] = self::convert_simplexml_to_array($v); - } - } - } - if (sizeof($arr) > 0) { - return $arr; - } else { - return (string)$sxml; - } - } - -} - - -class FacebookRestClientException extends Exception { -} - -// Supporting methods and values------ - -/** - * Error codes and descriptions for the Facebook API. - */ - -class FacebookAPIErrorCodes { - - const API_EC_SUCCESS = 0; - - /* - * GENERAL ERRORS - */ - const API_EC_UNKNOWN = 1; - const API_EC_SERVICE = 2; - const API_EC_METHOD = 3; - const API_EC_TOO_MANY_CALLS = 4; - const API_EC_BAD_IP = 5; - - /* - * PARAMETER ERRORS - */ - const API_EC_PARAM = 100; - const API_EC_PARAM_API_KEY = 101; - const API_EC_PARAM_SESSION_KEY = 102; - const API_EC_PARAM_CALL_ID = 103; - const API_EC_PARAM_SIGNATURE = 104; - const API_EC_PARAM_USER_ID = 110; - const API_EC_PARAM_USER_FIELD = 111; - const API_EC_PARAM_SOCIAL_FIELD = 112; - const API_EC_PARAM_ALBUM_ID = 120; - - /* - * USER PERMISSIONS ERRORS - */ - const API_EC_PERMISSION = 200; - const API_EC_PERMISSION_USER = 210; - const API_EC_PERMISSION_ALBUM = 220; - const API_EC_PERMISSION_PHOTO = 221; - - const FQL_EC_PARSER = 601; - const FQL_EC_UNKNOWN_FIELD = 602; - const FQL_EC_UNKNOWN_TABLE = 603; - const FQL_EC_NOT_INDEXABLE = 604; - - /** - * DATA STORE API ERRORS - */ - const API_EC_DATA_UNKNOWN_ERROR = 800; - const API_EC_DATA_INVALID_OPERATION = 801; - const API_EC_DATA_QUOTA_EXCEEDED = 802; - const API_EC_DATA_OBJECT_NOT_FOUND = 803; - const API_EC_DATA_OBJECT_ALREADY_EXISTS = 804; - const API_EC_DATA_DATABASE_ERROR = 805; - - - /* - * Batch ERROR - */ - const API_EC_BATCH_ALREADY_STARTED = 900; - const API_EC_BATCH_NOT_STARTED = 901; - const API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE = 902; - - public static $api_error_descriptions = array( - API_EC_SUCCESS => 'Success', - API_EC_UNKNOWN => 'An unknown error occurred', - API_EC_SERVICE => 'Service temporarily unavailable', - API_EC_METHOD => 'Unknown method', - API_EC_TOO_MANY_CALLS => 'Application request limit reached', - API_EC_BAD_IP => 'Unauthorized source IP address', - API_EC_PARAM => 'Invalid parameter', - API_EC_PARAM_API_KEY => 'Invalid API key', - API_EC_PARAM_SESSION_KEY => 'Session key invalid or no longer valid', - API_EC_PARAM_CALL_ID => 'Call_id must be greater than previous', - API_EC_PARAM_SIGNATURE => 'Incorrect signature', - API_EC_PARAM_USER_ID => 'Invalid user id', - API_EC_PARAM_USER_FIELD => 'Invalid user info field', - API_EC_PARAM_SOCIAL_FIELD => 'Invalid user field', - API_EC_PARAM_ALBUM_ID => 'Invalid album id', - API_EC_PERMISSION => 'Permissions error', - API_EC_PERMISSION_USER => 'User not visible', - API_EC_PERMISSION_ALBUM => 'Album not visible', - API_EC_PERMISSION_PHOTO => 'Photo not visible', - FQL_EC_PARSER => 'FQL: Parser Error', - FQL_EC_UNKNOWN_FIELD => 'FQL: Unknown Field', - FQL_EC_UNKNOWN_TABLE => 'FQL: Unknown Table', - FQL_EC_NOT_INDEXABLE => 'FQL: Statement not indexable', - FQL_EC_UNKNOWN_FUNCTION => 'FQL: Attempted to call unknown function', - FQL_EC_INVALID_PARAM => 'FQL: Invalid parameter passed in', - API_EC_DATA_UNKNOWN_ERROR => 'Unknown data store API error', - API_EC_DATA_INVALID_OPERATION => 'Invalid operation', - API_EC_DATA_QUOTA_EXCEEDED => 'Data store allowable quota was exceeded', - API_EC_DATA_OBJECT_NOT_FOUND => 'Specified object cannot be found', - API_EC_DATA_OBJECT_ALREADY_EXISTS => 'Specified object already exists', - API_EC_DATA_DATABASE_ERROR => 'A database error occurred. Please try again', - API_EC_BATCH_ALREADY_STARTED => 'begin_batch already called, please make sure to call end_batch first', - API_EC_BATCH_NOT_STARTED => 'end_batch called before start_batch', - API_EC_BATCH_METHOD_NOT_ALLOWED_IN_BATCH_MODE => 'this method is not allowed in batch mode', - ); -} diff --git a/modules/authfacebook/extlibinc/fb_ca_chain_bundle.crt b/modules/authfacebook/extlibinc/fb_ca_chain_bundle.crt new file mode 100644 index 0000000000000000000000000000000000000000..b92d7190e9fa44296b9812f507efa71213ec4b53 --- /dev/null +++ b/modules/authfacebook/extlibinc/fb_ca_chain_bundle.crt @@ -0,0 +1,121 @@ +-----BEGIN CERTIFICATE----- +MIIFgjCCBGqgAwIBAgIQDKKbZcnESGaLDuEaVk6fQjANBgkqhkiG9w0BAQUFADBm +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBDQS0zMB4XDTEwMDExMzAwMDAwMFoXDTEzMDQxMTIzNTk1OVowaDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEX +MBUGA1UEChMORmFjZWJvb2ssIEluYy4xFzAVBgNVBAMUDiouZmFjZWJvb2suY29t +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9rzj7QIuLM3sdHu1HcI1VcR3g +b5FExKNV646agxSle1aQ/sJev1mh/u91ynwqd2BQmM0brZ1Hc3QrfYyAaiGGgEkp +xbhezyfeYhAyO0TKAYxPnm2cTjB5HICzk6xEIwFbA7SBJ2fSyW1CFhYZyo3tIBjj +19VjKyBfpRaPkzLmRwIDAQABo4ICrDCCAqgwHwYDVR0jBBgwFoAUUOpzidsp+xCP +nuUBINTeeZlIg/cwHQYDVR0OBBYEFPp+tsFBozkjrHlEnZ9J4cFj2eM0MA4GA1Ud +DwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMF8GA1UdHwRYMFYwKaAnoCWGI2h0dHA6 +Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9jYTMtZmIuY3JsMCmgJ6AlhiNodHRwOi8vY3Js +NC5kaWdpY2VydC5jb20vY2EzLWZiLmNybDCCAcYGA1UdIASCAb0wggG5MIIBtQYL +YIZIAYb9bAEDAAEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0 +LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIB +UgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkA +YwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEA +bgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMA +UABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkA +IABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwA +aQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8A +cgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMA +ZQAuMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQUF +AAOCAQEACOkTIdxMy11+CKrbGNLBSg5xHaTvu/v1wbyn3dO/mf68pPfJnX6ShPYy +4XM4Vk0x4uaFaU4wAGke+nCKGi5dyg0Esg7nemLNKEJaFAJZ9enxZm334lSCeARy +wlDtxULGOFRyGIZZPmbV2eNq5xdU/g3IuBEhL722mTpAye9FU/J8Wsnw54/gANyO +Gzkewigua8ip8Lbs9Cht399yAfbfhUP1DrAm/xEcnHrzPr3cdCtOyJaM6SRPpRqH +ITK5Nc06tat9lXVosSinT3KqydzxBYua9gCFFiR3x3DgZfvXkC6KDdUlDrNcJUub +a1BHnLLP4mxTHL6faAXYd05IxNn/IA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGVTCCBT2gAwIBAgIQCFH5WYFBRcq94CTiEsnCDjANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA3MDQwMzAwMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR +CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv +KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 +BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf +1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs +zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d +32duXvsCAwEAAaOCAvcwggLzMA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w +ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 +LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH +AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy +AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj +AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg +AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ +AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt +AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj +AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl +AHIAZQBuAGMAZQAuMA8GA1UdEwEB/wQFMAMBAf8wNAYIKwYBBQUHAQEEKDAmMCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSBhzCB +hDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFz +c3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQu +Y29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSMEGDAW +gBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUBINTe +eZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAF1PhPGoiNOjsrycbeUpSXfh59bcqdg1 +rslx3OXb3J0kIZCmz7cBHJvUV5eR13UWpRLXuT0uiT05aYrWNTf58SHEW0CtWakv +XzoAKUMncQPkvTAyVab+hA4LmzgZLEN8rEO/dTHlIxxFVbdpCJG1z9fVsV7un5Tk +1nq5GMO41lJjHBC6iy9tXcwFOPRWBW3vnuzoYTYMFEuFFFoMg08iXFnLjIpx2vrF +EIRYzwfu45DC9fkpx1ojcflZtGQriLCnNseaIGHr+k61rmsb5OPs4tk8QUmoIKRU +9ZKNu8BVIASm2LAXFszj0Mi0PeXZhMbT9m5teMl5Q+h6N/9cNUm/ocU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEQjCCA6ugAwIBAgIEQoclDjANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEy +MjIxNTI3MjdaFw0xNDA3MjIxNTU3MjdaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV +BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD +1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt +cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 +OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd +HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm +t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET +MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr +BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo +dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v +Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU +mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 +UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF +BQADgYEAUuVY7HCc/9EvhaYzC1rAIo348LtGIiMduEl5Xa24G8tmJnDioD2GU06r +1kjLX/ktCdpdBgXadbjtdrZXTP59uN0AXlsdaTiFufsqVLPvkp5yMnqnuI3E2o6p +NpAkoQSbB6kUCNnXcW26valgOjDLZFOnr241QiwdBAJAAE/rRa8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- diff --git a/modules/authfacebook/lib/Auth/Source/Facebook.php b/modules/authfacebook/lib/Auth/Source/Facebook.php index aa08a65692633cc765b94c2f7f89eb84544d9d5e..923f56228f70009ab767bffd53668acbcf394b0e 100644 --- a/modules/authfacebook/lib/Auth/Source/Facebook.php +++ b/modules/authfacebook/lib/Auth/Source/Facebook.php @@ -15,16 +15,30 @@ class sspmod_authfacebook_Auth_Source_Facebook extends SimpleSAML_Auth_Source { */ const STAGE_INIT = 'facebook:init'; + /** * The key of the AuthId field in the state. */ const AUTHID = 'facebook:AuthId'; + /** + * Facebook App ID or API Key + */ private $api_key; + + + /** + * Facebook App Secret + */ private $secret; + /** + * Which additional data permissions to request from user + */ + private $req_perms; + /** * Constructor for this authentication source. @@ -39,18 +53,13 @@ class sspmod_authfacebook_Auth_Source_Facebook extends SimpleSAML_Auth_Source { /* Call the parent constructor first, as required by the interface. */ parent::__construct($info, $config); - if (!array_key_exists('api_key', $config)) - throw new Exception('Facebook authentication source is not properly configured: missing [api_key]'); - - $this->api_key = $config['api_key']; + $cfgParse = SimpleSAML_Configuration::loadFromArray($config, 'authsources[' . var_export($this->authId, TRUE) . ']'); - if (!array_key_exists('secret', $config)) - throw new Exception('Facebook authentication source is not properly configured: missing [secret]'); - - $this->secret = $config['secret']; + $this->api_key = $cfgParse->getString('api_key'); + $this->secret = $cfgParse->getString('secret'); + $this->req_perms = $cfgParse->getString('req_perms', NULL); require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/extlibinc/facebook.php'); - } @@ -69,38 +78,56 @@ class sspmod_authfacebook_Auth_Source_Facebook extends SimpleSAML_Auth_Source { SimpleSAML_Logger::debug('facebook auth state id = ' . $stateID); - $facebook = new Facebook($this->api_key, $this->secret); - $u = $facebook->require_login(SimpleSAML_Module::getModuleUrl('authfacebook') . '/linkback.php?next=' . $stateID); - # http://developers.facebook.com/documentation.php?v=1.0&method=users.getInfo - /* Causes an notice / warning... - if ($facebook->api_client->error_code) { - throw new Exception('Unable to load profile from facebook'); + $linkback = SimpleSAML_Module::getModuleURL('authfacebook/linkback.php'); + $linkback_next = $linkback . '?next=' . urlencode($stateID); + $linkback_cancel = $linkback . '?cancel=' . urlencode($stateID); + $fb_login_params = array('next' => $linkback_next, 'cancel_url' => $linkback_cancel, 'req_perms' => $this->req_perms); + + $facebook = new Facebook(array('appId' => $this->api_key, 'secret' => $this->secret, 'cookie' => false)); + + $fb_session = $facebook->getSession(); + + if (isset($fb_session)) { + try { + $uid = $facebook->getUser(); + if (isset($uid)) { + $info = $facebook->api("/me"); + } + } catch (FacebookApiException $e) { + if ($e->getType() != 'OAuthException') { + throw new SimpleSAML_Error_AuthSource($this->authId, 'Error getting user profile.', $e); + } + } + } + + if (!isset($info)) { + $url = $facebook->getLoginUrl($fb_login_params); + SimpleSAML_Utilities::redirect($url); + assert('FALSE'); } - */ - // http://developers.facebook.com/docs/reference/rest/users.getInfo - $info = $facebook->api_client->users_getInfo($u, array('uid', 'first_name', 'middle_name', 'last_name', 'name', 'locale', 'current_location', 'affiliations', 'pic_square', 'profile_url', 'sex', 'email', 'pic', 'username', 'about_me', 'status', 'profile_blurb')); $attributes = array(); - foreach($info[0] AS $key => $value) { - if (is_string($value) && !empty($value)) + foreach($info AS $key => $value) { + if (is_string($value) && !empty($value)) { $attributes['facebook.' . $key] = array((string)$value); + } } - if (array_key_exists('username', $info[0]) ) - $attributes['facebook_user'] = array($info[0]['username'] . '@facebook.com'); - else - $attributes['facebook_user'] = array($u . '@facebook.com'); + if (array_key_exists('username', $info)) { + $attributes['facebook_user'] = array($info['username'] . '@facebook.com'); + } else { + $attributes['facebook_user'] = array($uid . '@facebook.com'); + } - $attributes['facebook_targetedID'] = array('http://facebook.com!' . $u); - $attributes['facebook_cn'] = array($info[0]['name']); + $attributes['facebook_targetedID'] = array('http://facebook.com!' . $uid); + $attributes['facebook_cn'] = array($info['name']); - SimpleSAML_Logger::debug('Facebook Returned Attributes: '. implode(", ",array_keys($attributes))); + SimpleSAML_Logger::debug('Facebook Returned Attributes: '. implode(", ", array_keys($attributes))); $state['Attributes'] = $attributes; } - } ?> \ No newline at end of file diff --git a/modules/authfacebook/www/linkback.php b/modules/authfacebook/www/linkback.php index d185db71d25b5110bbfe8fe478ea632f023b62c1..1666f8c433b069e6943f2284c829dfa0e10c2fce 100644 --- a/modules/authfacebook/www/linkback.php +++ b/modules/authfacebook/www/linkback.php @@ -4,29 +4,42 @@ * Handle linkback() response from Facebook. */ +$cancel = FALSE; +if (array_key_exists('cancel', $_GET)) { + $stateID = $_GET['cancel']; + $cancel = TRUE; +} elseif (array_key_exists('next', $_GET)) { + $stateID = $_GET['next']; +} -#if (!array_key_exists('StateID', $_GET)) -# throw new SimpleSAML_Error_BadRequest('Missing StateID to facebook linkback endpoint'); - -if (!array_key_exists('next', $_GET)) - throw new SimpleSAML_Error_BadRequest('Missing parameter [next] to facebook linkback endpoint'); - -$stateID = $_GET['next']; +if (empty($stateID)) { + throw new SimpleSAML_Error_BadRequest('Missing state parameter on facebook linkback endpoint.'); +} $state = SimpleSAML_Auth_State::loadState($stateID, sspmod_authfacebook_Auth_Source_Facebook::STAGE_INIT); /* Find authentication source. */ -assert('array_key_exists(sspmod_authfacebook_Auth_Source_Facebook::AUTHID, $state)'); +if (!array_key_exists(sspmod_authfacebook_Auth_Source_Facebook::AUTHID, $state)) { + throw new SimpleSAML_Error_BadRequest('No data in state for ' . sspmod_authfacebook_Auth_Source_Facebook::AUTHID); +} $sourceId = $state[sspmod_authfacebook_Auth_Source_Facebook::AUTHID]; $source = SimpleSAML_Auth_Source::getById($sourceId); if ($source === NULL) { - throw new Exception('Could not find authentication source with id ' . $sourceId); + throw new SimpleSAML_Error_BadRequest('Could not find authentication source with id ' . var_export($sourceId, TRUE)); } -$config = SimpleSAML_Configuration::getInstance(); +try { + if ($cancel) { + throw new SimpleSAML_Error_UserAborted(); + } -$source->authenticate($state); + $source->authenticate($state); +} catch (SimpleSAML_Error_Exception $e) { + SimpleSAML_Auth_State::throwException($state, $e); +} catch (Exception $e) { + SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_AuthSource($sourceId, 'Error on facebook linkback endpoint.', $e)); +} SimpleSAML_Auth_Source::completeAuth($state);