aliroSession.php

Go to the documentation of this file.
00001 <?php
00002 
00003 /*******************************************************************************
00004  * Aliro - the modern, accessible content management system
00005  *
00006  * Aliro is open source software, free to use, and licensed under GPL.
00007  * You can find the full licence at http://www.gnu.org/copyleft/gpl.html GNU/GPL
00008  *
00009  * The author freely draws attention to the fact that Aliro derives from Mambo,
00010  * software that is controlled by the Mambo Foundation.  However, this section
00011  * of code is totally new.  If it should contain any fragments that are similar
00012  * to Mambo, please bear in mind (1) there are only so many ways to do things
00013  * and (2) the author of Aliro is also the author and copyright owner for large
00014  * parts of Mambo 4.6.
00015  *
00016  * Tribute should be paid to all the developers who took Mambo to the stage
00017  * it had reached at the time Aliro was created.  It is a feature rich system
00018  * that contains a good deal of innovation.
00019  *
00020  * Your attention is also drawn to the fact that Aliro relies on other items of
00021  * open source software, which is very much in the spirit of open source.  Aliro
00022  * wishes to give credit to those items of code.  Please refer to
00023  * http://aliro.org/credits for details.  The credits are not included within
00024  * the Aliro package simply to avoid providing a marker that allows hackers to
00025  * identify the system.
00026  *
00027  * Copyright in this code is strictly reserved by its author, Martin Brampton.
00028  * If it seems appropriate, the copyright will be vested in the Aliro Organisation
00029  * at a suitable time.
00030  *
00031  * Copyright (c) 2007 Martin Brampton
00032  *
00033  * http://aliro.org
00034  *
00035  * counterpoint@aliro.org
00036  *
00037  * aliroSession is the abstract class for session objects.  These are stored in
00038  * the database as a record of the currently active users.  Every user has a
00039  * session, whether logged in or not (admin side users cannot be anything but
00040  * logged in except when they are just about to log in).  The same session is
00041  * preserved across a user log in, so that e.g. a shopping cart will remain
00042  * intact when the user logs in.  But the session is not preserved across logout
00043  * as that would prejudice security and tidiness too much (that's my view at
00044  * present, anyway)
00045  *
00046  * aliroUserSession is the concrete class for the user side version of a session.
00047  *
00048  * aliroAdminSession is (!) the admin side session class.
00049  *
00050  * aliroSessionFactory will instantiate the appropriate kind of session object
00051  *
00052  * aliroSessionData is the class that stores session data in the database.
00053  *
00054  */
00055 
00056 abstract class aliroSession {
00057     protected static $currentSession = null;
00058     public $session_id=null;
00059     public $time=null;
00060     public $userid=0;
00061     public $usertype='';
00062     public $username='';
00063     public $gid=0;
00064     public $guest=1;
00065     protected $_lifetime;
00066     protected $_newsess = false;
00067 
00068     protected function __construct() {
00069         $this->time = time();
00070         ini_set('session.use_cookies', 1);
00071         ini_set('session.use_only_cookies', 1);
00072         session_name(md5('aliro_'.$this->_prefix.$this->getip().criticalInfo::getInstance()->absolute_path));
00073         if (!session_id()) {
00074             $sh = aliroSessionData::getInstance();
00075             session_set_save_handler(array($sh,'sess_open'), array($sh,'sess_close'), array($sh,'sess_read'),
00076             array($sh,'sess_write'), array($sh,'sess_destroy'), array($sh,'sess_gc'));
00077             session_start();
00078         }
00079     }
00080 
00081     protected function __clone () {
00082         // Enforce singleton
00083     }
00084 
00085     public function getip() {
00086         $ip = false;
00087         if (!empty($_SERVER['HTTP_CLIENT_IP'])) $ip = $_SERVER['HTTP_CLIENT_IP'];
00088         if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
00089             $ips = explode (', ', $_SERVER['HTTP_X_FORWARDED_FOR']);
00090             if ($ip != false) {
00091                 array_unshift($ips,$ip);
00092                 $ip = false;
00093             }
00094             $count = count($ips);
00095             // Exclude IP addresses that are reserved for LANs
00096             for ($i = 0; $i < $count; $i++) {
00097                 if (!preg_match("/^(10|172\.16|192\.168)\./i", $ips[$i])) {
00098                     $ip = $ips[$i];
00099                     break;
00100                 }
00101             }
00102         }
00103         if (false == $ip AND isset($_SERVER['REMOTE_ADDR'])) $ip = $_SERVER['REMOTE_ADDR'];
00104         return $ip;
00105     }
00106 
00107     public function cookiesAccepted () {
00108         return isset($_COOKIE['aliroCookieCheck']);
00109     }
00110 
00111     public function setNew () {
00112         $this->_newsess = true;
00113     }
00114 
00115     // For a session to be valid, we must check it against the sessions table in the database
00116     protected function checkValidSession () {
00117        if ($this->session_id = session_id()) {
00118             // We try to update the time stamp in the matching record of the session table
00119             $result = $this->updateTime();
00120             if (!$result) {
00121                 setcookie('aliroCookieCheck', 'A', time()+365*24*60*60, '/');
00122                 $this->saveOrphanData();
00123                 $this->session_id = '';
00124             }
00125             return $result;
00126         }
00127         else {
00128             trigger_error(T_('No session ID found, although aliroSession has been instantiated'));
00129             return false;
00130         }
00131     }
00132 
00133     private function saveOrphanData () {
00134         if (isset($_REQUEST['option']) AND ('login' == $_REQUEST['option'] OR 'logout' == $_REQUEST['option'])) return;
00135         $orphandata['get'] = $_GET;
00136         $orphandata['post'] = $_POST;
00137         $orphanstring = base64_encode(serialize($orphandata));
00138         $database = aliroCoreDatabase::getInstance();
00139         $database->doSQL("INSERT INTO #__orphan_data VALUES ('$this->session_id', '$orphanstring') ON DUPLICATE KEY UPDATE orphandata = '$orphanstring'");
00140         setcookie ('aliroOrphanData', $this->session_id, time()+300, '/');
00141     }
00142 
00143     public function rememberMe ($request) {
00144         if (!$this->_newsess) return;
00145         $user = aliroUser::getInstance();
00146         if (0 == $user->id AND $usercookie = isset($_COOKIE['usercookie']) ? $_COOKIE['usercookie'] : null) {
00147             // Remember me cookie exists. Login with usercookie information if all present.
00148             if (!empty($usercookie['username']) AND !empty($usercookie['password'])) {
00149                 // If the login is successful, then the session data will be updated
00150                 // In any case, the return will be set either to user data or to null
00151                 $message = aliroUserAuthenticator::getInstance()->systemLogin ($usercookie['username'], $usercookie['password'], 1);
00152                 if ($message) $request->setErrorMessage(T_('Remember Me login failed - incorrect username-password combination'), _ALIRO_ERROR_WARN);
00153                 else $user->reset();
00154             }
00155         }
00156     }
00157 
00158     private function updateTime () {
00159         if (aliro::getInstance()->installed) {
00160             $database = aliroCoreDatabase::getInstance();
00161             $past = $this->time - $this->_lifetime;
00162             $database->doSQL("UPDATE #__session SET time = '$this->time', marker = marker+1 WHERE session_id = '$this->session_id' AND isadmin = $this->isadmin AND time > $past");
00163             return ($database->getAffectedRows()) ? true : false;
00164         }
00165         return false;
00166     }
00167 
00168     protected function setSessionData ($my) {
00169         $database = aliroCoreDatabase::getInstance();
00170         if ($my->id AND !empty($_COOKIE['aliroOrphanData'])) {
00171             $database->setQuery("SELECT orphandata FROM #__orphan_data WHERE session_id = '{$_COOKIE['aliroOrphanData']}'");
00172             $orphanstring = $database->loadResult();
00173             if (!empty($orphanstring)) {
00174                 $orphandata = unserialize(base64_decode($orphanstring));
00175                 foreach (array_keys($_GET) as $key) unset($_REQUEST[$key]);
00176                 foreach (array_keys($_POST) as $key) unset($_REQUEST[$key]);
00177                 $_GET = $orphandata['get'];
00178                 $_POST = $orphandata['post'];
00179                 foreach ($_GET as $key=>$value) $_REQUEST[$key] = $value;
00180                 foreach ($_POST as $key=>$value) $_REQUEST[$key] = $value;
00181                 // $database->doSQL("DELETE FROM #__orphan_data WHERE session_id = '{$_COOKIE['aliroOrphanData']}'");
00182                 setcookie('aliroOrphanData', 'A', time()-7*24*60*60, '/');
00183             }
00184         }
00185         session_regenerate_id();
00186         $this->session_id = session_id();
00187         $this->httphost = $_SERVER['HTTP_HOST'];
00188         $this->servername = $_SERVER['SERVER_NAME'];
00189         $this->ipaddress = getenv('REMOTE_ADDR');
00190         $_SESSION["aliro_{$this->_prefix}id"] = $this->userid = $my->id;
00191         $_SESSION["aliro_{$this->_prefix}name"] = $my->name;
00192         $_SESSION["aliro_{$this->_prefix}username"] = $this->username = $my->username;
00193         $_SESSION["aliro_{$this->_prefix}email"] = $my->email;
00194         $_SESSION["aliro_{$this->_prefix}sendEmail"] = $my->sendEmail;
00195         $_SESSION["aliro_{$this->_prefix}type"] = $this->usertype = $my->usertype;
00196         $_SESSION["aliro_{$this->_prefix}gid"] = $this->gid = $my->gid;
00197         $_SESSION["aliro_{$this->_prefix}logintime"] = $this->time = time();
00198         if (!isset($_SESSION["aliro_{$this->_prefix}state"])) $_SESSION["aliro_{$this->_prefix}state"]  = array();
00199         $this->userid = (int) $this->userid;
00200         $this->gid = (int) $this->gid;
00201         $database->insertObject('#__session', $this);
00202         $this->purge();
00203     }
00204 
00205     public function purge($timeout=0) {
00206         // Note purge only records on the current side - admin or user - because lifetime may be different
00207         if (aliro::getInstance()->installed) {
00208             $past = time() - ($timeout ? $timeout : $this->_lifetime);
00209             $database = aliroCoreDatabase::getInstance();
00210             $database->setQuery("SELECT session_id, username, isadmin FROM #__session WHERE (time < $past) AND isadmin = $this->isadmin");
00211             $expired = $database->loadObjectList();
00212             if ($expired) foreach ($expired as $exp) {
00213                 $sessions[] = $exp->session_id;
00214                 $this->forceLogout ($exp);
00215             }
00216             if (isset($sessions)) {
00217                 $sessionlist = implode ("','", $sessions);
00218                 $database->doSQL("DELETE LOW_PRIORITY FROM `#__session` WHERE session_id IN('$sessionlist')");
00219             }
00220             aliroSessionData::getInstance()->sess_destroy_orphans();
00221         }
00222     }
00223 
00224     protected function forceLogout ($exp) {
00225         // Implemented by User Session, not Admin
00226     }
00227 
00228     public static function isAdminPresent () {
00229         if (isset($_COOKIE['aliroAdminSession'])) $admin_session = $_COOKIE['aliroAdminSession'];
00230         else return false;
00231         $database = aliroCoreDatabase::getInstance();
00232         $database->setQuery("SELECT COUNT(session_id) FROM #__session WHERE session_id = '$admin_session' AND isadmin = 1");
00233         return $database->loadResult() ? true : false;
00234     }
00235 
00236 }
00237 
00238 class aliroUserSession extends aliroSession {
00239     protected $_prefix = 'user';
00240     public $isadmin = 0;
00241 
00242     protected function __construct () {
00243         parent::__construct();
00244         $this->_lifetime = max (aliroCore::getInstance()->getCfg('lifetime'), 300);
00245     }
00246 
00247     public static function getInstance () {
00248         if (!is_object(self::$currentSession)) {
00249             self::$currentSession = new aliroUserSession();
00250             if (!self::$currentSession->checkValidSession()) {
00251                 // Must be a new visitor
00252                 self::$currentSession->setNew();
00253                 $_SESSION = array();
00254                 self::$currentSession->setNewUserData(new mosUser());
00255                 $_SESSION['aliro_user_session_start'] = @date('Y-M-d H:i:s');
00256             }
00257         }
00258         return self::$currentSession;
00259     }
00260 
00261     protected function forceLogout ($exp) {
00262         if ($exp->username) {
00263             $loginfo = new aliroLoginDetails($exp->username);
00264             aliroMambotHandler::getInstance()->trigger('expireLogout', array($loginfo));
00265         }
00266     }
00267 
00268     public function setNewUserData ($my) {
00269         if ($this->session_id) aliroCoreDatabase::getInstance()->doSQL("DELETE FROM #__session WHERE session_id = '$this->session_id' AND isadmin = $this->isadmin");
00270         $this->setSessionData($my);
00271     }
00272 
00273     public function logout () {
00274         $my = new mosUser();
00275         $stamp = $_SESSION['aliro_user_session_start'];
00276         $redirect = isset($_SESSION['aliro_redirect_here']) ? $_SESSION['aliro_redirect_here'] : '';
00277         $_SESSION = array();
00278         $_SESSION['aliro_user_session_start'] = $stamp;
00279         $this->setNewUserData($my);
00280         $request = aliroRequest::getInstance();
00281         if ($return = $request->getParam($_REQUEST, 'return')) $request->redirect($return);
00282         else $request->redirect ($redirect);
00283     }
00284 
00285 }
00286 
00287 class aliroAdminSession extends aliroSession {
00288     protected $_prefix = 'admin';
00289     public $isadmin = 1;
00290 
00291     protected function __construct () {
00292         parent::__construct();
00293         $this->_lifetime = max (aliroCore::getInstance()->getCfg('adminlife'), 300);
00294     }
00295 
00296     public static function getInstance () {
00297         if (!is_object(self::$currentSession)) {
00298             self::$currentSession = new aliroAdminSession();
00299             if (!self::$currentSession->checkValidSession()) {
00300                 self::$currentSession->logout();
00301                 $_SESSION = array();
00302                 setcookie ('aliroAdminSession', 0, time()-7*24*60*60, '/');
00303             }
00304         }
00305         return self::$currentSession;
00306     }
00307 
00308     public function setNewUserData ($my) {
00309         aliroCoreDatabase::getInstance()->doSQL("DELETE FROM #__session WHERE (session_id = '$this->session_id' OR userid = $this->userid) AND isadmin = $this->isadmin");
00310         $this->setSessionData($my);
00311         setcookie ('aliroAdminSession', $this->session_id, 0, '/');
00312     }
00313 
00314     public function logout () {
00315         if ($adminid = isset($_SESSION['aliro_adminid']) ? (int) $_SESSION['aliro_adminid'] : 0) {
00316             aliroCoreDatabase::getInstance()->doSQL( "DELETE FROM #__session WHERE isadmin = 1 AND userid='$adminid'");
00317         }
00318         setcookie ('aliroAdminSession', 0, time()-7*24*60*60, '/');
00319         $_SESSION = array();
00320     }
00321 }
00322 
00323 abstract class aliroSessionFactory {
00324 
00325     public static function getSession () {
00326         if (criticalInfo::getInstance()->isAdmin) return aliroAdminSession::getInstance();
00327         else return aliroUserSession::getInstance();
00328     }
00329 
00330 }
00331 
00332 class aliroSessionData {
00333     private static $instance = __CLASS__;
00334     private $db = null;
00335 
00336     private function __construct () {
00337         if (aliro::getInstance()->installed) $this->db = aliroCoreDatabase::getInstance();
00338     }
00339 
00340     private function __clone () {
00341         // Enforce singleton
00342     }
00343 
00344     public static function getInstance () {
00345         return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance());
00346     }
00347 
00348     public function sess_open () {
00349         // No action required
00350     }
00351 
00352     public function sess_close () {
00353         // No action required
00354     }
00355 
00356     public function sess_read ($session_id) {
00357         if (isset($_COOKIE['aliroTempSession'])) return base64_decode($_COOKIE['aliroTempSession']);
00358         if (!isset($_COOKIE['aliroCookieCheck']) OR !isset($this->db)) return '';
00359         $session_id = $this->db->getEscaped($session_id);
00360         $this->db->setQuery("SELECT session_data FROM #__session_data WHERE session_id = '$session_id'");
00361         return base64_decode($this->db->loadResult());
00362     }
00363 
00364     public function sess_write ($session_id, $session_data) {
00365         if ((!isset($_COOKIE['aliroCookieCheck']) AND !isset($_COOKIE['usercookie'])) OR !$this->db) {
00366             if (!headers_sent()) setcookie ('aliroTempSession', base64_encode($session_data), 0, '/');
00367             return true;
00368         }
00369         if (isset($_COOKIE['aliroTempSession'])) setcookie ('aliroTempSession', null, time()-7*24*60*60, '/');
00370         $session_id = $this->db->getEscaped($session_id);
00371         $session_data = base64_encode($session_data);
00372         $this->db->doSQL("INSERT INTO #__session_data (session_id, session_data) VALUES ('$session_id', '$session_data') ON DUPLICATE KEY UPDATE session_data = '$session_data'");
00373         return true;
00374     }
00375 
00376     public function sess_destroy ($session_id) {
00377         setcookie ('aliroTempSession', null, time()-7*24*60*60, '/');
00378         if (!isset($_COOKIE['aliroCookieCheck']) OR !isset($this->db)) return;
00379         $session_id = $this->db->getEscaped($session_id);
00380         $this->db->doSQL("DELETE FROM #__session_data WHERE session_id = '$session_id'");
00381         return true;
00382     }
00383 
00384     public function sess_destroy_orphans () {
00385         if ($this->db AND 42 == mt_rand(0,49)) {
00386             $this->db->doSQL("DELETE d FROM `#__session_data` AS d LEFT JOIN #__session AS s ON d.session_id = s.session_id WHERE s.session_id IS NULL");
00387             $this->db->doSQL("OPTIMIZE TABLE `#__session_data`");
00388             $this->db->doSQL("OPTIMIZE TABLE `#__session`");
00389         }
00390     }
00391 
00392     public function sess_gc ($timeout) {
00393         $session = aliroSessionFactory::getSession();
00394         $session->purge($timeout);
00395     }
00396 
00397 }

Generated on Thu Apr 17 13:03:27 2008 for ALIRO by  doxygen 1.5.5