<?php
/*
 * Copyright (c) 2009-2010 ATMAIL. All rights reserved
 * See http://atmail.com/license.php for license agreement
 */

require_once('Atmail/Password.php');


class Atmail_Session_SaveHandler_DbAdminSession implements Zend_Session_SaveHandler_Interface
{
	public static $_sessionStore;
	private static $_cryptFunction = Atmail_Enum::PASSWORD_PLAIN;

	public function Atmail_Session_SaveHandler_DbAdminSession()
	{
		//do sanity check
		$dbTables = new dbTables();
		$adminUsersSchema = Zend_Registry::get('dbAdapter')->describeTable($dbTables->AdminUsers);
		$requiredNewFields = array('ipAddress', 'modified', 'sessionData');
		foreach ($requiredNewFields as $requiredNewField)
		{
			if (!array_key_exists($requiredNewField, $adminUsersSchema))
			{
				throw new Exception('Missing required fields. ' . print_r($requiredNewFields, true));
			}
		}
		$dbConfig = Zend_Registry::get('dbConfig');
		self::$_cryptFunction = Atmail_Password::sessionCryptFunction();
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ':  Session crypt mode = ' . self::$_cryptFunction);

	}

	public function open($save_path, $name)
	{
		$dbTables = new dbTables();
		self::$_sessionStore = new Atmail_Db_Table(array('name' => $dbTables->AdminUsers));
		return true;
	}

	public function close()
	{
		return true;
	}

	public function read($SessionID)
	{
		// sanity check the sessionid
		if (!$SessionID || $SessionID == '' || !ctype_alnum($SessionID))
		{
			return '';
		}

		Zend_Registry::get('log')->info('Read session data for ' . $SessionID);
		
		$row = self::$_sessionStore->fetchRow(self::$_sessionStore->select()->where('SessionID = ?', $SessionID));

		if ($row && isset($row->sessionData))
		{
			if(self::$_cryptFunction != Atmail_Enum::PASSWORD_PLAIN)
			{
				// some sort of password crypt maybe enabled
				if(isset($row->sessionData) && $row->sessionData != '')
				{
					$newSessionData = Atmail_Password::hexaes_decrypt($row->sessionData);
					if($newSessionData != '')
					{
						$row->sessionData = $newSessionData;	
					}
				}
			}
			return $row->sessionData;
		}
		else
		{
			return '';
		}
	}

	public function write($SessionID, $sessionData, $account = null)
	{
		//for some reason session data is bound to an admin record not just by session id
		//at this point if we are fetching Zend_Auth instance then it is set up for admin auth
		if (!Zend_Auth::getInstance()->hasIdentity())
		{
			Zend_Registry::get('log')->debug("No admin user logged in so not writing admin Session data.");
			return;
		}

		if($sessionData != '' && self::$_cryptFunction != Atmail_Enum::PASSWORD_PLAIN)
		{
			// some sort of password crypt maybe enabled
			// make sure we are not storing passwords etc
			$sessionData = Atmail_Password::hexaes_encrypt($sessionData);
		}

		if($account == null)
		{
			$authData = Zend_Auth::getInstance()->getIdentity();
			$account = $authData;
		}
		
		if(!is_null($account) && strlen($account) > 1)
		{
			try
			{
				self::$_sessionStore->update(array('SessionID' => $SessionID, 'sessionData' => $sessionData, 'modified' => time()), self::$_sessionStore->getAdapter()->quoteInto('Username = ?', $account));
			}
			catch(Exception $e)
			{
				Zend_Registry::get('log')->info('Session clash for ' . $account . ' : ' . $SessionID . '.');
				return false;
			}
			Zend_Registry::get('log')->info('Wrote session data for  ' . $account . ' : ' . $SessionID . '.');
		}
		return true;
	}

	public function destroy($SessionID)
	{
		self::$_sessionStore->update(array('SessionID' => null, 'sessionData' => null), self::$_sessionStore->getAdapter()->quoteInto('SessionID = ?', $SessionID));
		$this->gc(ini_get("session.gc_maxlifetime"));
		return true;
	}

	public function gc($maxLifetime)
	{
		self::$_sessionStore->update(array('SessionID' => null, 'sessionData' => null), self::$_sessionStore->getAdapter()->quoteInto('LENGTH(`SessionID`) > 0 AND DATE_ADD(modified, INTERVAL ? SECOND) < ' . time(), intval($maxLifetime)));
		return true;
	}
	
	public function enforceUniqueSID( $username )
	{
		
		$currentSessionId = session_id();
		$retriesLeft = 3;
		while(	
			$retriesLeft-- > 0 
			//&& true // to test that it actually works - uncomment debug lines to see in action
			&& self::$_sessionStore->fetchRow( self::$_sessionStore->select()->where('SessionID = ?', $currentSessionId)->where('Username != ?', $username))  
		)
		{

			Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ':  Duplicate SID found (' . $username . '::' . $currentSessionId . '), regenerating');
			session_regenerate_id();
			$currentSessionId = session_id();
		}

	}
	
	public static function enableEncryptedSessions($sSecretKey)
	{
		if(!isset(self::$_sessionStore))
		{
			$dbTables = new dbTables();
			self::$_sessionStore = new Atmail_Db_Table( array('name' => $dbTables->AdminUsers) );
		}
		$sessions = self::$_sessionStore->fetchAll(self::$_sessionStore->select());
		self::$_cryptFunction = Atmail_Enum::PASSWORD_ENCRYPTED;
		Atmail_Password::setTempSecretKey($sSecretKey);

		foreach($sessions as $session)
		{
			if($session['SessionID'] == '' || $session['sessionData'] == '') continue;
			self::write($session['SessionID'], $session['sessionData'], $session['Username']);
		}
	}
}