Location: PHPKode > projects > DantoBB > DantoBB_1-0/sources/session.php
<?php

/*
	Copyright (C) 2009 DantoBB Team
	http://www.dantobb.com
*/

/**
 * Session management
 *
 * Contains the session class to do session management.
 *
 * @author	DantoBB Team
 * @link	http://www.dantobb.com
 * @license	GPL-2
 * @version	$Revision: 1.0 $
 * @copyright	Copyright (C) 2009 DantoBB Team
 * @package	DantoBB
 * @subpackage	Core
 */

//
// Die when called directly in browser
//
if ( !defined('INCLUDED') )
	exit();

/**
 * Session management
 *
 * Does all kinds of session management in addition to PHP's functions.
 *
 * @author	DantoBB Team
 * @link	http://www.dantobb.com
 * @license	GPL-2
 * @version	$Revision: 1.0 $
 * @copyright	Copyright (C) 2009 DantoBB Team
 * @package	DantoBB
 * @subpackage	Core
 */
class session {
	
	/**
	 * @var array Array containing all session info, as well as user info. update() must be called before this contains information.
	 */
	var $sess_info = array();

	/**
	 * Start or continue a session
	 */
	function start() {
		
		global $functions;
		
		//
		// Set the session save path
		//
		$proposed_save_path = $functions->get_config('session_save_path');
		if ( !empty($proposed_save_path) )
			session_save_path($proposed_save_path);
		
		//
		// Set some PHP session cookie configuration options
		// Use HttpOnly flag when enabled
		//
		// Also see the comments with setcookie() in functions.php.
		//
		$domain = $functions->get_config('cookie_domain');
		$secure = ( $functions->get_config('cookie_secure') ) ? 1 : 0;
		if ( empty($domain) || !$functions->get_config('cookie_httponly') )
			session_set_cookie_params($functions->get_config('session_max_lifetime')*60, $functions->get_config('cookie_path'), $domain, $secure);
		elseif ( version_compare(PHP_VERSION, '5.2.0RC2', '>=') )
			session_set_cookie_params($functions->get_config('session_max_lifetime')*60, $functions->get_config('cookie_path'), $domain, $secure, true);
		else
			session_set_cookie_params($functions->get_config('session_max_lifetime')*60, $functions->get_config('cookie_path'), $domain.'; HttpOnly', $secure);
		
		//
		// Set the session name
		//
		session_name($functions->get_config('session_name').'_sid');
		
		//
		// Start the session
		//
		if ( !ini_get('session.auto_start') )
			session_start();
		
		//
		// Several session info we maintain
		//
		$_SESSION['previous_visit'] = ( !empty($_SESSION['previous_visit']) ) ? $_SESSION['previous_visit'] : 0;
		$_SESSION['viewed_topics'] = ( isset($_SESSION['viewed_topics']) && is_array($_SESSION['viewed_topics']) ) ? $_SESSION['viewed_topics'] : array();
		$_SESSION['latest_post'] = ( !empty($_SESSION['latest_post']) ) ? $_SESSION['latest_post'] : 0;
		$_SESSION['dnsbl_checked'] = ( !empty($_SESSION['dnsbl_checked']) ) ? $_SESSION['dnsbl_checked'] : 0;
		$_SESSION['dnsbl_whitelisted'] = ( isset($_SESSION['dnsbl_whitelisted']) && $_SESSION['dnsbl_whitelisted'] );
		$_SESSION['antispam_question_posed'] = ( isset($_SESSION['antispam_question_posed']) && $_SESSION['antispam_question_posed'] );
		
	}
	
