1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * User session handling functions. |
---|
6 | */ |
---|
7 | |
---|
8 | function sess_open($save_path, $session_name) { |
---|
9 | return TRUE; |
---|
10 | } |
---|
11 | |
---|
12 | function sess_close() { |
---|
13 | return TRUE; |
---|
14 | } |
---|
15 | |
---|
16 | /** |
---|
17 | * Reads an entire session from the database (internal use only). |
---|
18 | * |
---|
19 | * Also initializes the $user object for the user associated with the session. |
---|
20 | * This function is registered with session_set_save_handler() to support |
---|
21 | * database-backed sessions. It is called on every page load when PHP sets |
---|
22 | * up the $_SESSION superglobal. |
---|
23 | * |
---|
24 | * This function is an internal function and must not be called directly. |
---|
25 | * Doing so may result in logging out the current user, corrupting session data |
---|
26 | * or other unexpected behavior. Session data must always be accessed via the |
---|
27 | * $_SESSION superglobal. |
---|
28 | * |
---|
29 | * @param $key |
---|
30 | * The session ID of the session to retrieve. |
---|
31 | * |
---|
32 | * @return |
---|
33 | * The user's session, or an empty string if no session exists. |
---|
34 | */ |
---|
35 | function sess_read($key) { |
---|
36 | global $user; |
---|
37 | |
---|
38 | // Write and Close handlers are called after destructing objects since PHP 5.0.5 |
---|
39 | // Thus destructors can use sessions but session handler can't use objects. |
---|
40 | // So we are moving session closure before destructing objects. |
---|
41 | register_shutdown_function('session_write_close'); |
---|
42 | |
---|
43 | // Handle the case of first time visitors and clients that don't store cookies (eg. web crawlers). |
---|
44 | if (empty($key) || !isset($_COOKIE[session_name()])) { |
---|
45 | $user = drupal_anonymous_user(); |
---|
46 | return ''; |
---|
47 | } |
---|
48 | |
---|
49 | // Otherwise, if the session is still active, we have a record of the client's session in the database. |
---|
50 | $user = db_fetch_object(db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = '%s'", $key)); |
---|
51 | |
---|
52 | // We found the client's session record and they are an authenticated, |
---|
53 | // active user. |
---|
54 | if ($user && $user->uid > 0 && $user->status == 1) { |
---|
55 | // This is done to unserialize the data member of $user |
---|
56 | $user = drupal_unpack($user); |
---|
57 | |
---|
58 | // Add roles element to $user |
---|
59 | $user->roles = array(); |
---|
60 | $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; |
---|
61 | $result = db_query("SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $user->uid); |
---|
62 | while ($role = db_fetch_object($result)) { |
---|
63 | $user->roles[$role->rid] = $role->name; |
---|
64 | } |
---|
65 | } |
---|
66 | // We didn't find the client's record (session has expired), or they are |
---|
67 | // blocked, or they are an anonymous user. |
---|
68 | else { |
---|
69 | $session = isset($user->session) ? $user->session : ''; |
---|
70 | $user = drupal_anonymous_user($session); |
---|
71 | } |
---|
72 | |
---|
73 | return $user->session; |
---|
74 | } |
---|
75 | |
---|
76 | /** |
---|
77 | * Writes an entire session to the database (internal use only). |
---|
78 | * |
---|
79 | * This function is registered with session_set_save_handler() to support |
---|
80 | * database-backed sessions. |
---|
81 | * |
---|
82 | * This function is an internal function and must not be called directly. |
---|
83 | * Doing so may result in corrupted session data or other unexpected behavior. |
---|
84 | * Session data must always be accessed via the $_SESSION superglobal. |
---|
85 | * |
---|
86 | * @param $key |
---|
87 | * The session ID of the session to write to. |
---|
88 | * @param $value |
---|
89 | * Session data to write as a serialized string. |
---|
90 | * |
---|
91 | * @return |
---|
92 | * Always returns TRUE. |
---|
93 | */ |
---|
94 | function sess_write($key, $value) { |
---|
95 | global $user; |
---|
96 | |
---|
97 | // If saving of session data is disabled or if the client doesn't have a session, |
---|
98 | // and one isn't being created ($value), do nothing. This keeps crawlers out of |
---|
99 | // the session table. This reduces memory and server load, and gives more useful |
---|
100 | // statistics. We can't eliminate anonymous session table rows without breaking |
---|
101 | // the throttle module and the "Who's Online" block. |
---|
102 | if (!session_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) { |
---|
103 | return TRUE; |
---|
104 | } |
---|
105 | |
---|
106 | db_query("UPDATE {sessions} SET uid = %d, cache = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '%s'", $user->uid, isset($user->cache) ? $user->cache : '', ip_address(), $value, time(), $key); |
---|
107 | if (db_affected_rows()) { |
---|
108 | // Last access time is updated no more frequently than once every 180 seconds. |
---|
109 | // This reduces contention in the users table. |
---|
110 | if ($user->uid && time() - $user->access > variable_get('session_write_interval', 180)) { |
---|
111 | db_query("UPDATE {users} SET access = %d WHERE uid = %d", time(), $user->uid); |
---|
112 | } |
---|
113 | } |
---|
114 | else { |
---|
115 | // If this query fails, another parallel request probably got here first. |
---|
116 | // In that case, any session data generated in this request is discarded. |
---|
117 | @db_query("INSERT INTO {sessions} (sid, uid, cache, hostname, session, timestamp) VALUES ('%s', %d, %d, '%s', '%s', %d)", $key, $user->uid, isset($user->cache) ? $user->cache : '', ip_address(), $value, time()); |
---|
118 | } |
---|
119 | |
---|
120 | return TRUE; |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * Called when an anonymous user becomes authenticated or vice-versa. |
---|
125 | */ |
---|
126 | function sess_regenerate() { |
---|
127 | $old_session_id = session_id(); |
---|
128 | |
---|
129 | // We code around http://bugs.php.net/bug.php?id=32802 by destroying |
---|
130 | // the session cookie by setting expiration in the past (a negative |
---|
131 | // value). This issue only arises in PHP versions before 4.4.0, |
---|
132 | // regardless of the Drupal configuration. |
---|
133 | // TODO: remove this when we require at least PHP 4.4.0 |
---|
134 | if (isset($_COOKIE[session_name()])) { |
---|
135 | setcookie(session_name(), '', time() - 42000, '/'); |
---|
136 | } |
---|
137 | |
---|
138 | session_regenerate_id(); |
---|
139 | |
---|
140 | db_query("UPDATE {sessions} SET sid = '%s' WHERE sid = '%s'", session_id(), $old_session_id); |
---|
141 | } |
---|
142 | |
---|
143 | /** |
---|
144 | * Counts how many users have sessions. Can count either anonymous sessions or authenticated sessions. |
---|
145 | * |
---|
146 | * @param int $timestamp |
---|
147 | * A Unix timestamp representing a point of time in the past. |
---|
148 | * The default is 0, which counts all existing sessions. |
---|
149 | * @param boolean $anonymous |
---|
150 | * TRUE counts only anonymous users. |
---|
151 | * FALSE counts only authenticated users. |
---|
152 | * @return int |
---|
153 | * The number of users with sessions. |
---|
154 | */ |
---|
155 | function sess_count($timestamp = 0, $anonymous = true) { |
---|
156 | $query = $anonymous ? ' AND uid = 0' : ' AND uid > 0'; |
---|
157 | return db_result(db_query('SELECT COUNT(sid) AS count FROM {sessions} WHERE timestamp >= %d'. $query, $timestamp)); |
---|
158 | } |
---|
159 | |
---|
160 | /** |
---|
161 | * Called by PHP session handling with the PHP session ID to end a user's session. |
---|
162 | * |
---|
163 | * @param string $sid |
---|
164 | * the session id |
---|
165 | */ |
---|
166 | function sess_destroy_sid($sid) { |
---|
167 | db_query("DELETE FROM {sessions} WHERE sid = '%s'", $sid); |
---|
168 | } |
---|
169 | |
---|
170 | /** |
---|
171 | * End a specific user's session |
---|
172 | * |
---|
173 | * @param string $uid |
---|
174 | * the user id |
---|
175 | */ |
---|
176 | function sess_destroy_uid($uid) { |
---|
177 | db_query('DELETE FROM {sessions} WHERE uid = %d', $uid); |
---|
178 | } |
---|
179 | |
---|
180 | function sess_gc($lifetime) { |
---|
181 | // Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough |
---|
182 | // value. For example, if you want user sessions to stay in your database |
---|
183 | // for three weeks before deleting them, you need to set gc_maxlifetime |
---|
184 | // to '1814400'. At that value, only after a user doesn't log in after |
---|
185 | // three weeks (1814400 seconds) will his/her session be removed. |
---|
186 | db_query("DELETE FROM {sessions} WHERE timestamp < %d", time() - $lifetime); |
---|
187 | |
---|
188 | return TRUE; |
---|
189 | } |
---|
190 | |
---|
191 | /** |
---|
192 | * Determine whether to save session data of the current request. |
---|
193 | * |
---|
194 | * This function allows the caller to temporarily disable writing of session data, |
---|
195 | * should the request end while performing potentially dangerous operations, such as |
---|
196 | * manipulating the global $user object. See http://drupal.org/node/218104 for usage |
---|
197 | * |
---|
198 | * @param $status |
---|
199 | * Disables writing of session data when FALSE, (re-)enables writing when TRUE. |
---|
200 | * @return |
---|
201 | * FALSE if writing session data has been disabled. Otherwise, TRUE. |
---|
202 | */ |
---|
203 | function session_save_session($status = NULL) { |
---|
204 | static $save_session = TRUE; |
---|
205 | if (isset($status)) { |
---|
206 | $save_session = $status; |
---|
207 | } |
---|
208 | return ($save_session); |
---|
209 | } |
---|