<?php
$start_time = microtime(TRUE);
$now = intval($start_time);

if(!file_exists(__DIR__.DIRECTORY_SEPARATOR.'constant.php'))
	header('location:install.php');

require_once(__DIR__.DIRECTORY_SEPARATOR.'constant.php');

//Activation du ssl a travers un reverse proxy
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) $_SERVER['HTTPS'] = 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ? 'on' : 'off';

mb_internal_encoding('UTF-8');

require_once(__ROOT__.'function.php');
date_default_timezone_set(TIME_ZONE);

//set_error_handler('errorToException');
set_exception_handler('unhandledException');
spl_autoload_register('app_autoloader');

global $myUser,$conf,$_,$success,$myFirm;

session_name (COOKIE_NAME.'-session');
session_start();

$conf = new Configuration();
$conf->getAll();

Api::token_session();

$_ = array_map('secure_user_vars', array_merge($_POST, $_GET));
$page = isset($_SERVER['REQUEST_URI'])? basename($_SERVER['REQUEST_URI']):'';
$myFirm = isset($_SESSION['firm']) ? unserialize($_SESSION['firm']): new Firm();
$myUser = isset($_SESSION['currentUser']) ? unserialize($_SESSION['currentUser']) : new User();

//Si le rang anonyme est défini et aucun user (anonyme avec rang ou connecté avec login) défini, on remplit le user avec le user anonyme avec rang (sauf pour le mode cli et le mode api)
if( !empty($conf->get('anonymous_default_rank')) && empty($myUser->ranks) && php_sapi_name() !== 'cli' && !Api::requested()) {
    //Établissement fictif pour les non-connectés
    $firm = Firm::anonymous_firm();
    $myUser->firms[$firm->id] = $firm;
    if(!isset($myFirm->id)) $myFirm = reset($myUser->firms);

    //Attribution des rangs par défaut pour les anonymes
    foreach(Rank::loadAll(array('id:IN'=>$conf->get('anonymous_default_rank'))) as $rank){
        if(!isset($myUser->ranks[$firm->id])) $myUser->ranks[$firm->id] = array();
        $myUser->ranks[$firm->id][$rank->id] = $rank;
    }
    $myUser->loadRights();
    $_SESSION['currentUser'] = serialize($myUser);
    $_SESSION['firm'] = serialize($firm);
}

