Now supporting page tabs similar to canvas pages.
[project/fb.git] / fb_settings.inc
1 <?php
2
3 /**
4 * @file
5 * This file is to be included from your sites/.../settings.php file.
6 *
7 * In this code we set up special session management and url
8 * rewriting. These things must be done before modules are loaded, so
9 * the code is here instead of fb.module. And that is why this
10 * must be include from settings.php.
11 *
12 */
13
14 // Each of these are things we can learn and store in fb_settings().
15 // The CB (callback) values are learned via URL rewriting.
16 // Include fb_url_rewrite.inc in your settings.php to enable this.
17 define('FB_SETTINGS_CB', 'fb_cb'); // The app id.
18 define('FB_SETTINGS_CB_PAGE', 'fb_page'); // Page id, for tabs.
19 define('FB_SETTINGS_CB_TYPE', 'fb_cb_type'); // For iframes within FBML canvas pages, now DEPRECATED.
20 define('FB_SETTINGS_CB_SESSION', 'fb_sess'); // For embedding session id within URL.
21
22 // Things we can learn from the facebook session.
23 define('FB_SETTINGS_APIKEY', 'apikey');
24 define('FB_SETTINGS_ID', 'app_id');
25 define('FB_SETTINGS_PAGE_ID', 'page_id');
26 define('FB_SETTINGS_FBU', 'fbu');
27 define('FB_SETTINGS_TOKEN', 'token');
28 define('FB_SETTINGS_TYPE', 'type'); // page type not same as cb type
29 define('FB_SETTINGS_COOKIE_DOMAIN', 'cookie_domain');
30
31 // Possible values for page type.
32 define('FB_SETTINGS_TYPE_CANVAS', 'canvas');
33 define('FB_SETTINGS_TYPE_CONNECT', 'connect');
34 define('FB_SETTINGS_TYPE_PROFILE', 'profile'); // deprecated, FBML tab
35 define('FB_SETTINGS_TYPE_PAGE_TAB', 'page_tab'); // iframe tab
36
37 /**
38 * Helper function to remember values as we learn them.
39 */
40 function fb_settings($key = NULL, $value = NULL) {
41 static $cache;
42 if (!isset($cache)) {
43 $cache = array();
44 }
45 if (isset($value)) {
46 $cache[$key] = $value;
47 }
48 if (isset($key)) {
49 return isset($cache[$key]) ? $cache[$key] : NULL;
50 }
51 else {
52 return $cache;
53 }
54 }
55
56
57 /**
58 * Helpers to parse signed_session. Copied from facebook.php.
59 */
60 /**
61 * Base64 encoding that doesn't need to be urlencode()ed.
62 * Exactly the same as base64_encode except it uses
63 * - instead of +
64 * _ instead of /
65 *
66 * @param String base64UrlEncodeded string
67 */
68 function _fb_settings_base64_url_decode($input) {
69 return base64_decode(strtr($input, '-_', '+/'));
70 }
71
72 /**
73 * See facebook.php for a more reliable version of this function. We skip
74 * validation because we do not yet know the app secret.
75 */
76 function _fb_settings_parse_signed_request($signed_request) {
77 list($encoded_sig, $payload) = explode('.', $signed_request, 2);
78
79 // decode the data
80 $sig = _fb_settings_base64_url_decode($encoded_sig);
81 $data = json_decode(_fb_settings_base64_url_decode($payload), TRUE);
82
83 return $data;
84 }
85
86 /**
87 * Get the fb_settings from a parsed signed request.
88 * http://developers.facebook.com/docs/authentication/canvas
89 */
90 function _fb_settings_honor_signed_request($sr) {
91 if (isset($sr['page'])) {
92 // Iframe page tab.
93 fb_settings(FB_SETTINGS_CB_PAGE, $sr['page']['id']);
94 fb_settings(FB_SETTINGS_PAGE_ID, $sr['page']['id']);
95 fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PAGE_TAB);
96 if (isset($sr['user_id'])) {
97 fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
98 }
99 }
100 if (isset($sr['profile_id'])) {
101 // Only on old FBML tabs. Deprecated now that iframe tabs are preferred.
102 fb_settings(FB_SETTINGS_CB_PAGE, $sr['profile_id']);
103 fb_settings(FB_SETTINGS_CB_PAGE, $sr['profile_id']);
104 fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PROFILE);
105 if ($sr['user_id'] != $sr['profile_id']) {
106 fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
107 }
108 }
109 elseif (isset($sr['user_id'])) {
110 // Probably a canvas page.
111 fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
112 }
113
114 if (isset($sr['oauth_token'])) {
115 fb_settings(FB_SETTINGS_TOKEN, $sr['oauth_token']);
116 if (!fb_settings(FB_SETTINGS_ID)) {
117 // Prefer app id learned from url rewriting over that learned from signed request (because sr may be encrypted).
118 $tokens = explode('|', $sr['oauth_token']);
119 if ($app_id = $tokens[0]) {
120 fb_settings(FB_SETTINGS_ID, $app_id);
121 }
122 }
123 }
124 }
125
126 /**
127 * Parse a facebook session cookie. Based on sample code
128 * http://developers.facebook.com/docs/guides/web.
129 */
130 function fb_settings_get_facebook_cookie($app_id, $application_secret = NULL) {
131 if (!isset($_COOKIE['fbs_' . $app_id]))
132 return;
133
134 $args = array();
135 parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
136 if ($application_secret) {
137 ksort($args);
138 $payload = '';
139 foreach ($args as $key => $value) {
140 if ($key != 'sig') {
141 $payload .= $key . '=' . $value;
142 }
143 }
144 if (md5($payload . $application_secret) != $args['sig']) {
145 return NULL;
146 }
147 }
148 if (!isset($args['session_key'])) {
149 // Session key missing first time facebook connect page is loaded (?)
150 if ($access_token = $args['access_token']) {
151 $tokens = explode('|', $access_token);
152 $args['session_key'] = $tokens[1] . '|' . $tokens[2];
153 }
154 }
155 //print(__FUNCTION__); print_r($args); flush(); // debug
156 return $args;
157 }
158
159
160 /**
161 * By changing the $cookie_domain, we force drupal to use a different session
162 * when a user is logged into a facebook application. We base the
163 * $cookie_domain on the id of the application, if we can learn it.
164 *
165 * Facebook provides a number of "migrations" and historically has offered
166 * different data to applications. So the code below tries a variety of ways
167 * to learn the settings.
168 */
169
170 if (function_exists('_fb_settings_parse') &&
171 ($id = _fb_settings_parse(FB_SETTINGS_CB))) {
172 // Learned id from url rewrite.
173 // Either canvas page or profile tab.
174 fb_settings(FB_SETTINGS_ID, $id);
175
176 if ($page_id = _fb_settings_parse(FB_SETTINGS_CB_PAGE)) {
177 fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PAGE_TAB);
178 fb_settings(FB_SETTINGS_PAGE_ID, $page_id);
179 }
180 else {
181 fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CANVAS);
182 }
183
184 if (isset($_REQUEST['signed_request']) &&
185 ($sr = _fb_settings_parse_signed_request($_REQUEST['signed_request']))) {
186 // Prefer signed request data to cookie data.
187 _fb_settings_honor_signed_request($sr);
188 }
189 else {
190 $data = fb_settings_get_facebook_cookie($id);
191 if (isset($data)) {
192 if (isset($data['uid'])) {
193 fb_settings(FB_SETTINGS_FBU, $data['uid']);
194 }
195 }
196 }
197 }
198 elseif (isset($_REQUEST['signed_request']) &&
199 ($sr = _fb_settings_parse_signed_request($_REQUEST['signed_request']))) {
200 // Reach this clause on canvas page when admin has not enabled url_rewrite.
201 // http://developers.facebook.com/docs/authentication/canvas
202
203 // We get useful info from signed_request only when user is logged in and
204 // therefore oauth_token is set.
205 _fb_settings_honor_signed_request($sr);
206
207 // Once upon a time, signed_request was only passed on canvas pages. No longer true.
208 // @TODO - somehow detect whether a signed request indicates canvas page or not.
209 //fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CANVAS);
210 }
211 else {
212 // We're not in a canvas page.
213 // We might be in a facebook connect page. We have to inspect cookies to make sure.
214 $id = isset($conf['fb_id']) ? $conf['fb_id'] : NULL;
215 $secret = isset($conf['fb_secret']) ? $conf['fb_secret'] : NULL;
216 if ($id) {
217 $session = fb_settings_get_facebook_cookie($id, $secret);
218 // Honor connect session only when cookie is set.
219 if (count($session)) {
220 fb_settings(FB_SETTINGS_ID, $id);
221 fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CONNECT);
222 fb_settings(FB_SETTINGS_FBU, $session['uid']);
223 }
224 }
225 }
226
227 if (fb_settings(FB_SETTINGS_TYPE) &&
228 fb_settings(FB_SETTINGS_TYPE) != FB_SETTINGS_TYPE_CONNECT) {
229 // Cookie domain unique to app and page type.
230 $unique_id = fb_settings(FB_SETTINGS_ID);
231 $cookie_domain = isset($cookie_domain) ? $cookie_domain : '' .
232 fb_settings(FB_SETTINGS_TYPE) . $unique_id;
233 fb_settings(FB_SETTINGS_COOKIE_DOMAIN, $cookie_domain); // for debugging.
234 }
235 if (fb_settings(FB_SETTINGS_FBU)) {
236 // Disable Drupal cache when logged into facebook.
237 $conf['cache'] = 0;
238 }