<?php
/*
 * Copyright 2007-2011 Charles du Jeu <contact (at) cdujeu.me>
 * This file is part of AjaXplorer.
 *
 * AjaXplorer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AjaXplorer 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with AjaXplorer.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://www.ajaxplorer.info/>.
 */
defined('AJXP_EXEC') or die( 'Access not allowed');

/**
 * Configuration holder. Singleton class accessed statically, encapsulates the confDriver implementation.
 * @package AjaXplorer
 * @subpackage Core
 */
class ConfService
{	
	private static $instance;
	private $errors = array();
	private $configs = array();
 	
	/**
	 * @param AJXP_PluginsService $ajxpPluginService
	 * @return AbstractConfDriver 
	 */
	public function confPluginSoftLoad($ajxpPluginService){

        $booter = $ajxpPluginService->softLoad("boot.conf", array());
        $coreConfigs = $booter->loadPluginConfig("core", "conf");
        $corePlug = $ajxpPluginService->softLoad("core.conf", array());
        $corePlug->loadConfigs($coreConfigs);
        return $corePlug->getConfImpl();

	}

    /**
     * @return AbstractConfDriver
     */
    public static function getBootConfStorageImpl(){
        $inst = AJXP_PluginsService::getInstance()->findPluginById("boot.conf");
        if(empty($inst)){
            $inst = AJXP_PluginsService::getInstance()->softLoad("boot.conf", array());
        }
        return $inst;
    }

    /**
     * Initialize singleton
     * @static
     * @return void
     */
	public static function init(){
		$inst = self::getInstance();
		$inst->initInst();
	}
	/**
     * Load the boostrap_* files and their configs
     * @return void
     */
	public function initInst()
	{
		// INIT AS GLOBAL
		$this->configs["AVAILABLE_LANG"] = self::listAvailableLanguages();
		if(isSet($_SERVER["HTTPS"]) && strtolower($_SERVER["HTTPS"]) == "on"){
			$this->configs["USE_HTTPS"] = true;
		}
		if(isSet($this->configs["USE_HTTPS"])){
			AJXP_Utils::safeIniSet("session.cookie_secure", true);
		}
		$this->configs["JS_DEBUG"] = AJXP_CLIENT_DEBUG;
		$this->configs["SERVER_DEBUG"] = AJXP_SERVER_DEBUG;


        if(is_file(AJXP_CONF_PATH."/bootstrap_repositories.php")){
            include(AJXP_CONF_PATH."/bootstrap_repositories.php");
            $this->configs["DEFAULT_REPOSITORIES"] = $REPOSITORIES;
        }else{
            $this->configs["DEFAULT_REPOSITORIES"] = array();
        }
	}
	/**
     * Start the singleton
     * @static
     * @return void
     */
	public static function start(){
		$inst = self::getInstance();
		$inst->startInst();
	}
	/**
     * Init CONF, AUTH drivers
     * Init Repositories
     * @return void
     */
	public function startInst(){
        AJXP_PluginsService::getInstance()->setPluginUniqueActiveForType("conf", self::getConfStorageImpl()->getName());
	}
	/**
     * Get errors generated by the boot sequence (init/start)
     * @static
     * @return array
     */
	public static function getErrors(){
		return self::getInstance()->errors;
	}

    /**
     * @static
     * @param $globalsArray
     * @param string $interfaceCheck
     * @internal param string $bootstrapConfigKey
     * @internal param string $bootstrapConfigType
     * @return AJXP_Plugin|null
     */
    public static function instanciatePluginFromGlobalParams($globalsArray, $interfaceCheck = ""){

        $plugin = false;

        if(is_string($globalsArray)){
            $globalsArray = array("instance_name" => $globalsArray);
        }

        if(isSet($globalsArray["instance_name"])){
            $pName = $globalsArray["instance_name"];
            unset($globalsArray["instance_name"]);
            $plugin = AJXP_PluginsService::getInstance()->softLoad($pName, $globalsArray);
            $plugin->performChecks();
        }

        if($plugin != false && !empty($interfaceCheck)){
            if(!is_a($plugin, $interfaceCheck)){
                $plugin = false;
            }
        }
        if($plugin !== false){
            AJXP_PluginsService::getInstance()->setPluginActive($plugin->getType(), $plugin->getName(), true, $plugin);
        }
        return $plugin;

    }
	/**
     * Check if the STDIN constant is defined
     * @static
     * @return bool
     */
	public static function currentContextIsCommandLine(){
		return php_sapi_name() === "cli";
	}
	/**
     * Check the presence of mcrypt and option CMDLINE_ACTIVE
     * @static
     * @return bool
     */
	public static function backgroundActionsSupported(){
		return function_exists("mcrypt_create_iv") && ConfService::getCoreConf("CMDLINE_ACTIVE");
	}

    /**
     * @var AbstractConfDriver
     */
    private static $tmpConfStorageImpl;
    /**
     * @var AbstractAuthDriver
     */
    private static $tmpAuthStorageImpl;

    /**
     * @param $confStorage AbstractConfDriver
     * @param $authStorage AbstractAuthDriver
     */
    public static function setTmpStorageImplementations($confStorage, $authStorage){
        self::$tmpConfStorageImpl = $confStorage;
        self::$tmpAuthStorageImpl = $authStorage;
    }

