[BUGFIX] Multiple Set-Cookie headers for deleting FE cookie
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Authentication / FrontendUserAuthentication.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Authentication;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 /**
31 * Extension class for Front End User Authentication.
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 * @author René Fritz <r.fritz@colorcube.de>
35 */
36 class FrontendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication {
37
38 // formfield with 0 or 1 // 1 = permanent login enabled // 0 = session is valid for a browser session only
39 /**
40 * @todo Define visibility
41 */
42 public $formfield_permanent = 'permalogin';
43
44 // Lifetime of session data in seconds.
45 protected $sessionDataLifetime = 86400;
46
47 /**
48 * @todo Define visibility
49 */
50 public $usergroup_column = 'usergroup';
51
52 /**
53 * @todo Define visibility
54 */
55 public $usergroup_table = 'fe_groups';
56
57 /**
58 * @todo Define visibility
59 */
60 public $groupData = array(
61 'title' => array(),
62 'uid' => array(),
63 'pid' => array()
64 );
65
66 // Used to accumulate the TSconfig data of the user
67 /**
68 * @todo Define visibility
69 */
70 public $TSdataArray = array();
71
72 /**
73 * @todo Define visibility
74 */
75 public $userTS = array();
76
77 /**
78 * @todo Define visibility
79 */
80 public $userTSUpdated = 0;
81
82 /**
83 * @todo Define visibility
84 */
85 public $showHiddenRecords = 0;
86
87 // Session and user data:
88 /*
89 There are two types of data that can be stored: UserData and Session-Data. Userdata is for the login-user, and session-data for anyone viewing the pages.
90 'Keys' are keys in the internal dataarray of the data. When you get or set a key in one of the data-spaces (user or session) you decide the type of the variable (not object though)
91 'Reserved' keys are:
92 - 'recs': Array: Used to 'register' records, eg in a shopping basket. Structure: [recs][tablename][record_uid]=number
93 - sys: Reserved for TypoScript standard code.
94 */
95 /**
96 * @todo Define visibility
97 */
98 public $sesData = array();
99
100 /**
101 * @todo Define visibility
102 */
103 public $sesData_change = 0;
104
105 /**
106 * @todo Define visibility
107 */
108 public $userData_change = 0;
109
110 protected $sessionDataTimestamp = NULL;
111
112 /**
113 * Default constructor.
114 */
115 public function __construct() {
116 // Disable cookie by default, will be activated if saveSessionData() is called,
117 // a user is logging-in or an existing session is found
118 $this->dontSetCookie = TRUE;
119
120 $this->session_table = 'fe_sessions';
121 $this->name = self::getCookieName();
122 $this->get_name = 'ftu';
123 $this->loginType = 'FE';
124 $this->user_table = 'fe_users';
125 $this->username_column = 'username';
126 $this->userident_column = 'password';
127 $this->userid_column = 'uid';
128 $this->lastLogin_column = 'lastlogin';
129 $this->enablecolumns = array(
130 'deleted' => 'deleted',
131 'disabled' => 'disable',
132 'starttime' => 'starttime',
133 'endtime' => 'endtime'
134 );
135 $this->formfield_uname = 'user';
136 $this->formfield_uident = 'pass';
137 $this->formfield_chalvalue = 'challenge';
138 $this->formfield_status = 'logintype';
139 $this->auth_timeout_field = 6000;
140 $this->sendNoCacheHeaders = FALSE;
141 $this->getFallBack = TRUE;
142 $this->getMethodEnabled = TRUE;
143 }
144
145 /**
146 * Returns the configured cookie name
147 *
148 * @return string
149 */
150 static public function getCookieName() {
151 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['FE']['cookieName']);
152 if (empty($configuredCookieName)) {
153 $configuredCookieName = 'fe_typo_user';
154 }
155 return $configuredCookieName;
156 }
157
158 /**
159 * Starts a user session
160 *
161 * @return void
162 * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::start()
163 * @todo Define visibility
164 */
165 public function start() {
166 if ((int)$this->auth_timeout_field > 0 && (int)$this->auth_timeout_field < $this->lifetime) {
167 // If server session timeout is non-zero but less than client session timeout: Copy this value instead.
168 $this->auth_timeout_field = $this->lifetime;
169 }
170 $this->sessionDataLifetime = (int)$GLOBALS['TYPO3_CONF_VARS']['FE']['sessionDataLifetime'];
171 if ($this->sessionDataLifetime <= 0) {
172 $this->sessionDataLifetime = 86400;
173 }
174 parent::start();
175 }
176
177 /**
178 * Returns a new session record for the current user for insertion into the DB.
179 *
180 * @return array User session record
181 * @todo Define visibility
182 */
183 public function getNewSessionRecord($tempuser) {
184 $insertFields = parent::getNewSessionRecord($tempuser);
185 $insertFields['ses_permanent'] = $this->is_permanent;
186 return $insertFields;
187 }
188
189 /**
190 * Determine whether a session cookie needs to be set (lifetime=0)
191 *
192 * @return boolean
193 * @internal
194 * @todo Define visibility
195 */
196 public function isSetSessionCookie() {
197 return ($this->newSessionID || $this->forceSetCookie)
198 && ($this->lifetime == 0 || !isset($this->user['ses_permanent']) || !$this->user['ses_permanent']);
199 }
200
201 /**
202 * Determine whether a non-session cookie needs to be set (lifetime>0)
203 *
204 * @return boolean
205 * @internal
206 * @todo Define visibility
207 */
208 public function isRefreshTimeBasedCookie() {
209 return $this->lifetime > 0 && isset($this->user['ses_permanent']) && $this->user['ses_permanent'];
210 }
211
212 /**
213 * Returns an info array with Login/Logout data submitted by a form or params
214 *
215 * @return array
216 * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::getLoginFormData()
217 * @todo Define visibility
218 */
219 public function getLoginFormData() {
220 $loginData = parent::getLoginFormData();
221 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 0 || $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 1) {
222 if ($this->getMethodEnabled) {
223 $isPermanent = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP($this->formfield_permanent);
224 } else {
225 $isPermanent = \TYPO3\CMS\Core\Utility\GeneralUtility::_POST($this->formfield_permanent);
226 }
227 if (strlen($isPermanent) != 1) {
228 $isPermanent = $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'];
229 } elseif (!$isPermanent) {
230 // To make sure the user gets a session cookie and doesn't keep a possibly existing time based cookie,
231 // we need to force seeting the session cookie here
232 $this->forceSetCookie = TRUE;
233 }
234 $isPermanent = $isPermanent ? 1 : 0;
235 } elseif ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 2) {
236 $isPermanent = 1;
237 } else {
238 $isPermanent = 0;
239 }
240 $loginData['permanent'] = $isPermanent;
241 $this->is_permanent = $isPermanent;
242 return $loginData;
243 }
244
245 /**
246 * Creates a user session record and returns its values.
247 * However, as the FE user cookie is normally not set, this has to be done
248 * before the parent class is doing the rest.
249 *
250 * @param array $tempuser User data array
251 * @return array The session data for the newly created session.
252 */
253 public function createUserSession($tempuser) {
254 $this->setSessionCookie();
255 return parent::createUserSession($tempuser);
256 }
257
258 /**
259 * Will select all fe_groups records that the current fe_user is member of - and which groups are also allowed in the current domain.
260 * It also accumulates the TSconfig for the fe_user/fe_groups in ->TSdataArray
261 *
262 * @return integer Returns the number of usergroups for the frontend users (if the internal user record exists and the usergroup field contains a value)
263 * @todo Define visibility
264 */
265 public function fetchGroupData() {
266 $this->TSdataArray = array();
267 $this->userTS = array();
268 $this->userTSUpdated = 0;
269 $this->groupData = array(
270 'title' => array(),
271 'uid' => array(),
272 'pid' => array()
273 );
274 // Setting default configuration:
275 $this->TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultUserTSconfig'];
276 // Get the info data for auth services
277 $authInfo = $this->getAuthInfoArray();
278 if ($this->writeDevLog) {
279 if (is_array($this->user)) {
280 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Get usergroups for user: ' . \TYPO3\CMS\Core\Utility\GeneralUtility::arrayToLogString($this->user, array($this->userid_column, $this->username_column)), 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
281 } else {
282 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Get usergroups for "anonymous" user', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
283 }
284 }
285 $groupDataArr = array();
286 // Use 'auth' service to find the groups for the user
287 $serviceChain = '';
288 $subType = 'getGroups' . $this->loginType;
289 while (is_object($serviceObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
290 $serviceChain .= ',' . $serviceObj->getServiceKey();
291 $serviceObj->initAuth($subType, array(), $authInfo, $this);
292 $groupData = $serviceObj->getGroups($this->user, $groupDataArr);
293 if (is_array($groupData) && count($groupData)) {
294 // Keys in $groupData should be unique ids of the groups (like "uid") so this function will override groups.
295 $groupDataArr = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($groupDataArr, $groupData);
296 }
297 unset($serviceObj);
298 }
299 if ($this->writeDevLog && $serviceChain) {
300 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog($subType . ' auth services called: ' . $serviceChain, 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
301 }
302 if ($this->writeDevLog && !count($groupDataArr)) {
303 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog('No usergroups found by services', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
304 }
305 if ($this->writeDevLog && count($groupDataArr)) {
306 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog(count($groupDataArr) . ' usergroup records found by services', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
307 }
308 // Use 'auth' service to check the usergroups if they are really valid
309 foreach ($groupDataArr as $groupData) {
310 // By default a group is valid
311 $validGroup = TRUE;
312 $serviceChain = '';
313 $subType = 'authGroups' . $this->loginType;
314 while (is_object($serviceObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
315 $serviceChain .= ',' . $serviceObj->getServiceKey();
316 $serviceObj->initAuth($subType, array(), $authInfo, $this);
317 if (!$serviceObj->authGroup($this->user, $groupData)) {
318 $validGroup = FALSE;
319 if ($this->writeDevLog) {
320 \TYPO3\CMS\Core\Utility\GeneralUtility::devLog($subType . ' auth service did not auth group: ' . \TYPO3\CMS\Core\Utility\GeneralUtility::arrayToLogString($groupData, 'uid,title'), 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication', 2);
321 }
322 break;
323 }
324 unset($serviceObj);
325 }
326 unset($serviceObj);
327 if ($validGroup) {
328 $this->groupData['title'][$groupData['uid']] = $groupData['title'];
329 $this->groupData['uid'][$groupData['uid']] = $groupData['uid'];
330 $this->groupData['pid'][$groupData['uid']] = $groupData['pid'];
331 $this->groupData['TSconfig'][$groupData['uid']] = $groupData['TSconfig'];
332 }
333 }
334 if (count($this->groupData) && count($this->groupData['TSconfig'])) {
335 // TSconfig: collect it in the order it was collected
336 foreach ($this->groupData['TSconfig'] as $TSdata) {
337 $this->TSdataArray[] = $TSdata;
338 }
339 $this->TSdataArray[] = $this->user['TSconfig'];
340 // Sort information
341 ksort($this->groupData['title']);
342 ksort($this->groupData['uid']);
343 ksort($this->groupData['pid']);
344 }
345 return count($this->groupData['uid']) ?: 0;
346 }
347
348 /**
349 * Returns the parsed TSconfig for the fe_user
350 * First time this function is called it will parse the TSconfig and store it in $this->userTS. Subsequent requests will not re-parse the TSconfig but simply return what is already in $this->userTS
351 *
352 * @return array TSconfig array for the fe_user
353 * @todo Define visibility
354 */
355 public function getUserTSconf() {
356 if (!$this->userTSUpdated) {
357 // Parsing the user TS (or getting from cache)
358 $this->TSdataArray = \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
359 $userTS = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
360 $parseObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
361 $parseObj->parse($userTS);
362 $this->userTS = $parseObj->setup;
363 $this->userTSUpdated = 1;
364 }
365 return $this->userTS;
366 }
367
368 /*****************************************
369 *
370 * Session data management functions
371 *
372 ****************************************/
373 /**
374 * Fetches the session data for the user (from the fe_session_data table) based on the ->id of the current user-session.
375 * The session data is restored to $this->sesData
376 * 1/100 calls will also do a garbage collection.
377 *
378 * @return void
379 * @access private
380 * @see storeSessionData()
381 * @todo Define visibility
382 */
383 public function fetchSessionData() {
384 // Gets SesData if any AND if not already selected by session fixation check in ->isExistingSessionRecord()
385 if ($this->id && !count($this->sesData)) {
386 $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('*', 'fe_session_data', 'hash = :hash');
387 $statement->execute(array(':hash' => $this->id));
388 if (($sesDataRow = $statement->fetch()) !== FALSE) {
389 $this->sesData = unserialize($sesDataRow['content']);
390 $this->sessionDataTimestamp = $sesDataRow['tstamp'];
391 }
392 $statement->free();
393 }
394 }
395
396 /**
397 * Will write UC and session data.
398 * If the flag $this->userData_change has been set, the function ->writeUC is called (which will save persistent user session data)
399 * If the flag $this->sesData_change has been set, the fe_session_data table is updated with the content of $this->sesData
400 * If the $this->sessionDataTimestamp is NULL there was no session record yet, so we need to insert it into the database
401 *
402 * @return void
403 * @see fetchSessionData(), getKey(), setKey()
404 * @todo Define visibility
405 */
406 public function storeSessionData() {
407 // Saves UC and SesData if changed.
408 if ($this->userData_change) {
409 $this->writeUC('');
410 }
411 if ($this->sesData_change && $this->id) {
412 if (empty($this->sesData)) {
413 // Remove session-data
414 $this->removeSessionData();
415 // Remove cookie if not logged in as the session data is removed as well
416 if (empty($this->user['uid']) && $this->isCookieSet()) {
417 $this->removeCookie($this->name);
418 }
419 } elseif ($this->sessionDataTimestamp === NULL) {
420 // Write new session-data
421 $insertFields = array(
422 'hash' => $this->id,
423 'content' => serialize($this->sesData),
424 'tstamp' => $GLOBALS['EXEC_TIME']
425 );
426 $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
427 $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_session_data', $insertFields);
428 // Now set the cookie (= fix the session)
429 $this->setSessionCookie();
430 } else {
431 // Update session data
432 $updateFields = array(
433 'content' => serialize($this->sesData),
434 'tstamp' => $GLOBALS['EXEC_TIME']
435 );
436 $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
437 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_session_data', 'hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'fe_session_data'), $updateFields);
438 }
439 }
440 }
441
442 /**
443 * Removes data of the current session.
444 *
445 * @return void
446 */
447 public function removeSessionData() {
448 $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_session_data', 'hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'fe_session_data'));
449 }
450
451 /**
452 * Log out current user!
453 * Removes the current session record, sets the internal ->user array to a blank string
454 * Thereby the current user (if any) is effectively logged out!
455 * Additionally the cookie is removed
456 *
457 * @return void
458 */
459 public function logoff() {
460 parent::logoff();
461 // Remove the cookie on log-off
462 if ($this->isCookieSet()) {
463 $this->removeCookie($this->name);
464 }
465 }
466
467 /**
468 * Executes the garbage collection of session data and session.
469 * The lifetime of session data is defined by $TYPO3_CONF_VARS['FE']['sessionDataLifetime'].
470 *
471 * @return void
472 */
473 public function gc() {
474 $timeoutTimeStamp = (int)($GLOBALS['EXEC_TIME'] - $this->sessionDataLifetime);
475 $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_session_data', 'tstamp < ' . $timeoutTimeStamp);
476 parent::gc();
477 }
478
479 /**
480 * Returns session data for the fe_user; Either persistent data following the fe_users uid/profile (requires login) or current-session based (not available when browse is closed, but does not require login)
481 *
482 * @param string $type Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
483 * @param string $key Key from the data array to return; The session data (in either case) is an array ($this->uc / $this->sesData) and this value determines which key to return the value for.
484 * @return mixed Returns whatever value there was in the array for the key, $key
485 * @see setKey()
486 * @todo Define visibility
487 */
488 public function getKey($type, $key) {
489 if ($key) {
490 switch ($type) {
491 case 'user':
492 return $this->uc[$key];
493 break;
494 case 'ses':
495 return $this->sesData[$key];
496 break;
497 }
498 }
499 }
500
501 /**
502 * Saves session data, either persistent or bound to current session cookie. Please see getKey() for more details.
503 * When a value is set the flags $this->userData_change or $this->sesData_change will be set so that the final call to ->storeSessionData() will know if a change has occurred and needs to be saved to the database.
504 * Notice: The key "recs" is already used by the function record_registration() which stores table/uid=value pairs in that key. This is used for the shopping basket among other things.
505 * Notice: Simply calling this function will not save the data to the database! The actual saving is done in storeSessionData() which is called as some of the last things in index_ts.php. So if you exit before this point, nothing gets saved of course! And the solution is to call $GLOBALS['TSFE']->storeSessionData(); before you exit.
506 *
507 * @param string $type Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
508 * @param string $key Key from the data array to store incoming data in; The session data (in either case) is an array ($this->uc / $this->sesData) and this value determines in which key the $data value will be stored.
509 * @param mixed $data The data value to store in $key
510 * @return void
511 * @see setKey(), storeSessionData(), record_registration()
512 * @todo Define visibility
513 */
514 public function setKey($type, $key, $data) {
515 if ($key) {
516 switch ($type) {
517 case 'user':
518 if ($this->user['uid']) {
519 if ($data === NULL) {
520 unset($this->uc[$key]);
521 } else {
522 $this->uc[$key] = $data;
523 }
524 $this->userData_change = 1;
525 }
526 break;
527 case 'ses':
528 if ($data === NULL) {
529 unset($this->sesData[$key]);
530 } else {
531 $this->sesData[$key] = $data;
532 }
533 $this->sesData_change = 1;
534 break;
535 }
536 }
537 }
538
539 /**
540 * Returns the session data stored for $key.
541 * The data will last only for this login session since it is stored in the session table.
542 *
543 * @param string $key
544 * @return mixed
545 */
546 public function getSessionData($key) {
547 return $this->getKey('ses', $key);
548 }
549
550 /**
551 * Saves the tokens so that they can be used by a later incarnation of this class.
552 *
553 * @param string $key
554 * @param mixed $data
555 * @return void
556 */
557 public function setAndSaveSessionData($key, $data) {
558 $this->setKey('ses', $key, $data);
559 $this->storeSessionData();
560 }
561
562 /**
563 * Registration of records/"shopping basket" in session data
564 * This will take the input array, $recs, and merge into the current "recs" array found in the session data.
565 * If a change in the recs storage happens (which it probably does) the function setKey() is called in order to store the array again.
566 *
567 * @param array $recs The data array to merge into/override the current recs values. The $recs array is constructed as [table]][uid] = scalar-value (eg. string/integer).
568 * @param integer $maxSizeOfSessionData The maximum size of stored session data. If zero, no limit is applied and even confirmation of cookie session is discarded.
569 * @return void
570 * @todo Define visibility
571 */
572 public function record_registration($recs, $maxSizeOfSessionData = 0) {
573 // Storing value ONLY if there is a confirmed cookie set,
574 // otherwise a shellscript could easily be spamming the fe_sessions table
575 // with bogus content and thus bloat the database
576 if (!$maxSizeOfSessionData || $this->isCookieSet()) {
577 if ($recs['clear_all']) {
578 $this->setKey('ses', 'recs', array());
579 }
580 $change = 0;
581 $recs_array = $this->getKey('ses', 'recs');
582 foreach ($recs as $table => $data) {
583 if (is_array($data)) {
584 foreach ($data as $rec_id => $value) {
585 if ($value != $recs_array[$table][$rec_id]) {
586 $recs_array[$table][$rec_id] = $value;
587 $change = 1;
588 }
589 }
590 }
591 }
592 if ($change && (!$maxSizeOfSessionData || strlen(serialize($recs_array)) < $maxSizeOfSessionData)) {
593 $this->setKey('ses', 'recs', $recs_array);
594 }
595 }
596 }
597
598 /**
599 * Determine whether there's an according session record to a given session_id
600 * in the database. Don't care if session record is still valid or not.
601 *
602 * This calls the parent function but additionally tries to look up the session ID in the "fe_session_data" table.
603 *
604 * @param integer $id Claimed Session ID
605 * @return boolean Returns TRUE if a corresponding session was found in the database
606 * @todo Define visibility
607 */
608 public function isExistingSessionRecord($id) {
609 // Perform check in parent function
610 $count = parent::isExistingSessionRecord($id);
611 // Check if there are any fe_session_data records for the session ID the client claims to have
612 if ($count == FALSE) {
613 $statement = $GLOBALS['TYPO3_DB']->prepare_SELECTquery('content,tstamp', 'fe_session_data', 'hash = :hash');
614 $res = $statement->execute(array(':hash' => $id));
615 if ($res !== FALSE) {
616 if ($sesDataRow = $statement->fetch()) {
617 $count = TRUE;
618 $this->sesData = unserialize($sesDataRow['content']);
619 $this->sessionDataTimestamp = $sesDataRow['tstamp'];
620 }
621 $statement->free();
622 }
623 }
624 return $count;
625 }
626
627 }