	/**
	 * Update the session table for this session
	 *
	 * This method also checks for banned IP-addresses or user accounts and checks for auto-login cookies. update() must be called before $sess_info contains usable information.
	 *
	 * @param string $location Current forum location (current location when missing)
	 * @param int $user_id New user ID (current ID when missing)
	 */
	function update($location=NULL, $user_id=NULL) {
		
		global $functions, $db;
		
		//
		// First, get the user's IP address and time
		//
		$current_time = time();
		$ip_addr = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : getenv('REMOTE_ADDR');
		
		//
		// Check if we will run cleanup
		// Cleanup is ran about 1 time per 10 requests
		//
		$run_cleanup = ( mt_rand(0, 9) === 0 ) ? true : false;
		
		//
		// Get banned IP addresses
		//
		$ip_banned = false;
		if ( $functions->get_config('enable_ip_bans') ) {
			
			$result = $db->query("SELECT ip_addr FROM ".TABLE_PREFIX."bans WHERE ip_addr <> ''");
			$banned_ips_sql = array();
			while ( $out = $db->fetch_result($result) ) {
				
				$out['ip_addr'] = stripslashes($out['ip_addr']);
				
				if ( !$ip_banned && preg_match('#^'.str_replace(array('\*', '\?'), array('[0-9]*', '[0-9]'), preg_quote($out['ip_addr'])).'$#', $ip_addr) )
					$ip_banned = true;
				
				if ( $run_cleanup )
					$banned_ips_sql[] = "ip_addr LIKE '".str_replace(array('*', '?'), array('%', '_'), $out['ip_addr'])."'";
				
			}
			
		}
		
		if ( $run_cleanup ) {
			
			$add_to_remove_query = array();
			
			//
			// Remove older clone sessions if needed
			//
			if ( !$functions->get_config('allow_multi_sess') )
				$add_to_remove_query[] = "( ip_addr = '".$ip_addr."' AND sess_id <> '".session_id()."' )";
			
			//
			// Remove outdated sessions and searches if needed
			//
			if ( $functions->get_config('session_max_lifetime') ) {
				
				$min_updated = $current_time - ( $functions->get_config('session_max_lifetime') * 60 );
				$add_to_remove_query[] = "updated < ".$min_updated;
				$db->query("DELETE FROM ".TABLE_PREFIX."searches WHERE time < ".$min_updated);
				
			}
			
			//
			// Remove sessions with banned IP addresses
			//
			if ( $functions->get_config('enable_ip_bans') && count($banned_ips_sql) )
				$add_to_remove_query[] = join(' OR ', $banned_ips_sql);
			
			//
			// Now run the cleanup query
			//
			if ( count($add_to_remove_query) )
				$db->query("DELETE FROM ".TABLE_PREFIX."sessions WHERE ".join(' OR ', $add_to_remove_query));
			
		}
		
		//
		// IP address banned
		//
		if ( $ip_banned ) {
			
			//
			// Save session information with the banned key and
			// IP address if this IP address is banned
			//
			$this->sess_info = array(
				'sess_id' => session_id(),
				'user_id' => 0,
				'ip_addr' => $ip_addr,
				'updated' => $current_time,
				'ip_banned' => true,
				'location' => ( $location !== NULL ) ? $location : ''
			);
			
			return;
			
		}
		
		//
		// Get information about the current session (and user)
		//
		$result = $db->query("SELECT s.user_id, s.started, s.location AS slocation, s.pages, s.ip_addr, u.* FROM ".TABLE_PREFIX."sessions s LEFT JOIN ".TABLE_PREFIX."members u ON u.id = s.user_id WHERE sess_id = '".session_id()."'");
		$user_data = $db->fetch_result($result);
		
		if ( is_array($user_data) ) {
			
			//
			// Data exists in DB
			//
			$session_started = true;
			
			$user_data['user_id'] = ( !empty($user_data['user_id']) ) ? $user_data['user_id'] : 0;
			$current_sess_info = array(
				'user_id' => $user_data['user_id'],
				'started' => $user_data['started'],
				'location' => $user_data['slocation'],
				'pages' => $user_data['pages'],
				'ip_addr' => $user_data['ip_addr']
			);
			
			if ( $current_sess_info['user_id'] )
				unset($user_data['user_id'], $user_data['started'], $user_data['slocation'], $user_data['pages'], $user_data['ip_addr']);
			else
				unset($user_data);
			
		} else {
			
			$session_started = false;
			unset($user_data);
			
		}
		
		//
		// If this session ID exists in DB and if it doesn't belong to this IP address
		//
		if ( $session_started && $current_sess_info['ip_addr'] !== $ip_addr ) {
			
			//
			// Reload the page, stripping the wrong session ID
			// in the URL (if present) and unsetting the cookie
			//
			$SID = SID;
			$functions->setcookie($functions->get_config('session_name').'_sid', '');
			$functions->raw_redirect(str_replace($SID, '', $_SERVER['REQUEST_URI']));
			
			//
			// Developer note:
			// This is a dirty way of getting rid of the session ID, but it is the only one,
			// as PHP starts the session anyway when getting a session ID, and we are only
			// working on top of it. Also we do not want to destroy the session itself.
			//
			
		}
		
		//
		// DNSBL powered banning
		//
		if ( function_exists('checkdnsrr') && !ON_WINDOWS ) {
			
			$dnsrr_available = true;
			$dnsrr_function = 'checkdnsrr';
			
		} elseif ( ON_WINDOWS ) {
			
			$dnsrr_available = true;
			$dnsrr_function = 'checkdnsrr_win';
			
		} else {
			
			$dnsrr_available = false;
			
		}
		
		if ( $functions->get_config('enable_dnsbl_powered_banning') && $dnsrr_available && !$_SESSION['dnsbl_whitelisted'] && ( !$_SESSION['dnsbl_checked'] || ( $functions->get_config('dnsbl_powered_banning_recheck_minutes') && $_SESSION['dnsbl_checked'] <= ( time() - $functions->get_config('dnsbl_powered_banning_recheck_minutes') * 60 ) ) ) ) {
			
			$whitelist = $functions->get_config('dnsbl_powered_banning_whitelist');			
			
			if ( count($whitelist) ) {
				
				foreach ( $whitelist as $ip_host ) {
					
					if ( preg_match('#[a-z]#i', $ip_host) ) {
						
						//
						// This is a hostname
						//
						if ( preg_match('#^'.str_replace(array('\*', '\?'), array('[a-z0-9\-\.]*', '[a-z0-9\-\.]'), preg_quote($ip_host)).'$#i', gethostbyaddr($ip_addr)) ) {
							
							$_SESSION['dnsbl_whitelisted'] = true;
							break;
							
						}
						
					} else {
						
						//
						// This is an IP address
						//
						if ( preg_match('#^'.str_replace(array('\*', '\?'), array('[0-9\.]*', '[0-9]'), preg_quote($ip_host)).'$#', $ip_addr) ) {
							
							$_SESSION['dnsbl_whitelisted'] = true;
							break;
							
						}
						
					}
					
				}
				
			}
			
			if ( !$_SESSION['dnsbl_whitelisted'] ) {
				
				$dnsbl_servers = $functions->get_config('dnsbl_powered_banning_servers');
				$dnsbl = join('.', array_reverse(explode('.', $ip_addr)));
				
				$hits_found = 0;
				foreach ( $dnsbl_servers as $dnsbl_server ) {
					
					if ( preg_match('#^(?:[a-z0-9\-]+\.){1,}[a-z]{2,}$#i', $dnsbl_server) && call_user_func($dnsrr_function, $dnsbl.'.'.$dnsbl_server, 'A') )
						$hits_found++;
					
				}
				
				if ( $hits_found >= $functions->get_config('dnsbl_powered_banning_min_hits') ) {
					
					$db->query("INSERT INTO ".TABLE_PREFIX."bans(ip_addr) VALUES('".$ip_addr."')");
					
					$this->sess_info = array(
						'sess_id' => session_id(),
						'user_id' => 0,
						'ip_addr' => $ip_addr,
						'updated' => $current_time,
						'ip_banned' => true,
						'location' => ( $location !== NULL ) ? $location : ''
					);
					
					return;
					
				} else {
					
					$_SESSION['dnsbl_checked'] = time();
					
				}
				
			}
			
		} else {
			
			$_SESSION['dnsbl_checked'] = time();
			
		}
		
		//
		// There are some possibilities now:
		// - Use the id from the parameters
		// OR
		// - The user is not yet logged in
		//   -> check auto login cookie
		// - The user is logged in
		//   -> check current user data
		//
		if ( $user_id !== NULL && valid_int($user_id) ) {
			
			//
			// ID passed by parameter
			//
			$user_data = $this->get_user_data($user_id);
			$user_id = ( $this->check_user($user_data) ) ? $user_id : 0;
			
		} elseif ( !$session_started || !$current_sess_info['user_id'] ) {
			
			//
			// Not logged in
			//
			if ( $functions->isset_al() ) {
				
				//
				// Try auto login cookie
				//
				$cookie_data = $functions->get_al();
				
				if ( is_array($cookie_data) && !empty($cookie_data[0]) && valid_int($cookie_data[0]) && !empty($cookie_data[1]) && strlen($cookie_data[1]) == 32 ) {
					
					$user_data = $this->get_user_data($cookie_data[0]);
					$user_id = ( $cookie_data[1] === $user_data['passwd'] && $this->check_user($user_data) ) ? $cookie_data[0] : 0;
					
					//
					// Unset cookie when credentials are invalid
					//
					if ( !$user_id )
						$functions->unset_al();
					
				} else {
					
					//
					// Invalid cookie
					//
					$functions->unset_al();
					$user_id = 0;
					
				}
				
			} else {
				
				//
				// Remain guest
				//
				$user_id = 0;
				
			}
			
		} else {
			
			//
			// Check current user data
			//
			$user_id = ( $this->check_user($user_data) ) ? $current_sess_info['user_id'] : 0;
			
		}
		
		//
		// Set new session data
		//
		$new_sess_info = array(
			'user_id' => $user_id,
			'started' => ( $session_started ) ? $current_sess_info['started'] : $current_time,
			'updated' => $current_time,
			'pages' => ( $session_started ) ? $current_sess_info['pages']+1 : 1,
			'ip_addr' => $ip_addr,
			'pose_antispam_question' => ( $functions->get_config('antispam_question_mode') && !$user_id && !$_SESSION['antispam_question_posed'] && preg_match('#^(register|reply:|posttopic:)#', $location) )
		);
		
		if ( $location !== NULL ) {
			
			$new_sess_info['location'] = $location;
			
		} else {
			
			$new_sess_info['location'] = ( $session_started ) ? $current_sess_info['location'] : '';
			
		}
		
		//
		// If the user logged in, use a new session ID (security measure)
		//
		if ( ( !$session_started && $new_sess_info['user_id'] ) || ( $session_started && $new_sess_info['user_id'] > $current_sess_info['user_id'] ) ) {
			
			//
			// Try to find a new session ID that does not yet exist
			//
			do {
				
				$new_sid = md5(uniqid(mt_rand(), true));
				$return = $db->query("SELECT COUNT(*) AS exist FROM ".TABLE_PREFIX."sessions WHERE sess_id = '".$new_sid."'");
				$exists = $db->fetch_result($return);
				$exists = (bool)$exists['exist'];
				
			} while ( $exists );
			
			//
			// Set the new session ID and update the cookie (not done automatically)
			//
			$old_sid = session_id($new_sid);
			$functions->setcookie($functions->get_config('session_name').'_sid', $new_sid);
			
			//
			// Update the searches
			//
			$db->query("UPDATE ".TABLE_PREFIX."searches SET sess_id = '".$new_sid."' WHERE sess_id = '".$old_sid."'");
			
		}
		
		//
		// Save data in DB
		//
		if ( $session_started ) {
			
			//
			// Session already exists in DB
			//
			if ( isset($old_sid) ) {
				
				//
				// Logged in, change session ID
				//
				$update_query = "UPDATE ".TABLE_PREFIX."sessions SET
					user_id = ".$new_sess_info['user_id'].",
					updated = ".$new_sess_info['updated'].",
					location = '".$new_sess_info['location']."',
					pages = ".$new_sess_info['pages'].",
					ip_addr = '".$new_sess_info['ip_addr']."',
					sess_id = '".$new_sid."'
				WHERE sess_id = '".$old_sid."'";
				
			} else {
				
				//
				// Just update the data, keep session ID
				//
				$update_query = "UPDATE ".TABLE_PREFIX."sessions SET
					user_id = ".$new_sess_info['user_id'].",
					updated = ".$new_sess_info['updated'].",
					location = '".$new_sess_info['location']."',
					pages = ".$new_sess_info['pages'].",
					ip_addr = '".$new_sess_info['ip_addr']."'
				WHERE sess_id = '".session_id()."'";
				
			}
			
		} else {
			
			//
			// Session does not already exist in DB
			//
			$update_query = "INSERT INTO ".TABLE_PREFIX."sessions VALUES (
				'".session_id()."',
				".$new_sess_info['user_id'].",
				'".$new_sess_info['ip_addr']."',
				".$new_sess_info['started'].",
				".$new_sess_info['updated'].",
				'".$new_sess_info['location']."',
				".$new_sess_info['pages']."
			)";
			
		}
		$db->query($update_query);
		