//CONFS GÉNÉRALES
Configuration::setting('configuration-global',array(
	//Thème
	"Thème",
	"logo-light" => array("label"=>"Logo application (clair)", "legend"=>"S'appliquera sur les fonds sombre de l'ensemble des établissements", "type"=>"image"/*,"default"=>'img/logo/default-logo.png'*/, "attributes"=>array(
		"data-action"=>"core_general_logo",
		"data-id"=>"logo-light",
		"data-data"=>"{\"variant\":\"light\"}",

	)),
	"logo-dark" => array("label"=>"Logo application (sombre)", "legend"=>"S'appliquera sur les fonds clairs de l'ensemble des établissements", "type"=>"image"/*, "default"=>"img/logo/default-logo.dark.png"*/, "attributes"=>array(
		"data-action"=>"core_general_logo",
		"data-id"=>"logo-dark",
		"data-data"=>"{\"variant\":\"dark\"}",
	)),
	"favicon" => array("label"=>"Favicon application", "legend"=>"Affichée dans les onglets de navigation", "type"=>"image"/*, "default"=>"img/logo/default-favicon.png"*/, "attributes"=>array(
		"data-action"=>"core_general_favicon",
		"data-id"=>"favicon",
	)),
	//Confs générales
	"Gestion des configurations générales :",
	'core_public_seo' => array("label"=>"Activer le référencement public","type"=>"boolean","legend"=>"Cocher pour activer le référencement public par les moteurs de recherche"),
	'home_page' => array("label"=>"Page d'accueil","type"=>"text","legend"=>"Laisser vide pour gérer en automatique","placeholder"=>"e.g. index.php?module=example"),
	'logo_website_header' => array("label"=>"Site web cible au clic sur le logo","type"=>"text","legend"=>"Dans le menu de navigation, laisser vide pour pointer vers l'Accueil du projet", "placeholder"=>"e.g. https://example.com"),
	'show_application_name' => array("label"=>"Afficher le nom du programme","type"=>"boolean","legend"=>"Dans le menu de navigation"),
	'show_application_name_footer' => array("label"=>"Afficher le nom du programme", "legend"=>"Dans le pied de page", "type"=>"boolean"),
	'show_application_author_footer' => array("label"=>"Afficher le nom de l'éditeur", "legend"=>"Dans le pied de page", "type"=>"boolean"),
	'show_process_time_footer' => array("label"=>"Afficher le temps de traitement", "legend"=>"Dans le pied de page", "type"=>"boolean"),
	'application_author_website_footer' => array("label"=>"Lien vers le site de l'éditeur", "legend"=>"Dans le pied de page, laisser vide pour ne rien afficher", "type"=>"text", "placeholder"=>"e.g. https://example.com"),
	'show_application_documentation_footer' => array("label"=>"Lien vers la documentation utilisateur", "legend"=>"Dans le pied de page, laisser vide pour ne rien afficher", "type"=>"text"),
	'hide_header_login' => array("label"=>"Masquer le formulaire de connexion dans le header sur la page de connexion","type"=>"boolean","legend"=>"(Barre de menu en haut à droite)"),
	"jwtauth_secret" => array("label"=>"Clé JWT token API du coeur","type"=>"password","legend"=>"Code / Clé JWT token API du coeur","placeholder"=>"e.g. db678804676..."),
	// Clé API composant location
	"Gestion des clés API du composant <code>location</code> :",
	'maps_api_suggest_url' => array("label"=>"URL de l'API de Suggestion","type"=>"text","legend"=>"URL de l'API à attaquer pour autocomplétion des adresses","placeholder"=>"e.g. http://autocomplete.suggest.api.example.com/..."),
	'maps_api_geocode_url' => array("label"=>"URL de l'API Geocoder","type"=>"text","legend"=>"URL de l'API à attaquer pour récupérer les détails d'une localisation","placeholder"=>"e.g. http://autocomplete.geocoder.api.example.com/..."),
	'maps_api_id' => array("label"=>"ID de l'application","type"=>"text","legend"=>"Identifiant de l'application API pour le composant location","placeholder"=>"e.g. pl0749TULNDW..."),
	'maps_api_key' => array("label"=>"Clé publique de l'application","type"=>"password","legend"=>"Code / Clé de l'application API pour le composant location","placeholder"=>"e.g. db678804676..."),
	"Authentification :",
	'account_block' => array("label"=>"Activer le blocage de compte au bout de N essais sur le même login","legend"=>"Tous les utilisateurs seront soumis à la règle","type"=>"boolean"),
	'account_block_try' => array("label"=>"Nombre d'essais avant blocage du compte","legend"=>"L'utilisateur aura N tentatives pour se connecter avant d'être bloqué","type"=>"integer", "placeholder"=>"e.g. 10"),
	'account_block_delay' => array("label"=>"Durée de blocage", "legend"=>"(en minutes)", "type"=>"integer", "placeholder"=>"e.g. 30"),
	'anonymous_default_rank' => array("label"=>"Rangs par défaut attribué aux utilisateurs non connectés", "legend"=>"Laisser vide pour n'appliquer aucun rang par défaut aux utilisateurs anonymes", "type"=>"rank", "attributes"=>array("data-multiple"=>true)),
	'connected_default_rank' => array("label"=>"Rangs par défaut attribué aux utilisateurs connectés", "legend"=>"Laisser vide pour n'appliquer aucun rang par défaut aux utilisateurs connectés", "type"=>"rank", "attributes"=>array("data-multiple"=>true)),
	'logout_inactivity_delay' => array("label"=>"Délai (en secondes) au bout duquel l'utilisateur est considéré comme inactif peut être déconnecté", "legend"=>"Laisser vide pour n'appliquer aucune déconnection automatique", "placeholder"=>"e.g. 3600", "type"=>"integer","attributes"=>array("min"=>60)),
	// Identifiant
	"Identifiant :",
	'login_forbidden_char' => array("label"=>"Caractères interdits","type"=>"text","legend"=>"<small class='text-danger'> La virgule ','' est par défaut interdite pour tout identifiant</small>","placeholder"=>"e.g. <>&!?"),
	//Mots de passe
	'Mots de passe : <div class="btn btn-warning btn-small float-right" onclick="core_general_password_reset_delay()"><i class="fas fa-exclamation-triangle"></i> Forcer le renouvellement</div>',
	'password_forbidden_char' => array("label"=>"Caractères interdits","type"=>"text","legend"=>"<small class='text-danger'> Aucun caractère n'est interdit par défaut </small>","placeholder"=>"e.g. <>&!?"),
	'password_delay'=>array("label"=>"Renouvellement", "legend"=>"Forcer l'utilisateur a renouveller son mot de passe tous les X jours (laisser vide pour désactiver)", "type"=>"integer", "placeholder"=>"e.g. 30"),
	'password_allow_lost'=>array("label"=>"Oubli de mot de passe", "legend"=>"Proposer la récuperation du mot de passe oublié", "type"=>"boolean"),
	'password_lost_expeditor'=>array("label"=>"Adresse mail d'expédition", "legend"=>"L'adresse mail qui apparaitra en tant qu'expéditeur du mail de mot de passe oublié", "type"=>"mail"),
	'password_lost_firm'=>array("label"=>"Établissement", "legend"=>"L'établissement qui apparaitra dans le header \"organization\" du mail de mot de passe oublié", "type"=>"firm"),
	'password_lost_mail_expire' => array("label"=>"Expiration lien de récuperation de mot de passe","legend"=>"Délai en jours avant expiration du lien de récuperation (laisser vide ou 0 pour aucune expiration)","type"=>"integer", "placeholder"=>"e.g. 28"),
	//Connectivité
	"Connectivité :",
	'offline_mode' => array("label"=>"Activer le mode hors ligne","legend"=>"(Désactive toutes les fonctionnalités ayant besoin d'un accès internet depuis le poste client cdn...)","type"=>"boolean"),
));