	/**
	 * Get conf driver implementation
	 *
	 * @return AbstractConfDriver
	 */
	public static function getConfStorageImpl(){
        if(isSet(self::$tmpConfStorageImpl)) return self::$tmpConfStorageImpl;
		return AJXP_PluginsService::getInstance()->getPluginById("core.conf")->getConfImpl();
	}

	/**
	 * Get auth driver implementation
	 *
	 * @return AbstractAuthDriver
	 */
	public static function getAuthDriverImpl(){
        if(isSet(self::$tmpAuthStorageImpl)) return self::$tmpAuthStorageImpl;
        return AJXP_PluginsService::getInstance()->getPluginById("core.auth")->getAuthImpl();
	}

	public static function switchUserToActiveRepository($loggedUser, $parameterId = -1){
        if(isSet($_SESSION["PENDING_REPOSITORY_ID"]) && isSet($_SESSION["PENDING_FOLDER"])){
            $loggedUser->setArrayPref("history", "last_repository", $_SESSION["PENDING_REPOSITORY_ID"]);
            $loggedUser->setPref("pending_folder", $_SESSION["PENDING_FOLDER"]);
            $loggedUser->save("user");
            AuthService::updateUser($loggedUser);
            unset($_SESSION["PENDING_REPOSITORY_ID"]);
            unset($_SESSION["PENDING_FOLDER"]);
        }
        $currentRepoId = ConfService::getCurrentRepositoryId();
        $lastRepoId  = $loggedUser->getArrayPref("history", "last_repository");
        $defaultRepoId = AuthService::getDefaultRootId();
        if($defaultRepoId == -1){
            return false;
        }else {
            if($lastRepoId !== "" && $lastRepoId!==$currentRepoId && $parameterId == -1 && $loggedUser->canSwitchTo($lastRepoId)){
                ConfService::switchRootDir($lastRepoId);
            }else if($parameterId != -1 && $loggedUser->canSwitchTo($parameterId)){
                ConfService::switchRootDir($parameterId);
            }else if(!$loggedUser->canSwitchTo($currentRepoId)){
                ConfService::switchRootDir($defaultRepoId);
            }
        }
        return true;
    }


    /**
     * See instance method
     * @static
     * @param $rootDirIndex
     * @param bool $temporary
     * @return void
     */
	public static function switchRootDir($rootDirIndex = -1, $temporary = false){
		self::getInstance()->switchRootDirInst($rootDirIndex, $temporary);
	}
	/**
     * Switch the current repository
     * @param $rootDirIndex
     * @param bool $temporary
     * @return void
     */
	public function switchRootDirInst($rootDirIndex=-1, $temporary=false)
	{
        $currentRepos = $this->getLoadedRepositories();
		if($rootDirIndex == -1){
			if(isSet($_SESSION['REPO_ID']) && array_key_exists($_SESSION['REPO_ID'], $currentRepos))
			{			
				$this->configs["REPOSITORY"] = $currentRepos[$_SESSION['REPO_ID']];
			}
			else 
			{
				$keys = array_keys($currentRepos);
				$this->configs["REPOSITORY"] = $currentRepos[$keys[0]];
				$_SESSION['REPO_ID'] = $keys[0];
			}
		}
		else 
		{
			if($temporary && isSet($_SESSION['REPO_ID'])){
				$crtId = $_SESSION['REPO_ID'];
                if($crtId != $rootDirIndex && !isSet($_SESSION['SWITCH_BACK_REPO_ID'])){
                    $_SESSION['SWITCH_BACK_REPO_ID'] = $crtId;
                    //AJXP_Logger::debug("switching to $rootDirIndex, registering $crtId");
                }
			}else{
                $crtId = $_SESSION['REPO_ID'];
                $_SESSION['PREVIOUS_REPO_ID'] = $crtId;
				//AJXP_Logger::debug("switching back to $rootDirIndex");
			}
			$this->configs["REPOSITORY"] = $currentRepos[$rootDirIndex];
			$_SESSION['REPO_ID'] = $rootDirIndex;
			if(isSet($this->configs["ACCESS_DRIVER"])) unset($this->configs["ACCESS_DRIVER"]);
		}
		
		if(isSet($this->configs["REPOSITORY"]) && $this->configs["REPOSITORY"]->getOption("CHARSET")!=""){
			$_SESSION["AJXP_CHARSET"] = $this->configs["REPOSITORY"]->getOption("CHARSET");
		}else{
			if(isSet($_SESSION["AJXP_CHARSET"])){
				unset($_SESSION["AJXP_CHARSET"]);
			}
		}
		
		
		if($rootDirIndex!=-1 && AuthService::usersEnabled() && AuthService::getLoggedUser()!=null){
			$loggedUser = AuthService::getLoggedUser();
			$loggedUser->setArrayPref("history", "last_repository", $rootDirIndex);
			$loggedUser->save("user");
		}	
				
	}

    /**
     * See instance method
     * @static
     * @param String $scope "user" or "all"
     * @return Repository[]
     */
	public static function getRepositoriesList($scope = "user"){
        if($scope == "user"){
            return self::getInstance()->getLoadedRepositories();
        }else{
            return self::getInstance()->initRepositoriesListInst("all");
        }
	}

