| 1 |
<?php
|
| 2 |
|
| 3 |
/**
|
| 4 |
* Here we override Drupal's session management. Actually, we try not
|
| 5 |
* to change things, unless we're servicing a facebook app. We
|
| 6 |
* include drupal's session.inc at the end of this file so that
|
| 7 |
* default behavior will take place. But first we detect whether
|
| 8 |
* we're handling a facebook app and if so we do a few special things.
|
| 9 |
*
|
| 10 |
* We need to handle several cases: FBML canvas pages, iframe canvas
|
| 11 |
* pages, and facebook connect pages.
|
| 12 |
*
|
| 13 |
* In the FBML case, the request comes from facebook, not the user's
|
| 14 |
* browser. So we can't set cookies. Instead we look to $_REQUEST
|
| 15 |
* for facebook's session key. If found, we muck with $_COOKIE
|
| 16 |
* variables so that session.inc, when we include it later, will do
|
| 17 |
* reasonable things.
|
| 18 |
*
|
| 19 |
* One weak point is FBML canvas pages where the user is not logged
|
| 20 |
* in. In this case facebook gives us no session state info. This is
|
| 21 |
* difficult for drupal to deal with. Perhaps the best option is to
|
| 22 |
* require login for application pages. A must for any pages which
|
| 23 |
* require session to work.
|
| 24 |
*
|
| 25 |
* Iframe canvas pages are a tricky case. Here, we can set cookies on
|
| 26 |
* the browser. On the first iframe request, facebook will provide
|
| 27 |
* additional session state info. On subsequest requests, if iframe
|
| 28 |
* links to a local url (without target=_top) we won't have the
|
| 29 |
* facebook params.
|
| 30 |
*
|
| 31 |
* In general with iframes, the user could also be visiting the
|
| 32 |
* regular website. We don't want iframe sessions to compete with
|
| 33 |
* regular sessions, so we change the session_name.
|
| 34 |
*
|
| 35 |
* Some browsers (Safari) will not accept the cookies we assign to an
|
| 36 |
* iframe. Setting $conf[fb_session_cookieless_iframe] attempts to
|
| 37 |
* work around this.
|
| 38 |
*
|
| 39 |
* For Facebook Connect, we have to honor facebook's session state
|
| 40 |
* info, so that when a user logs out of facebook, they are also
|
| 41 |
* logged out of their connect session. Also, we want to preserve
|
| 42 |
* previous session state. So for example if a user is already logged
|
| 43 |
* into Drupal, we'll know that after they hit the connect button.
|
| 44 |
*/
|
| 45 |
|
| 46 |
$orig_session_name = session_name();
|
| 47 |
if (isset($_COOKIE[$orig_session_name]))
|
| 48 |
$orig_session_id = $_COOKIE[$orig_session_name];
|
| 49 |
else
|
| 50 |
$orig_session_id = '';
|
| 51 |
|
| 52 |
$nid = _fb_settings_parse(FB_SETTINGS_APP_NID);
|
| 53 |
|
| 54 |
if ($nid && isset($_REQUEST['fb_sig_api_key'])) {
|
| 55 |
// Canvas page or event callback
|
| 56 |
|
| 57 |
// If facebook provides a session key, us it. Allows us to share
|
| 58 |
// a session between FBML and iframe, and when forms are submitted
|
| 59 |
// from FBML canvas pages.
|
| 60 |
$new_session_name = "fb_canvas_{$nid}_" . $orig_session_name;
|
| 61 |
if (isset($_REQUEST['fb_sig_session_key']))
|
| 62 |
$new_session_id = "fb_canvas_{$nid}_" . $_REQUEST['fb_sig_session_key'];
|
| 63 |
else if ($orig_session_id) {
|
| 64 |
// When user is logged into facebook, but not authorized app, cookies are honored. (confirm this???)
|
| 65 |
$new_session_id = "fb_canvas_{$nid}_" . $orig_session_id;
|
| 66 |
}
|
| 67 |
else {
|
| 68 |
// If we have no session (user not logged into facebook) all such users will share one session!!!
|
| 69 |
$new_session_id = "fb_canvas_{$nid}_shared_session";
|
| 70 |
}
|
| 71 |
|
| 72 |
// Force url() to include the cookie-less session when in iframe
|
| 73 |
if (variable_get('fb_session_cookieless_iframe', FALSE) &&
|
| 74 |
isset($_REQUEST['fb_sig_in_iframe']) &&
|
| 75 |
$_REQUEST['fb_sig_in_iframe']) {
|
| 76 |
fb_settings(FB_SETTINGS_SESSION_KEY, $_REQUEST['fb_sig_session_key']);
|
| 77 |
}
|
| 78 |
}
|
| 79 |
else if ($nid && variable_get('fb_session_cookieless_iframe', FALSE) &&
|
| 80 |
($sess_key = _fb_settings_parse(FB_SETTINGS_SESSION_KEY))) {
|
| 81 |
// using sessionless iframes
|
| 82 |
// similar logic to clause above, using session key in url path
|
| 83 |
$new_session_id = "fb_canvas_{$nid}_" . $sess_key;
|
| 84 |
$new_session_name = "fb_canvas_{$nid}_" . $orig_session_name;
|
| 85 |
}
|
| 86 |
else {
|
| 87 |
// Try to learn session key from cookies (Facebook Connect)
|
| 88 |
|
| 89 |
$apikey = NULL;
|
| 90 |
// Discover APIKEY by inspecting cookies.
|
| 91 |
// This could be made more efficient by looking only for the primary apikey. I hesitate because some sites may need to support multiple connect apps. (I.e. one for the website and other for resizeable iframes in canvas pages)
|
| 92 |
foreach ($_COOKIE as $key => $value) {
|
| 93 |
if ($pos = strpos($key, '_session_key')) {
|
| 94 |
$apikey = substr($key, 0, $pos);
|
| 95 |
}
|
| 96 |
}
|
| 97 |
|
| 98 |
if ($apikey && isset($_COOKIE[$apikey . '_ss'])) {
|
| 99 |
// We're logged into Facebook Connect.
|
| 100 |
// If fbConnect, we want to use another session id, so that if the
|
| 101 |
// user logs out of facebook, they are also logged out of drupal.
|
| 102 |
|
| 103 |
// Use globals to remember some values, for fb_connect.module to use.
|
| 104 |
$GLOBALS['fb_connect_apikey'] = $apikey;
|
| 105 |
|
| 106 |
// Rename the session id, so the Facebook Connect session is distinct from the original drupal session.
|
| 107 |
$new_session_id = 'fb_connect_' . $_COOKIE[$apikey . '_session_key'];
|
| 108 |
|
| 109 |
}
|
| 110 |
}
|
| 111 |
|
| 112 |
if (isset($new_session_name)) {
|
| 113 |
session_name($new_session_name);
|
| 114 |
}
|
| 115 |
|
| 116 |
if (isset($new_session_id)) {
|
| 117 |
if (!variable_get('fb_session_long_keys', TRUE)) {
|
| 118 |
// Facebook appends user id, time and expiry info which is not necessary for uniqueness. Here we truncate that information to ensure the sid fits in sessions table.
|
| 119 |
$new_session_id = substr($new_session_id, 0, 64);
|
| 120 |
}
|
| 121 |
if ($new_session_id != $orig_session_id) {
|
| 122 |
session_id($new_session_id);
|
| 123 |
if (isset($GLOBALS['fb_connect_apikey'])) {
|
| 124 |
// We can preserve the session state when going into fbconnect
|
| 125 |
db_query("DELETE FROM {sessions} WHERE sid='%s'", $new_session_id);
|
| 126 |
db_query("UPDATE {sessions} SET sid = '%s' WHERE sid = '%s'", $new_session_id, $orig_session_id);
|
| 127 |
}
|
| 128 |
|
| 129 |
// If we've changed the session id, disable drupal's caching
|
| 130 |
$GLOBALS['conf']['cache'] = 0;
|
| 131 |
}
|
| 132 |
}
|
| 133 |
else {
|
| 134 |
// No session from facebook, so make sure we're not using an out of date one.
|
| 135 |
if (strpos($orig_session_id, 'fb_connect') === 0) {
|
| 136 |
// Old fbconnect session can be deleted
|
| 137 |
db_query("DELETE FROM {sessions} WHERE sid='%s'", $orig_session_id);
|
| 138 |
session_id(md5(uniqid(microtime()) . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']));
|
| 139 |
}
|
| 140 |
else if (strpos($orig_session_id, 'fb_connect') === 0) {
|
| 141 |
// Canvas session should not be deleted as it could be a user visiting both the website and an iframe app
|
| 142 |
if (!$nid)
|
| 143 |
session_id(md5(uniqid(microtime()) . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']));
|
| 144 |
}
|
| 145 |
}
|
| 146 |
|
| 147 |
|
| 148 |
if ($nid && !isset($_COOKIE[session_name()])) {
|
| 149 |
// requests from facebook (FBML canvas pages) will not have cookies.
|
| 150 |
// We want Drupal's session.inc to work properly, as if the session
|
| 151 |
// came via cookie.
|
| 152 |
if (!$_COOKIE || !count($_COOKIE))
|
| 153 |
// Remember that cookies are actually disabled, some apps will want to display a message and/or redirect in this case.
|
| 154 |
$_COOKIE['_fb_cookie_fake'] = TRUE;
|
| 155 |
$_COOKIE[session_name()] = session_id();
|
| 156 |
}
|
| 157 |
|
| 158 |
|
| 159 |
// Finally, include the logic of Drupal's session.inc
|
| 160 |
include('includes/session.inc');
|
| 161 |
|
| 162 |
?>
|