<?php
/*
 * DON'T REMOVE THE FOLLOWING LICENSE
 *           INFORMATION!
 * ----------------------------------
 * Copyright by
 * Dennis Ritz
 * Author: Dennis Ritz
 * dennis.ritz@gmx.net
 * 2007-2008
 * ----------------------------------
 */

define('AUTH_SESSION',		'AUTH');
define('AUTH_UNDEFINED',	 0);
define('AUTH_NOT_LOGGED',	-1);
define('AUTH_LOGGED_IN',	 1);
define('AUTH_LOGGED_OUT',	-2);
define('AUTH_EXPIRED',		-3);
define('AUTH_TIMEOUT',		-4);
define('AUTH_FAILED',		-5);
define('AUTH_ERROR',		-6);
define('AUTH_SECURITY',		-7);
define('AUTH_CONFIRM_LOGIN',-8);

class Auth
{	
	private $_status    = AUTH_UNDEFINED;
	private $_options   = array("timeout"	=>		1800,
			                    "expired"	=>		0
			               );
	private $_props     = array(	'username'   => null,
									'password'   => null,
									'session' => null,
									'userAgent' => null,
									'ip'  => null
							);
	
	private $_authSessionsFile;
	public $authFile;

	protected function __construct(){
		if(!file_exists(ROOT_DIR."sessions/".session_id().".xml")){	
			copy(ROOT_DIR."lib/session.xml",ROOT_DIR."sessions/".session_id().".xml");
		}
		$this->_authSessionsFile = session_id().".xml";
		$this->authFile = ROOT_DIR."lib/auth.xml";
	}

	final public function assignUser($p_username,$p_password)
	{
		/* Empty passwords are allowed. Empty usernames not. */

		if ($p_username
				&& $p_username != '') {
			$this->_props['username'] = $p_username;

			if ($p_password) {
				$this->_props['password'] = $p_password;
			}
		}
	}

	final public function getAuth()
	{
		return $this->getAuthStatus() == AUTH_LOGGED_IN;// && $this->_props['username'] != $this->_default['username'];
	}

	final public function getAuthStatus()
	{
		if ($this->_status != AUTH_UNDEFINED)
			return $this->_status;

		return $this->_processStatus();
	}

	private function _processStatus()
	{			
		$this->_updateTimeout();

		$status = $this->_checkLogin();

		return $status;
	}

	public function login($p_username=null,$p_password=null)
	{
		$this->_status = AUTH_UNDEFINED;

		if(!empty($p_username) && !empty($p_password))
			$this->assignUser($p_username,$p_password);
		
		if ($this->_getProp('username') != null) {
			$password = $this->_getPassword();
			if (isset($password)) {
				if (! strcmp($password, md5($this->_getProp('password')))) {
					if($this->_confirmLogin() && $this->getAuthStatus() != AUTH_CONFIRM_LOGIN && !$this->_getProp("global")) {
                    	$status = AUTH_CONFIRM_LOGIN;
					} else {
						$status = AUTH_LOGGED_IN;
					}
				} else {
					$status = AUTH_FAILED;
				}
			} else {
				$status = AUTH_FAILED;
			}
		} else {
			$status = AUTH_NOT_LOGGED;
		}
		if ($status == AUTH_LOGGED_IN) {
			$this->_insertSession();
			$_SESSION[AUTH_SESSION] = $this->_getProp('session');
		} else if ($status == AUTH_FAILED) {
			$this->_logFailure();
			unset($_SESSION[AUTH_SESSION]);
		} else if ($status == AUTH_CONFIRM_LOGIN) {
			$this->_insertConfirmSession();
			$_SESSION[AUTH_SESSION] = $this->_getProp('session');
		}
		return $status;
	}
	
	private function _confirmLogin() {
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);	
		