//CACHE CSS & JS
$cacheVersion = SOURCE_VERSION;
if(file_exists(__DIR__.SLASH.'.git'.SLASH.'HEAD')){
	$versionFile = str_replace(array('ref: ',PHP_EOL,"\r","\n"),'',file_get_contents(__DIR__.SLASH.'.git'.SLASH.'HEAD'));
	if(file_exists(__DIR__.SLASH.'.git'.SLASH.$versionFile)){
		$cacheVersion = str_replace(array("\r","\n"),'',file_get_contents(__DIR__.SLASH.'.git'.SLASH.$versionFile));
	}
}

if($myUser->login==null && isset($_COOKIE[COOKIE_NAME])){
	$cookie = UserPreference::load(array('key'=>'cookie','value'=>$_COOKIE[COOKIE_NAME]));
	Log::put("Tentative de connexion via le cookie", 'Utilisateur');

	if($conf->get('account_block')==1){
		$try = is_numeric($conf->get('account_block_try')) ? $conf->get('account_block_try') : 5;
		$delay = is_numeric($conf->get('account_block_delay')) ? $conf->get('account_block_delay') : 10;
		$trying = Log::loadAll(array('category'=>'auth_fail', 'label'=>$_['login'], 'created:>'=>(time() - ($delay*60))));

		if(count($trying)>=$try) throw new Exception("Suite à un trop grand nombre de tentatives, votre compte est bloqué pour un délai de ".$delay." minutes",509);
	}

	if($cookie!=false){
		Log::put("Cookie trouvé, connexion via le cookie", 'Utilisateur');
	    if(Plugin::is_active('fr.core.activedirectory'))
	        require_once(PLUGIN_PATH.'activedirectory'.SLASH.'activedirectory.plugin.php');

	    $myUser = User::byLogin($cookie->user);
        if($myUser!=false){
        	Log::put("Utilisateur trouvé lors de la connexion via le cookie", 'Utilisateur');
	        User::initialization($myUser, true);
	        if($myUser->superadmin) Log::put("Connexion avec un compte SuperAdmin via le cookie", 'Utilisateur');
	    } else {
	    	Log::put("Aucun utilisateur trouvé lors de la connexion via le cookie", 'Utilisateur');
	    	throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur");
	    }
        $_SESSION['currentUser'] = serialize($myUser);
        $_SESSION['firm'] = serialize($myFirm);
	    Log::put("Connexion réussie via le cookie avec l'utilisateur \"".$myUser->login."\"",'Utilisateur');
    }
}

//MENUS
Plugin::addHook("menu_account", function(&$accountMenu){
	global $myUser;
	if(!$myUser->connected()) throw new Exception('Vous devez être connecté pour accéder à cette fonctionnalité');
	$accountMenu[]= array(
		'sort' =>0,
		'url' => 'account.php?section=global',
		'icon' => 'fas fa-angle-right',
		'label' => 'Général',
	);
	$accountMenu[]= array(
		'sort' =>10,
		'url' => 'account.php?section=api',
		'icon' => 'fas fa-angle-right',
		'label' => 'API',
	);
});

