1<?php
2/**
3 * MyBB 1.8
4 * Copyright 2014 MyBB Group, All Rights Reserved
5 *
6 * Website: http://www.mybb.com
7 * License: http://www.mybb.com/about/license
8 *
9 */
10
11class session
12{
13	/**
14	 * @var int
15	 */
16	public $sid = 0;
17	/**
18	 * @var int
19	 */
20	public $uid = 0;
21	/**
22	 * @var string
23	 */
24	public $ipaddress = '';
25	/**
26	 * @var string
27	 */
28	public $packedip = '';
29	/**
30	 * @var string
31	 */
32	public $useragent = '';
33	/**
34	 * @var bool
35	 */
36	public $is_spider = false;
37
38	/**
39	 * Request parameters that are to be ignored for location storage
40	 *
41	 * @var array
42	 */
43	public $ignore_parameters = array(
44		'my_post_key',
45		'logoutkey',
46	);
47
48	/**
49	 * Initialize a session
50	 */
51	function init()
52	{
53		global $db, $mybb, $cache, $plugins;
54
55		// Get our visitor's IP.
56		$this->ipaddress = get_ip();
57		$this->packedip = my_inet_pton($this->ipaddress);
58
59		// Find out the user agent.
60		$this->useragent = $_SERVER['HTTP_USER_AGENT'];
61
62		// Attempt to find a session id in the cookies.
63		if(isset($mybb->cookies['sid']) && !defined('IN_UPGRADE'))
64		{
65			$sid = $db->escape_string($mybb->cookies['sid']);
66
67			// Load the session if not using a bot sid
68			if(substr($sid, 3, 1) !== '=')
69			{
70				$query = $db->simple_select("sessions", "*", "sid='{$sid}'");
71				$session = $db->fetch_array($query);
72				if(!empty($session) && $session['sid'])
73				{
74					$this->sid = $session['sid'];
75				}
76			}
77		}
78
79		if(isset($plugins))
80		{
81			$plugins->run_hooks('pre_session_load', $this);
82		}
83
84		// If we have a valid session id and user id, load that users session.
85		if(!empty($mybb->cookies['mybbuser']))
86		{
87			$logon = explode("_", $mybb->cookies['mybbuser'], 2);
88			$this->load_user($logon[0], $logon[1]);
89		}
90
91		// If no user still, then we have a guest.
92		if(!isset($mybb->user['uid']))
93		{
94			// Detect if this guest is a search engine spider. (bots don't get a cookied session ID so we first see if that's set)
95			if(!$this->sid)
96			{
97				$spiders = $cache->read("spiders");
98				if(is_array($spiders))
99				{
100					foreach($spiders as $spider)
101					{
102						if(my_strpos(my_strtolower($this->useragent), my_strtolower($spider['useragent'])) !== false)
103						{
104							$this->load_spider($spider['sid']);
105						}
106					}
107				}
108			}
109
110			// Still nothing? JUST A GUEST!
111			if(!$this->is_spider)
112			{
113				$this->load_guest();
114			}
115		}
116
117		// As a token of our appreciation for getting this far (and they aren't a spider), give the user a cookie
118		if($this->sid && (!isset($mybb->cookies['sid']) || $mybb->cookies['sid'] != $this->sid) && $this->is_spider != true)
119		{
120			my_setcookie("sid", $this->sid, -1, true);
121		}
122	}
123
124	/**
125	 * Load a user via the user credentials.
126	 *
127	 * @param int $uid The user id.
128	 * @param string $loginkey The user's loginkey.
129	 * @return bool
130	 */
131	function load_user($uid, $loginkey='')
132	{
133		global $mybb, $db, $time, $lang, $mybbgroups, $cache;
134
135		$uid = (int)$uid;
136		$query = $db->query("
137			SELECT u.*, f.*
138			FROM ".TABLE_PREFIX."users u
139			LEFT JOIN ".TABLE_PREFIX."userfields f ON (f.ufid=u.uid)
140			WHERE u.uid='$uid'
141			LIMIT 1
142		");
143		$mybb->user = $db->fetch_array($query);
144
145		// Check the password if we're not using a session
146		if(empty($loginkey) || $loginkey !== $mybb->user['loginkey'] || !$mybb->user['uid'])
147		{
148			unset($mybb->user);
149			$this->uid = 0;
150			return false;
151		}
152		$this->uid = $mybb->user['uid'];
153
154		// Set the logout key for this user
155		$mybb->user['logoutkey'] = md5($mybb->user['loginkey']);
156
157		// Sort out the private message count for this user.
158		if(($mybb->user['totalpms'] == -1 || $mybb->user['unreadpms'] == -1) && $mybb->settings['enablepms'] != 0) // Forced recount
159		{
160			$update = 0;
161			if($mybb->user['totalpms'] == -1)
162			{
163				$update += 1;
164			}
165			if($mybb->user['unreadpms'] == -1)
166			{
167				$update += 2;
168			}
169
170			require_once MYBB_ROOT."inc/functions_user.php";
171			$pmcount = update_pm_count('', $update);
172			if(is_array($pmcount))
173			{
174				$mybb->user = array_merge($mybb->user, $pmcount);
175			}
176		}
177		$mybb->user['pms_total'] = $mybb->user['totalpms'];
178		$mybb->user['pms_unread'] = $mybb->user['unreadpms'];
179
180		if($mybb->user['lastip'] != $this->packedip && array_key_exists('lastip', $mybb->user) && !defined('IN_UPGRADE'))
181		{
182			$lastip_add = ", lastip=".$db->escape_binary($this->packedip);
183		}
184		else
185		{
186			$lastip_add = '';
187		}
188
189		// If the last visit was over 900 seconds (session time out) ago then update lastvisit.
190		$time = TIME_NOW;
191		if($time - $mybb->user['lastactive'] > 900)
192		{
193			$db->shutdown_query("UPDATE ".TABLE_PREFIX."users SET lastvisit='{$mybb->user['lastactive']}', lastactive='$time'{$lastip_add} WHERE uid='{$mybb->user['uid']}'");
194			$mybb->user['lastvisit'] = $mybb->user['lastactive'];
195			require_once MYBB_ROOT."inc/functions_user.php";
196			update_pm_count('', 2);
197		}
198		else
199		{
200			$timespent = TIME_NOW - $mybb->user['lastactive'];
201			$db->shutdown_query("UPDATE ".TABLE_PREFIX."users SET lastactive='$time', timeonline=timeonline+$timespent{$lastip_add} WHERE uid='{$mybb->user['uid']}'");
202		}
203
204		// Sort out the language and forum preferences.
205		if($mybb->user['language'] && $lang->language_exists($mybb->user['language']))
206		{
207			$mybb->settings['bblanguage'] = $mybb->user['language'];
208		}
209		if($mybb->user['dateformat'] != 0 && $mybb->user['dateformat'] != '')
210		{
211			global $date_formats;
212			if($date_formats[$mybb->user['dateformat']])
213			{
214				$mybb->settings['dateformat'] = $date_formats[$mybb->user['dateformat']];
215			}
216		}
217
218		// Choose time format.
219		if($mybb->user['timeformat'] != 0 && $mybb->user['timeformat'] != '')
220		{
221			global $time_formats;
222			if($time_formats[$mybb->user['timeformat']])
223			{
224				$mybb->settings['timeformat'] = $time_formats[$mybb->user['timeformat']];
225			}
226		}
227
228		// Find out the threads per page preference.
229		if($mybb->user['tpp'])
230		{
231			$mybb->settings['threadsperpage'] = $mybb->user['tpp'];
232		}
233
234		// Find out the posts per page preference.
235		if($mybb->user['ppp'])
236		{
237			$mybb->settings['postsperpage'] = $mybb->user['ppp'];
238		}
239
240		// Does this user prefer posts in classic mode?
241		if($mybb->user['classicpostbit'])
242		{
243			$mybb->settings['postlayout'] = 'classic';
244		}
245		else
246		{
247			$mybb->settings['postlayout'] = 'horizontal';
248		}
249
250		$usergroups = $cache->read('usergroups');
251
252		if(!empty($usergroups[$mybb->user['usergroup']]) && $usergroups[$mybb->user['usergroup']]['isbannedgroup'] == 1)
253		{
254			$ban = $db->fetch_array(
255				$db->simple_select('banned', '*', 'uid='.(int)$mybb->user['uid'], array('limit' => 1))
256			);
257
258			if($ban)
259			{
260				$mybb->user['banned'] = 1;
261				$mybb->user['bandate'] = $ban['dateline'];
262				$mybb->user['banlifted'] = $ban['lifted'];
263				$mybb->user['banoldgroup'] = $ban['oldgroup'];
264				$mybb->user['banolddisplaygroup'] = $ban['olddisplaygroup'];
265				$mybb->user['banoldadditionalgroups'] = $ban['oldadditionalgroups'];
266				$mybb->user['banreason'] = $ban['reason'];
267			}
268			else
269			{
270				$mybb->user['banned'] = 0;
271			}
272		}
273
274		// Check if this user is currently banned and if we have to lift it.
275		if(!empty($mybb->user['bandate']) && (isset($mybb->user['banlifted']) && !empty($mybb->user['banlifted'])) && $mybb->user['banlifted'] < $time)  // hmmm...bad user... how did you get banned =/
276		{
277			// must have been good.. bans up :D
278			$db->shutdown_query("UPDATE ".TABLE_PREFIX."users SET usergroup='".(int)$mybb->user['banoldgroup']."', additionalgroups='".$db->escape_string($mybb->user['banoldadditionalgroups'])."', displaygroup='".(int)$mybb->user['banolddisplaygroup']."' WHERE uid='".$mybb->user['uid']."'");
279			$db->shutdown_query("DELETE FROM ".TABLE_PREFIX."banned WHERE uid='".$mybb->user['uid']."'");
280			// we better do this..otherwise they have dodgy permissions
281			$mybb->user['usergroup'] = $mybb->user['banoldgroup'];
282			$mybb->user['displaygroup'] = $mybb->user['banolddisplaygroup'];
283			$mybb->user['additionalgroups'] = $mybb->user['banoldadditionalgroups'];
284
285			$mybbgroups = $mybb->user['usergroup'];
286			if($mybb->user['additionalgroups'])
287			{
288				$mybbgroups .= ','.$mybb->user['additionalgroups'];
289			}
290		}
291		else if(!empty($mybb->user['bandate']) && (empty($mybb->user['banlifted'])  || !empty($mybb->user['banlifted']) && $mybb->user['banlifted'] > $time))
292        {
293            $mybbgroups = $mybb->user['usergroup'];
294        }
295        else
296        {
297			// Gather a full permission set for this user and the groups they are in.
298			$mybbgroups = $mybb->user['usergroup'];
299			if($mybb->user['additionalgroups'])
300			{
301				$mybbgroups .= ','.$mybb->user['additionalgroups'];
302			}
303        }
304
305		$mybb->usergroup = usergroup_permissions($mybbgroups);
306		if(!$mybb->user['displaygroup'])
307		{
308			$mybb->user['displaygroup'] = $mybb->user['usergroup'];
309		}
310
311		$mydisplaygroup = usergroup_displaygroup($mybb->user['displaygroup']);
312		if(is_array($mydisplaygroup))
313		{
314			$mybb->usergroup = array_merge($mybb->usergroup, $mydisplaygroup);
315		}
316
317		if(!$mybb->user['usertitle'])
318		{
319			$mybb->user['usertitle'] = $mybb->usergroup['usertitle'];
320		}
321
322		// Update or create the session.
323		if(!defined("NO_ONLINE") && !defined('IN_UPGRADE'))
324		{
325			if(!empty($this->sid))
326			{
327				$this->update_session($this->sid, $mybb->user['uid']);
328			}
329			else
330			{
331				$this->create_session($mybb->user['uid']);
332			}
333		}
334		return true;
335	}
336
337	/**
338	 * Load a guest user.
339	 *
340	 */
341	function load_guest()
342	{
343		global $mybb, $time, $db, $lang;
344
345		// Set up some defaults
346		$time = TIME_NOW;
347		$mybb->user['usergroup'] = 1;
348		$mybb->user['additionalgroups'] = '';
349		$mybb->user['username'] = '';
350		$mybb->user['uid'] = 0;
351		$mybbgroups = 1;
352		$mybb->user['displaygroup'] = 1;
353		$mybb->user['invisible'] = 0;
354
355		// Has this user visited before? Lastvisit need updating?
356		if(isset($mybb->cookies['mybb']['lastvisit']))
357		{
358			if(!isset($mybb->cookies['mybb']['lastactive']))
359			{
360				$mybb->user['lastactive'] = $time;
361				$mybb->cookies['mybb']['lastactive'] = $mybb->user['lastactive'];
362			}
363			else
364			{
365				$mybb->user['lastactive'] = (int)$mybb->cookies['mybb']['lastactive'];
366			}
367			if($time - $mybb->cookies['mybb']['lastactive'] > 900)
368			{
369				my_setcookie("mybb[lastvisit]", $mybb->user['lastactive']);
370				$mybb->user['lastvisit'] = $mybb->user['lastactive'];
371			}
372			else
373			{
374				$mybb->user['lastvisit'] = (int)$mybb->cookies['mybb']['lastactive'];
375			}
376		}
377
378		// No last visit cookie, create one.
379		else
380		{
381			my_setcookie("mybb[lastvisit]", $time);
382			$mybb->user['lastvisit'] = $time;
383		}
384
385		// Update last active cookie.
386		my_setcookie("mybb[lastactive]", $time);
387
388		// Gather a full permission set for this guest
389		$mybb->usergroup = usergroup_permissions($mybbgroups);
390		$mydisplaygroup = usergroup_displaygroup($mybb->user['displaygroup']);
391		if(is_array($mydisplaygroup))
392		{
393			$mybb->usergroup = array_merge($mybb->usergroup, $mydisplaygroup);
394		}
395
396		// Update the online data.
397		if(!defined("NO_ONLINE") && !defined('IN_UPGRADE'))
398		{
399			if(!empty($this->sid))
400			{
401				$this->update_session($this->sid);
402			}
403			else
404			{
405				$this->create_session();
406			}
407		}
408	}
409
410	/**
411	 * Load a search engine spider.
412	 *
413	 * @param int $spider_id The ID of the search engine spider
414	 */
415	function load_spider($spider_id)
416	{
417		global $mybb, $time, $db, $lang;
418
419		// Fetch the spider preferences from the database
420		$query = $db->simple_select("spiders", "*", "sid='{$spider_id}'");
421		$spider = $db->fetch_array($query);
422
423		// Set up some defaults
424		$time = TIME_NOW;
425		$this->is_spider = true;
426		if($spider['usergroup'])
427		{
428			$mybb->user['usergroup'] = $spider['usergroup'];
429		}
430		else
431		{
432			$mybb->user['usergroup'] = 1;
433		}
434		$mybb->user['username'] = '';
435		$mybb->user['uid'] = 0;
436		$mybb->user['displaygroup'] = $mybb->user['usergroup'];
437		$mybb->user['additionalgroups'] = '';
438		$mybb->user['invisible'] = 0;
439
440		// Set spider language
441		if($spider['language'] && $lang->language_exists($spider['language']))
442		{
443			$mybb->settings['bblanguage'] = $spider['language'];
444		}
445
446		// Set spider theme
447		if($spider['theme'])
448		{
449			$mybb->user['style'] = $spider['theme'];
450		}
451
452		// Gather a full permission set for this spider.
453		$mybb->usergroup = usergroup_permissions($mybb->user['usergroup']);
454		$mydisplaygroup = usergroup_displaygroup($mybb->user['displaygroup']);
455		if(is_array($mydisplaygroup))
456		{
457			$mybb->usergroup = array_merge($mybb->usergroup, $mydisplaygroup);
458		}
459
460		// Update spider last minute (only do so on two minute intervals - decrease load for quick spiders)
461		if($spider['lastvisit'] < TIME_NOW-120)
462		{
463			$updated_spider = array(
464				"lastvisit" => TIME_NOW
465			);
466			$db->update_query("spiders", $updated_spider, "sid='{$spider_id}'");
467		}
468
469		// Update the online data.
470		if(!defined("NO_ONLINE") && !defined('IN_UPGRADE'))
471		{
472			$this->sid = "bot=".$spider_id;
473			$this->create_session();
474		}
475
476	}
477
478	/**
479	 * Update a user session.
480	 *
481	 * @param int $sid The session id.
482	 * @param int $uid The user id.
483	 */
484	function update_session($sid, $uid=0)
485	{
486		global $db;
487
488		// Find out what the special locations are.
489		$speciallocs = $this->get_special_locations();
490		if($uid)
491		{
492			$onlinedata['uid'] = $uid;
493		}
494		else
495		{
496			$onlinedata['uid'] = 0;
497		}
498		$onlinedata['time'] = TIME_NOW;
499
500		$onlinedata['location'] = $db->escape_string(substr(get_current_location(false, $this->ignore_parameters), 0, 150));
501		$onlinedata['useragent'] = $db->escape_string(my_substr($this->useragent, 0, 200));
502
503		$onlinedata['location1'] = (int)$speciallocs['1'];
504		$onlinedata['location2'] = (int)$speciallocs['2'];
505		$onlinedata['nopermission'] = 0;
506		$sid = $db->escape_string($sid);
507
508		$db->update_query("sessions", $onlinedata, "sid='{$sid}'");
509	}
510
511	/**
512	 * Create a new session.
513	 *
514	 * @param int $uid The user id to bind the session to.
515	 */
516	function create_session($uid=0)
517	{
518		global $db;
519		$speciallocs = $this->get_special_locations();
520
521		// If there is a proper uid, delete by uid.
522		if($uid > 0)
523		{
524			$db->delete_query("sessions", "uid='{$uid}'");
525			$onlinedata['uid'] = $uid;
526		}
527		else
528		{
529			// Is a spider - delete all other spider references
530			if($this->is_spider == true)
531			{
532				$db->delete_query("sessions", "sid='{$this->sid}'");
533			}
534
535			$onlinedata['uid'] = 0;
536		}
537
538		// If the user is a search enginge spider, ...
539		if($this->is_spider == true)
540		{
541			$onlinedata['sid'] = $this->sid;
542		}
543		else
544		{
545			$onlinedata['sid'] = md5(random_str(50));
546		}
547		$onlinedata['time'] = TIME_NOW;
548		$onlinedata['ip'] = $db->escape_binary($this->packedip);
549
550		$onlinedata['location'] = $db->escape_string(substr(get_current_location(false, $this->ignore_parameters), 0, 150));
551		$onlinedata['useragent'] = $db->escape_string(my_substr($this->useragent, 0, 200));
552
553		$onlinedata['location1'] = (int)$speciallocs['1'];
554		$onlinedata['location2'] = (int)$speciallocs['2'];
555		$onlinedata['nopermission'] = 0;
556		$db->replace_query("sessions", $onlinedata, "sid", false);
557		$this->sid = $onlinedata['sid'];
558		$this->uid = $onlinedata['uid'];
559	}
560
561	/**
562	 * Find out the special locations.
563	 *
564	 * @return array Special locations array.
565	 */
566	function get_special_locations()
567	{
568		global $mybb;
569		$array = array('1' => '', '2' => '');
570		if(preg_match("#forumdisplay.php#", $_SERVER['PHP_SELF']) && $mybb->get_input('fid', MyBB::INPUT_INT) > 0 && $mybb->get_input('fid', MyBB::INPUT_INT) < 4294967296)
571		{
572			$array[1] = $mybb->get_input('fid', MyBB::INPUT_INT);
573			$array[2] = '';
574		}
575		elseif(preg_match("#showthread.php#", $_SERVER['PHP_SELF']))
576		{
577			global $db;
578
579			if($mybb->get_input('tid', MyBB::INPUT_INT) > 0 && $mybb->get_input('tid', MyBB::INPUT_INT) < 4294967296)
580			{
581				$array[2] = $mybb->get_input('tid', MyBB::INPUT_INT);
582			}
583
584			// If there is no tid but a pid, trick the system into thinking there was a tid anyway.
585			elseif(isset($mybb->input['pid']) && !empty($mybb->input['pid']))
586			{
587				$options = array(
588					"limit" => 1
589				);
590				$query = $db->simple_select("posts", "tid", "pid=".$mybb->get_input('pid', MyBB::INPUT_INT), $options);
591				$post = $db->fetch_array($query);
592				$array[2] = $post['tid'];
593			}
594
595			$thread = get_thread($array[2]);
596			$array[1] = $thread['fid'];
597		}
598		return $array;
599	}
600}
601