    /**
     * @return Repository[]
     */
    private function getLoadedRepositories(){
        if(isSet($this->configs["REPOSITORIES"])){
            return $this->configs["REPOSITORIES"];
        }
        $this->configs["REPOSITORIES"] = $this->initRepositoriesListInst();
        return $this->configs["REPOSITORIES"];
    }

    private function invalidateLoadedRepositories(){
        $this->configs["REPOSITORIES"] = null;
    }

    /**
     * @static
     * @param AbstractAjxpUser $userObject
     * @param bool $details
     * @param bool $labelOnly
     * @param bool $skipShared
     * @return Repository[]
     */
    public static function getAccessibleRepositories($userObject=null, $details=false, $labelOnly = false, $skipShared = false){
        $result = array();
        foreach (ConfService::getRepositoriesList("all") as $repositoryId => $repositoryObject)
        {
            if(!ConfService::repositoryIsAccessible($repositoryId, $repositoryObject, $userObject, $details, $skipShared)){
                continue;
            }

            if($labelOnly){
                $result[$repositoryId] = $repositoryObject->getDisplay();
            }else{
                $result[$repositoryId] = $repositoryObject;
            }

        }
        return $result;
    }

    /**
     * @param String $repositoryId
     * @param Repository $repositoryObject
     * @param AbstractAjxpUser $userObject
     * @param bool $details
     * @param bool $skipShared
     *
     * @return bool
     */
    public static function repositoryIsAccessible($repositoryId, $repositoryObject, $userObject = null, $details=false, $skipShared=false){
        if($userObject == null) $userObject = AuthService::getLoggedUser();
        if($userObject == null && AuthService::usersEnabled()){
            return false;
        }
        if(!AuthService::canAssign($repositoryObject, $userObject)) {
            return false;
        }
        if($repositoryObject->isTemplate) {
            return false;
        }
        if($repositoryObject->getAccessType()=="ajxp_conf" && $userObject != null){
            if(AuthService::usersEnabled() && !$userObject->isAdmin()){
                return false;
            }
        }
        if($repositoryObject->getAccessType() == "ajxp_shared" && !AuthService::usersEnabled()){
            return false;
        }
        if($repositoryObject->getUniqueUser() && (!AuthService::usersEnabled() || $userObject == null  || $userObject->getId() == "shared" || $userObject->getId() != $repositoryObject->getUniqueUser() )){
            return false;
        }
        if( $userObject != null && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId)) && !$details){
            return false;
        }
        if($userObject == null || $userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) || $details) {
            // Do not display standard repositories even in details mode for "sub"users
            if($userObject != null && $userObject->hasParent() && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) )) {
                return false;
            }
            // Do not display shared repositories otherwise.
            if($repositoryObject->hasOwner() && $skipShared){
                return false;
            }
            if($userObject != null && $repositoryObject->hasOwner() && !$userObject->hasParent()){
                // Display the repositories if allow_crossusers is ok
                if(ConfService::getCoreConf("ALLOW_CROSSUSERS_SHARING", "conf") === false 
                || ConfService::getCoreConf("ALLOW_CROSSUSERS_SHARING", "conf") === 0) {
                    return false;
                }
                // But still do not display its own shared repositories!
                if($repositoryObject->getOwner() == $userObject->getId()) {
                    return false;
                }
            }
            if($repositoryObject->hasOwner() && $userObject != null &&  $details && !($userObject->canRead($repositoryId) || $userObject->canWrite($repositoryId) ) ){
                return false;
            }
        }
        return true;
    }

    /**
     * Return the full list of repositories, as id => objects
     * @return array
     */
	public function getRepositoriesListInst()
	{
		return $this->getLoadedRepositories();
	}
	
	/**
     * See instance method
     * @static
     * @return string
     */
	public static function getCurrentRepositoryId(){
		return self::getInstance()->getCurrentRepositoryIdInst();
	}
    /**
     * Get the current repository ID;
     * @return string
     */
	public function getCurrentRepositoryIdInst()
	{
        $currentRepos = $this->getLoadedRepositories();
		if(isSet($_SESSION['REPO_ID']) &&  isSet($currentRepos[$_SESSION['REPO_ID']])){
			return $_SESSION['REPO_ID'];
		}
		return array_shift(array_keys($currentRepos));
	}
	/**
     * Get the current repo label
     * @static
     * @return string
     */
	public static function getCurrentRootDirDisplay(){
		return self::getInstance()->getCurrentRootDirDisplayInst();
	}
    /**
     * @return string
     */
	public function getCurrentRootDirDisplayInst()
	{
        $currentRepos = $this->getLoadedRepositories();
		if(isSet($currentRepos[$_SESSION['REPO_ID']])){
			$repo = $currentRepos[$_SESSION['REPO_ID']];
			return $repo->getDisplay();
		}
		return "";
	}

    /**
     * @param $scope String "user", "all"
     * @return array
     */
	protected function initRepositoriesListInst($scope = "user")
	{
		// APPEND CONF FILE REPOSITORIES

		$objList = array();
		foreach($this->configs["DEFAULT_REPOSITORIES"] as $index=>$repository)
		{
			$repo = self::createRepositoryFromArray($index, $repository);
			$repo->setWriteable(false);
			$objList[$repo->getId()] = $repo;
		}
		// LOAD FROM DRIVER
		$confDriver = self::getConfStorageImpl();
		$drvList = $confDriver->listRepositories($scope == "user" ? AuthService::getLoggedUser() : null);
		if(is_array($drvList)){
			foreach ($drvList as $repoId=>$repoObject){
                $driver = AJXP_PluginsService::getInstance()->getPluginByTypeName("access", $repoObject->getAccessType());
                if(!is_object($driver) || !$driver->isEnabled()){
                    unset($drvList[$repoId]);
                }else{
                    $repoObject->setId($repoId);
                	$drvList[$repoId] = $repoObject;
                }
			}
			$objList = array_merge($objList, $drvList);
		}
		return $objList;
	}
	/**
     * See instance method
     * @static
     * @param bool $register
     * @return array
     */
	public static function detectRepositoryStreams($register = false){
		return self::getInstance()->detectRepositoryStreamsInst($register);
	}
    /**
     * Call the detectStreamWrapper method
     * @param bool $register
     * @return array
     */
	public function detectRepositoryStreamsInst($register = false){
		$streams = array();
        $currentRepos = $this->getLoadedRepositories();
		foreach ($currentRepos as $repository) {
			$repository->detectStreamWrapper($register, $streams);
		}
		return $streams;
	}
	
	/**
	 * Create a repository object from a config options array
	 *
	 * @param integer $index
	 * @param Array $repository
	 * @return Repository
	 */
	public static function createRepositoryFromArray($index, $repository){
		return self::getInstance()->createRepositoryFromArrayInst($index, $repository);
	}
    /**
     * See static method
     * @param string $index
     * @param array $repository
     * @return Repository
     */
	public function createRepositoryFromArrayInst($index, $repository){
		$repo = new Repository($index, $repository["DISPLAY"], $repository["DRIVER"]);
		if(isSet($repository["DISPLAY_ID"])){
			$repo->setDisplayStringId($repository["DISPLAY_ID"]);
		}
		if(isSet($repository["DESCRIPTION_ID"])){
			$repo->setDescription($repository["DESCRIPTION_ID"]);
		}
		if(isSet($repository["AJXP_SLUG"])){
			$repo->setSlug($repository["AJXP_SLUG"]);
		}
        if(isSet($repository["IS_TEMPLATE"]) && $repository["IS_TEMPLATE"]){
            $repo->isTemplate = true;
            $repo->uuid = $index;
        }
		if(array_key_exists("DRIVER_OPTIONS", $repository) && is_array($repository["DRIVER_OPTIONS"])){
			foreach ($repository["DRIVER_OPTIONS"] as $oName=>$oValue){
				$repo->addOption($oName, $oValue);
			}
		}
		// BACKWARD COMPATIBILITY!
		if(array_key_exists("PATH", $repository)){
			$repo->addOption("PATH", $repository["PATH"]);
			$repo->addOption("CREATE", $repository["CREATE"]);
			$repo->addOption("RECYCLE_BIN", $repository["RECYCLE_BIN"]);
		}
		return $repo;
	}
	
	/**
	 * Add dynamically created repository
	 *
	 * @param Repository $oRepository
	 * @return void|-1 if error
	 */
	public static function addRepository($oRepository){
		return self::getInstance()->addRepositoryInst($oRepository);
	}
    /**
     * @param $oRepository
     * @return void|-1 on error
     */
	public function addRepositoryInst($oRepository){
		$confStorage = self::getConfStorageImpl();
		$res = $confStorage->saveRepository($oRepository);		
		if($res == -1){
			return $res;
		}
		AJXP_Logger::logAction("Create Repository", array("repo_name"=>$oRepository->getDisplay()));
        $this->invalidateLoadedRepositories();
	}

    public static function findRepositoryByIdOrAlias($idOrAlias){
        $repository = ConfService::getRepositoryById($idOrAlias);
        if($repository != null ) return $repository;
        $repository = ConfService::getRepositoryByAlias($idOrAlias);
        if($repository != null) return $repository;
        return null;
    }

	/**
	 * Retrieve a repository object
	 *
	 * @param String $repoId
	 * @return Repository
	 */
	public static function getRepositoryById($repoId){
		return self::getInstance()->getRepositoryByIdInst($repoId);
	}
    /**
     * See static method
     * @param $repoId
     * @return Repository|null
     */
	public function getRepositoryByIdInst($repoId){
        $currentRepos = $this->getLoadedRepositories();
		if(isSet($currentRepos[$repoId])){
			return $currentRepos[$repoId];
		}
        return $this->getConfStorageImpl()->getRepositoryById($repoId);
	}
	
	/**
	 * Retrieve a repository object by its slug
	 *
	 * @param String $repoAlias
	 * @return Repository
	 */
	public static function getRepositoryByAlias($repoAlias){
		$repo = self::getConfStorageImpl()->getRepositoryByAlias($repoAlias);
		if($repo !== null) return $repo;
		// check default repositories
		return self::getInstance()->getRepositoryByAliasInstDefaults($repoAlias);
	}
	/**
     * See static method
     * @param $repoAlias
     * @return Repository|null
     */
	public function getRepositoryByAliasInstDefaults($repoAlias){
		$conf = $this->configs["DEFAULT_REPOSITORIES"];
		foreach($conf as $repoId => $repoDef){
			if($repoDef["AJXP_SLUG"] == $repoAlias){
				return $this->getRepositoryByIdInst($repoId);
			}
		}
		return null;
	}
	
	
	/**
	 * Replace a repository by an update one.
	 *
	 * @param String $oldId
	 * @param Repository $oRepositoryObject
	 * @return mixed
	 */
	public static function replaceRepository($oldId, $oRepositoryObject){
		return self::getInstance()->replaceRepositoryInst($oldId, $oRepositoryObject);
	}
    /**
     * See static method
     * @param $oldId
     * @param $oRepositoryObject
     * @return void
     */
	public function replaceRepositoryInst($oldId, $oRepositoryObject){
		$confStorage = self::getConfStorageImpl();
		$res = $confStorage->saveRepository($oRepositoryObject, true);
		if($res == -1){
			return $res;
		}
		AJXP_Logger::logAction("Edit Repository", array("repo_name"=>$oRepositoryObject->getDisplay()));		
		$this->invalidateLoadedRepositories();
	}
    /**
     * Set a temp repository id but not in the session
     * @static
     * @param $repositoryObject
     * @return void
     */
    public static function tmpReplaceRepository($repositoryObject){
        $inst = self::getInstance();
        if(isSet($inst->configs["REPOSITORIES"][$repositoryObject->getUniqueId()])){
            $inst->configs["REPOSITORIES"][$repositoryObject->getUniqueId()] = $repositoryObject;
        }
    }
	/**
     * Remove a repository using the conf driver implementation
     * @static
     * @param $repoId
     * @return void
     */
	public static function deleteRepository($repoId){
		return self::getInstance()->deleteRepositoryInst($repoId);
	}
    /**
     * See static method
     * @param $repoId
     * @return void
     */
	public function deleteRepositoryInst($repoId){
		$confStorage = self::getConfStorageImpl();
		$res = $confStorage->deleteRepository($repoId);
		if($res == -1){
			return $res;
		}				
		AJXP_Logger::logAction("Delete Repository", array("repo_id"=>$repoId));
		$this->invalidateLoadedRepositories();
	}

    /**
     * Check if the gzopen function exists
     * @static
     * @return bool
     */
	public static function zipEnabled()
	{
        if(ConfService::getCoreConf("DISABLE_ZIP_BROWSING") === true) return false;
		return (function_exists("gzopen")?true:false);
	}
    /**
     * Get the list of all "conf" messages
     * @static
     * @param bool $forceRefresh Refresh the list
     * @return
     */
    public static function getMessagesConf($forceRefresh = false){
        return self::getInstance()->getMessagesInstConf($forceRefresh);
    }
    /**
     * See static method
     * @param bool $forceRefresh
     * @return
     */
    public function getMessagesInstConf($forceRefresh = false)
    {
        // make sure they are loaded
        $mess = $this->getMessagesInst($forceRefresh);
        return $this->configs["CONF_MESSAGES"];
    }

    /**
     * Get all i18n message
     * @static
     * @param bool $forceRefresh
     * @return
     */
	public static function getMessages($forceRefresh = false){
		return self::getInstance()->getMessagesInst($forceRefresh);
	}
    /**
     * Get i18n messages
     * @param bool $forceRefresh
     * @return
     */
	public function getMessagesInst($forceRefresh = false)
	{
        $crtLang = self::getLanguage();
        $messageCacheDir = dirname(AJXP_PLUGINS_MESSAGES_FILE)."/i18n";
        $messageFile = $messageCacheDir."/".$crtLang."_".basename(AJXP_PLUGINS_MESSAGES_FILE);
        if(isSet($this->configs["MESSAGES"]) && !$forceRefresh){
            return $this->configs["MESSAGES"];
        }
        if(!isset($this->configs["MESSAGES"]) && is_file($messageFile)){
            include($messageFile);
            if(isSet($MESSAGES)){
                $this->configs["MESSAGES"] = $MESSAGES;
            }
            if(isSet($CONF_MESSAGES)){
                $this->configs["CONF_MESSAGES"] = $CONF_MESSAGES;
            }
        }else {
			$this->configs["MESSAGES"] = array();
			$this->configs["CONF_MESSAGES"] = array();
			$nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//i18n", "nodes");
			foreach ($nodes as $node){
				$nameSpace = $node->getAttribute("namespace");
				$path = $node->getAttribute("path");
				$lang = $crtLang;
				if(!is_file($path."/".$crtLang.".php")){
					$lang = "en"; // Default language, minimum required.
				}
				if(is_file($path."/".$lang.".php")){
					require($path."/".$lang.".php");
                    if(isSet($mess)){
                        foreach ($mess as $key => $message){
                            $this->configs["MESSAGES"][(empty($nameSpace)?"":$nameSpace.".").$key] = $message;
                        }
                    }
				}
                $lang = $crtLang;
                if(!is_file($path."/conf/".$crtLang.".php")){
                    $lang = "en";
                }
                if(is_file($path."/conf/".$lang.".php")){
                    require($path."/conf/".$lang.".php");
                    $this->configs["CONF_MESSAGES"] = array_merge($this->configs["CONF_MESSAGES"], $mess);
                }
			}
            if(!is_dir($messageCacheDir)) mkdir($messageCacheDir);
            @file_put_contents($messageFile, "<?php \$MESSAGES = ".var_export($this->configs["MESSAGES"], true) ." ; \$CONF_MESSAGES = ".var_export($this->configs["CONF_MESSAGES"], true) ." ; ");
		}
		
		return $this->configs["MESSAGES"];
	}

    /**
     * Get all registered extensions, from both the conf/extensions.conf.php and from the plugins
     * @static
     * @return
     */
    public static function getRegisteredExtensions(){
        return self::getInstance()->getRegisteredExtensionsInst();
    }
    /**
     * See static method
     * @return
     */
    public function getRegisteredExtensionsInst(){
        if(!isSet($this->configs["EXTENSIONS"])){
            $EXTENSIONS = array();
            $RESERVED_EXTENSIONS = array();
            include_once(AJXP_CONF_PATH."/extensions.conf.php");
            $EXTENSIONS = array_merge($RESERVED_EXTENSIONS, $EXTENSIONS);
            foreach($EXTENSIONS as $key => $value){
                unset($EXTENSIONS[$key]);
                $EXTENSIONS[$value[0]] = $value;
            }
            $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//extensions/extension", "nodes", true);
            $res = array();
            foreach($nodes as $node){
                $res[$node->getAttribute("mime")] = array($node->getAttribute("mime"), $node->getAttribute("icon"), $node->getAttribute("messageId"));
            }
            if(count($res)){
                $EXTENSIONS = array_merge($EXTENSIONS, $res);
            }
            $this->configs["EXTENSIONS"] = $EXTENSIONS;
        }
        return $this->configs["EXTENSIONS"];
    }
	/**
     * Get the actions that declare to skip the secure token in the plugins
     * @static
     * @return array
     */
	public static function getDeclaredUnsecureActions(){
        $test = AJXP_PluginsService::getInstance()->loadFromPluginQueriesCache("//action[@skipSecureToken]");
        if(!empty($test) && is_array($test)) {
            return $test;
        }else{
            $nodes = AJXP_PluginsService::getInstance()->searchAllManifests("//action[@skipSecureToken]", "nodes");
            $res = array();
            foreach($nodes as $node){
                $res[] = $node->getAttribute("name");
            }
            AJXP_PluginsService::getInstance()->storeToPluginQueriesCache("//action[@skipSecureToken]", $res);
            return $res;
        }

    }
	/**
     * Detect available languages from the core i18n library
     * @static
     * @return array
     */
	public static function listAvailableLanguages(){
		// Cache in session!
		if(isSet($_SESSION["AJXP_LANGUAGES"]) && !isSet($_GET["refresh_langs"])){
			return $_SESSION["AJXP_LANGUAGES"];
		}
		$langDir = AJXP_COREI18N_FOLDER;
		$languages = array();
		if(($dh = opendir($langDir))!==FALSE){
			while (($file = readdir($dh)) !== false) {
				$matches = array();
				if(preg_match("/(.*)\.php/", $file, $matches) == 1){
					$fRadical = $matches[1];
					include($langDir."/".$fRadical.".php");
					$langName = isSet($mess["languageLabel"])?$mess["languageLabel"]:"Not Found";
					$languages[$fRadical] = $langName;
				}
			}
			closedir($dh);
		}
		if(count($languages)){
			$_SESSION["AJXP_LANGUAGES"] = $languages;
		}
		return $languages;
	}

    /**
     * Get a config by its name
     * @static
     * @param string $varName
     * @return mixed
     */
	public static function getConf($varName){
		return self::getInstance()->getConfInst($varName);
	}
    /**
     * Set a config by its name
     * @static
     * @param string $varName
     * @param mixed $varValue
     * @return void
     */
	public static function setConf($varName, $varValue){
		return self::getInstance()->setConfInst($varName, $varValue);
	}
    /**
     * See static method
     * @param $varName
     * @return mixed
     */
	public function getConfInst($varName)	
	{
		if(isSet($this->configs[$varName])){
			return $this->configs[$varName];
		}
        if(defined("AJXP_".$varName)){
            return eval("return AJXP_".$varName.";");
        }
		return null;
	}
    /**
     * See static method
     * @param $varName
     * @param $varValue
     * @return void
     */
	public function setConfInst($varName, $varValue)	
	{
		$this->configs[$varName] = $varValue;
	}
	/**
     * Get config from the core.$coreType plugin
     * @static
     * @param string $varName
     * @param string $coreType
     * @return mixed|null|string
     */
	public static function getCoreConf($varName, $coreType = "ajaxplorer"){
		$coreP = AJXP_PluginsService::getInstance()->findPlugin("core", $coreType);
		if($coreP === false) return null;
		$confs = $coreP->getConfigs();
        $confs = AuthService::filterPluginParameters("core.".$coreType, $confs);
		return (isSet($confs[$varName]) ? AJXP_VarsFilter::filter($confs[$varName]) : null);
	}
	
	/**
     * Set the language in the session
     * @static
     * @param string $lang
     * @return void
     */
	public static function setLanguage($lang){
		return self::getInstance()->setLanguageInst($lang);
	}
    /**
     * See static method
     * @param string $lang
     * @return void
     */
	public function setLanguageInst($lang)
	{
		if(array_key_exists($lang, $this->configs["AVAILABLE_LANG"]))
		{
			$this->configs["LANGUE"] = $lang;
		}
	}
	/**
     * Get the language from the session
     * @static
     * @return string
     */
	public static function getLanguage()
	{		
		$lang = self::getInstance()->getConfInst("LANGUE");
        if($lang == null){
            $lang = self::getInstance()->getCoreConf("DEFAULT_LANGUAGE");
        }
        if(empty($lang)) return "en";
        return $lang;
    }
		
	/**
     * The current repository
	 * @return Repository
	 */
	public static function getRepository(){
		return self::getInstance()->getRepositoryInst();
	}
    /**
     * See static method
     * @return Repository
     */
	public function getRepositoryInst()
	{
		if(isSet($_SESSION['REPO_ID']) && isSet($this->configs["REPOSITORIES"])  &&  isSet($this->configs["REPOSITORIES"][$_SESSION['REPO_ID']])){
			return $this->configs["REPOSITORIES"][$_SESSION['REPO_ID']];
		}
		return $this->configs["REPOSITORY"];
	}
	
	/**
	 * Returns the repository access driver
	 * @return AJXP_Plugin
	 */
	public static function loadRepositoryDriver(){
		return self::getInstance()->loadRepositoryDriverInst();
	}
    /**
     * See static method
     * @throws Exception
     * @return AJXP_Plugin
     */
	public function loadRepositoryDriverInst()
	{
		if(isSet($this->configs["ACCESS_DRIVER"]) && is_a($this->configs["ACCESS_DRIVER"], "AbstractAccessDriver")){			
			return $this->configs["ACCESS_DRIVER"];
		}
        $this->switchRootDirInst();
		$crtRepository = $this->getRepositoryInst();
		$accessType = $crtRepository->getAccessType();
		$pServ = AJXP_PluginsService::getInstance();
		$plugInstance = $pServ->getPluginByTypeName("access", $accessType);

        // TRIGGER BEFORE INIT META
        $metaSources = $crtRepository->getOption("META_SOURCES");
        if(isSet($metaSources) && is_array($metaSources) && count($metaSources)){
            $keys = array_keys($metaSources);
            foreach ($keys as $plugId){
                if($plugId == "") continue;
                $split = explode(".", $plugId);
                $instance = $pServ->getPluginById($plugId);
                if(!is_object($instance)) {
                    continue;
                }
                if(!method_exists($instance, "beforeInitMeta")){
                    continue;
                }
                try{
                    $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $crtRepository->getId()));
                    $instance->beforeInitMeta($plugInstance);
                }catch(Exception $e){
                    AJXP_Logger::logAction('ERROR : Cannot instanciate Meta plugin, reason : '.$e->getMessage());
                    $this->errors[] = $e->getMessage();
                }
            }
        }

        // INIT MAIN DRIVER
		$plugInstance->init($crtRepository);
		try{
			$plugInstance->initRepository();
            $crtRepository->driverInstance = $plugInstance;
		}catch (Exception $e){
			// Remove repositories from the lists
			unset($this->configs["REPOSITORIES"][$crtRepository->getId()]);
            if(isSet($_SESSION["PREVIOUS_REPO_ID"]) && $_SESSION["PREVIOUS_REPO_ID"] !=$crtRepository->getId()){
                $this->switchRootDir($_SESSION["PREVIOUS_REPO_ID"]);
            }else{
                $this->switchRootDir();
            }
			throw $e;
		}
		$pServ->setPluginUniqueActiveForType("access", $accessType);			
		
        // TRIGGER INIT META
		$metaSources = $crtRepository->getOption("META_SOURCES");
		if(isSet($metaSources) && is_array($metaSources) && count($metaSources)){
			$keys = array_keys($metaSources);			
			foreach ($keys as $plugId){
				if($plugId == "") continue;
				$split = explode(".", $plugId);				
				$instance = $pServ->getPluginById($plugId);
				if(!is_object($instance)) {
					continue;
				}
                try{
                    $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $crtRepository->getId()));
                    $instance->initMeta($plugInstance);
                }catch(Exception $e){
                    AJXP_Logger::logAction('ERROR : Cannot instanciate Meta plugin, reason : '.$e->getMessage());
                    $this->errors[] = $e->getMessage();
                }
                $pServ->setPluginActive($split[0], $split[1]);
			}
		}
        if(count($this->errors)>0){
            $e = new AJXP_Exception("Error while loading repository feature : ".implode(",",$this->errors));
			// Remove repositories from the lists
			unset($this->configs["REPOSITORIES"][$crtRepository->getId()]);
            if(isSet($_SESSION["PREVIOUS_REPO_ID"]) && $_SESSION["PREVIOUS_REPO_ID"] !=$crtRepository->getId()){
                $this->switchRootDir($_SESSION["PREVIOUS_REPO_ID"]);
            }else{
                $this->switchRootDir();
            }
			throw $e;
        }

		$this->configs["ACCESS_DRIVER"] = $plugInstance;
		return $this->configs["ACCESS_DRIVER"];
	}

    /**
     * @static
     * @param Repository $repository
     * @return AbstractAccessDriver
     */
    public static function loadDriverForRepository(&$repository){
        return self::getInstance()->loadRepositoryDriverREST($repository);
    }

    /**
     * See static method
     * @param Repository $repository
     * @throws AJXP_Exception|Exception
     * @return AbstractAccessDriver
     */
	public function loadRepositoryDriverREST(&$repository)
	{
        if(isset($repository->driverInstance)) {
            return $repository->driverInstance;
        }
		$accessType = $repository->getAccessType();
		$pServ = AJXP_PluginsService::getInstance();
		$plugInstance = $pServ->getPluginByTypeName("access", $accessType);

        // TRIGGER BEFORE INIT META
        $metaSources = $repository->getOption("META_SOURCES");
        if(isSet($metaSources) && is_array($metaSources) && count($metaSources)){
            $keys = array_keys($metaSources);
            foreach ($keys as $plugId){
                if($plugId == "") continue;
                $instance = $pServ->getPluginById($plugId);
                if(!is_object($instance)) {
                    continue;
                }
                if(!method_exists($instance, "beforeInitMeta")){
                    continue;
                }
                try{
                    $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $repository->getId()));
                    $instance->beforeInitMeta($plugInstance);
                }catch(Exception $e){
                    AJXP_Logger::logAction('ERROR : Cannot instanciate Meta plugin, reason : '.$e->getMessage());
                    $this->errors[] = $e->getMessage();
                }
            }
        }

        // INIT MAIN DRIVER
		$plugInstance->init($repository);
		try{
			$plugInstance->initRepository();
		}catch (Exception $e){
			throw $e;
		}
		$pServ->setPluginUniqueActiveForType("access", $accessType);

        // TRIGGER INIT META
		$metaSources = $repository->getOption("META_SOURCES");
		if(isSet($metaSources) && is_array($metaSources) && count($metaSources)){
			$keys = array_keys($metaSources);
			foreach ($keys as $plugId){
				if($plugId == "") continue;
				$split = explode(".", $plugId);
				$instance = $pServ->getPluginById($plugId);
				if(!is_object($instance)) {
					continue;
				}
                try{
                    $instance->init(AuthService::filterPluginParameters($plugId, $metaSources[$plugId], $repository->getId()));
                    $instance->initMeta($plugInstance);
                }catch(Exception $e){
                    AJXP_Logger::logAction('ERROR : Cannot instanciate Meta plugin, reason : '.$e->getMessage());
                    $this->errors[] = $e->getMessage();
                }
                $pServ->setPluginActive($split[0], $split[1]);
			}
		}
        if(count($this->errors)>0){
            $e = new AJXP_Exception("Error while loading repository feature : ".implode(",",$this->errors));
			throw $e;
        }

        $repository->driverInstance = $plugInstance;
        if(isSet($_SESSION["REPO_ID"]) && $_SESSION["REPO_ID"] == $repository->getId()){
            $this->configs["REPOSITORY"] = $repository;
            $this->configs["REPOSITORIES"][$_SESSION['REPO_ID']] = $repository;
        }
		return $plugInstance;
	}

    /**
     * Search the manifests declaring ajxpdriver as their root node. Remove ajxp_conf & ajxp_shared
     * @static
     * @param string $filterByTagName
     * @param string $filterByDriverName
     * @param bool $limitToEnabledPlugins
     * @return string
     */
	public static function availableDriversToXML($filterByTagName = "", $filterByDriverName="", $limitToEnabledPlugins = false){
		$nodeList = AJXP_PluginsService::searchAllManifests("//ajxpdriver", "node", false, $limitToEnabledPlugins);
		$xmlBuffer = "";
		foreach($nodeList as $node){
			$dName = $node->getAttribute("name");
			if($filterByDriverName != "" && $dName != $filterByDriverName) continue;
            if($dName == "ajxp_conf" || $dName == "ajxp_shared") continue;
			if($filterByTagName == ""){
				$xmlBuffer .= $node->ownerDocument->saveXML($node);
				continue;
			}
			$q = new DOMXPath($node->ownerDocument);			
			$cNodes = $q->query("//".$filterByTagName, $node);
			$nodeAttr = $node->attributes;
			$xmlBuffer .= "<ajxpdriver ";
			foreach($node->attributes as $attr) $xmlBuffer.= " $attr->name=\"$attr->value\" ";
			$xmlBuffer .=">";
			foreach($cNodes as $child){
				$xmlBuffer .= $child->ownerDocument->saveXML($child);
			}
			$xmlBuffer .= "</ajxpdriver>";
		}
		return $xmlBuffer;
	}

 	/**
 	 * Singleton method
 	 *
 	 * @return ConfService the service instance
 	 */
 	public static function getInstance()
 	{
 		if(!isSet(self::$instance)){
 			$c = __CLASS__;
 			self::$instance = new $c;
 		}
 		return self::$instance;
 	}
 	private function __construct(){}
	public function __clone(){
        trigger_error("Cannot clone me, i'm a singleton!", E_USER_ERROR);
    } 	
	
}
