Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
2 | namespace wcf\system\session; | |
5a05fde9 AE |
3 | use wcf\data\session\virtual\SessionVirtual; |
4 | use wcf\data\session\virtual\SessionVirtualAction; | |
5 | use wcf\data\session\virtual\SessionVirtualEditor; | |
11ade432 | 6 | use wcf\data\user\User; |
235c040a | 7 | use wcf\data\user\UserEditor; |
8f3fc897 | 8 | use wcf\page\ITrackablePage; |
e8d26212 | 9 | use wcf\system\cache\builder\SpiderCacheBuilder; |
b401cd0d | 10 | use wcf\system\cache\builder\UserGroupPermissionCacheBuilder; |
11ade432 | 11 | use wcf\system\exception\PermissionDeniedException; |
7aa1a486 | 12 | use wcf\system\request\RequestHandler; |
75afb100 | 13 | use wcf\system\user\authentication\UserAuthenticationFactory; |
c96ee721 | 14 | use wcf\system\user\storage\UserStorageHandler; |
11ade432 AE |
15 | use wcf\system\SingletonFactory; |
16 | use wcf\system\WCF; | |
ac094463 | 17 | use wcf\util\HeaderUtil; |
d4f5c98c | 18 | use wcf\util\PasswordUtil; |
11ade432 AE |
19 | use wcf\util\StringUtil; |
20 | use 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 | */ |
32 | class 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', '&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 | } |