summaryrefslogtreecommitdiffstats
path: root/fb_settings.inc
blob: 411909417369ab5a86e002c9849699dc1b1c7491 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<?php

/**
 * @file
 * This file is to be included from your sites/.../settings.php file.
 *
 * In this code we set up special session management and url
 * rewriting.  These things must be done before modules are loaded, so
 * the code is here instead of fb.module. And that is why this
 * must be include from settings.php.
 *
 */

// Each of these are things we can learn and store in fb_settings().
// The CB (callback) values are learned via URL rewriting.
// Include fb_url_rewrite.inc in your settings.php to enable this.
define('FB_SETTINGS_CB', 'fb_cb'); // The app id.
define('FB_SETTINGS_CB_PAGE', 'fb_page'); // Page id, for tabs.
define('FB_SETTINGS_CB_TYPE', 'fb_cb_type'); // For iframes within FBML canvas pages, now DEPRECATED.
define('FB_SETTINGS_CB_SESSION', 'fb_sess'); // For embedding session id within URL.

// Things we can learn from the facebook session.
define('FB_SETTINGS_APIKEY', 'apikey');
define('FB_SETTINGS_ID', 'app_id');
define('FB_SETTINGS_PAGE_ID', 'page_id');
define('FB_SETTINGS_FBU', 'fbu');
define('FB_SETTINGS_TOKEN', 'token');
define('FB_SETTINGS_TYPE', 'type'); // page type not same as cb type
define('FB_SETTINGS_COOKIE_DOMAIN', 'cookie_domain');

// Possible values for page type.
define('FB_SETTINGS_TYPE_CANVAS', 'canvas');
define('FB_SETTINGS_TYPE_CONNECT', 'connect');
define('FB_SETTINGS_TYPE_PROFILE', 'profile'); // deprecated, FBML tab
define('FB_SETTINGS_TYPE_PAGE_TAB', 'page_tab'); // iframe tab

/**
 * Helper function to remember values as we learn them.
 */
function fb_settings($key = NULL, $value = NULL) {
  static $cache;
  if (!isset($cache)) {
    $cache = array();
  }
  if (isset($value)) {
    $cache[$key] = $value;
  }
  if (isset($key)) {
    return isset($cache[$key]) ? $cache[$key] : NULL;
  }
  else {
    return $cache;
  }
}


/**
 * Helpers to parse signed_session.  Copied from facebook.php.
 */
/**
 * 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
 */
function _fb_settings_base64_url_decode($input) {
  return base64_decode(strtr($input, '-_', '+/'));
}

/**
 * See facebook.php for a more reliable version of this function.  We skip
 * validation because we do not yet know the app secret.
 */
function _fb_settings_parse_signed_request($signed_request) {
  list($encoded_sig, $payload) = explode('.', $signed_request, 2);

  // decode the data
  $sig = _fb_settings_base64_url_decode($encoded_sig);
  $data = json_decode(_fb_settings_base64_url_decode($payload), TRUE);

  return $data;
}

/**
 * Get the fb_settings from a parsed signed request.
 * http://developers.facebook.com/docs/authentication/canvas
 */
function _fb_settings_honor_signed_request($sr) {
  if (isset($sr['page'])) {
    // Iframe page tab.
    fb_settings(FB_SETTINGS_CB_PAGE, $sr['page']['id']);
    fb_settings(FB_SETTINGS_PAGE_ID, $sr['page']['id']);
    fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PAGE_TAB);
    if (isset($sr['user_id'])) {
      fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
    }
  }
  if (isset($sr['profile_id'])) {
    // Only on old FBML tabs.  Deprecated now that iframe tabs are preferred.
    fb_settings(FB_SETTINGS_CB_PAGE, $sr['profile_id']);
    fb_settings(FB_SETTINGS_CB_PAGE, $sr['profile_id']);
    fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PROFILE);
    if ($sr['user_id'] != $sr['profile_id']) {
      fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
    }
  }
  elseif (isset($sr['user_id'])) {
    // Probably a canvas page.
    fb_settings(FB_SETTINGS_FBU, $sr['user_id']);
  }

  if (isset($sr['oauth_token'])) {
    fb_settings(FB_SETTINGS_TOKEN, $sr['oauth_token']);
    if (!fb_settings(FB_SETTINGS_ID)) {
      // Prefer app id learned from url rewriting over that learned from signed request (because sr may be encrypted).
      $tokens = explode('|', $sr['oauth_token']);
      if ($app_id = $tokens[0]) {
        fb_settings(FB_SETTINGS_ID, $app_id);
      }
    }
  }
}

