<?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.
*
* Fichier
*
* Ce fichier contient la classe d'accès aux données pour l'annuaire LDAP
*
* @author Initiance <www.initiance.com|Vincent Narbeburu|olivier@initiance.com|commendatore@comendatore.net>
* @link www.rooty.me
* @since 2018/04/08
* @version 0.3
* @copyright Copyright &copy; 2009-2018, Rooty
* @package system_data_source
* @subpackage ldap
*/

#####################################################################################################
################################# Parametrage
#####################################################################################################
/**
* declaration du system... si cette variable n'est pas définie, les fichier inclus ne marche pas...
* cette variable est déclarée sur la page d'execution du script.
*<p> if ( !defined('SYSTEM_IN') )
* {
* die("Hacking attempt");
* }</p>
*/
if ( !defined('SYSTEM_IN') )
{
 die("Hacking attempt");
}

/**
* classe ldap_annu
*
*Classe de gestion de LDAP
*
* @package system_data_source
* @subpackage ldap
*/
class LdapAnnu {
/**#@+
* @access public
*/
# ----------------------------------------- paramètres de connexion ---------------------------------------#

/**
* @var chaine
* @desc contient l'adresse du serveur
*/
  var $hote     = "";

/**
* @var numeric
* @desc contient le port du serveur
*/
  var $port     = 389;

/**
* @var numeric
* @desc contient la version du protocole Ldap à utiliser
*/
  var $protocoleVersion     = 3;

/**
* @var chaine
* @desc contient l'utilisateur de l'annuaire
*/
  var $utilisateur     = "";

/**
* @var chaine
* @desc contient le mot de passe de l'utilisateur
*/
  var $mDP = "";

/**
* @var chaine
* @desc contient la racine de l'annuaire
*/
  var $baseDN	= "";

# ---------------------------------- numéro et label d'erreur courants  ----------------------------------#
/**
* @var entier
* @desc contient le numero d'erreur
*/
  var $noErreur    = 0;

/**
* @var chaine
* @desc contient le message d'erreur
*/
  var $erreur    = null;
/**#@-*/

# ------------------------------------ handles de lien et de requête  ------------------------------------#
/**#@+
* @access private
*/

/**
* @var entier
* @desc identifiant de connection a l'annuaire
*/
  var $idConnexion  = null;

/**
* @var entier
* @desc identifiant de session
*/
  var $idSession  = 0;

/**
* @var entier
* @desc Resultat
*/
  var $resultat = 0;

/**
* @var idresul
* @desc Identifiant de résultat
*/
  var $idResultat = null;

# --------------------------------------------- flags d'état ---------------------------------------------#
/**
* @var booleen
* @desc indique si l'on est connecté à l'annuaire
*/
# est a un si l'instance de la classe est connectée à un annuaire sinon, sinon elle est à 0
  var $flagConnecte = null;

/**
* @var booleen
* @desc indique si l'on est authetifié a l'annuaire
*/
# est a un si un utilisateur est authentifié auprès de l'annuaire, sinon elle est à 0
  var $flagAuthentifie = null;
/**#@-*/


/**
*Constructeur de la classe LdapAnnu
*
*<p>Initialise la connection à l'annuaire LDAP</p>
*prend en entrée deux paramètres obligatoires:
*<br>
*<br>  hote : machine hote (ip ou nom dns )
*<br>  baseDN : emplacement dans l'arbre ou l'on est placé après login
*<br>
*<br>Et deux paramètres facultatifs:
*<br>
*<br> utilisateur : nom d'utilisateur ( cn=monUtil,dc=exemple,dc=com )
*<br> mDP : mot de passe de l'utilisateur
*<br>
*<br>
*<br> Remarque : il est inutile de faire appel a la fonction "connecte() car le constructeur le fait pour vous.
*<br> Si utilisateur et mdp sont renseignés, le constructeur fera directement un appel a authentifie
*<br>
*<br>
*<br>
*
* @param chaine hote de l'annuaire
* @param chaine Racine de l'annuaire
* @param chaine compte utilisateur sur l'annuaire
* @param chaine mot de passe de l'utilisateur
*
*/
function LdapAnnu($hote , $port , $protocoleVersion , $baseDN , $utilisateur = null , $mDP = null )
{
	$this->__construct($hote , $baseDN , $utilisateur , $mDP);
}

function __construct($hote , $port , $protocoleVersion , $baseDN , $utilisateur = null , $mDP = null )
{
	$this->flagConnecte=0;
	$this->flagAuthentifie=0;
	$this->baseDN = $baseDN;
	$this->hote = $hote;
	$this->port = $port;
	$this->protocoleVersion = $protocoleVersion;
	if (!$this->Connecte()) $this->afficheErreur();
	# si l'on a un argument de fourni pour l'utilisateur et le mot de passe, on tente de connecter:
	if ( !is_null($utilisateur ) &&  !is_null($mDP ) && $this->flagConnecte)
	{
		if (!$this->Authentifie($utilisateur , $mDP )) $this->afficheErreur();
	}
}

/**
* permet de connecter la classe à un annuaire LDAP
*
* <BR> retourne l'idConnexion si succès, null sinon
* <BR>
* <BR> Cette fonction n'est pas utile en principe, à moins de vouloir forcer une connexion autre que celle établie lors
* <BR> de l'instanciation de la classe.
* <BR>
* <BR> Argument facultatif:
* <BR> - hote: permet de désigner la machine accueillant l'annuaire
* <BR>         si cet argument n'est pas renseigné, la fonction tente de reconnecter à l'hote en cours.
* <BR>
* <BR> Retourne l'idConnexion si tout va bien, null sinon
* <BR>
* <BR>
*
* @param chaine nom de l'hôte
* @return entier identifiant de connection
*/
function connecte($hote = null)
{
	  # si l'hote n'est pas renseigné, prendre l'hote en cours de la classe
	  if ($hote != "" )  $this->hote = $hote;

	  # etablit la connexion si elle n'existe pas déja
	if ( !$this->flagConnecte )
	{
		$this->idConnexion=ldap_connect($this->hote,$this->port);

		if (is_null($this->idConnexion) )
		{
        		$this->flagConnecte = 0;
        		$this->afficheErreur();
        		return null;
        	}
		else $this->flagConnecte = 1;

	@ldap_set_option($this->idConnexion,LDAP_OPT_PROTOCOL_VERSION,$this->protocoleVersion);

	}

	if (DATA_SOURCE_LDAP_USE_TLS == "True")
	{
		if (!ldap_start_tls($this->idConnexion))
		{
			$this->afficheErreur();
		}
	}
	return $this->idConnexion;
}

/**
*déconnecte la classe de l'annuaire
*
* <BR>
* <BR> cette fonction n'est a priori pas utile mais est fournie pour permettre des utilisations exotiques de la classe
*  telles que des déconnexions/reconnexion à l'annuaire avec une seule instance.
* <BR>
*/
function deconnecte()
{
	if ( $this->idConnexion )
	{
		ldap_close( $this->idConnexion );
		$this->flagConnecte = 0;
	}
}

/**
* permet de s'autentifier auprès de l'annuaire en tant qu'utilisateur "utilisateur" avec pour mot de passe "mDP" . ( en principe, c'est fait a la création de l'instance)
*
* <BR>
* <BR> paramètres facultatifs :
* <BR>- utilisateur : nom de l'utilisateur en format LDAP (...,dc=test,dc=com)
* <BR>- mDP : mot de passe de l'utilisateur
* <BR>
* <BR> retourne le idSession si l'authentification est un succès et null sinon.
* <BR>
*
* @param chaine nom utilisateur format LDAP
* @param chaine Mot de passe de d'utilisateur
* @return entier identifiant de session
*/
function authentifie($utilisateur = null, $mDP = null )
{
	# Handle defaults
	if (  is_null($this->utilisateur)  ) $this->utilisateur = $utilisateur;
	if (  is_null($this->mDP) )          $this->mDP         = $mDP;
	if ( !is_null($this->utilisateur) )
	{
		$this->idSession = @ldap_bind( $this->idConnexion, $utilisateur, $mDP );
		if (!$this->idSession)
			{
				$this->flagAuthentifie=null;
				$this->afficheErreur();
				return null;
			}
	}
	else
	{
		$this->afficheErreur();
		return false;
	}
	$this->flagAuthentifie=1;
	return $this->idSession;
}

/**
*permet de déloguer de l'annuaire
*
* <BR>
* <BR> cette fonction n'est a priori pas utile mais est fournie pour permettre des utilisations exotiques de la classe
* telles que des fermetures de session/réauthentification à l'annuaire avec une seule instance.
* <BR>
*/
function delie()
{
	if ( $this->idSession )
	{
		ldap_unbind( $this->idConnexion );
		$this->idSession = null;
	}
}

/**
*permet d'effectuer une recherche brute dans l'annuaire LDAP
*
* <BR>
* <BR>prend deux arguments:
* <BR>- la chaîne à rechercher
* <BR>- le DN racine de la recherche
* <BR>
* <BR> retourne un tableau contenant le résultat brut de la recherche
* <BR>
* @param chaine Chaine de recherche
* @param chaine Racine de recherche
* @return tableau
*/
function recherche($SearchString,$dn="")
{
	# racine ou en dessous ?
	if ($dn!="") $dn = $dn.','.$this->baseDN;
		else $dn=$this->baseDN;

	# Pas de requêtes vides SVP: ca crashe PHP4.
	if ($SearchString == "") return 0;

	# si non connecté, quitter ...
	if (!$this->flagConnecte)
		$this->afficheErreur();

	#recherche en elle même.
	# retourne un identifiant de résultat, ou bien FALSE en cas d'erreur
	$this->idResultat=ldap_search($this->idConnexion,$dn,$SearchString);
	$this->resultat=ldap_get_entries($this->idConnexion, $this->idResultat);
	return $this->resultat;
}

/**
*permet d'effectuer une recherche ciblée dans l'annuaire LDAP
*
* <BR>
* <BR>prend trois arguments:
* <BR>- la chaîne à rechercher
* <BR>- le DN racine de la recherche
* <BR>- liste des objets Ldap à extraire (expérimental par Tristan)
* <BR>
* <BR> retourne un tableau contenant le résultat brut de la recherche
* <BR>
* @param chaine Chaine de recherche
* @param chaine Racine de recherche
* @param tableau Objets Ldap
* @return tableau
* @todo contrôle du tableau
*/
function rechercheCible($SearchString,$dn="",$ObjectsArray)
{
	# racine ou en dessous ?
	if ($dn!="") $dn = $dn.','.$this->baseDN;
		else $dn=$this->baseDN;

	# Pas de requêtes vides SVP: ca crashe PHP4.
	if ($SearchString == "") return 0;

	# si non connecté, quitter ...
	if (!$this->flagConnecte)
		$this->afficheErreur();

	#recherche en elle même.
	# retourne un identifiant de résultat, ou bien FALSE en cas d'erreur
	$this->idResultat=ldap_search($this->idConnexion,$dn,$SearchString,$ObjectsArray);
	$this->resultat=ldap_get_entries($this->idConnexion, $this->idResultat);
	return $this->resultat;
}

/**
*permet de classer le résultat de la recherche.
*
* <BR> (une recherche doit impérativement avoir été faite avant...)
* <BR>
* <BR> ATTENTION : (PHP 4 >= 4.2.0)
* <BR> argument
* <BR> - filtre pour classer ("givenname","lastname" ... )
* @param chaine Classer selon ce critere
* @return tableau tableau classé
*/
function classe($filtre)
{
	if ( $this->idResultat ) return ldap_sort( $this->idConnexion, $this->idResultat, $filtre );
	else return null;
}

/**
*
* fonction récursive interne
*
* @access private
*/
# fonction interne de parcours récursif
function recurs($objet, &$r)
{
	foreach($objet as $keys => $value)
		if (is_array($value)) $this->recurs($value,$r);
			else
				{
					if($keys != "count" || is_numeric($keys) )
					$r .= "\n[".$keys."] = ".$value;
				}
}

/**
*permet de récuperer l'intégralité des résultats de la recherche dans une chaine.
*
* <BR> (une recherche doit impérativement avoir été faite avant...)
* <BR>
*
@return chaine Chaine a afficher avec un echo
*/
function toutAfficher()
{
	if (!$this->resultat) return "résultat vide...";
	else
		{
		$r= "\n".count($this->resultat);
		$r .= "Données pour ".$this->resultat["count"]." entrées:<p>";
		$this->recurs($this->resultat,$r);
		return $r;
		}
}

/**
*permet d'afficher la dernière erreur en date.
*
*/
function afficheErreur()
{
	echo "<br>".ldap_error($this->idConnexion);
	echo "<br>".ldap_err2str(ldap_errno($this->idConnexion));
}

/**
*permet d'ajouter une entrée dans l'annuaire ldap a partir de son DN relatif
*
* <BR> Cette fonction ne prend en paramètre que le DN relatif au base DN de l'entrée
* <BR> Il est a noter que la racine de l'annuaire est automatiquement ajoutée, elle ne doit
* <BR> donc pas être comprise dans le DN.
* <BR>
* <BR> dans l'arbre "dc=masociete,dc=com"
* <BR> pour ajouter l'entrée "cn=adr,ou=societes,ou=granilim,dc=masociete,dc=com"
* <BR> on appellera: $objet_ldap->addEntry(cn=adr,ou=societes,ou=granilim);
* <BR>
* <BR> la racine de l'arbre est définie lors de la création de l'objet LdapAnnu: paramètre chaine $baseDN.
* <BR>
*
* @param chaine dn relatif de l'objet
* @param tableau Objets Ldap
* @return bool true/false
*/
function addEntry($dn,$arrayObjects)
{
	$dn = $dn.','.$this->baseDN;
		if(!@ldap_add($this->idConnexion,$dn,$arrayObjects))
		{
			$this->afficheErreur();
			return false;
		}
		else return true;
}

/**
*permet de supprimer un objet ldap a partire de son DN complet
*
* <BR> Cette fonction ne prend en paramètre que le DN de l'objet
* <BR> Il est a noter que la racine de l'annuaire est automatiquement ajoutée, elle ne doit
* <BR> donc pas être comprise dans le DN.
* <BR>
* <BR> dans l'arbre "dc=masociete,dc=com"
* <BR> pour supprimer l'objet "cn=adr,ou=societes,ou=granilim,dc=masociete,dc=com"
* <BR> on appellera: $objet_ldap->supprimer(cn=adr,ou=societes,ou=granilim);
* <BR>
* <BR> la racine de l'arbre est définie lors de la création de l'objet LdapAnnu: paramètre chaine $baseDN.
* <BR>
*
* @param chaine dn relatif de l'objet
*/
function supprimer($dn)
{
	$dn = $dn.','.$this->baseDN;
		if(!@ldap_delete($this->idConnexion,$dn))
		{
			$this->afficheErreur();
			return false;
		}
		else return true;
}

/**
*permet d'ajoutter un attribut a un objet
*
* <BR> la fonction prend deux paramètres obligatoires en entrée:
* <BR>  - le dn relatif (sans la racine) de l'objet sous forme de chaine
* <BR>  - la valeurs a ajouter sous forme de tableau
*
* @param chaine dn relatif de l'objet a modifier
* @param tableau données à ajouter
*/
function ajoutAttribut($dn,$donnees)
{
	$dn = $dn.','.$this->baseDN;
	if (!@ldap_mod_add($this->idConnexion,$dn,$donnees))
		$this->afficheErreur();
}

/**
*permet de supprimer un attribut d'un objet
*
* <BR> la fonction prend deux paramètres obligatoires en entrée:
* <BR>  - le dn relatif (sans la racine) de l'objet sous forme de chaine
* <BR>  - la valeurs à supprimer sous forme de tableau
*
* @param chaine dn relatif de l'objet a modifier
* @param tableau donnée à supprimer
*/
function suppressionUser($dn,$donnees)
{
	$dn = $dn.','.$this->baseDN;
	if (!@ldap_mod_del($this->idConnexion,$dn,$donnees))
	{
		$this->afficheErreur();
		return false;
	}
	else return true;
}

/**
*permet de remplacer les attributs d'un objet
*
* <BR> la fonction prend deux paramètres obligatoires en entrée:
*
* - le dn relatif (sans la racine) de l'objet sous forme de chaine <br>
* - la valeurs à supprimer sous forme de tableau
*
* @param chaine dn relatif de l'objet a modifier
* @param tableau donnée à supprimer
*/
function remplacementAttribut($dn,$donnees)
{
	$dn = $dn.','.$this->baseDN;
	if (!@ldap_mod_replace($this->idConnexion,$dn,$donnees))
	{
		$this->afficheErreur();
		return false;
	}
	return true;
}

/**
*permet d'inserer un utilisateur
*
* <BR> cette fonction prend trois paramètres obligatoires en entrée:
* <BR> - un tableau contenant l'ensemble des informations facultatives de l'utilisateur (mail, etc.. )
* <BR> - le nom de l'utilisateur en lui même sous forme de chaine ("toto"...)
* <BR> - la chaine contenant le chemin ou insérer: "uo=super,uo=utilisateurs"
* <BR>
*
* @param tableau tableau contenant les informations relative a l'utilisateur
* @param chaine chaine décrivant l'utilisateur > common name de l'objet
* @param chaine chemin ou inserer l'objet
*/
function ajoutUser($infos,$utilisateur,$chemin=null)
{
	if ($chemin == "" ) $chemin = $this->baseDN;
		else $chemin = $chemin.','.$this->baseDN;

	$infos["cn"]=$utilisateur;
	$infos["objectclass"]=array("officeperson","inetorgperson");
	$r=@ldap_add($this->idConnexion, "cn=".$utilisateur.",".$chemin, $infos);

	if (ldap_errno($this->idConnexion) == 68 )
	{
		$this->afficheErreur();
		return false;
	}
	else
		if (ldap_errno($this->idConnexion) != 0 )
		{
			$this->afficheErreur();
			return false;
		}
	return true;
}

/**
*permet d'inserer une OU > En cours
*
* <BR> cette fonction prend deux paramètres obligatoires en entrée:
* <BR> - le nom de l'OU sous forme de chaine ("toto"...)
* <BR> - la chaine contenant le chemin ou insérer: "ou=super,ou=utilisateurs"
* <BR>
*
* @param chaine chaine décrivant l'utilisateur > common name de l'objet
* @param chaine chemin ou inserer l'objet
*/
function ajoutOU($nom,$chemin=null)
{
	if ($chemin == "" ) $chemin = $this->baseDN;
		else $chemin = $chemin.','.$this->baseDN;

	$infos["ou"]=$nom;
	$infos["objectClass"]=array("top","organizationalunit");

	$r=ldap_add($this->idConnexion, "ou=".$nom.",".$chemin, $infos);

	if (ldap_errno($this->idConnexion) == 68 )
		$this->afficheErreur();
	else
	if (ldap_errno($this->idConnexion) != 0 )
		$this->afficheErreur();
  }
}
?>