Plugin::addHook("menu_setting", function(&$settingMenu){
	global $myUser;

	$settingMenu[]= array(
		'sort' =>0,
		'url' => 'setting.php?section=global',
		'icon' => 'fas fa-angle-right',
		'label' => 'Général',
	);

	if($myUser->can('plugin','configure'))
		$settingMenu[]= array(
			'sort' =>18,
			'url' => 'setting.php?section=plugin',
			'icon' => 'fas fa-angle-right',
			'label' => 'Plugins',
			'category' => 'administration'
		);

	if($myUser->can('user','configure'))
		$settingMenu[]= array(
			'sort' =>20,
			'url' => 'setting.php?section=user',
			'icon' => 'fas fa-angle-right',
			'label' => 'Utilisateurs',
			'category' => 'administration'
		);
	if($myUser->can('rank','configure'))
		$settingMenu[]= array(
			'sort' =>21,
			'url' => 'setting.php?section=rank',
			'icon' => 'fas fa-angle-right',
			'label' => 'Rangs & Accès',
			'category' => 'administration'
		);

	if($myUser->can('firm','configure'))
		$settingMenu[]= array(
			'sort' =>22,
			'url' => 'setting.php?section=firm',
			'icon' => 'fas fa-angle-right',
			'label' => 'Établissements',
			'category' => 'administration'
		);

	if($myUser->can('user','configure'))
		$settingMenu[]= array(
			'sort' =>23,
			'url' => 'setting.php?section=userfirmrank',
			'icon' => 'fas fa-angle-right',
			'label' => 'Établissement / Utilisateur / Rang',
			'category' => 'administration'
		);

	if($myUser->can('dictionary','configure'))
		$settingMenu[]= array(
			'sort' =>20,
			'url' => 'setting.php?section=dictionary',
			'icon' => 'fas fa-angle-right',
			'label' => 'Listes de valeur'
		);

	if($myUser->can('log','read'))
		$settingMenu[]= array(
			'sort' =>16,
			'url' => 'setting.php?section=log',
			'icon' => 'fas fa-angle-right',
			'label' => 'Logs',
			'category' => 'administration'
		);

	if($myUser->login!='')
		$settingMenu[]= array(
			'sort' =>16,
			'url' => 'setting.php?section=update',
			'icon' => 'fas fa-angle-right',
			'label' => 'Mises à jour',
			'category' => 'administration'
		);
});

Plugin::addHook("menu_main", function(&$mainMenu) {
	global $myUser;

	if(!$myUser->connected()) return;
	$mainMenu[] = array(
		'sort' =>0,
		'icon' => 'fas fa-fw fa-home',
		'label' => 'Accueil',
		'url' => 'index.php',
		'color' => '#383838'
	);
});

Plugin::addHook("menu_user", function(&$userMenu){
	global $myUser,$myFirm;

	$rankLabels = array();
	if($myUser->superadmin){
	    $rankLabels[] = 'Super Admin';
	} else {
	    if(isset($myUser->ranks[$myFirm->id])){
		    foreach($myUser->ranks[$myFirm->id] as $rank){
		    	$rankLabels[] = $rank->label;
		    }
		}
	}
    $ranksHtml = count($rankLabels)!=0 ? '<div class="firm-ranks mt-1"><ul><li>'.implode('</li><li>',$rankLabels).'</li></ul></div>' : '';

	$userMenu[]= array(
		'sort' => -2,
		'custom' => "<div class='firm-item' onclick='event.stopPropagation();'><small>Rang : ".$ranksHtml."</small></div><div class='dropdown-divider'></div>",
	);

	if(count($myUser->firms)>1){
		$userIcon = 'far fa-user';
		$options = '';

		foreach ($myUser->firms as $firm)
			$options .= '<option '.($myFirm->id == $firm->id ? "selected='selected'":"").' value="'.$firm->id.'">'.$firm->label.'</option>';

		$userMenu[]= array(
			'sort' => 1,
			'custom' => "<div class='firm-item mt-2' onclick='event.stopPropagation();'><small class='mb-1'>Établissement : </small><select class=\"form-control form-control-sm\" onchange=\"window.location='action.php?action=core_firm_select&firm='+$(this).val();\">".$options."</select></div><div class='dropdown-divider mb-1'></div>",
		);
	} else {
		$userIcon = 'fas fa-user';
		$userMenu[]= array(
			'sort' => -1,
			'custom' => "<div class='firm-item' onclick='event.stopPropagation();'><small>Établissement : ".$myFirm->label."</small></div><div class='dropdown-divider mb-1'></div>",
		);
	}

	if($myUser->can('account','read'))
		$userMenu[]= array(
			'sort' => 0,
			'label' => 'Mon compte',
			'icon' => $userIcon,
			'url' => 'account.php'
		);

	if($myUser->can('setting_global', 'read'))
		$userMenu[]= array(
			'sort' => 1,
			'icon' => 'fas fa-cog',
			'label' => 'Réglages',
			'url' => 'setting.php'
		);

	$userMenu[]= array(
		'sort' => 100,
		'icon' => 'fas fa-sign-out-alt',
		'label' => 'Déconnexion',
		'custom' => '<a class="dropdown-item user-menu-item px-3 text-danger" onclick="core_logout();">
				<i class="fa-fw fas fa-sign-out-alt"></i> Déconnexion
			</a>'
	);
});