		//
		// Eventually update member data
		//
		if ( $new_sess_info['user_id'] ) {
			
			$add_to_update_query = ( !$session_started || $current_sess_info['user_id'] !== $new_sess_info['user_id'] ) ? ', last_login = '.$current_time : '';
			$db->query("UPDATE ".TABLE_PREFIX."members SET last_pageview = ".$current_time.$add_to_update_query." WHERE id = ".$new_sess_info['user_id']);
			
		}
		
		//
		// Now save the session information locally
		//
		$this->sess_info = array_merge($new_sess_info, array(
			'sess_id' => session_id(),
			'ip_banned' => false,
			'user_info' => ( $new_sess_info['user_id'] ) ? $user_data : array('level' => LEVEL_GUEST)
		));
		
		//
		// Set previous visit timestamp for markers
		//
		$_SESSION['previous_visit'] = ( $new_sess_info['user_id'] && ( !$session_started || $current_sess_info['user_id'] !== $new_sess_info['user_id'] || empty($_SESSION['previous_visit']) ) ) ? $user_data['last_pageview'] : $_SESSION['previous_visit'];
		
	}
	
	/**
	 * Get user data
	 *
	 * @param int $user_id User ID
	 * @returns array User data array from query
	 */
	function get_user_data($user_id) {
		
		global $db;
		
		$result = $db->query("SELECT * FROM ".TABLE_PREFIX."members WHERE id = ".$user_id);
		return $db->fetch_result($result);
		
	}
	
	/**
	 * Check a user data to see if he/she may log in
	 *
	 * @param array $user_data User data array from query
	 * @returns bool May login
	 */
	function check_user($user_data) {
		
		global $functions;
		
		return ( $user_data['active'] && !$user_data['banned'] && ( !$functions->get_config('board_closed') || $user_data['level'] == LEVEL_ADMIN ) );
		
	}
	
	/**
	 * Destroy a running session
	 */
	function destroy() {
		
		global $functions, $db;
		
		$functions->unset_al();
		$db->query("DELETE FROM ".TABLE_PREFIX."sessions WHERE sess_id = '".session_id()."'");
		$db->query("DELETE FROM ".TABLE_PREFIX."searches WHERE sess_id = '".session_id()."'");
		$_SESSION = array();
		session_destroy();
		$functions->setcookie($functions->get_config('session_name').'_sid', '');
		
	}
	
}

?>
Return current item: DantoBB