		//$sessionsForUsername = $authSessionsXpath->query("/sessions/session/@status[./../@username='".$this->_getProp("username")."' and ./../@status='online' and ./../@ip!='".$this->_getProp("ip")."']");;
		$sessionsForUsername = $authSessionsXpath->query("/sessions/session/@status[./../@username='".utf8_encode($this->_getProp("username"))."' and ./../@status='online']");;
		if($sessionsForUsername->length > 0) {	
			return true;
		} else {
			return false;
		}
	}

	public function logout()
	{
		if ($this->getAuth()) {
			$this->_logout();
			$this->_processStatus();
		}
		return AUTH_LOGGED_OUT;
	}

	final public function getUsername(){
		return $this->_getUsername();
	}
	
	final public function isRole($p_role) {
		switch ($p_role) {
			case "admin": if($this->getUsername() == "master" || $this->getUsername() == "admin") return true;
			              break;
			case "master": if($this->getUsername() == "master") return true;
			               break;
			case "user":  return true;
			                 break;
		}
		return false;
	}
	
	final protected function getAllUsernames($p_configFile=""){
		return $this->_getAllUsernames($p_configFile);
	}
	
	public function getSecurityLevel() {
		return $this->_getProp("securityLevel");
	}

	private function _getProp($property) {
		if (isset($this->_props[$property]))
			return $this->_props[$property];
		$ret = null;
		switch ($property) {
			case 'userAgent':
				global $_SERVER;
				$ret = $_SERVER['HTTP_USER_AGENT'];
				$ret = substr(stripslashes($ret), 0, 255);
				break;

			case 'ip':
				global $_SERVER;
				/* Sending HTTP_X_FORWARDED_FOR? OK, send anything you want,
				   but it must persist for the whole session. */
				$ret = array();
				foreach (array('REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR') as $key) {
					if (isset($_SERVER[$key]))
						$ret[] = $_SERVER[$key];
				}
				$ret = join(' / ', $ret);
				break;

			case 'session':
				if (isset($_SESSION[AUTH_SESSION])) {
					$ret = $_SESSION[AUTH_SESSION];
					$ret = intval(stripslashes($ret));
				} else {
					$ret = '';
				}
				break;

			case 'username':
				$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
				$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
				$authSessionsXpath = new DOMXpath($authSessionsDOM);	
		
				$sessions = $authSessionsXpath->query("/sessions/session/@username[./../@id='".$this->_getProp('session')."' and ./../@status='online' and ./../@ip='".$this->_getProp('ip')."' and ./../@userAgent='".utf8_encode($this->_getProp('userAgent'))."']");
		
				if($sessions->length > 0) {		
					$ret = $sessions->item($sessions->length-1)->nodeValue;
				} else {
					$ret = "";
				}				
				break;
			
			case 'global':
				$authDOM = new DOMDocument('1.0', 'iso-8859-1');
				$authDOM->load(dirname(__file__)."/".$this->authFile);
				$authXpath = new DOMXpath($authDOM);	
		
				$global = @$authXpath->query("/auth/users/user/@global[./../@username='".utf8_encode($this->_getProp('username'))."']")->item(0)->nodeValue;
		
				if($global == "true") {		
					$ret = true;
				} else {
					$ret = false;
				}				
				break;				
				
			case 'securityLevel':
				$authDOM = new DOMDocument('1.0', 'iso-8859-1');
				$authDOM->load(dirname(__file__)."/".$this->authFile);
				$authXpath = new DOMXpath($authDOM);	
		
				$securityLevels = $authXpath->query("/auth/users/user/@securityLevel[./../@username='".utf8_encode($this->_getProp('username'))."']");
		
				if($securityLevels->length > 0) {		
					$ret = $securityLevels->item($securityLevels->length-1)->nodeValue;
				} else {
					$ret = null;
				}				
				break;				
		}
		if(!empty($ret)) {
			$this->_props[$property] = $ret;
		}
		return $ret;
	}

	private function _getPassword(){
		$MISConfig = new MISConfig();
		$authDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authDOM->load(dirname(__file__)."/".$this->authFile);
		$authXpath = new DOMXpath($authDOM);
		
		$passwords = $authXpath->query("/auth/users/user/@password[(./../@setting='".basename($MISConfig->getConfigFile(),".xml")."' or ./../@setting='') and ./../@username = '".utf8_encode($this->_getProp('username'))."']");
		
		if($passwords->length > 0) {
			$password = $passwords->item($passwords->length-1)->nodeValue;
		} else {
			$password = "";
		}
		return $password;
	}
	
	private function _deleteOldSessions() {
		$verzeichnis = openDir(ROOT_DIR."sessions/");
		// Verzeichnis lesen
		while ($file = readDir($verzeichnis)) {
		 // Höhere Verzeichnisse nicht anzeigen!
		 if ($file != "." && $file != ".." && (filemtime(ROOT_DIR."sessions/".$this->_authSessionsFile)<(time()-604800))) {
		 // Link erstellen
		  echo "<a href=\"daten/$file\">$file</a><br>\n";
		 }
		}
		 // Verzeichnis schließen
		closeDir($verzeichnis);
		
		
		
        $authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
        $authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
        $authSessionsXpath = new DOMXpath($authSessionsDOM);
        
        $sessions = $authSessionsXpath->query("/sessions/session[@login < '".(time()-604800)."' and @login != '' and @id!='master']");
        if($sessions->length > 0) {
        $sessionParent = $sessions->item(0)->parentNode;
        foreach($sessions as $session) {
        	$sessionParent->removeChild($session);
        }
        }
        $authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);
	}

	private function _insertSession(){
		$this->_deleteOldSessions();
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);

		do {
			$id = mt_rand();
			$sessions = $authSessionsXpath->query("/sessions/session[@id = '".$id."']");
		} while ($sessions->length > 0);
		
		//$sessions = $authSessionsXpath->query("/sessions/session[@status='online' and @username='".$this->_getProp('username')."' and @ip!='".$this->_getProp('ip')."']");
		if(!$this->_getProp("global")) {
		$sessions = $authSessionsXpath->query("/sessions/session[@status='online' and @username='".utf8_encode($this->_getProp('username'))."']");
		if($sessions->length > 0) {
			//Set status = 'security'
			for($i=0;$i<$sessions->length;$i++) {
				$session = $sessions->item($i);
				$session->attributes->getNamedItem("status")->nodeValue = "security";
				$session->attributes->getNamedItem("logout")->nodeValue = time();
			}
		}

		$sessions = $authSessionsXpath->query("/sessions/session[@status='confirm' and @session='".$this->_getProp("session")."' and @username='".utf8_encode($this->_getProp('username'))."' and @ip='".$this->_getProp('ip')."']");
		if($sessions->length > 0) {
			//Set status = 'security'
			for($i=0;$i<$sessions->length;$i++) {
				$session = $sessions->item($i);
				$session->attributes->getNamedItem("status")->nodeValue = "online";
				$session->attributes->getNamedItem("logout")->nodeValue = time();
			}
			return;			
		}
		}

		$master = $authSessionsXpath->query("/sessions/session[@id = 'master']")->item(0);
		$newSession = $master->cloneNode(true);
		$newSession->attributes->getNamedItem("id")->nodeValue = $id;
		$newSession->attributes->getNamedItem("username")->nodeValue = utf8_encode($this->_getProp('username'));
		$newSession->attributes->getNamedItem("password")->nodeValue = "";
		$newSession->attributes->getNamedItem("status")->nodeValue = "online";
		$newSession->attributes->getNamedItem("login")->nodeValue = time();
		$newSession->attributes->getNamedItem("logout")->nodeValue = time();
		$newSession->attributes->getNamedItem("ip")->nodeValue = $this->_getProp("ip");
		$newSession->attributes->getNamedItem("userAgent")->nodeValue = utf8_encode($this->_getProp("userAgent"));
		
		$master->parentNode->appendChild($newSession);
		$authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);
		
		$this->_props['session'] = $id;
	}

	private function _insertConfirmSession(){
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);		
		do {
			$id = mt_rand();
			$sessions = $authSessionsXpath->query("/sessions/session[@id = '".$id."']");
		} while ($sessions->length > 0);

		$master = $authSessionsXpath->query("/sessions/session[@id = 'master']")->item(0);
		$newSession = $master->cloneNode(true);
		$newSession->attributes->getNamedItem("id")->nodeValue = $id;
		$newSession->attributes->getNamedItem("username")->nodeValue = utf8_encode($this->_getProp('username'));
		$newSession->attributes->getNamedItem("password")->nodeValue = "";
		$newSession->attributes->getNamedItem("status")->nodeValue = "confirm";
		$newSession->attributes->getNamedItem("login")->nodeValue = time();
		$newSession->attributes->getNamedItem("logout")->nodeValue = time();
		$newSession->attributes->getNamedItem("ip")->nodeValue = $this->_getProp("ip");
		$newSession->attributes->getNamedItem("userAgent")->nodeValue = utf8_encode($this->_getProp("userAgent"));
		
		$master->parentNode->appendChild($newSession);
		$authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);
		
		$this->_props['session'] = $id;
	}

	private function _logFailure() {
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);

		$master = $authSessionsXpath->query("/sessions/session[@id = 'master']")->item(0);
		$newSession = $master->cloneNode(true);
		$newSession->attributes->getNamedItem("id")->nodeValue = $this->_getProp('session');
		$newSession->attributes->getNamedItem("username")->nodeValue = utf8_encode($this->_getProp('username'));
		$newSession->attributes->getNamedItem("password")->nodeValue = "";
		$newSession->attributes->getNamedItem("status")->nodeValue = "failed";
		$newSession->attributes->getNamedItem("login")->nodeValue = time();
		$newSession->attributes->getNamedItem("logout")->nodeValue = time();
		$newSession->attributes->getNamedItem("ip")->nodeValue = $this->_getProp("ip");
		$newSession->attributes->getNamedItem("userAgent")->nodeValue = utf8_encode($this->_getProp("userAgent"));
		
		$master->parentNode->appendChild($newSession);
		$authSessionsDOM->save(dirname(__file__)."/".$this->_authSessionsFile);		
	}

	private function _updateTimeout() {		
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);
		
		if ($this->_options["timeout"] > 0) {
			$timeoutTime = time()-$this->_options['timeout'];
			$timeoutSessions = $authSessionsXpath->query("/sessions/session[@status = 'online' and @logout < ".$timeoutTime."]");
			for($i=0;$i < $timeoutSessions->length;$i++) {
				$timeoutSession = $timeoutSessions->item($i);
				$timeoutSession->attributes->getNamedItem("status")->nodeValue = "timeout";
			}
		}
		if ($this->_options["expired"] > 0) {
			$expiredTime = time()-$this->_options['expired'];
			$expiredSessions = $authSessionsXpath->query("/sessions/session[@status = 'online' and @login < ".$timeoutTime."]");
			for($i=0;$i < $expiredSessions->length;$i++) {
				$expiredSession = $expiredSessions->item($i);
				$expiredSession->attributes->getNamedItem("status")->nodeValue = "expired";
			}						
		}
		$authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);
	}

	// This method needs ISDN fastfix
	private function _logout() {
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);
		
		$sessions = $authSessionsXpath->query("/sessions/session[@status='online' and @id='".$this->_getProp("session")."' and @ip='".$this->_getProp("ip")."' and @userAgent='".utf8_encode($this->_getProp("userAgent"))."']");
		if($sessions->length > 0) {
			$session = $sessions->item(($sessions->length-1));
			$session->attributes->getNamedItem("status")->nodeValue = "logout";
			$session->attributes->getNamedItem("logout")->nodeValue = time();
			$authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);
		}
	}

	// This method needs ISDN fastfix
	private function _updateLogin(){
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);		
		
		$sessions = $authSessionsXpath->query("/sessions/session[@status = 'online' and @id = '".$this->_getProp('session')."' and @ip = '".$this->_getProp('ip')."' and @userAgent = '".utf8_encode($this->_getProp('userAgent'))."']");
		if($sessions->length > 0) {
			$session = $sessions->item(($sessions->length-1));
			$session->attributes->getNamedItem("logout")->nodeValue = time();
			$authSessionsDOM->save(ROOT_DIR."sessions/".$this->_authSessionsFile);			
			return true;
		} else {
			return false;
		}
	}

	// This method needs ISDN fastfix
	private function _checkLogin(){
		if ($this->_updateLogin() == true)
			return AUTH_LOGGED_IN;
			
		$authSessionsDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authSessionsDOM->load(ROOT_DIR."sessions/".$this->_authSessionsFile);
		$authSessionsXpath = new DOMXpath($authSessionsDOM);	

		$sessions = $authSessionsXpath->query("/sessions/session/@status[./../@id='".$this->_getProp('session')."' and ./../@id!='' and ./../@ip='".$this->_getProp('ip')."' and ./../@userAgent='".utf8_encode($this->_getProp('userAgent'))."']");
		if($sessions->length > 0) {		
			$status = $sessions->item($sessions->length-1)->nodeValue;
		} else {
			$status = null;
		}

		if ($status == 'logout') return AUTH_LOGGED_OUT;
		if ($status == 'failed') return AUTH_FAILED;
		if ($status == 'timeout') return AUTH_TIMEOUT;
		if ($status == 'expired') return AUTH_EXPIRED;
		if ($status == 'security') return AUTH_SECURITY;
		if ($status == 'confirm') return AUTH_CONFIRM_LOGIN;
		if ($status == 'online') return AUTH_LOGGED_IN;
		return AUTH_NOT_LOGGED;
	}
	
	private function _getUsername() {
		return $this->_getProp("username");
	}
	
	private function _getAllUsernames($p_configFile) {
		$MISConfig = new MISConfig();
		$authDOM = new DOMDocument('1.0', 'iso-8859-1');
		$authDOM->load(dirname(__file__)."/".$this->authFile);
		$authXpath = new DOMXpath($authDOM);	
		if($p_configFile == "") $configFile = $MISConfig->getConfigFile();
		else $configFile = $p_configFile;
		$usersArr = array();
		$usersList = $authXpath->query("/auth/users/user[@setting='".basename($configFile,".xml")."' or @setting='']");
		for($i=0;$i<$usersList->length;$i++) {
			if($usersList->item($i)->getAttribute('username') == "") continue;
			array_push($usersArr,$usersList->item($i)->getAttribute('username'));
		}
		return $usersArr;
	}
	
	public function notice($p_msg) {
		Log::notice(__FILE__,__LINE__,$p_msg);
	}
	
	public function warning($p_msg) {
		Log::warning(__FILE__,__LINE__,$p_mgs);
	}
	
	public function error($p_msg) {
		Log::error(__FILE__,__LINE__,$p_msg);
	}
}

?>