//Security policies
Plugin::addHook("rewrite", function ($requested){
	global $conf,$myUser;
	$requested = trim($requested, '/');
	if(substr($requested, 0,24)!='.well-known/security.txt') return;
	header('Content-type:application/text');
	echo 'Contact: mailto:'.ADMIN_MAIL.'
Preferred-Languages: fr,en
Canonical: https://'.ROOT_URL.'/.well-known/security.txt';
});

//API handle
Plugin::addHook("rewrite", function ($requested){
	global $conf,$myUser,$_;
	$requested = trim($requested, '/');
	$directories = explode('/',$requested);
	if(array_shift($directories) != 'api') return;
	$_['command'] = implode('/',$directories);
	require_once(__DIR__.SLASH."api.php");
	Api::run();
});

//media
Plugin::addHook("rewrite", function ($requested){
	global $conf,$myUser;
	$requested = trim($requested, '/');
	if(substr($requested, 0,6)!='media/') return;
	//Vérification que le dossier est vraiment publique (nomplugin/public/*) on donne volontairement une erreur vague pour tromper le chaland
	if(!preg_match('/^media\/.*\/public\/.*/is', $requested)) throw new Exception("Media inexistant.");
	$requested = substr($requested, 6);
	$file = File::dir().$requested;
	$file = preg_replace('/\?v=[a-z0-9]*/i','',$file);
	$files = glob($file);

	if(count($files) ==0){
		$file = __ROOT__.'/img/image-not-found.jpg';
	}else{
		$file = $files[0];
	}
	File::downloadFile($file);
});


//robots.txt
Plugin::addHook("rewrite", function ($requested){
	global $conf,$myUser;
	$requested = trim($requested, '/');

	if(substr($requested, 0,10)!='robots.txt') return;
	if(!empty($conf->get('core_public_seo'))){
		header('HTTP/1.0 404 Not Found');
		return;
	}
	header('Content-type:application/text');
	echo 'User-agent: *
Disallow: /';
});

//Page de login
Plugin::addHook("page", function(){
    global $_,$myUser,$conf;
    if(isset($_['module']) || $myUser->connected()) return;

    $page = !isset($_['page']) ? 'login' : $_['page'];
    $file = __DIR__.SLASH.'page.'.$page.'.php';

    if(!file_exists($file)) throw new Exception("Page ".$page." inexistante");
    require_once($file);
});


Plugin::addHook("content_setting", function(){
	global $_;
	$_['section'] = !isset($_['section']) ? 'global': $_['section'];
	if(in_array($_['section'],array('global','plugin','rank','right','user','firm','userfirmrank','firmPlugin','log','dictionary','update')) && file_exists('setting.'.$_['section'].'.php'))
		require_once('setting.'.$_['section'].'.php');
});
Plugin::addHook("content_account", function(){
	global $_;
	$_['section'] = !isset($_['section']) ? 'global': $_['section'];
	if(in_array($_['section'],array('global','api')) && file_exists('account.'.$_['section'].'.php'))
		require_once('account.'.$_['section'].'.php');
});



Right::register('setting_global', array('label'=> 'Gestion des parametres globaux'));
Right::register('user', array('label'=> 'Gestion des utilisateurs'));
Right::register('profile', array('label'=> 'Accès aux profils'));
Right::register('firm', array('label'=> 'Gestion des établissements'));
Right::register('plugin', array('label'=> 'Gestion des plugins'));
Right::register('rank', array('label'=> 'Gestion des rangs et droits'));
Right::register('log', array('label'=> 'Gestion des logs programme'));
Right::register('dictionary', array('label'=> 'Gestion des listes programme'));
Right::register('file', array('label'=> 'Gestion des fichiers'));
Right::register('account', array('label'=> 'Gestion du compte courant'));
Right::register('history', array('label'=> 'Gestion des panels commentaires'));


Plugin::addHook("cron",function($time){
	if(date('H:i', $time)!='01:00') return;
	//Clear automatique des logs
	global $conf;
	foreach(Log::staticQuery('SELECT DISTINCT category FROM {{table}}',array(),true) as $log):
		$slug = slugify($log->category);
		$key = 'log_retention_time_'.$slug;
		if($conf->get($key)=='') continue;
		Log::clear($conf->get($key));
	endforeach;
});

Plugin::includeAll();

?>