/**
 * Parse a facebook session cookie.  Based on sample code
 * http://developers.facebook.com/docs/guides/web.
 */
function fb_settings_get_facebook_cookie($app_id, $application_secret = NULL) {
  if (!isset($_COOKIE['fbs_' . $app_id]))
    return;

  $args = array();
  parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
  if ($application_secret) {
    ksort($args);
    $payload = '';
    foreach ($args as $key => $value) {
      if ($key != 'sig') {
        $payload .= $key . '=' . $value;
      }
    }
    if (md5($payload . $application_secret) != $args['sig']) {
      return NULL;
    }
  }
  if (!isset($args['session_key'])) {
    // Session key missing first time facebook connect page is loaded (?)
    if ($access_token = $args['access_token']) {
      $tokens = explode('|', $access_token);
      $args['session_key'] = $tokens[1] . '|' . $tokens[2];
    }
  }
  //print(__FUNCTION__); print_r($args); flush(); // debug
  return $args;
}


/**
 * By changing the $cookie_domain, we force drupal to use a different session
 * when a user is logged into a facebook application.  We base the
 * $cookie_domain on the id of the application, if we can learn it.
 *
 * Facebook provides a number of "migrations" and historically has offered
 * different data to applications.  So the code below tries a variety of ways
 * to learn the settings.
 */

if (function_exists('_fb_settings_parse') &&
    ($id = _fb_settings_parse(FB_SETTINGS_CB))) {
  // Learned id from url rewrite.
  // Either canvas page or profile tab.
  fb_settings(FB_SETTINGS_ID, $id);

  if ($page_id = _fb_settings_parse(FB_SETTINGS_CB_PAGE)) {
    fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_PAGE_TAB);
    fb_settings(FB_SETTINGS_PAGE_ID, $page_id);
  }
  else {
    fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CANVAS);
  }

  if (isset($_REQUEST['signed_request']) &&
      ($sr = _fb_settings_parse_signed_request($_REQUEST['signed_request']))) {
    // Prefer signed request data to cookie data.
    _fb_settings_honor_signed_request($sr);
  }
  else {
    $data = fb_settings_get_facebook_cookie($id);
    if (isset($data)) {
      if (isset($data['uid'])) {
        fb_settings(FB_SETTINGS_FBU, $data['uid']);
      }
    }
  }
}
elseif (isset($_REQUEST['signed_request']) &&
        ($sr = _fb_settings_parse_signed_request($_REQUEST['signed_request']))) {
  // Reach this clause on canvas page when admin has not enabled url_rewrite.
  // http://developers.facebook.com/docs/authentication/canvas

  // We get useful info from signed_request only when user is logged in and
  // therefore oauth_token is set.
  _fb_settings_honor_signed_request($sr);

  // Once upon a time, signed_request was only passed on canvas pages.  No longer true.
  // @TODO - somehow detect whether a signed request indicates canvas page or not.
  //fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CANVAS);
}
else {
  // We're not in a canvas page.
  // We might be in a facebook connect page.  We have to inspect cookies to make sure.
  $id = isset($conf['fb_id']) ? $conf['fb_id'] : NULL;
  $secret = isset($conf['fb_secret']) ? $conf['fb_secret'] : NULL;
  if ($id) {
    $session = fb_settings_get_facebook_cookie($id, $secret);
    // Honor connect session only when cookie is set.
    if (count($session)) {
      fb_settings(FB_SETTINGS_ID, $id);
      fb_settings(FB_SETTINGS_TYPE, FB_SETTINGS_TYPE_CONNECT);
      fb_settings(FB_SETTINGS_FBU, $session['uid']);
    }
  }
}

if (fb_settings(FB_SETTINGS_TYPE) &&
    fb_settings(FB_SETTINGS_TYPE) != FB_SETTINGS_TYPE_CONNECT) {
  // Cookie domain unique to app and page type.
  $unique_id = fb_settings(FB_SETTINGS_ID);
  $cookie_domain = isset($cookie_domain) ? $cookie_domain : '' .
    fb_settings(FB_SETTINGS_TYPE) . $unique_id;
  fb_settings(FB_SETTINGS_COOKIE_DOMAIN, $cookie_domain); // for debugging.
}
if (fb_settings(FB_SETTINGS_FBU)) {
  // Disable Drupal cache when logged into facebook.
  $conf['cache'] = 0;
}