<?php

/**------------------------------------------------
*
* Rooty, 2018 <rooty@rooty.me>
*
*
* This software is protected by copyright, please
* read the file COPYRIGHT.
* 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. Please
* read the file LICENCE.
*
* @link www.rooty.me
* @copyright Copyright &copy; 2004-2005, ARD Limousin
* @author Rooty <www.rooty.me|rooty@rooty.me>
* @since 2004/09/03
* @version 0.3
* @package package
* @subpackage backupFile
*/

if ( !defined('SYSTEM_IN') )
{
	//die("Hacking attempt");
}

/**
* classe de gestion de gestion de l'arborescence système.
* @package package
* @subpackage backupFile
*/ 
Class backupFile
{

/**
*Liste le contenu d'un répertoire
* @access public
* @param chaine path vers le répertoire à lister
* @return tableau tableau deux dimensions avec chemin+nom de fichier, type de fichier(rep="Directory", fichier="File", other="Inconnu"), nom fichier
*/ 
	function getContentDir($path)
	{
		$cptfile=0;
		$tabFile=array();

		//Test de l'existence du répertoire
		if (is_dir($path)) 
		{
			//Ouverture du répertoire
			if ($rep = opendir($path))
			{
				//Lecture et stockage du contenu du repertoire dans le tableau en précisant le type du fichier lu
				//les répertoires "." et ".." sont ignorés
				while (($file = readdir($rep)) !== false ) 
				{ 
					if ($file != "." && $file != "..") 
					{
						$tabFile[$cptfile][2]=$file;
						$file=$path.$file;
						$tabFile[$cptfile][0]=$file;
						if (is_dir($file))
						{
						$tabFile[$cptfile][1]="Directory";
						}
						else
						{
							if (is_file($file))
							{
								$tabFile[$cptfile][1]="File";
							}
							else
							{
								$tabFile[$cptfile][1]="Inconnu";
							}
						}
						$cptfile++;
					}
				}
			}
			closedir($rep); 
		}
		else
		{
			addError(3 , "BackUpFile", "chemin non valide ".$path, __line__, __file__);
		}
		return $tabFile;
	}

/**
* Suppresion d'un repertoire (récursivement)
* @access public
* @param chaine path vers le fichier à tester
* @param chaine droit à tester sur le fichier(r:read,w:write,x:execute)==>exemple(rwx)
* @return booleen
*/
	function deleteDir($path)
	{
		$cptfile=0;
		if ($rep = opendir($path))
		{
			//Lecture et stockage du contenu du repertoire dans le tableau en précisant le type du fichier lu
			//les répertoires "." et ".." sont ignorés
			while (($file = readdir($rep)) !== false ) 
			{ 
				if ($file != "." && $file != "..") 
				{
					if (is_dir($file))
					{
						$this->deleteDir($path."/".$file);
					}
					else
					{
						@unlink($path."/".$file);
					}
					$cptfile++;
				}
			}
		}
		closedir($rep);
		@rmdir($path);
	}

/**
*Test les droits sur un fichier
* @access public
* @param chaine path vers le fichier à tester
* @param chaine droit à tester sur le fichier(r:read,w:write,x:execute)==>exemple(rwx)
* @return booleen
*/
	function verifFilePerm($filePath, $droit)
	{
		$nbtest=strlen($droit);

		//Recupere le nombre de test de droit à effectuer	
		switch ($nbtest)
		{
			case 1:
				if ($droit=="r"){
					if (is_readable($filePath)){
						return true;
					}
				}
				if ($droit=="w"){
					if (is_writable($filePath)){
						return true;
					}
				}
				if ($droit=="x"){
					if (is_executable($filePath)){
						return true;
					}
				}
				break;
			case 2:
				if ( $droit=="rw" || $droit=="wr"){
					if (is_readable($filePath) && is_writable($filePath)){
						return true;
					}
				}
				if ( $droit=="rx" || $droit=="xr"){
					if (is_readable($filePath) && is_executable($filePath)){
						return true;
					}
				}
				if ( $droit=="xw" || $droit=="wx"){
					if (is_executable($filePath) && is_writable($filePath)){
						return true;
					}
				}
				break;
			case 3:
				if (is_readable($filePath) && is_writable($filePath) && is_executable($filePath)){
					return true;
				}
				break;
		}
		return false;
	}

/**
*Renomme un fichier
* @access public
* @param chaine path vers le fichier à renommer
* @param chaine nouveau nom (penser à préciser l'extension!)
* @return booleen
*/
	function renameFile($sourcepath, $destination_name)
	{
		//Teste de l'existence du fichier
		if (file_exists($sourcepath))
		{
			//On renomme le fichier
			if (rename($sourcepath,$destination_name))
			{
				return true;
			}
		}
		return false;	
	}

/**
*Renomme les fichiers éxistant pour un éventuel backup
* @access public
* @param chaine path vers le fichier à renommer
* @param chaine extension du fichier sauvegardé
* @return tableau tableau contenant l'ancien nom [oldname], le nouveau nom [newname], et le status de reussite ou non du renommage (true or false)
*/
	function rename($sourcepath,$ext=".bkp")
	{
		$tabbackup=array();

		//On change l'extension	en gardant le path	
		$pos=strrpos($sourcepath,".");
		$ch=substr($sourcepath,0,$pos);
		$ch=$ch.$ext;

		//On renomme le fichier
		if ($this->renameFile($sourcepath,$ch))
		{
			$tabbackup["oldname"]=$sourcepath;
			$tabbackup["newname"]=$ch;
			$tabbackup["success"]=true;
		}
		else
		{
			$tabbackup["oldname"]=$sourcepath;
			$tabbackup["newname"]=$ch;
			$tabbackup["success"]=false;
		}
		return $tabbackup;
	}

/**
*copie un fichier
* @access public
* @param chaine path vers le fichier à renommer
* @param chaine nouveau nom (penser à préciser l'extension!)
* @return booleen
*/
	function copyFile($sourcepath, $destination_name){
		//Teste de l'existence du fichier
		if (file_exists($sourcepath))
		{
			//On renomme le fichier
			if (copy($sourcepath,$destination_name))
			{
				return true;
			}
		}
		return false;	
	}

/**
*Supprime un répertoire
*Utilisation de la récursivité pour supprimé les sous-répertoire du répertoire racine passé en paramétre
*Retourne TRUE si suppression reussie, retourne FALSE si un répertoire n'est pas vide
* @access public
* @param chaine path vers le répertoire a supprimer
* @return booleen
*/
	function EraseDir($sourcepath)
	{
		$tabficdir=$this->getContentDir($sourcepath);

		if (count($tabficdir)==0)
		{
			rmdir($sourcepath);
			return true;
		}
		else
		{
			foreach ($tabficdir as $key=>$val)
			{
				if ($tabficdir[$key][1]=="Directory")
				{
					$this->EraseDir($tabficdir[$key][0]);
				}
				else
				{
					return False;
				}
			}
		}
	}

/**
*copie les fichiers éxistant pour un éventuel backup
* @access public
* @param chaine path vers le fichier à renommer
* @param chaine extension du fichier sauvegardé
* @return tableau tableau contenant l'ancien nom [oldname], le nouveau nom [newname], et le status de reussite ou non du renommage (true or false)
*/
	function copy($sourcepath,$ext=".bkp")
	{
		$tabbackup=array();

		//On change l'extension	en gardant le path	
		$pos=strrpos($sourcepath,".");
		$ch=substr($sourcepath,0,$pos);
		$ch=$ch.$ext;
		//On renomme le fichier
		if ($this->copyFile($sourcepath,$ch))
		{
			$tabbackup["oldname"]=$sourcepath;
			$tabbackup["newname"]=$ch;
			$tabbackup["success"]=true;
		}
		else
		{
			$tabbackup["oldname"]=$sourcepath;
			$tabbackup["newname"]=$ch;
			$tabbackup["success"]=false;
		}
		return $tabbackup;
	}

/**
*Cette fonction permet d'attribuée des droits à un fichier 
* @access public
* @param chaine path vers le fichier
* @param entier droits à attribuer (ex:0775==>droits de lecture et d'écriture pour l'utilisateur et le groupe, droits d'exécution pour tous)
* @return booleen 
*/
	function setFilePerm($Path,$FileMode)
	{
		//Test de l'existence du fichier
		if (file_exists($Path))
		{
			//Affectation des droits si l'utilisateur éxécutant le script est autorisé
			if (@chmod($Path, $FileMode))
				return true;
			else
				return false;
		}
		else
		{
			echo "<br>Le fichier n'existe pas!";
			return false;
		}
	}

/**
*Fonction permettant d'affecter des droits à un répertoire de maniére recurssive
* @access public
* @param chaine path vers le repertoire
* @param entier droit à attribuer (ex:0775==>droits de lecture et d'écriture pour l'utilisateur et le groupe, droits d'exécution pour tous)
* @return tableau tableau 2 dimensions avec une dimension pour la profondeur et une dimension pour les path du fichier, les droits tester et la reussite ou non de l'affection des doits
*/
	function setTreePerm($dirPath, $chmod)
	{
		$tabarbo=array();
		$tabarbo=$this->getContentDir($dirPath);
		$tabresult=array();
		$cpt=0;

		//Parcours du repertoire
		for ($i=0;$i < count($tabarbo);$i++) {
			if ($tabarbo[$i][1]=="Directory")
			{
				//Parcours des sous-répertoire de maniére recursive
				$tabresult=array_merge($tabresult,$this->setTreePerm($tabarbo[$i][0]."/", $chmod));
				$cpt=count($tabresult);
			}
			//Affectation des droits et enregistrement des opérations dans le tableau
			else if ($tabarbo[$i][1]=="File")
			{
					$tabresult[$cpt]["path"]=$tabarbo[$i][0];
					$tabresult[$cpt]["perm"]=$chmod;
					if ($this->setFilePerm($tabarbo[$i][0],$chmod))
					{
						$tabresult[$cpt]["success"]=true;
					}
					else
					{
						$tabresult[$cpt]["success"]=false;
					}			
					$cpt++;
			}
		}
		return $tabresult;
	}

/**
* Fonction qui parcours le fichier .ini et verifie l'existence des fichiers sources
* @access public
* @param chaine path vers le fichier ini
* @return tableau tableau assosiatif du contenu du fichier .ini
*/
	function parcoursInitoTab($PathIni, $path="./", $chmod)
	{
		$tabresult=array();
		$tabini=array();
		//Copie des fichiers dans l'arbo avec enregistrement dans le tableau du resultat de la copie
		//enregistrement également des paths source et destination
		if ($tabini=parse_ini_file($PathIni))
		{
			foreach ($tabini as $key=>$val) 
			{
				//Verifie l'existence du fichier source
				$tabresult[$key][0]=$val;
				if (file_exists($path.$key) && $this->setFilePerm($path.$key, $chmod))
				{
					$tabresult[$key][0]=$val;
					$tabresult[$key][1]=True;
				}
				else
				{
					$tabresult[$key][1]=False;
				}
			}
		}
		else
		{
			echo "<br>Erreur de lecture du fichier .ini!";
		}
		return $tabresult;
	}

/**
* Création d'un répertoire
* Ne gére pas un path absolu
* @access public
* @param tableau tableau associatif du fichier ini
* @param entier droit à attribuer à la création d'un répertoire
* @return tableau tableau associatif des états de création des reps
*/
	function createRep($tab, $chmod)
	{
		$tabTmp=array();
		$tabresult=array();
		$cpt=0;
		//Création de l'arbo avec les valeurs contenues dans le fichier .ini
		foreach ($tab as $key=>$val) 
		{
			$tabTmp=mb_split('/',$this->getSystemPath($val[0]));
			$pathTmp="";
			$tabresult[$cpt][0]=$this->getSystemPath($val[0]);
			$tabresult[$cpt][1]=true;
			for ($i=0; $i<(count($tabTmp)-1); $i++)
			{
				$pathTmp.=$tabTmp[$i]."/";
				if ( !is_dir($pathTmp) )
				{
					if(mkdir($pathTmp,$chmod)===true){
						$tabresult[$cpt][1]=true;
						chmod($pathTmp, $chmod);
					}else{
						$tabresult[$cpt][1]=false;
						return $tabresult;
					}
				}
			}
			$cpt++;
		}
		return $tabresult;
	}

/**
*Cette fonction charge un fichier ini, qui indique ou copier les fichier dans l'arbo granilim
*Le fichier .ini doit etre strucuturé comme suit:
*[source]
*testsource="./test/testdestination/"
* @access public
* @param chaine path vers le fichier ini
* @param entier valeur du umask
* @return tableau tableau d'état des fichier copier avec les paths sources et destinations et le status de la copie (true or false)
*/
	function copyFileFromTab($tabini, $pkgname ,$chmod)
	{
		$tabresultcopie=array();
		$tabperm=array();
		$tabrep=array();
		$tabbackup=array();
		$cpt=0;

		//Création des répertoires inexistant
		//Appel fonction création rep, avec un umask en lecture et écriture sur tous les reps créés et les fichiers copiés
		$tabrep=$this->createRep($tabini,$chmod);

		if (count($tabini)>0){
			foreach ($tabini as $key=>$struct) 
			{
				$val=$this->getSystemPath($struct[0]);
				//On verifie les permissions pour écraser le fichier s'il existe
				if ($tabini[$key][1]===true && $tabrep[$cpt][1]===true)
				{
					if ($this->rename($val, ".php~")!==false)
					{
						if (copy($GLOBALS["CONF_DATA_PATH"]."data/upload/install/package/".$pkgname."/".$key, $val))
						{
							$tabresultcopie[$key][0]=$val;
							$tabresultcopie[$key][1]=$this->setFilePerm($val, $chmod);
							if (file_exists($val."~"))
							{
								@unlink($val."~");
							}
						}
						else
						{
							$tabresultcopie[$key][0]=$val;
							$tabresultcopie[$key][1]=false;
						}
					}else{
						$tabresultcopie[$key][0]=$val;
						$tabresultcopie[$key][1]=false;
					}
				}
				$cpt++; 
			}
		}
		return $tabresultcopie;
	}

/**
*Fonction qui verifie que les fichiers distants existent, et si ils existent qu'on a les droits pour les écraser.
* @access public
* @param tableau type tableau renvoyé par ParcoursIniTab
* @return tableau tableau contenant le path vers le fichier dont on veut vérifier les perms, et l'état de la permission testée (true or false)
*/
	function verifFilePermFromTab($tab, $chmod)
	{
		$tabresult=array();
		if (count($tab)>0)
		{
			//Vérification que les fichiers a copier existent, et si ils existent qu'on a les droits pour les écraser.
			foreach ($tab as $key=>$val)
			{
				$tabresult[$key][0]=$this->getSystemPath($tab[$key][0]);
				if (file_exists($this->getSystemPath($tab[$key][0])))
				{
					//Test des permission pour l'écraser
					if ($this->verifFilePerm($this->getSystemPath($tab[$key][0]), "rw")) {
						$tabresult[$key][1]=true;
					}else{//Pas les droit pour écraser
						if ($this->setFilePerm($this->getSystemPath($tab[$key][0]), $chmod)){
							$tabresult[$key][1]=true;
						}else{
							$tabresult[$key][1]=false;
						}
					}  
				}
				else
				{
					//Le fichier n'existe pas
					$tabresult[$key][1]=true;
				}
			}
		}
		return $tabresult;
	}

/**
* Permet de récupérer le bon path en fonction de la configuration serveur
* Ce path s'appuira sur les variables:
* - CONF_LIB_PATH (chemin d'accés à lib)<br/>
* - CONF_DATA_PATH (chemin d'accés aux données)<br/>
* - CONF_LOG_PATH (chemin d'accés aux logs)<br/>
* - $COMMUN_PATH_CONFIG (chemin d'accés aux fichiers de configuration)<br/>
* @return float version du fichier sinon 0
*/
	function getSystemPath($pathFile){
		$tabTmp=mb_split('/',$pathFile);
		if ( (is_null($tabTmp[0])) || ($tabTmp[0]==".") ){
			unset($tabTmp[0]);
			return ($this->getSystemPath(implode("/", $tabTmp)));
		}
		# pathFile doit être correct maintenant
		if(substr($pathFile, 0, 4)=="data")
			return $GLOBALS["CONF_DATA_PATH"].$pathFile;
		elseif(substr($pathFile, 0, 3)=="lib")
			return $GLOBALS["CONF_LIB_PATH"].$pathFile;
		elseif(substr($pathFile, 0, 3)=="log")
			return $GLOBALS["CONF_LOG_PATH"].$pathFile;
		elseif(substr($pathFile, 0, 6)=="config")
			return $GLOBALS["COMMUN_PATH_CONFIG"].$pathFile;
		elseif(substr($pathFile, 0, 7)=="install")
			return "./".$pathFile;
		return $pathFile;
	}

/**
* Vérifie la configuration des droits systéme (droit php && utilisateur)
* retourne faux si les fichiers uploadés ne sont pas accessibles via php sinon int pour les droits à passer en chmod!!!
* @return entier droits a affecter aux fichiers && faux si inaccessibles
*/
	function checkConfigForPerms($path=null){
		if ($path==null)
		{
			$path=$GLOBALS["CONF_DATA_PATH"]."data/upload/install/";
		}

		//
		// Gestion des droits d'acces
		//
		$ok = false;
		$self = basename($_SERVER['PHP_SELF']);
		$uid = @fileowner('.');
		$uid2 = @fileowner($self);
		$gid = @filegroup('.');
		$gid2 = @filegroup($self);
		$perms = @fileperms($self);
// 		var_dump(intval(substr(decoct( fileperms($self) ), 2)));

		// Comparer l'appartenance d'un fichier cree par PHP
		// avec celle du script et du repertoire courant
		if (is_dir($path.'test')){
			@rmdir($path.'test');
		}elseif(is_file($path.'test')){
			@unlink($path.'test'); // effacer au cas ou
		}

		@touch($path.'test');
		if ($uid > 0 && $uid == $uid2 && @fileowner($path.'test') == $uid)
			$chmod = 0700;
		else if ($gid > 0 && $gid == $gid2 && @filegroup($path.'test') == $gid)
			$chmod = 0770;
		else
			$chmod = 0775;

		// Appliquer de plus les droits d'acces du script
		if ($perms > 0) {
			$perms = ($perms & 0775) | (($perms & 0444) >> 2);
			$chmod |= $perms;
		}
		@unlink($path.'test');

		// Verifier que les valeurs sont correctes
		@mkdir($path.'test', $chmod);
		@chmod($path.'test', $chmod);
		$f = @fopen($path.'test/test.php', 'w');
		if ($f) {
			@fputs($f, '<?php $ok = true; ?>');
			@fclose($f);
			@chmod($path.'test/test.php', $chmod);
			include($path.'test/test.php');
		}

		if (!$ok) {
			return false;
		}else{
			@unlink($path.'test/test.php');
			@rmdir($path.'test');
			return $chmod;
		}
	}

/**
* chown et chgrp en recursif !!!!
* merci php.net
* @link http://fr3.php.net/manual/fr/function.chown.php
*/
	function recurse_chown_chgrp($path2dir, $uid, $gid){
		$dir = new dir($path2dir);
		while(($file = $dir->read()) !== false) {
			if(is_dir($dir->path.$file)) {
				recurse_chown_chgrp($dir->path.$file, $uid, $gid);
			} else {
				chown($file, $uid);
				chgrp($file, $gid);
			}
		}
		$dir->close();
	} 
}
?>
