Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / session / SessionHandler.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\system\session;
5a05fde9
AE
3use wcf\data\session\virtual\SessionVirtual;
4use wcf\data\session\virtual\SessionVirtualAction;
5use wcf\data\session\virtual\SessionVirtualEditor;
11ade432 6use wcf\data\user\User;
235c040a 7use wcf\data\user\UserEditor;
8f3fc897 8use wcf\page\ITrackablePage;
e8d26212 9use wcf\system\cache\builder\SpiderCacheBuilder;
b401cd0d 10use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
11ade432 11use wcf\system\exception\PermissionDeniedException;
7aa1a486 12use wcf\system\request\RequestHandler;
75afb100 13use wcf\system\user\authentication\UserAuthenticationFactory;
c96ee721 14use wcf\system\user\storage\UserStorageHandler;
11ade432
AE
15use wcf\system\SingletonFactory;
16use wcf\system\WCF;
ac094463 17use wcf\util\HeaderUtil;
d4f5c98c 18use wcf\util\PasswordUtil;
11ade432
AE
19use wcf\util\StringUtil;
20use wcf\util\UserUtil;
21
22/**
a17de04e 23 * Handles sessions.
11ade432
AE
24 *
25 * @author Alexander Ebert
ca4ba303 26 * @copyright 2001-2014 WoltLab GmbH
11ade432
AE
27 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
28 * @package com.woltlab.wcf
29 * @subpackage system.session
9f959ced 30 * @category Community Framework
11ade432
AE
31 */
32class SessionHandler extends SingletonFactory {
33 /**
34 * prevents update on shutdown
11ade432 35 * @var boolean
a79013ef 36 */
11ade432
AE
37 protected $doNotUpdate = false;
38
39 /**
40 * various environment variables
11ade432 41 * @var array
a79013ef 42 */
11ade432
AE
43 protected $environment = array();
44
45 /**
46 * group data and permissions
9f959ced 47 * @var array<array>
a79013ef 48 */
11ade432
AE
49 protected $groupData = null;
50
51 /**
acfe7efb 52 * language id for active user
11ade432 53 * @var integer
a79013ef 54 */
11ade432
AE
55 protected $languageID = 0;
56
57 /**
58 * language ids for active user
11ade432 59 * @var array<integer>
a79013ef 60 */
11ade432
AE
61 protected $languageIDs = null;
62
63 /**
64 * session object
0ad90fc3 65 * @var \wcf\data\acp\session\ACPSession
a79013ef 66 */
11ade432
AE
67 protected $session = null;
68
11ade432
AE
69 /**
70 * session class name
11ade432 71 * @var string
a79013ef 72 */
11ade432
AE
73 protected $sessionClassName = '';
74
75 /**
76 * session editor class name
11ade432
AE
77 * @var string
78 */
79 protected $sessionEditorClassName = '';
80
5a05fde9
AE
81 /**
82 * virtual session support
83 * @var boolean
84 */
85 protected $supportsVirtualSessions = false;
86
83736ee3
AE
87 /**
88 * style id
89 * @var integer
90 */
91 protected $styleID = null;
92
11ade432
AE
93 /**
94 * enable cookie support
11ade432
AE
95 * @var boolean
96 */
97 protected $useCookies = false;
98
99 /**
100 * user object
0ad90fc3 101 * @var \wcf\data\user\User
a79013ef 102 */
11ade432
AE
103 protected $user = null;
104
105 /**
106 * session variables
11ade432 107 * @var array
a79013ef 108 */
11ade432
AE
109 protected $variables = null;
110
111 /**
112 * indicates if session variables changed and must be saved upon shutdown
11ade432 113 * @var boolean
a79013ef 114 */
11ade432
AE
115 protected $variablesChanged = false;
116
5a05fde9
AE
117 /**
118 * virtual session object, null for guests
119 * @var \wcf\data\session\virtual\SessionVirtual
120 */
121 protected $virtualSession = false;
122
11ade432
AE
123 /**
124 * Provides access to session data.
125 *
126 * @param string $key
127 * @return mixed
a79013ef 128 */
11ade432
AE
129 public function __get($key) {
130 if (isset($this->environment[$key])) {
131 return $this->environment[$key];
132 }
133
134 return $this->session->{$key};
135 }
136
137 /**
138 * Loads an existing session or creates a new one.
9f959ced 139 *
11ade432 140 * @param string $sessionEditorClassName
11ade432 141 * @param string $sessionID
a79013ef 142 */
75cf36c3 143 public function load($sessionEditorClassName, $sessionID) {
11ade432
AE
144 $this->sessionEditorClassName = $sessionEditorClassName;
145 $this->sessionClassName = call_user_func(array($sessionEditorClassName, 'getBaseClass'));
827e7aea 146 $this->supportsVirtualSessions = call_user_func(array($this->sessionClassName, 'supportsVirtualSessions'));
11ade432
AE
147
148 // try to get existing session
149 if (!empty($sessionID)) {
150 $this->getExistingSession($sessionID);
151 }
152
153 // create new session
154 if ($this->session === null) {
155 $this->create();
156 }
157 }
158
159 /**
160 * Initializes session system.
a79013ef 161 */
11ade432
AE
162 public function initSession() {
163 // init session environment
164 $this->loadVariables();
165 $this->initSecurityToken();
166 $this->defineConstants();
167
83736ee3
AE
168 // assign language and style id
169 $this->languageID = ($this->getVar('languageID') === null) ? $this->user->languageID : $this->getVar('languageID');
170 $this->styleID = ($this->getVar('styleID') === null) ? $this->user->styleID : $this->getVar('styleID');
11ade432
AE
171
172 // init environment variables
173 $this->initEnvironment();
174 }
175
176 /**
177 * Enables cookie support.
a79013ef 178 */
11ade432
AE
179 public function enableCookies() {
180 $this->useCookies = true;
181 }
182
183 /**
184 * Initializes environment variables.
185 */
186 protected function initEnvironment() {
187 $this->environment = array(
188 'lastRequestURI' => $this->session->requestURI,
189 'lastRequestMethod' => $this->session->requestMethod,
190 'ipAddress' => UserUtil::getIpAddress(),
191 'userAgent' => UserUtil::getUserAgent(),
192 'requestURI' => UserUtil::getRequestURI(),
167068f7 193 'requestMethod' => (!empty($_SERVER['REQUEST_METHOD']) ? substr($_SERVER['REQUEST_METHOD'], 0, 7) : '')
11ade432
AE
194 );
195 }
196
197 /**
198 * Disables update on shutdown.
199 */
200 public function disableUpdate() {
201 $this->doNotUpdate = true;
202 }
203
204 /**
9f959ced 205 * Defines global wcf constants related to session.
11ade432
AE
206 */
207 protected function defineConstants() {
e8d26212 208 if ($this->useCookies || $this->session->spiderID) {
11ade432
AE
209 if (!defined('SID_ARG_1ST')) define('SID_ARG_1ST', '');
210 if (!defined('SID_ARG_2ND')) define('SID_ARG_2ND', '');
211 if (!defined('SID_ARG_2ND_NOT_ENCODED')) define('SID_ARG_2ND_NOT_ENCODED', '');
212 if (!defined('SID')) define('SID', '');
213 if (!defined('SID_INPUT_TAG')) define('SID_INPUT_TAG', '');
214 }
215 else {
216 if (!defined('SID_ARG_1ST')) define('SID_ARG_1ST', '?s='.$this->sessionID);
217 if (!defined('SID_ARG_2ND')) define('SID_ARG_2ND', '&amp;s='.$this->sessionID);
218 if (!defined('SID_ARG_2ND_NOT_ENCODED')) define('SID_ARG_2ND_NOT_ENCODED', '&s='.$this->sessionID);
219 if (!defined('SID')) define('SID', $this->sessionID);
220 if (!defined('SID_INPUT_TAG')) define('SID_INPUT_TAG', '<input type="hidden" name="s" value="'.$this->sessionID.'" />');
221 }
222
223 // security token
224 if (!defined('SECURITY_TOKEN')) define('SECURITY_TOKEN', $this->getSecurityToken());
225 if (!defined('SECURITY_TOKEN_INPUT_TAG')) define('SECURITY_TOKEN_INPUT_TAG', '<input type="hidden" name="t" value="'.$this->getSecurityToken().'" />');
226 }
227
228 /**
229 * Initializes security token.
a79013ef 230 */
11ade432
AE
231 protected function initSecurityToken() {
232 if ($this->getVar('__SECURITY_TOKEN') === null) {
233 $this->register('__SECURITY_TOKEN', StringUtil::getRandomID());
234 }
235 }
236
237 /**
238 * Returns security token.
239 *
240 * @return string
241 */
242 public function getSecurityToken() {
243 return $this->getVar('__SECURITY_TOKEN');
244 }
245
246 /**
247 * Validates the given security token, returns false if
248 * given token is invalid.
249 *
250 * @param string $token
251 * @return boolean
252 */
253 public function checkSecurityToken($token) {
d4f5c98c 254 return PasswordUtil::secureCompare($this->getSecurityToken(), $token);
11ade432
AE
255 }
256
257 /**
258 * Registers a session variable.
9f959ced 259 *
11ade432
AE
260 * @param string $key
261 * @param string $value
a79013ef 262 */
11ade432
AE
263 public function register($key, $value) {
264 $this->variables[$key] = $value;
265 $this->variablesChanged = true;
266 }
267
268 /**
269 * Unsets a session variable.
270 *
271 * @param string $key
a79013ef 272 */
11ade432
AE
273 public function unregister($key) {
274 unset($this->variables[$key]);
275 $this->variablesChanged = true;
276 }
277
278 /**
279 * Returns the value of a session variable.
280 *
281 * @param string $key
282 */
283 public function getVar($key) {
284 if (isset($this->variables[$key])) {
285 return $this->variables[$key];
286 }
287
288 return null;
289 }
290
291 /**
292 * Initializes session variables.
a79013ef 293 */
11ade432 294 protected function loadVariables() {
75cf36c3 295 @$this->variables = unserialize($this->session->sessionVariables);
11ade432
AE
296 if (!is_array($this->variables)) {
297 $this->variables = array();
298 }
299 }
300
301 /**
302 * Returns the user object of this session.
9f959ced 303 *
0ad90fc3 304 * @return \wcf\data\user\User $user
11ade432
AE
305 */
306 public function getUser() {
307 return $this->user;
308 }
309
310 /**
9f959ced
MS
311 * Tries to read existing session identified by the given session id.
312 *
313 * @param string $sessionID
a79013ef 314 */
11ade432
AE
315 protected function getExistingSession($sessionID) {
316 $this->session = new $this->sessionClassName($sessionID);
5a05fde9 317 if (!$this->session->sessionID) {
75cf36c3 318 $this->session = null;
11ade432
AE
319 return;
320 }
321
11ade432 322 $this->user = new User($this->session->userID);
5a05fde9
AE
323 $this->loadVirtualSession();
324
325 if (!$this->validate()) {
326 $this->session = null;
327 $this->user = null;
328 $this->virtualSession = false;
329
330 return;
331 }
332 }
333
334 /**
335 * Loads the virtual session object unless the user is not logged in or the session
336 * does not support virtual sessions. If there is no virtual session yet, it will be
337 * created on-the-fly.
338 *
339 * @param boolean $forceReload
340 */
341 protected function loadVirtualSession($forceReload = false) {
342 if ($this->virtualSession === false || $forceReload) {
343 $this->virtualSession = null;
344 if ($this->user->userID && $this->supportsVirtualSessions) {
827e7aea 345 $virtualSessionAction = new SessionVirtualAction(array(), 'create', array('data' => array('sessionID' => $this->session->sessionID)));
4ac2611e
AE
346 $returnValues = $virtualSessionAction->executeAction();
347 $this->virtualSession = $returnValues['returnValues'];
5a05fde9
AE
348 }
349 }
11ade432
AE
350 }
351
352 /**
e89c399a 353 * Validates the ip address and the user agent of this session.
11ade432
AE
354 *
355 * @return boolean
11ade432
AE
356 */
357 protected function validate() {
e89c399a 358 if (SESSION_VALIDATE_IP_ADDRESS) {
5a05fde9
AE
359 if ($this->supportsVirtualSessions && ($this->virtualSession instanceof SessionVirtual)) {
360 if ($this->virtualSession->ipAddress != UserUtil::getIpAddress()) {
361 return false;
362 }
363 }
364 else if ($this->session->ipAddress != UserUtil::getIpAddress()) {
e89c399a
MW
365 return false;
366 }
367 }
5a05fde9 368
e89c399a 369 if (SESSION_VALIDATE_USER_AGENT) {
5a05fde9
AE
370 if ($this->supportsVirtualSessions && ($this->virtualSession instanceof SessionVirtual)) {
371 if ($this->virtualSession->userAgent != UserUtil::getUserAgent()) {
372 return false;
373 }
374 }
375 else if ($this->session->userAgent != UserUtil::getUserAgent()) {
e89c399a
MW
376 return false;
377 }
378 }
379
11ade432
AE
380 return true;
381 }
382
383 /**
384 * Creates a new session.
a79013ef 385 */
11ade432 386 protected function create() {
e8d26212
MW
387 $spiderID = null;
388 if ($this->sessionEditorClassName == 'wcf\data\session\SessionEditor') {
389 // get spider information
390 $spiderID = $this->getSpiderID(UserUtil::getUserAgent());
391 if ($spiderID !== null) {
392 // try to use existing session
393 if (($session = $this->getExistingSpiderSession($spiderID)) !== null) {
394 $this->user = new User(null);
395 $this->session = $session;
396 return;
397 }
398 }
399 }
400
11ade432
AE
401 // create new session hash
402 $sessionID = StringUtil::getRandomID();
403
404 // get user automatically
2a51a8f9 405 $this->user = UserAuthenticationFactory::getInstance()->getUserAuthentication()->loginAutomatically(call_user_func(array($this->sessionClassName, 'supportsPersistentLogins')));
11ade432
AE
406
407 // create user
408 if ($this->user === null) {
409 // no valid user found
410 // create guest user
411 $this->user = new User(null);
412 }
5a05fde9 413 else if (!$this->supportsVirtualSessions) {
05c21784 414 // delete all other sessions of this user
ece1a6c7 415 call_user_func(array($this->sessionEditorClassName, 'deleteUserSessions'), array($this->user->userID));
05c21784
MW
416 }
417
827e7aea
AE
418 $createNewSession = true;
419 if ($this->supportsVirtualSessions) {
420 // find existing session
421 $session = call_user_func(array($this->sessionClassName, 'getSessionByUserID'), $this->user->userID);
422
423 if ($session !== null) {
424 // inherit existing session
425 $this->session = $session;
426 $this->loadVirtualSession(true);
427
428 $createNewSession = false;
429 }
430 }
5a05fde9 431
827e7aea
AE
432 if ($createNewSession) {
433 // save session
434 $sessionData = array(
435 'sessionID' => $sessionID,
436 'userID' => $this->user->userID,
437 'ipAddress' => UserUtil::getIpAddress(),
438 'userAgent' => UserUtil::getUserAgent(),
439 'lastActivityTime' => TIME_NOW,
440 'requestURI' => UserUtil::getRequestURI(),
441 'requestMethod' => (!empty($_SERVER['REQUEST_METHOD']) ? substr($_SERVER['REQUEST_METHOD'], 0, 7) : '')
442 );
443
444 if ($spiderID !== null) $sessionData['spiderID'] = $spiderID;
445 $this->session = call_user_func(array($this->sessionEditorClassName, 'create'), $sessionData);
446 $this->loadVirtualSession(true);
447 }
11ade432
AE
448 }
449
450 /**
451 * Returns the value of the permission with the given name.
9f959ced
MS
452 *
453 * @param string $permission
11ade432
AE
454 * @return mixed permission value
455 */
456 public function getPermission($permission) {
457 $this->loadGroupData();
458
459 if (!isset($this->groupData[$permission])) return false;
460 return $this->groupData[$permission];
461 }
462
463 /**
e3369fd2 464 * Checks if the active user has the given permissions and throws a
7c720e36 465 * PermissionDeniedException if that isn't the case.
11ade432 466 */
7c720e36 467 public function checkPermissions(array $permissions) {
11ade432
AE
468 foreach ($permissions as $permission) {
469 if (!$this->getPermission($permission)) {
470 throw new PermissionDeniedException();
471 }
472 }
473 }
474
475 /**
476 * Loads group data from cache.
a79013ef 477 */
11ade432
AE
478 protected function loadGroupData() {
479 if ($this->groupData !== null) return;
480
481 // work-around for setup process (package wcf does not exist yet)
482 if (!PACKAGE_ID) {
483 $groupIDs = array();
484 $sql = "SELECT groupID
485 FROM wcf".WCF_N."_user_to_group
486 WHERE userID = ?";
487 $statement = WCF::getDB()->prepareStatement($sql);
488 $statement->execute(array($this->user->userID));
489 while ($row = $statement->fetchArray()) {
490 $groupIDs[] = $row['groupID'];
491 }
492 }
493 else {
494 $groupIDs = $this->user->getGroupIDs();
495 }
496
11ade432 497 // get group data from cache
b401cd0d 498 $this->groupData = UserGroupPermissionCacheBuilder::getInstance()->getData($groupIDs);
23253208 499 if (isset($this->groupData['groupIDs']) && $this->groupData['groupIDs'] != $groupIDs) {
11ade432
AE
500 $this->groupData = array();
501 }
502 }
503
504 /**
505 * Returns language ids for active user.
59dc0db6 506 *
11ade432 507 * @return array<integer>
a79013ef 508 */
11ade432
AE
509 public function getLanguageIDs() {
510 $this->loadLanguageIDs();
511
512 return $this->languageIDs;
513 }
514
515 /**
516 * Loads language ids for active user.
a79013ef 517 */
11ade432
AE
518 protected function loadLanguageIDs() {
519 if ($this->languageIDs !== null) return;
520
521 $this->languageIDs = array();
522
523 if (!$this->user->userID) {
524 return;
525 }
526
527 // work-around for setup process (package wcf does not exist yet)
528 if (!PACKAGE_ID) {
529 $sql = "SELECT languageID
530 FROM wcf".WCF_N."_user_to_language
531 WHERE userID = ?";
532 $statement = WCF::getDB()->prepareStatement($sql);
533 $statement->execute(array($this->user->userID));
534 while ($row = $statement->fetchArray()) {
535 $this->languageIDs[] = $row['languageID'];
536 }
537 }
538 else {
539 $this->languageIDs = $this->user->getLanguageIDs();
540 }
541 }
542
543 /**
544 * Stores a new user object in this session, e.g. a user was guest because not
545 * logged in, after the login his old session is used to store his full data.
9f959ced 546 *
0ad90fc3 547 * @param \wcf\data\userUser $user
9f959ced 548 * @param boolean $hideSession if true, database won't be updated
ac094463 549 * @return boolean
a79013ef 550 */
e7fcae9d 551 public function changeUser(User $user, $hideSession = false) {
ac094463
AE
552 if ($this->supportsVirtualSessions) {
553 return $this->changeUserVirtual($user);
554 }
555
11ade432 556 $sessionTable = call_user_func(array($this->sessionClassName, 'getDatabaseTableName'));
11ade432 557
ac094463
AE
558 if ($user->userID && !$hideSession) {
559 // user is not a guest, delete all other sessions of this user
560 $sql = "DELETE FROM ".$sessionTable."
561 WHERE sessionID <> ?
562 AND userID = ?";
563 $statement = WCF::getDB()->prepareStatement($sql);
564 $statement->execute(array($this->sessionID, $user->userID));
565
566 // reset session variables
567 $this->variables = array();
568 $this->variablesChanged = true;
569 }
570
571 // update user reference
572 $this->user = $user;
573
574 if (!$hideSession) {
575 // update session
576 $sessionEditor = new $this->sessionEditorClassName($this->session);
577 $sessionEditor->update(array(
578 'userID' => $this->user->userID
579 ));
580 }
581
582 // reset caches
583 $this->groupData = null;
584 $this->languageIDs = null;
585 $this->languageID = $this->user->languageID;
586 $this->styleID = $this->user->styleID;
587
588 return true;
589 }
590
591 /**
592 * Changes the user stored in the session, this method is different from changeUser() because it
593 * attempts to re-use sessions unless there are other virtual sessions for the same user (userID != 0).
594 * In reverse, logging out attempts to re-use the current session or spawns a new session depending
595 * on other virtual sessions.
596 *
597 * @param \wcf\data\user\User $user
598 */
599 protected function changeUserVirtual(User $user) {
600 $sessionTable = call_user_func(array($this->sessionClassName, 'getDatabaseTableName'));
601
602 switch ($user->userID) {
603 //
604 // user -> guest (logout)
605 //
606 case 0:
607 // delete virtual session
608 $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
609 $virtualSessionEditor->delete();
610
611 // there are still other virtual sessions, create a new session
612 if (SessionVirtual::countVirtualSessions($this->session->sessionID)) {
613 // save session
614 $sessionData = array(
615 'sessionID' => StringUtil::getRandomID(),
616 'userID' => $user->userID,
617 'ipAddress' => UserUtil::getIpAddress(),
618 'userAgent' => UserUtil::getUserAgent(),
619 'lastActivityTime' => TIME_NOW,
620 'requestURI' => UserUtil::getRequestURI(),
621 'requestMethod' => (!empty($_SERVER['REQUEST_METHOD']) ? substr($_SERVER['REQUEST_METHOD'], 0, 7) : '')
622 );
623
624 $this->session = call_user_func(array($this->sessionEditorClassName, 'create'), $sessionData);
625
626 HeaderUtil::setCookie('cookieHash', $this->session->sessionID);
627 }
628 else {
629 // this was the last virtual session, re-use current session
630 // update session
631 $sessionEditor = new $this->sessionEditorClassName($this->session);
632 $sessionEditor->update(array(
633 'userID' => $user->userID
634 ));
635 }
636 break;
637
638 //
639 // guest -> user (login)
640 //
641 default:
642 // find existing session for this user
5a05fde9
AE
643 $session = call_user_func(array($this->sessionClassName, 'getSessionByUserID'), $user->userID);
644
ac094463
AE
645 // no session exists, re-use current session
646 if ($session === null) {
647 // update session
648 $sessionEditor = new $this->sessionEditorClassName($this->session);
649 $sessionEditor->update(array(
650 'userID' => $user->userID
651 ));
652 }
653 else {
5a05fde9
AE
654 // delete guest session
655 $sessionEditor = new $this->sessionEditorClassName($this->session);
656 $sessionEditor->delete();
657
658 // inherit existing session
659 $this->session = $session;
5a05fde9 660 }
ac094463 661 break;
11ade432
AE
662 }
663
ac094463
AE
664 $this->user = $user;
665 $this->loadVirtualSession(true);
11ade432 666
fee2c2e5
TD
667 // reset caches
668 $this->groupData = null;
669 $this->languageIDs = null;
670 $this->languageID = $this->user->languageID;
83736ee3 671 $this->styleID = $this->user->styleID;
ac094463
AE
672
673 return false;
11ade432
AE
674 }
675
676 /**
677 * Updates user session on shutdown.
a79013ef 678 */
11ade432
AE
679 public function update() {
680 if ($this->doNotUpdate) return;
681
75cf36c3
MW
682 // set up data
683 $data = array(
ccc092e2 684 'ipAddress' => UserUtil::getIpAddress(),
11ade432
AE
685 'userAgent' => $this->userAgent,
686 'requestURI' => $this->requestURI,
687 'requestMethod' => $this->requestMethod,
b955b9e3 688 'lastActivityTime' => TIME_NOW
75cf36c3 689 );
64788124 690 if (!class_exists('wcf\system\CLIWCF', false) && PACKAGE_ID && RequestHandler::getInstance()->getActiveRequest() && RequestHandler::getInstance()->getActiveRequest()->getRequestObject() instanceof ITrackablePage && RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->isTracked()) {
596e20e2
MW
691 $data['controller'] = RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->getController();
692 $data['parentObjectType'] = RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->getParentObjectType();
693 $data['parentObjectID'] = RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->getParentObjectID();
694 $data['objectType'] = RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->getObjectType();
695 $data['objectID'] = RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->getObjectID();
696 }
11ade432 697 if ($this->variablesChanged) {
75cf36c3 698 $data['sessionVariables'] = serialize($this->variables);
11ade432 699 }
75cf36c3
MW
700
701 // update session
702 $sessionEditor = new $this->sessionEditorClassName($this->session);
703 $sessionEditor->update($data);
5a05fde9
AE
704
705 if ($this->virtualSession instanceof SessionVirtual) {
706 $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
707 $virtualSessionEditor->updateLastActivityTime();
708 }
11ade432
AE
709 }
710
dd932bc6
AE
711 /**
712 * Updates last activity time to protect session from expiring.
713 */
714 public function keepAlive() {
715 $this->disableUpdate();
716
717 // update last activity time
718 $sessionEditor = new $this->sessionEditorClassName($this->session);
719 $sessionEditor->update(array(
720 'lastActivityTime' => TIME_NOW
721 ));
5a05fde9
AE
722
723 if ($this->virtualSession instanceof SessionVirtual) {
724 $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
725 $virtualSessionEditor->updateLastActivityTime();
726 }
dd932bc6
AE
727 }
728
11ade432
AE
729 /**
730 * Deletes this session and it's related data.
a79013ef 731 */
11ade432 732 public function delete() {
235c040a
MW
733 // clear storage
734 if ($this->user->userID) {
735 self::resetSessions(array($this->user->userID));
5a05fde9 736
235c040a
MW
737 // update last activity time
738 if (!class_exists('\wcf\system\WCFACP', false)) {
739 $editor = new UserEditor($this->user);
740 $editor->update(array('lastActivityTime' => TIME_NOW));
741 }
742 }
743
9b1e73a7 744 // set user to guest
ac094463 745 $deleteSession = $this->changeUser(new User(null), true);
9b1e73a7 746
11ade432 747 // remove session
ac094463 748 if ($deleteSession !== false) {
5a05fde9
AE
749 $sessionEditor = new $this->sessionEditorClassName($this->session);
750 $sessionEditor->delete();
751 }
11ade432 752
11ade432
AE
753 // disable update
754 $this->disableUpdate();
755 }
756
757 /**
81e33547 758 * Returns currently active language id.
9f959ced 759 *
11ade432 760 * @return integer
a79013ef 761 */
11ade432
AE
762 public function getLanguageID() {
763 return $this->languageID;
764 }
765
81e33547
MS
766 /**
767 * Sets the currently active language id.
9f959ced 768 *
81e33547 769 * @param integer $languageID
a79013ef 770 */
81e33547
MS
771 public function setLanguageID($languageID) {
772 $this->languageID = $languageID;
83736ee3
AE
773 $this->register('languageID', $this->languageID);
774 }
775
776 /**
777 * Returns currently active style id.
778 *
779 * @return integer
780 */
781 public function getStyleID() {
782 return $this->styleID;
783 }
784
785 /**
786 * Sets the currently active style id.
787 *
788 * @param integer $styleID
789 */
790 public function setStyleID($styleID) {
791 $this->styleID = $styleID;
792 $this->register('styleID', $this->styleID);
81e33547
MS
793 }
794
11ade432
AE
795 /**
796 * Resets session-specific storage data.
9f959ced 797 *
68f7dd36 798 * @param array<integer> $userIDs
a79013ef 799 */
11ade432 800 public static function resetSessions(array $userIDs = array()) {
15fa2802 801 if (!empty($userIDs)) {
c96ee721
MS
802 UserStorageHandler::getInstance()->reset($userIDs, 'groupIDs', 1);
803 UserStorageHandler::getInstance()->reset($userIDs, 'languageIDs', 1);
11ade432
AE
804 }
805 else {
c96ee721
MS
806 UserStorageHandler::getInstance()->resetAll('groupIDs', 1);
807 UserStorageHandler::getInstance()->resetAll('languageIDs', 1);
11ade432
AE
808 }
809 }
e8d26212
MW
810
811 /**
812 * Returns the spider id for given user agent.
59dc0db6 813 *
06355ec3 814 * @param string $userAgent
e8d26212
MW
815 * @return mixed
816 */
817 protected function getSpiderID($userAgent) {
818 $spiderList = SpiderCacheBuilder::getInstance()->getData();
819 $userAgent = strtolower($userAgent);
820
821 foreach ($spiderList as $spider) {
822 if (strpos($userAgent, $spider->spiderIdentifier) !== false) {
823 return $spider->spiderID;
824 }
825 }
826
827 return null;
828 }
829
830 /**
831 * Searches for existing session of a search spider.
59dc0db6 832 *
06355ec3 833 * @param integer $spiderID
0ad90fc3 834 * @return \wcf\data\session\Session
e8d26212
MW
835 */
836 protected function getExistingSpiderSession($spiderID) {
06355ec3
MS
837 $sql = "SELECT *
838 FROM wcf".WCF_N."_session
839 WHERE spiderID = ?
e8d26212
MW
840 AND userID IS NULL";
841 $statement = WCF::getDB()->prepareStatement($sql);
842 $statement->execute(array($spiderID));
843 $row = $statement->fetchArray();
844 if ($row !== false) {
845 // fix session validation
846 $row['ipAddress'] = UserUtil::getIpAddress();
847 $row['userAgent'] = UserUtil::getUserAgent();
06355ec3 848
e8d26212
MW
849 // return session object
850 return new $this->sessionClassName(null, $row);
851 }
852
853 return null;
854 }
11ade432 855}