<?php //$Id$
//Copyright (c) 2012-2016 Pierre Pronchery <khorben@defora.org>
//This file is part of DeforaOS Web DaPortal
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, version 3 of the License.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.



//SessionAuth
class SessionAuth extends Auth
{
	//protected
	//methods
	//SessionAuth::match
	protected function match(Engine $engine)
	{
		//return the result cached if called twice
		if($this->match_score !== FALSE)
			return $this->match_score;
		if(!function_exists('session_get_cookie_params')
				|| !isset($_SERVER['SCRIPT_NAME'])
				|| !isset($_SERVER['SERVER_PROTOCOL']))
		{
			$this->match_score = 0;
			return 0;
		}
		@ini_set('session.use_only_cookies', 1);
		@ini_set('session.use_trans_sid', 0);
		$params = session_get_cookie_params();
		//XXX probably not always as strict as it could be
		$params['path'] = dirname($_SERVER['SCRIPT_NAME']);
		if(isset($_SERVER['HTTP_HOST']))
		{
			$domain = $_SERVER['HTTP_HOST'];
			if(($pos = strpos($domain, ':', 1)) !== FALSE)
				$domain = substr($domain, 0, $pos);
			$params['domain'] = $domain;
		}
		if(isset($_SERVER['HTTPS']))
			$params['secure'] = 1;
	       	//XXX we may have to set it to 0 later
		$params['httponly'] = 1;
		session_set_cookie_params($params['lifetime'], $params['path'],
				$params['domain'], $params['secure'],
				$params['httponly']);
		$this->match_score = @session_start() ? 100 : 0;
		session_write_close();
		return $this->match_score;
	}


	//SessionAuth::attach
	protected function attach(Engine $engine)
	{
		//attaching depends on the code in match()
		$this->match($engine);
	}


	//public
	//accessors
	//SessionAuth::getCredentials
	public function getCredentials(Engine $engine)
	{
		$uid = $this->getVariable($engine, 'SessionAuth::uid');
		$username = $this->getVariable($engine,
				'SessionAuth::username');

		if($uid === FALSE || $uid == 0 || $username === FALSE)
			return parent::getCredentials($engine);
		$user = User::lookup($engine, $username, $uid);
		if($user === FALSE || $user->isLocked())
			return parent::getCredentials($engine);
		$cred = new AuthCredentials($user->getUserID(),
				$user->getUsername(), $user->getGroupID(),
				$user->getGroupname(), $user->isAdmin());
		parent::setCredentials($engine, $cred);
		return parent::getCredentials($engine);
	}


	//SessionAuth::getVariable
	public function getVariable(Engine $engine, $variable)
	{
		if(isset($_SESSION[$variable]))
			return $_SESSION[$variable];
		return FALSE;
	}


	//SessionAuth::setCredentials
	public function setCredentials(Engine $engine,
			AuthCredentials $credentials = NULL)
	{
		global $config;

		if(is_null($credentials))
			$credentials = new AuthCredentials();
		//avoid session-fixation attacks
		session_start();
		$message = 'Could not regenerate the session';
		if($config->get('auth::session', 'regenerate') == 1
				&& session_regenerate_id(TRUE) !== TRUE)
			$engine->log(LOG_WARNING, $message);
		$this->_setVariable($engine, 'SessionAuth::uid',
				$credentials->getUserID());
		$this->_setVariable($engine, 'SessionAuth::username',
				$credentials->getUsername());
		session_write_close();
		return parent::setCredentials($engine, $credentials);
	}


	//SessionAuth::setIdempotent
	public function setIdempotent(Engine $engine, Request &$request,
			$idempotent)
	{
		if($idempotent === TRUE)
		{
			$request->setIdempotent(TRUE);
			return;
		}
		//prevent CSRF attacks
		$idempotent = TRUE;
		if(($token = $request->get('_token')) === FALSE)
			return TRUE;
		//remove token from the request
		$parameters = $request->getParameters();
		unset($parameters['_token']);
		$request = new Request($request->getModule(),
			$request->getAction(), $request->getID(),
			$request->getTitle(), $parameters);
		//check for the availability of tokens
		if(!isset($_SESSION['tokens'])
				|| !is_array($_SESSION['tokens']))
			return TRUE;
		//delete old tokens
		foreach($_SESSION['tokens'] as $k => $v)
			if($v < time())
				unset($_SESSION['tokens'][$k]);
		if(isset($_SESSION['tokens'][$token]))
		{
			//the request is not idempotent
			unset($_SESSION['tokens'][$token]);
			$idempotent = FALSE;
		}
		$request->setIdempotent($idempotent);
	}


	//SessionAuth::setVariable
	public function setVariable(Engine $engine, $variable, $value)
	{
		//XXX errors when output has already started can be ignored
		@session_start();
		$ret = $this->_setVariable($engine, $variable, $value);
		session_write_close();
		return $ret;
	}
	private function _setVariable(Engine $engine, $variable, $value)
	{
		$_SESSION[$variable] = $value;
		return TRUE;
	}


	//private
	//properties
	private $match_score = FALSE;
}

?>
