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         if (!$this->isadmin) $this->handleCoreDumps();
00223     }
00224     
00225     protected function handleCoreDumps () {
00226         $docroot = new aliroDirectory(_ALIRO_ABSOLUTE_PATH);
00227         $dumps = $docroot->listFiles('^core.');
00228         if (count($dumps)) {
00229             $dumpfile = $dumps[0];
00230             $dump = _ALIRO_ABSOLUTE_PATH.'/'.$dumpfile;
00231             $f = fopen ($dump, 'rb');
00232             fseek($f, -3000, SEEK_END);
00233             $chars = fread($f, 3000);
00234             $chars = strstr($chars, '/usr/bin/php');
00235             $chars = trim(preg_replace('/[[:^print:]]/', ' ', $chars));
00236             $later = strstr($chars, 'SERVER_SIGNATURE=');
00237             $latesize = strlen($later);
00238             $later = str_replace(' SERVER', "\nSERVER", $later);
00239             $chars = substr($chars, 0, -$latesize)."\n".$later."\n";
00240             $subhead = T_('CORE DUMP ANALYSIS:');
00241             $chars .= "\n$subhead\n";
00242             exec('gdb --batch /usr/bin/php '.$dumpfile, $output);
00243             foreach ($output as $line) $chars .= $line."\n";
00244             $chars = $dumpfile."\n".$chars;
00245             aliroFileManager::getInstance()->deleteFile($dump);
00246             $recorder = aliroErrorRecorder::getInstance();
00247             $recorder->recordError (T_('CORE_DUMP ').$dumpfile, $dumpfile, $chars);
00248         }
00249     }
00250 
00251     protected function forceLogout ($exp) {
00252         // Implemented by User Session, not Admin
00253     }
00254 
00255     public static function isAdminPresent () {
00256         if (isset($_COOKIE['aliroAdminSession'])) $admin_session = $_COOKIE['aliroAdminSession'];
00257         else return false;
00258         $database = aliroCoreDatabase::getInstance();
00259         $database->setQuery("SELECT COUNT(session_id) FROM #__session WHERE session_id = '$admin_session' AND isadmin = 1");
00260         return $database->loadResult() ? true : false;
00261     }
00262 
00263 }
00264 
00265 class aliroUserSession extends aliroSession {
00266     protected $_prefix = 'user';
00267     public $isadmin = 0;
00268 
00269     protected function __construct () {
00270         parent::__construct();
00271         $this->_lifetime = max (aliroCore::getInstance()->getCfg('lifetime'), 300);
00272     }
00273 
00274     public static function getInstance () {
00275         if (!is_object(self::$currentSession)) {
00276             self::$currentSession = new aliroUserSession();
00277             if (!self::$currentSession->checkValidSession()) {
00278                 // Must be a new visitor
00279                 self::$currentSession->setNew();
00280                 $_SESSION = array();
00281                 self::$currentSession->setNewUserData(new mosUser());
00282                 $_SESSION['aliro_user_session_start'] = @date('Y-M-d H:i:s');
00283             }
00284         }
00285         return self::$currentSession;
00286     }
00287 
00288     protected function forceLogout ($exp) {
00289         if ($exp->username) {
00290             $loginfo = new aliroLoginDetails($exp->username);
00291             aliroMambotHandler::getInstance()->trigger('expireLogout', array($loginfo));
00292         }
00293     }
00294 
00295     public function setNewUserData ($my) {
00296         if ($this->session_id) aliroCoreDatabase::getInstance()->doSQL("DELETE FROM #__session WHERE session_id = '$this->session_id' AND isadmin = $this->isadmin");
00297         $this->setSessionData($my);
00298     }
00299 
00300     public function logout () {
00301         $my = new mosUser();
00302         $stamp = $_SESSION['aliro_user_session_start'];
00303         $redirect = isset($_SESSION['aliro_redirect_here']) ? $_SESSION['aliro_redirect_here'] : '';
00304         $_SESSION = array();
00305         $_SESSION['aliro_user_session_start'] = $stamp;
00306         $this->setNewUserData($my);
00307         $request = aliroRequest::getInstance();
00308         if ($return = $request->getParam($_REQUEST, 'return')) $request->redirect($return);
00309         else $request->redirect ($redirect);
00310     }
00311 
00312 }
00313 
00314 class aliroAdminSession extends aliroSession {
00315     protected $_prefix = 'admin';
00316     public $isadmin = 1;
00317 
00318     protected function __construct () {
00319         parent::__construct();
00320         $this->_lifetime = max (aliroCore::getInstance()->getCfg('adminlife'), 300);
00321     }
00322 
00323     public static function getInstance () {
00324         if (!is_object(self::$currentSession)) {
00325             self::$currentSession = new aliroAdminSession();
00326             if (!self::$currentSession->checkValidSession()) {
00327                 self::$currentSession->logout();
00328                 $_SESSION = array();
00329                 setcookie ('aliroAdminSession', 0, time()-7*24*60*60, '/');
00330             }
00331         }
00332         return self::$currentSession;
00333     }
00334 
00335     public function setNewUserData ($my) {
00336         aliroCoreDatabase::getInstance()->doSQL("DELETE FROM #__session WHERE (session_id = '$this->session_id' OR userid = $this->userid) AND isadmin = $this->isadmin");
00337         $this->setSessionData($my);
00338         setcookie ('aliroAdminSession', $this->session_id, 0, '/');
00339     }
00340 
00341     public function logout () {
00342         if ($adminid = isset($_SESSION['aliro_adminid']) ? (int) $_SESSION['aliro_adminid'] : 0) {
00343             aliroCoreDatabase::getInstance()->doSQL( "DELETE FROM #__session WHERE isadmin = 1 AND userid='$adminid'");
00344         }
00345         setcookie ('aliroAdminSession', 0, time()-7*24*60*60, '/');
00346         $_SESSION = array();
00347     }
00348 }
00349 
00350 abstract class aliroSessionFactory {
00351 
00352     public static function getSession () {
00353         if (criticalInfo::getInstance()->isAdmin) return aliroAdminSession::getInstance();
00354         else return aliroUserSession::getInstance();
00355     }
00356 
00357 }
00358 
00359 class aliroSessionData {
00360     private static $instance = __CLASS__;
00361     private $db = null;
00362 
00363     private function __construct () {
00364         if (aliro::getInstance()->installed) $this->db = aliroCoreDatabase::getInstance();
00365     }
00366 
00367     private function __clone () {
00368         // Enforce singleton
00369     }
00370 
00371     public static function getInstance () {
00372         return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance());
00373     }
00374 
00375     public function sess_open () {
00376         // No action required
00377     }
00378 
00379     public function sess_close () {
00380         // No action required
00381     }
00382 
00383     public function sess_read ($session_id) {
00384         if (isset($_COOKIE['aliroTempSession'])) return base64_decode($_COOKIE['aliroTempSession']);
00385         if (!isset($_COOKIE['aliroCookieCheck']) OR !isset($this->db)) return '';
00386         $session_id = $this->db->getEscaped($session_id);
00387         $this->db->setQuery("SELECT session_data FROM #__session_data WHERE session_id = '$session_id'");
00388         return base64_decode($this->db->loadResult());
00389     }
00390 
00391     public function sess_write ($session_id, $session_data) {
00392         if ((!isset($_COOKIE['aliroCookieCheck']) AND !isset($_COOKIE['usercookie'])) OR !$this->db) {
00393             if (!headers_sent()) setcookie ('aliroTempSession', base64_encode($session_data), 0, '/');
00394             return true;
00395         }
00396         if (isset($_COOKIE['aliroTempSession'])) setcookie ('aliroTempSession', null, time()-7*24*60*60, '/');
00397         $session_id = $this->db->getEscaped($session_id);
00398         $session_data = base64_encode($session_data);
00399         $this->db->doSQL("INSERT INTO #__session_data (session_id, session_data) VALUES ('$session_id', '$session_data') ON DUPLICATE KEY UPDATE session_data = '$session_data'");
00400         return true;
00401     }
00402 
00403     public function sess_destroy ($session_id) {
00404         setcookie ('aliroTempSession', null, time()-7*24*60*60, '/');
00405         if (!isset($_COOKIE['aliroCookieCheck']) OR !isset($this->db)) return;
00406         $session_id = $this->db->getEscaped($session_id);
00407         $this->db->doSQL("DELETE FROM #__session_data WHERE session_id = '$session_id'");
00408         return true;
00409     }
00410 
00411     public function sess_destroy_orphans () {
00412         if ($this->db AND 42 == mt_rand(0,49)) {
00413             $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");
00414             $this->db->doSQL("OPTIMIZE TABLE `#__session_data`");
00415             $this->db->doSQL("OPTIMIZE TABLE `#__session`");
00416         }
00417     }
00418 
00419     public function sess_gc ($timeout) {
00420         $session = aliroSessionFactory::getSession();
00421         $session->purge($timeout);
00422     }
00423 
00424 }

Generated on Wed May 14 13:01:56 2008 for ALIRO by  doxygen 1.5.5