Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / session / SessionHandler.class.php
1 <?php
2 namespace wcf\system\session;
3 use wcf\data\session\virtual\SessionVirtual;
4 use wcf\data\session\virtual\SessionVirtualAction;
5 use wcf\data\session\virtual\SessionVirtualEditor;
6 use wcf\data\user\User;
7 use wcf\data\user\UserEditor;
8 use wcf\page\ITrackablePage;
9 use wcf\system\cache\builder\SpiderCacheBuilder;
10 use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
11 use wcf\system\exception\PermissionDeniedException;
12 use wcf\system\request\RequestHandler;
13 use wcf\system\user\authentication\UserAuthenticationFactory;
14 use wcf\system\user\storage\UserStorageHandler;
15 use wcf\system\SingletonFactory;
16 use wcf\system\WCF;
17 use wcf\util\HeaderUtil;
18 use wcf\util\PasswordUtil;
19 use wcf\util\StringUtil;
20 use wcf\util\UserUtil;
21
22 /**
23 * Handles sessions.
24 *
25 * @author Alexander Ebert
26 * @copyright 2001-2014 WoltLab GmbH
27 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
28 * @package com.woltlab.wcf
29 * @subpackage system.session
30 * @category Community Framework
31 */
32 class SessionHandler extends SingletonFactory {
33 /**
34 * prevents update on shutdown
35 * @var boolean
36 */
37 protected $doNotUpdate = false;
38
39 /**
40 * various environment variables
41 * @var array
42 */
43 protected $environment = array();
44
45 /**
46 * group data and permissions
47 * @var array<array>
48 */
49 protected $groupData = null;
50
51 /**
52 * language id for active user
53 * @var integer
54 */
55 protected $languageID = 0;
56
57 /**
58 * language ids for active user
59 * @var array<integer>
60 */
61 protected $languageIDs = null;
62
63 /**
64 * session object
65 * @var \wcf\data\acp\session\ACPSession
66 */
67 protected $session = null;
68
69 /**
70 * session class name
71 * @var string
72 */
73 protected $sessionClassName = '';
74
75 /**
76 * session editor class name
77 * @var string
78 */
79 protected $sessionEditorClassName = '';
80
81 /**
82 * virtual session support
83 * @var boolean
84 */
85 protected $supportsVirtualSessions = false;
86
87 /**
88 * style id
89 * @var integer
90 */
91 protected $styleID = null;
92
93 /**
94 * enable cookie support
95 * @var boolean
96 */
97 protected $useCookies = false;
98
99 /**
100 * user object
101 * @var \wcf\data\user\User
102 */
103 protected $user = null;
104
105 /**
106 * session variables
107 * @var array
108 */
109 protected $variables = null;
110
111 /**
112 * indicates if session variables changed and must be saved upon shutdown
113 * @var boolean
114 */
115 protected $variablesChanged = false;
116
117 /**
118 * virtual session object, null for guests
119 * @var \wcf\data\session\virtual\SessionVirtual
120 */
121 protected $virtualSession = false;
122
123 /**
124 * Provides access to session data.
125 *
126 * @param string $key
127 * @return mixed
128 */
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.
139 *
140 * @param string $sessionEditorClassName
141 * @param string $sessionID
142 */
143 public function load($sessionEditorClassName, $sessionID) {
144 $this->sessionEditorClassName = $sessionEditorClassName;
145 $this->sessionClassName = call_user_func(array($sessionEditorClassName, 'getBaseClass'));
146 $this->supportsVirtualSessions = call_user_func(array($this->sessionClassName, 'supportsVirtualSessions'));
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.
161 */
162 public function initSession() {
163 // init session environment
164 $this->loadVariables();
165 $this->initSecurityToken();
166 $this->defineConstants();
167
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');
171
172 // init environment variables
173 $this->initEnvironment();
174 }
175
176 /**
177 * Enables cookie support.
178 */
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(),
193 'requestMethod' => (!empty($_SERVER['REQUEST_METHOD']) ? substr($_SERVER['REQUEST_METHOD'], 0, 7) : '')
194 );
195 }
196
197 /**
198 * Disables update on shutdown.
199 */
200 public function disableUpdate() {
201 $this->doNotUpdate = true;
202 }
203
204 /**
205 * Defines global wcf constants related to session.
206 */
207 protected function defineConstants() {
208 if ($this->useCookies || $this->session->spiderID) {
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.
230 */
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) {
254 return PasswordUtil::secureCompare($this->getSecurityToken(), $token);
255 }
256
257 /**
258 * Registers a session variable.
259 *
260 * @param string $key
261 * @param string $value
262 */
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
272 */
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.
293 */
294 protected function loadVariables() {
295 @$this->variables = unserialize($this->session->sessionVariables);
296 if (!is_array($this->variables)) {
297 $this->variables = array();
298 }
299 }
300
301 /**
302 * Returns the user object of this session.
303 *
304 * @return \wcf\data\user\User $user
305 */
306 public function getUser() {
307 return $this->user;
308 }
309
310 /**
311 * Tries to read existing session identified by the given session id.
312 *
313 * @param string $sessionID
314 */
315 protected function getExistingSession($sessionID) {
316 $this->session = new $this->sessionClassName($sessionID);
317 if (!$this->session->sessionID) {
318 $this->session = null;
319 return;
320 }
321
322 $this->user = new User($this->session->userID);
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) {
345 $virtualSessionAction = new SessionVirtualAction(array(), 'create', array('data' => array('sessionID' => $this->session->sessionID)));
346 $returnValues = $virtualSessionAction->executeAction();
347 $this->virtualSession = $returnValues['returnValues'];
348 }
349 }
350 }
351
352 /**
353 * Validates the ip address and the user agent of this session.
354 *
355 * @return boolean
356 */
357 protected function validate() {
358 if (SESSION_VALIDATE_IP_ADDRESS) {
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()) {
365 return false;
366 }
367 }
368
369 if (SESSION_VALIDATE_USER_AGENT) {
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()) {
376 return false;
377 }
378 }
379
380 return true;
381 }
382
383 /**
384 * Creates a new session.
385 */
386 protected function create() {
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
401 // create new session hash
402 $sessionID = StringUtil::getRandomID();
403
404 // get user automatically
405 $this->user = UserAuthenticationFactory::getInstance()->getUserAuthentication()->loginAutomatically(call_user_func(array($this->sessionClassName, 'supportsPersistentLogins')));
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 }
413 else if (!$this->supportsVirtualSessions) {
414 // delete all other sessions of this user
415 call_user_func(array($this->sessionEditorClassName, 'deleteUserSessions'), array($this->user->userID));
416 }
417
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 }
431
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 }
448 }
449
450 /**
451 * Returns the value of the permission with the given name.
452 *
453 * @param string $permission
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 /**
464 * Checks if the active user has the given permissions and throws a
465 * PermissionDeniedException if that isn't the case.
466 */
467 public function checkPermissions(array $permissions) {
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.
477 */
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
497 // get group data from cache
498 $this->groupData = UserGroupPermissionCacheBuilder::getInstance()->getData($groupIDs);
499 if (isset($this->groupData['groupIDs']) && $this->groupData['groupIDs'] != $groupIDs) {
500 $this->groupData = array();
501 }
502 }
503
504 /**
505 * Returns language ids for active user.
506 *
507 * @return array<integer>
508 */
509 public function getLanguageIDs() {
510 $this->loadLanguageIDs();
511
512 return $this->languageIDs;
513 }
514
515 /**
516 * Loads language ids for active user.
517 */
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.
546 *
547 * @param \wcf\data\userUser $user
548 * @param boolean $hideSession if true, database won't be updated
549 * @return boolean
550 */
551 public function changeUser(User $user, $hideSession = false) {
552 if ($this->supportsVirtualSessions) {
553 return $this->changeUserVirtual($user);
554 }
555
556 $sessionTable = call_user_func(array($this->sessionClassName, 'getDatabaseTableName'));
557
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
643 $session = call_user_func(array($this->sessionClassName, 'getSessionByUserID'), $user->userID);
644
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 {
654 // delete guest session
655 $sessionEditor = new $this->sessionEditorClassName($this->session);
656 $sessionEditor->delete();
657
658 // inherit existing session
659 $this->session = $session;
660 }
661 break;
662 }
663
664 $this->user = $user;
665 $this->loadVirtualSession(true);
666
667 // reset caches
668 $this->groupData = null;
669 $this->languageIDs = null;
670 $this->languageID = $this->user->languageID;
671 $this->styleID = $this->user->styleID;
672
673 return false;
674 }
675
676 /**
677 * Updates user session on shutdown.
678 */
679 public function update() {
680 if ($this->doNotUpdate) return;
681
682 // set up data
683 $data = array(
684 'ipAddress' => UserUtil::getIpAddress(),
685 'userAgent' => $this->userAgent,
686 'requestURI' => $this->requestURI,
687 'requestMethod' => $this->requestMethod,
688 'lastActivityTime' => TIME_NOW
689 );
690 if (!class_exists('wcf\system\CLIWCF', false) && PACKAGE_ID && RequestHandler::getInstance()->getActiveRequest() && RequestHandler::getInstance()->getActiveRequest()->getRequestObject() instanceof ITrackablePage && RequestHandler::getInstance()->getActiveRequest()->getRequestObject()->isTracked()) {
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 }
697 if ($this->variablesChanged) {
698 $data['sessionVariables'] = serialize($this->variables);
699 }
700
701 // update session
702 $sessionEditor = new $this->sessionEditorClassName($this->session);
703 $sessionEditor->update($data);
704
705 if ($this->virtualSession instanceof SessionVirtual) {
706 $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
707 $virtualSessionEditor->updateLastActivityTime();
708 }
709 }
710
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 ));
722
723 if ($this->virtualSession instanceof SessionVirtual) {
724 $virtualSessionEditor = new SessionVirtualEditor($this->virtualSession);
725 $virtualSessionEditor->updateLastActivityTime();
726 }
727 }
728
729 /**
730 * Deletes this session and it's related data.
731 */
732 public function delete() {
733 // clear storage
734 if ($this->user->userID) {
735 self::resetSessions(array($this->user->userID));
736
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
744 // set user to guest
745 $deleteSession = $this->changeUser(new User(null), true);
746
747 // remove session
748 if ($deleteSession !== false) {
749 $sessionEditor = new $this->sessionEditorClassName($this->session);
750 $sessionEditor->delete();
751 }
752
753 // disable update
754 $this->disableUpdate();
755 }
756
757 /**
758 * Returns currently active language id.
759 *
760 * @return integer
761 */
762 public function getLanguageID() {
763 return $this->languageID;
764 }
765
766 /**
767 * Sets the currently active language id.
768 *
769 * @param integer $languageID
770 */
771 public function setLanguageID($languageID) {
772 $this->languageID = $languageID;
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);
793 }
794
795 /**
796 * Resets session-specific storage data.
797 *
798 * @param array<integer> $userIDs
799 */
800 public static function resetSessions(array $userIDs = array()) {
801 if (!empty($userIDs)) {
802 UserStorageHandler::getInstance()->reset($userIDs, 'groupIDs', 1);
803 UserStorageHandler::getInstance()->reset($userIDs, 'languageIDs', 1);
804 }
805 else {
806 UserStorageHandler::getInstance()->resetAll('groupIDs', 1);
807 UserStorageHandler::getInstance()->resetAll('languageIDs', 1);
808 }
809 }
810
811 /**
812 * Returns the spider id for given user agent.
813 *
814 * @param string $userAgent
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.
832 *
833 * @param integer $spiderID
834 * @return \wcf\data\session\Session
835 */
836 protected function getExistingSpiderSession($spiderID) {
837 $sql = "SELECT *
838 FROM wcf".WCF_N."_session
839 WHERE spiderID = ?
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();
848
849 // return session object
850 return new $this->sessionClassName(null, $row);
851 }
852
853 return null;
854 }
855 }