<?php
/** **************************************************************************
 * Copyright (C) 2004-2006 Olivier Cortes <olive@deep-ocean.net>
 * Copyright (C) 2004-2016 Eric Seigne <eric.seigne@ryxeo.com>
 * Copyright (C) 2016-2018 Eric Seigne <eric.seigne@cap-rel.fr>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * ***************************************************************************
 *
 * Author  : Olivier Cortes
 *           mailto:olivier.cortes@ryxeo.com
 *           http://www.ryxeo.com/
 * Date    : 14/05/2004
 * Licence : GNU/GPL Version 2
 *
 * Author  : Eric Seigne
 *           mailto:eric.seigne@ryxeo.com
 *           http://www.ryxeo.com/
 * Date    : 2/10/2004
 * Licence : GNU/GPL Version 2
 *
 * Description:
 * ------------
 *
 * @version    $Id: AbE_Tools.php,v 1.9 2005/05/26 16:06:53 erics Exp $
 * @author     Olivier Cortes
 * @author     Eric Seigne
 * @project    AbulÉdu Pro
 * @copyright  Olivier Cortes 14/05/2004, 08/2005
 * @copyright  Eric Seigne 2/09/2004
 * @modifier   Frucot Jean-Louis 16/07/2009 mailto:frucot.jeanlouis@pourlecole.net
 * ************************************************************************* */

global $system_gid_min, $system_gid_max, $system_uid_min, $system_uid_max;
$system_gid_min=trim(`grep ^GID_MIN /etc/login.defs | awk '{print $2}'`);
$system_gid_max=trim(`grep ^GID_MAX /etc/login.defs | awk '{print $2}'`);
$system_uid_min=trim(`grep ^UID_MIN /etc/login.defs | awk '{print $2}'`);
$system_uid_max=trim(`grep ^UID_MAX /etc/login.defs | awk '{print $2}'`);

function save_config($config) {
  global $conf_fichier_conf;
  $textetosave  	= $config->GetTextToSave();
  $commande 		= "
cat << EOF > $conf_fichier_conf
$textetosave
EOF
chmod 644 $conf_fichier_conf
";
  $res = fichier_script($commande,"iconnect");
  return $res;
}

if (!function_exists('maxlengthcsv')) {
  function maxlengthcsv($file) {
    // Retourne la taille maxi qu'on peut avoir dans un fichier csv
    // ne sera plus utilisé en php5
    if(! file_exists($file)) {
      return FALSE;
    }
    
    $length = 1024;
    $array = file($file);
    for($i=0;$i<count($array);$i++) {
      if ($length < strlen($array[$i])) {
	$length = strlen($array[$i]);
      }
    }
    unset($array);
    return $length;
  }
}

if (!function_exists('array_trim')) {
  function array_trim(&$arr) {
    /**
     * Fait un trim propre d'un tableau passé en paramètre
     * cette fonction marche en récursif comme ça si on a
     * un tableau dans un tableau ça marche tout seul
     *
     * @version   1.0
     * @author    Minots Estich <minots@d0x.de> (dans les contribs de http://fr.php.net/array) modifications: eric seigne
     * @access    public
     */
    if(! is_array($arr))
      return $arr;
    //    print_r($arr);
    while (list($key,$val)=each($arr)) {
      if (is_string($val)) {
	$arr[$key] = trim($val);
	//      print "on a $key / $val :" . $arr[$key] . ":<br>\n";
      }
      if (is_array($val)) {
	$arr[$key] = array_trim($val);
      }
    }
    return $arr;
  }
 }

function abstraction_indexwebadmin($s) {
  // On "traduit" les @@MOTS@@ qui sont dans $conf_fichier_conf
  global $config;
  if(is_string($s)) {   
    if(strpos($s,"@") !== false) {
      if(! is_object($config)) {
	$config = new AbE_General_Config();
      }
      $tab = explode("@@",$s);
      $texte = $tab[0];
      for($i = 1; $i < count($tab); $i++) {
	if($i%2 == 0)
	  $texte .= $tab[$i];
	else {
	  // use the variable already defined in AbE_Config_Set_Variables.php
	  // ex : @@USAGERS_TITLE@@ ==> ${$tab[$i]} == ${USAGERS_TITLE} == "Élèves"
	  global ${$tab[$i]};
	  $texte .= ucfirst(${$tab[$i]});
	}
      }
    }
    else {
      $texte = $s;
    }
  }
  return $texte;
}

if (!function_exists('nbaleatoire')) {
  function nbaleatoire() { 
    // Fonction qui retourne un nombre aléatoire
    mt_srand((double)microtime()*1000000); 
    $num = mt_rand(46,122); 
    return $num; 
  } 
}

if (!function_exists('gencharactere')) {
  function gencharactere() { 
    // Fonction qui retourne un caractère classique cad
    // soir un chiffre, fois une lettre sans accent
    do{ 
      $num = nbaleatoire(); 
    } while ( ( $num > 57 && $num < 65 ) || ( $num > 90 && $num < 97 )); 
    $char = chr($num); 
    return $char; 
  }
}

//-------------------------------------------------
/**
 * Retourne une clé de la bonne taille
 *
 * @version 	1.0
 * @author	Eric Seigne
 * @access    private
 * @return    -1 on error
 */
if (!function_exists('saltstr')) {
  function saltstr($size) { 
    for($i=1;$i<=$size;$i++) { 
      $string = $string.gencharactere(); 
    } 
    return $string; 
  }
}

//-------------------------------------------------
/**
 * Retourne une clef de cryptage en fonction de
 * l'algorithme d'encryption qu'on désire utiliser !
 *
 * @version 	1.0
 * @author	Eric Seigne
 * @access    private
 * @return    -1 on error
 */
if (!function_exists('generer_clef')) {
  function generer_clef($type) { 
    if($type == "des-std") { /* des-standard salt (2 chr) */ 
      return saltstr(2); 
    } else if ($type == "des-ext") { /* des-extended salt (9 chr) */ 
      return saltstr(9); 
    } else if ($type == "md5") { /* md5 salt (12 char, starts with $1$
				    ends with $ */ 
      $tmpslt = saltstr(8); 
      return sprintf("$1$%s$",$tmpslt); 
    } else if ($type == "blowfish") { /* blowfish salt (16 char, starts
				         with $2$ ends with $ */ 
      $tmpslt = saltstr(13); 
      return sprintf("$2$%s$",$tmpslt); 
    } else { /* Catch everything else to des-std */ 
      return saltstr(2); 
    }
  }
}

//-------------------------------------------------
/**
 * Pose le fichier CSV passé en paramètre dans un tableau
 * ad hoc ...
 *
 * @version   1.0
 * @author    Eric Seigne
 * @access    private
 * @return    array
 */
if (!function_exists('file_csv')) {
  function file_csv($file,$sep) { 
    if(! file_exists($file)) {
      return FALSE;
    }
    
    $f = file($file);
    for($i = 0; $i < count($f); $i++) {
      $t = explode($sep,$f[$i]);
      $tab[$i] = $t;
    }
    //    print "on cherche dans $file: <pre>" . print_r($tab) . "</pre>\n";
    return $tab; 
  }
}

//-------------------------------------------------
/**
 * Pose le fichier CSV passé en paramètre dans un tableau
 * ad hoc ... avec 1er champ = clé du tableau
 *
 * @version   1.0
 * @author    Eric Seigne
 * @access    private
 * @return    array
 */
function file_cache_csv($file,$sep) { 
  $f = @file($file);
  for($i = 0; $i < count($f); $i++) {
    $t = explode($sep,$f[$i]);
    $tab[$t[0]] = $t;
  }
  return $tab;
}

function to_group($gid) {
  // FIXME : this function is a duplicate from another one in abe_groups, but
  // it helps sometimes, not beeing obliged to instantiate an abe_groups just
  // for that.
  //
  @assert(is_numeric($gid));
  
  $group = trim(`grep ":$gid:" /etc/group | cut -d: -f1`);
  if ("" == $group) {
    return false;
  }
  else {
    return $group;
  }
}

function to_gid($groupname) {
  // FIXME : this function is a duplicate from another one in abe_groups, but
  // it helps sometimes, not beeing obliged to instantiate an abe_groups just
  // for that.
  
  $gid = trim(`grep "^$groupname:" /etc/group | cut -d: -f3`);
  
  if ("" == $gid) {
    return false;
  }
  else {
    return $gid;
  }
}

// ---------------------------------------------
// On vire les espaces ponctuations etc.
// agressif = 0 on est gentil - on ne fait rien
//          = 1 on vire les accents + tout en minuscules
//          = 2 on vire aussi les tirets et espaces (login / pass)
function anti_speciaux($t, $agressif=0) {
  if(trim($t) == "")
  return ;


  if($agressif >= 1) {
    $texte = strtolower($t);
    $texte = remove_accents($texte);
 
    if($agressif == 2) {
      // On vire les espaces
      $texte = str_replace(" ","",$texte);
      $texte = str_replace("-","",$texte);
      $texte = str_replace("_","",$texte);
//le python accepte - et _ dans le login apres une lettre

      }
  }
  else {
       $texte=$t;
       }

return $texte;
}

/*
 * START LTSConf functions
 */
function compare_ip($a,$b) {
  // Comparaison de deux adresses IP
  // 0 adresses identiques
  // -1 adresse a < adresse b
  // 1 adresse a > adresse B
  // 2 erreur !
  // FIXME : Il faudra prendre en compte les netmask dans la prochaine version !
  $taba = explode(".",$a);
  $tabb = explode(".",$b);
  $retour = 0;
  for($i = 0; ($i < count($taba)) && ($retour == 0); $i++) {
    
    if($taba[$i] < $tabb[$i]) {
      $retour = -1;
    }
    else if($taba[$i] > $tabb[$i]) {
      $retour = 1;
    }
  }
  return $retour;
}

//---------------------------------------------------------
//Retourne la liste des terminaux présents dans les fichiers
//de conf. Le tableau de retour est à trois dim.
function liste_terminaux($fic_dhcp = ""){
  abe_api_debug("entering liste_terminaux()", 2);
  global $conf_fic_dhcp, $conf_zone;

  //Pour eviter que la fonction terminaux leases ne retourne des tx deja fixés
  global $tab_macaddr_terminaux;
  if($fic_dhcp == "")
    $fic_dhcp = $conf_fic_dhcp;

  $i = 0;
  $fdhcp = @fopen($fic_dhcp,"r");
  if($fdhcp){
    while(!feof($fdhcp)){
      $ligne = fgets($fdhcp,4096);
      if(ereg("(host )(.*)( \{)",$ligne,$res)){
	
	//Si on a des VLAN le nom du poste est complet avec le nom du domaine (conf_zone) au final
	//Or il ne nous le faut pas dans son utilisation ...
	$tab[$i]["host"] = str_replace(".".$conf_zone, "", $res[2]);
	$tab[$i]["ip"] = @gethostbyname($res[2]);
	
	$ligne = fgets($fdhcp,4096);
	
	if(ereg("(option option-128)",$ligne, $res)){
	  $ligne = fgets($fdhcp,4096);
	}
	
	if(ereg("(option option-129)",$ligne, $res)){
	  $ligne = fgets($fdhcp,4096);
	}
	
	if(ereg("(hardware ethernet )(.*)(;)",$ligne,$res)){
	  $tab[$i]["mac"] = strtolower($res[2]);
	  $tab_macaddr_terminaux[] = strtolower($res[2]);
	  abe_api_debug("found a TX: " . $tab[$i]['host'] . ", mac=" . $tab[$i]['mac'], 5);
	  
	}

	//last image used
	$file = "/home/machines/" . $tab[$i]["host"] . "/.maquette";
	if(file_exists($file)) {
	  $contenu = file_get_contents($file);
	  $tab[$i]["maquette"] = $contenu;
	}

	//last boot
	$file = "/home/machines/" . $tab[$i]["host"] . "/horizon-computers_last_seen";
	if(file_exists($file)) {
	  $contenu = file_get_contents($file);
	  $tab[$i]["lastboot"] = $contenu;
	}

	//boot option
	$file = "/home/machines/" . $tab[$i]["host"] . "/boot.pxe";
	if(file_exists($file)) {
	  $contenu = file_get_contents($file);
	  if(strpos($contenu,"=restaure")) {
	    $tab[$i]["boot"] = "Restaure";
	  }
	  else if(strpos($contenu,"=backup")) {
	    $tab[$i]["boot"] = "Sauvegarde";
	  }
	  else if(strpos($contenu,"initrd=initrd-p2.gz")) {
	    $tab[$i]["boot"] = "Terminal léger v4";
	  }
	  else if(strpos($contenu,"=startx")) {
	    $tab[$i]["boot"] = "Terminal léger v4";
	  }
	  else if(strpos($contenu,"append hd0")) {
	    $tab[$i]["boot"] = "Disque dur";
	  }
	  else if(strpos($contenu,"localboot 0x80")) {
	    $tab[$i]["boot"] = "Disque dur";
	  }
	  else if(strpos($contenu,"nbdport=")) {
	    $tab[$i]["boot"] = "Terminal léger v5";
	  }
	}
	$i++;
      }
    }
    fclose($fdhcp);
  }
  //Il faut trier le tableau & virer les doublons sur le champ "mac"
  //
  //DESACTIVÉ PAR OLIVE 2006 04 05, sinon seul le premier TX est affiché
  // sans doute une incompatibilité avec une array_* de php5 ? non vérifié.
  //if(count($tab))
  //  $tab = array_unique($tab);
  return $tab;
}



function liste_terminaux_leases(){
  //Retourne la liste des terminaux présents dans le fichier de leases DHCP
  global $conf_fic_dhcp_leases;

  //Pour eviter que la fonction terminaux leases ne retourne des tx deja fixés
  global $tab_macaddr_terminaux;
  $i = 0;  
  $fdhcp = @fopen($conf_fic_dhcp_leases,"r");
  if($fdhcp){
    while(!feof($fdhcp)) {
      $ligne = fgets($fdhcp,4096);
      if(ereg("(lease )(.*)( \{)",$ligne,$res)) {
	$active = 0;
	$name = "";
	$ip =  $res[2];
	//print "<pre>$ip</pre>\n";
	//dans les lignes suivantes on devrait avoir la mac
	for(;trim($ligne) != "}" && !feof($fdhcp); $ligne = fgets($fdhcp,4096)) { 
	  if(ereg("(hardware ethernet )(.*)(;)",$ligne,$res)) {
	    $mac = strtoupper($res[2]);
	    //print "<pre> -> $ip -> $mac</pre>\n";
	  }
	  if(ereg("(client-hostname )(.*)(;)",$ligne,$res)) {
	    $name = $res[2];
	    //print "<pre> -> $ip -> $mac " . $res[2] . "</pre>\n";
	  }
	  if(ereg("(binding state )(.*)(;)",$ligne,$res)) {
	    if(trim($res[2]) == "active") {
	      $active = 1;
	    }
	  }
	}
	if($active) {
	    $tabip[$mac] = $ip;
	    $tabmac[] = $mac;
	    $tabname[$mac] = $name;	  
	}
      }
    }
    fclose($fdhcp);
  }
  if(count($tabmac)>0) {
    $tabmactemp = array_unique($tabmac);
    $i = 0;
    foreach($tabmactemp as $macaddr) { 
      //On supprime les TX deja fixes
      if(is_array($tab_macaddr_terminaux)) {
	if(!in_array(strtolower($macaddr), array_map('strtolower',$tab_macaddr_terminaux))) {
	  $tab[$i]["mac"] = $macaddr;
	  $tab[$i]["ip"] = $tabip[$macaddr];
	  $tab[$i]["host"] = _("en attente");
	  if($tabname[$macaddr] != "")
	    $tab[$i]["host"] .= " (" . $tabname[$macaddr] . ")";
	  $i++;
	}
      }
      else {
	$tab[$i]["mac"] = $macaddr;
	$tab[$i]["ip"] = $tabip[$macaddr];
	$tab[$i]["host"] = _("en attente");
	if($tabname[$macaddr] != "")
	  $tab[$i]["host"] .= " (" . $tabname[$macaddr] . ")";
	$i++;
      }
    }
  }
  return $tab;
}

function hostname_deja_pris($hostname,$vlan=""){
  //Recherche si ce nom de machine n'est pas déjà utilisé
  //plus pratique que le fopen quand on a des vlan: on cherche si ce nom est deja
  //pris sur l'integralite du reseau ... c'est un choix
  global $conf_dir_bind;
  if(trim($vlan) != "")
    $res = exec("grep -w " . $hostname . "." . $vlan . " " . $conf_dir_bind . "/* | wc -l");
  else
    $res = exec("grep -w " . $hostname . " " . $conf_dir_bind . "/* | wc -l");

  if(trim($res) > 0)
    $trouve = 1;
  else
    $trouve = 0;
  return $trouve;
}

function erreur_mac_addr($mac){
  // Vérifie que l'adresse MAC est correcte ; retourne 0 pas d'erreur, 1 si erreur
  $retour = 0;
  // Vérification que cette adresse ne soit pas déjà dans
  // le fichier de dhcpd.conf
  $liste = liste_terminaux();
  for($i = 0; ($i < count($liste)) && ($retour == 0); $i++) {
    
    if($liste[$i]["mac"] ==  strtolower($mac)) {
      
      $retour = 1;
    }
  }
  return $retour;
}

//-------------------------------------
//Calcule la prochaine adresse ip libre
function prochaine_ip_libre($fic_bind_rev,$min_ip,$dhcp_bas){
  $temp = explode(".",$dhcp_bas);
  $dhcp_bas_local = $temp[3];
  if($dhcp_bas_local < 0 || $dhcp_bas_local > 250) {
    return -1;
  }

  $fd = @fopen($fic_bind_rev, "r");
  $retour = 0;
  $tabip = array();
  if($fd) {
    while (!(feof($fd))) {
      $buffer = fgets($fd);
      unset($tab);
      if(preg_match("/(.*).*IN.*PTR.*/",$buffer,$tab)) {
	$tabip[] = chop($tab[1]);
      }
    }
    fclose($fd);
  }

  // Comment fait-on pour ... recuperer les trous laissés par la suppression
  // des vieux TX ? on trie le tableau précédent et on le parcours à la recherche
  // du 1er trou ...
  asort($tabip);
  for($i = $min_ip; $retour == 0 && $i < $dhcp_bas_local; $i++) {
    if(! in_array($i, $tabip)) {
      $retour = $i;
    }
  }
  
  // FIXME : Il faut vérifier que cette IP soit > à min
  if ($retour < $min_ip) {
    $retour = $min_ip;
  }
  else { 
    $retour = $retour;
  }

  //Si l'ip fixe deborde sur la zone de dhcp ... on stoppe tout !!!
  if($retour > 255 || $retour > $dhcp_bas_local)
    $retour = -1;
  return $retour;
}

function recup_infos($h){
  // Retourne un tableau de configuration actuelle du terminal X
  
  global $conf_fic_ltsconf;
  
  /*
   * FIXME : aller chercher les defaults de la section [global]
   * plutôt que de les remplir ici à la main avant les infos du terminal
   * en cours...
   */
  
  // Le tableau est composé des champs suivants :
  $tab["computerarch"] = "default"; // Rycks added
  $tab["serverx"] = "";
  $tab["mouse"] = "";
  $tab["mousetty"] = "";
  $tab["mousebtns"] = "";
  $tab["video1"] = "";
  $tab["bpp"] = "";
  $tab["sound"] = "none";
  $tab["appendopt"] = "";
  $tab["windowslicence"] = "";
  $tab["localstorage"] = "Y";
  $tab["swapsize"] = "";
  
  $sortir = 0;
  $fp = fopen($conf_fic_ltsconf,"r");
  if($fp) {
    
    while(!feof($fp)) {
      
      $ligne = fgets($fp,4096);
      
      if(ereg("\[$h\]",$ligne)) {
	
	//print "$ligne<br />";
	//On est au début de la zone
	while( ! feof($fp) && ! $sortir) {
	  $ligne = fgetcsv($fp,4096,"=");
	  //print $ligne[0] . " = " . $ligne[1] . " <br />";
	  array_trim($ligne);
	  switch($ligne[0]) {
	  case "COMPUTERARCH":
	    $tab["computerarch"] = $ligne[1];
	    if ("none" == $tab["computerarch"] ) {
	      /*
	       * anything other than thin client is not managed by LTSP/LTSConf
	       * so we exit. Any other field than computerarch will not be used anyway.
	       */
	      $sortir = 1;
	    }
	    // covert old types.
	    if ($tab["computerarch"] == "maui") {
	      $tab["computerarch"] = "ltsp";
	    }
	    else if ($tab["computerarch"] == "p1") {
	      $tab["computerarch"] = "ltsp3";
	    }
	    
	    break;
	  case "XSERVER":
	    if(trim($tab["serverx"]) != "")
	      $tab["serverx"] = $ligne[1];
	    else
	      $tab["serverx"] = "auto";
	    break;
	  case "SOUND":
	    if ($ligne[1] == "N") {
	      $tab["sound"] = "none";
	    }
	    break;
	  case "SOUND_DAEMON":
	    $tab["sound"] = $ligne[1];
	    break;
	  case "APPENDOPT":
	    $tab["appendopt"] = $ligne[1];
	    break;
	  case "WINDOWSLICENCE":
	    $tab["windowslicence"] = $ligne[1];
	    break;
	  case "LOCAL_STORAGE":
	    $tab["localstorage"] = $ligne[1];
	    break;
	  case "X_MOUSE_PROTOCOL":
	    $tab["mouse"] = $ligne[1];
	    break;
	  case "X_MOUSE_DEVICE":
	    $tab["mousetty"] = $ligne[1];
	    break;
	  case "X_MOUSE_BUTTONS":
	    $tab["mousebtns"] = $ligne[1];
	    break;
	  case "X_MODE_0":
	    $tab["video1"] = $ligne[1];
	    break;
	  case "X_COLOR_DEPTH":
	    $tab["bpp"] = $ligne[1];
	    break;
	  case "SWAP_BLOCKS":
	    $tab["swapsize"] = $ligne[1];
	    break;
	  case "ISA_NIC":
	    $tab["nic"] = $ligne[1];
	    break;
	  case "XkbModel":
	  case "XKBMODEL":
	    $tab["keyboard"] = $ligne[1];
	    break;
	  case "XkbLayout":
	  case "XKBLAYOUT":
	    $tab["keyboardlang"] = $ligne[1];
	    break;
	  case "XKBNUMPAD":
	    $tab["keynumpad"] = $ligne[1];
	    break;
	  case "PRINTER_0_DEVICE":
	    $tab["printerdevice"] = $ligne[1];
	    break;
	  case "PRINTER_0_TYPE":
	    $tab["printertype"] = $ligne[1];
	    break;
	  case "WRITE_ONLY" :
	    $tab["printerdevice"] .= "w";
	    break;
	  case "PRINTERSAVAILABLE" :
	    $tab["printersavailable"] = $ligne[1];
	    break;
	  case "MODULE_01":
	    if($ligne[1] == "i810")
	      $tab["serverx"] = "XF86_SVGA-i810";
	    else if($ligne[1] == "radeon")
	      $tab["serverx"] = "auto-radeon";
	    $tab["MODULE_01"] = $ligne[1];
	    break;
	  case "MODULE_02":
	    if($ligne[1] == "i810")
	      $tab["serverx"] = "XF86_SVGA-i810";
	    else if($ligne[1] == "radeon")
	      $tab["serverx"] = "auto-radeon";
	    $tab["MODULE_02"] = $ligne[1];
	    break;
	  case "MODULE_03":
	    if($ligne[1] == "i810")
	      $tab["serverx"] = "XF86_SVGA-i810";
	    else if($ligne[1] == "radeon")
	      $tab["serverx"] = "auto-radeon";
	    $tab["MODULE_03"] = $ligne[1];
	    break;
	  case "OptionParamS0":
	    if ($ligne[1] == "sw_cursor" and $tab["serverx"] == "XF86_SVGA")
	      $tab["serverx"] = "XF86_S3-3D";
	    break;
	  }
	  //Si on est sur un autre poste
	  if(ereg("\[.*\]",$ligne[0])) {
	    //	    print "on a un autre concurent : $ligne[0]\n";
	    $sortir = 1;
	  }
	}
      }
    }
    fclose($fp);
  }
  return $tab;
}

function make_dhcp_block($hostname, $mac, $computerarch, $nic = "") {
  // don't suppress the empty lines before and after the DHCP block, they
  // are needed for the "sed" operation (in fixe_tx()) to succeed !
  global $conf_ip_server;
  if($computerarch == "none") {
    return "\\\n\\\thost $hostname {\\\n\\\t\\\thardware ethernet $mac;\\\n\\\t\\\t	fixed-address $hostname;\\\n\\\t}\\\n\\\n";
  }
  else {
    $isa_block="";
    if ($computerarch == "ltsp5") {
      // FIXME : finish this !
      $kernel = "kernel-maui.nbi";
    }
    else if ($computerarch == "qooqv2") {
      // FIXME : finish this !
      $kernel = "kernel-qooqv2";
    }
    else if ($computerarch == "ltsp44") {
      // FIXME : finish this !
      $kernel = "kernel-maui.nbi";
    }
    else if ( $computerarch == "ltsp3" ) {
      if (!empty($nic) && $nic != "none") {
	$isa_block = '\\n\\t\\toption option-128 e4:45:74:68:00:00;\\n\\t\\toption option-129 "NIC=' . $nic . '";';
      }
      $kernel = "kernel-p1.nbi";
    }
    else if ( $computerarch == "ltsp" ) {
      $kernel = "kernel-p2.nbi";
    }
    else {
      $kernel = "kernel-default.nbi";
    }
    return "\\\n\\\thost $hostname {\\\n\\\t\\\thardware ethernet $mac;$isa_block\\\n\\\t\\\tfixed-address $hostname;\\\n\\\t\\\tif substring (option vendor-class-identifier, 0, 9) = \"PXEClient\" {\\\n\\\t\\\t\\\tfilename \"pxelinux.0\";\\\n\\\t\\\t}\\\n\\\t\\\telse {\\\n\\\t\\\t\\\tfilename \"$kernel\";\\\n\\\t\\\t}\\\n\\\t\\\toption root-path \"$conf_ip_server:\/opt\/$computerarch\/i386\";\\\n\\\t}\\\n\\\n";
  }
}

function make_syslinux_block($arch, $appendopt, $specialcase="", $imagename="", $action) {
  global $conf_ip_server;

  //Attention a mettre à jour en cas d'upgrade de clonezilla mais on garde le nom complet pour
  //les machines qui peuvent marcher en version x du kernel mais pas en x+1 ... expérience vécue
  $kernelclonezilla = "vmlinuz-4.19.0-4-amd64";
  $initrdclonezilla = "initrd.img-4.19.0-4-amd64";

  //nouveau cas particulier mai 2014 : en on a affaire a une image GPT il
  //faut passer sur le nouveau clonezilla
  if($imagename != "" && $action == "restaure") {
    //On regarde si on a un clonezilla officiel et si c'est le cas on ne reflechis pas on l'utilise
    if(is_dir("/opt/clonezilla")) {
      //On passe sur clonezilla moderne 
      return "DEFAULT vesamenu.c32
MENU INCLUDE messages/graphics.conf
TIMEOUT 50
TOTALTIMEOUT 9000

label abuledu
	kernel $kernelclonezilla
	append SCREEN_01=restaure initrd=$initrdclonezilla nofb live-config boot=live noswap nolocales edd=on ocs_live_extra_param=\\\"\\\" keyboard-layouts=NONE ocs_live_batch=\\\"no\\\" locales=\\\"fr_FR.UTF-8\\\" ip=frommedia nosplash noprompt netboot=nfs nfsroot=$conf_ip_server:/opt/clonezilla/ ocs_server=\\\"$conf_ip_server\\\" ocs_daemonon=\\\"ssh\\\" ocs_prerun=\\\"mount -t nfs -o vers=3 $conf_ip_server:/opt/ltsp44/i386 /opt\\\" ocs_prerun1=\\\"/opt/etc/screen.d/restaure2\\\" ocs_live_run=\\\"\\\" ocs_postrun=\\\"\\\" net.ifnames=0 biosdevname=0

";
     
    }
  }

  //cas particulier si on backup/restaure ca ne marche qu'en ltsp44 ...
  if($specialcase == "backup-restaure") {
    $arch="ltsp44";
  }
  switch($arch) {
  case "ltsp":
    $kernel = "kernel-p2";
    $initrd = "initrd-p2.gz";
    break;
  case "ltsp44":
    $kernel = "kernel-maui";
    $initrd = "initrd-maui.gz";
    break;
  case "qooqv2":
    $kernel = "kernel-qooqv2";
    $initrd = "initrd-qooqv2.gz";
    break;
  case "ltsp5": //Attention specifique, merci LTSP5
    return "DEFAULT vesamenu.c32
MENU INCLUDE messages/graphics.conf
TIMEOUT 50
TOTALTIMEOUT 9000

label abuledu
	kernel vmlinuz
	append ro initrd=initrd.img $appendopt quiet splash nbdport=2001
";
    break;
  default:
    $kernel = "kernel-default";
    $initrd = "initrd-default.gz";
    break;
  }

  //Attention si on a un clonezilla "officiel" on l'utilise en priorité
  //(version de kernel up to date, prise en compte cartes reseaux modernes, windows 8 etc.)
  if(is_dir("/opt/clonezilla") && $specialcase == "backup-restaure") {
    return "DEFAULT vesamenu.c32
MENU INCLUDE messages/graphics.conf
TIMEOUT 50
TOTALTIMEOUT 9000

label abuledu
	kernel $kernelclonezilla
        append SCREEN_01=backup initrd=$initrdclonezilla nofb live-config boot=live noswap nolocales edd=on ocs_live_extra_param=\\\"\\\" keyboard-layouts=NONE ocs_live_batch=\\\"no\\\" locales=\\\"fr_FR.UTF-8\\\" ip=frommedia nosplash noprompt netboot=nfs nfsroot=$conf_ip_server:/opt/clonezilla/ ocs_server=\\\"$conf_ip_server\\\" ocs_daemonon=\\\"ssh\\\" ocs_prerun=\\\"mount -t nfs -o vers=3 $conf_ip_server:/opt/ltsp44/i386 /opt/\\\" ocs_prerun1=\\\"/opt/etc/screen.d/backup2\\\" ocs_live_run=\\\"\\\" ocs_postrun=\\\"\\\"

";
    
  }
  else {
    return "DEFAULT vesamenu.c32
MENU INCLUDE messages/graphics.conf
TIMEOUT 50
TOTALTIMEOUT 9000

label abuledu
	kernel $kernel
	append init=/linuxrc $appendopt rw root=/dev/ram0 initrd=$initrd MOPTS=nolock,ro,wsize=2048,rsize=2048 quiet splash
";
  }
}

function fixe_tx($tab, $mac, $ip, $ip_actuelle, $hostname, $addnewtx, $have_to_build_syslinux=1, $local_arch_for_dhcp="") {
  // Fixe le tx en question, si addnewtx est = 1 c'est un ajout
  // et = 0 c'est un update ... [rq 2008 addnewtx semble ne plus servir a rien]
  // have_to_build_syslinux=0 si on ne veut pas re-creer le syslinux
  // par exemple dans backup/restaure du poste on gere le syslinux localement

  global $conf_fic_bind, $conf_fic_bind_rev,
    $conf_zone,
    $conf_fic_ltsconf, $conf_fic_dhcp, 
    $conf_rep_syslinux,
    $conf_ip_network_base,
    $conf_fic_dhcp_leases, $computerarch,
    $fichier_script_output, $vlan, $conf_dir_bind, $conf_dir_dhcp,
    $metamode, $comptemachine;

  //parachute en cas de pb si comptemachine n'est pas definie avant l'appel de cette fonctio
  if($comptemachine == "")
    $comptemachine = $hostname;

  if(trim($mac) == "" ||
     trim($ip) == "" ||
     trim($hostname) == "")
    return -1;

  $config = new AbE_General_Config();
  //On gere le cas des VLAN et donc les fichiers DNS ET DHCP sont differents
  if(trim($vlan) != "") {
    $fic_bind        = $conf_dir_bind . "/db." . $vlan;
    $fic_bind_rev    = $conf_dir_bind . "/db." . $vlan . ".rev";
    $fic_dhcp        = $conf_dir_dhcp . "/dhcpd.hosts-" . $vlan . ".conf";
    $ip_network_base = $config->GetValue(strtoupper($vlan) . "_BASE");
    $dhcphostname    = $hostname . "." . $vlan . "." . $conf_zone;
    $bindhostname    = $hostname . "." . $vlan . "." . $conf_zone . ".";
  }
  else{
    $fic_bind        = $conf_fic_bind;
    $fic_bind_rev    = $conf_fic_bind_rev;
    $fic_dhcp        = $conf_fic_dhcp;
    $ip_network_base = $conf_ip_network_base;
    $dhcphostname    = $hostname;
    $bindhostname    = $hostname . "." . $conf_zone . ".";
  }
  
  //Si on a passe une adresse ip complete on en prends que le dernier bloc
  //toujours limitation reseau de classe C
  if(strpos($ip,".") > 0) {
    $tip = explode(".",$ip);
    $ip = $tip[count($tip)-1];
  }
  $retour = "";
  
  $dns    = "$hostname	IN	A	$ip_network_base.$ip";
  $dnsrev = "$ip			IN	PTR	$bindhostname";
  if(trim($computerarch) == "")
    $computerarch = "ltsp";

  if($local_arch_for_dhcp == "") {
    $dhcp = make_dhcp_block($dhcphostname, $mac, $computerarch, $tab["nic"]);
  }
  else {
    //Si on est en mode backup/restaure
    $dhcp = make_dhcp_block($dhcphostname, $mac, $local_arch_for_dhcp, $tab["nic"]);
  }

  // ce qu'il faut pour le PXEBoot que si on le demande
  if($have_to_build_syslinux) {
    $syslinuxfile = "$conf_rep_syslinux/01-" . strtolower(strtr($mac,":","-"));
    $syslinuxsymlink = "/home/machines/" . $comptemachine . "/boot.pxe";
    $syslinux = make_syslinux_block($computerarch,$tab["appendopt"]);
  }
	
  // Imprimante branchée sur le TX
  $tab["printerdevice"] = "";
  $tab["printertype"]   = "";
  if($tab["printer"] != "") {
    if(strstr($tab["printer"],"lp0w")) {
      $tab["printerdevice"] = "/dev/lp0";
      $writeonly            = "Y";
    }
    else {
      $writeonly            = "";
      $tab["printerdevice"] = "/dev/" . $tab["printer"];
    }
    if(strstr($tab["printer"],"lp"))
      $tab["printertype"] = "P";
    else if(strstr($tab["printer"],"tty"))
      $tab["printertype"] = "S";
  }

  // de base on émule jamais le 3e bouton, sauf si 
  // l'admin nous indique qu'il n'y a que 2 boutons sur
  // la souris du TX.
  $mousetbemulate = "";

  //Souris Logitech Cordless Desktop MX
  if($tab["mouse"] == "ExplorerPS/2") {
    //Souris à molette
    $tab["mousebtns"]      = "7";
    $tab["zaxismapping"]   = "6 7";      
  }
  else if($tab["mousebtns"] == 2){
    $mousetbemulate = "Y";
  }
  else if($tab["mousebtns"] == 5){
    //Souris à molette
    $tab["mousebtns"]      = "5";
    $tab["zaxismapping"]   = "4 5";
  }

  if($tab["serverx"] == "XF86_S3-3D") {
    $tab["serverx"] = "XF86_SVGA";
    $specialparam = "        OptionParamS0       = \\\"sw_cursor\\\"";
  }
  else if($tab["serverx"] == "XF86_SVGA-i810") {
    // Rajouter le module agp
    $tab["serverx"] = "\\\"XF86_SVGA\\\"";
    $specialparam = "        MODULE_01           = \\\"agpgart\\\"
        MODULE_02           = \\\"i810\\\"";
    $nb_modules=2;
  }
  else if($tab["serverx"] == "auto-radeon") {
    // Rajouter le module agp
    $tab["serverx"] = "\\\"auto\\\"";
    $specialparam = "        MODULE_01           = \\\"agpgart\\\"
        MODULE_02           = \\\"radeon\\\"";
    $nb_modules=2;
  }
	
  $ltsconf = "#$comptemachine debut
[$comptemachine]
";
  if ( "none" == $computerarch ) {
    $ltsconf .= "        # NOT A THIN CLIENT, NOT MANAGED BY LTSP/LTSCONF
        computerarch     = \\\"none\\\"
";
  } 
  else {

    $ltsconf .= "        XSERVER             = \\\"" . $tab["serverx"]		. "\\\"
        X_MOUSE_PROTOCOL    = \\\"" . $tab["mouse"]         . "\\\"
        X_MOUSE_DEVICE      = \\\"/dev/" . str_replace("/dev/","",$tab["mousetty"]) . "\\\"
        X_MOUSE_BUTTONS     = \\\"" . $tab["mousebtns"] 	. "\\\"
";

    if (!empty($mousetbemulate)) {
      $ltsconf .= "        X_MOUSE_EMULATE3BTN = \\\"" . $mousetbemulate       . "\\\"
";
    }

    if (!empty( $tab["zaxismapping"])) {
      $ltsconf .= "        ZAXISMAPPING        = \\\"" . $tab["zaxismapping"]	. "\\\"
";
    }
		
    /*
     * not needed any more.
     * RUN_FLOPPYD         = \\\"" . $tab["floppyd"] 		. "\\\"
     * RUN_KARTAPUSS       = \\\"" . $tab["kartapuss"] 	. "\\\"
     */
    if ( !empty($tab["swapsize"])) {
      $ltsconf .= "        SWAP_BLOCKS         = \\\"" . $tab["swapsize"] 		. "\\\"
";
    }
		
    $ltsconf .= "        XKBLAYOUT           = \\\"" . $tab["keyboardlang"]	. "\\\"
        XKBMODEL            = \\\"" . $tab["keyboard"]		. "\\\"
        XKBNUMPAD           = \\\"" . $tab["keynumpad"]		. "\\\"
";

    if ( !empty($tab["printerdevice"]) ) {
      $ltsconf .= "        PRINTER_0_DEVICE    = \\\"" . $tab["printerdevice"] . "\\\"
        PRINTER_0_TYPE      = \\\"" . $tab["printertype"]	. "\\\"
";
    }
    if ( !empty($writeonly)) {
      $ltsconf .= "        # please ensure WRITE_ONLY always comes *after* PRINTER_0_DEVICE, else
        # the web interface will not parse the config correctly.
        WRITE_ONLY          = \\\"Y\\\"
";
    }

    // les imprimantes disponibles depuis ce poste: une liste avec des virgules en sep. de champ
    if( !empty($tab["printersavailable"]) ) {
      $ltsconf .= "        PRINTERSAVAILABLE   = \\\"" . $tab["printersavailable"] . "\\\"
";
    }


    $ltsconf .= "        COMPUTERARCH        = \\\"" . $computerarch 		. "\\\"
        X_COLOR_DEPTH       = \\\"" . $tab["bpp"] . "\\\"
        X_MODE_0            = \\\"" . $tab["video1"]        . "\\\"
";

    if ( "1280x1024" == $tab["video1"] || "1280x800" == $tab["video1"] 
	 || "1280x960" == $tab["video1"] || "1280x768" == $tab["video1"]) {
      $ltsconf .= "        X_VERTREFRESH       = \\\"50-75\\\"
        # 1024x768 and 800x600 will be automatically appended during client bootup.
        X_HORZSYNC          = \\\"31-120\\\"
";
    }
    if ( "1400x1050" == $tab["video1"] || "1440x900" == $tab["video1"]  ) {
      $ltsconf .= "        X_MODE_1            = \\\"" . ("1400x1050" == $tab["video1"]?"1280x1024":"1280x800") . "\\\"
        # 1024x768 and 800x600 will be automatically appended during client bootup.
        X_VERTREFRESH       = \\\"60-100\\\"
        X_HORZSYNC          = \\\"31-120\\\"
";
    }
    if ( "1600x1200" == $tab["video1"] || "1680x1050" == $tab["video1"]   ) {
      $ltsconf .= "        X_MODE_1            = \\\"" . ("1600x1200" == $tab["video1"]?"1400x1050":"1440x900") . "\\\"
        X_MODE_2            = \\\"" . ("1600x1200" == $tab["video1"]?"1280x1024":"1280x800") . "\\\"
        # 1024x768 and 800x600 will be automatically
        # appended during thin client bootup.
        X_VERTREFRESH       = \\\"60-120\\\"
        X_HORZSYNC          = \\\"31-150\\\"
";
    }

    if ( $tab["sound"] == "esd" ) {
      $ltsconf .= "        SOUND               = \\\"Y\\\"
        SOUND_DAEMON        = \\\"" . $tab["sound"] . "\\\"
";
    }
    else {
      $ltsconf .= "        SOUND               = \\\"N\\\"
";
    }
    $ltsconf .= "        WINDOWSLICENCE          = \\\"" . $tab["windowslicence"] . "\\\"
";
    $ltsconf .= "        LOCAL_STORAGE          = \\\"" . $tab["localstorage"] . "\\\"
";
    $ltsconf .= "        APPENDOPT               = \\\"" . $tab["appendopt"] . "\\\"
";
    if (!empty($tab["screen_01"]) ) {
      $ltsconf .= "        SCREEN_01           = \\\"" . $tab["screen_01"] . "\\\"
";
    }
    if (!empty($specialparam))
      $ltsconf .= $specialparam . "\n";
    

    if (!empty($tab["nic"]) and $tab["nic"] != "none") {
      $ltsconf .= "
        # this is used to generate a DHCP bloc, not directly by LTSP.
        ISA_NIC             = \\\"" . $tab["nic"] . "\\\"
";
    }
  }
  $ltsconf .= "#$comptemachine fin
";

  /*
    print "<pre>DNS:$dns</pre>\n";
    print "<pre>REVERSE:$dnsrev</pre>\n";
    print "<pre>DHCP:$dhcp</pre>\n";
    print "<pre>LTS:$ltsconf</pre>\n";
  */

  $commande = "
#creer le nouveau compte machine
horizon-add user --profile machines --disabled-password " . $comptemachine . " || true
if [ -d /home/machines/" . $comptemachine . " ]; then
  if [ ! -d /home/machines/" . $comptemachine . "/horizon-apt/ ]; then
    mkdir /home/machines/" . $comptemachine . "/horizon-apt/
    touch /home/machines/" . $comptemachine . "/horizon-apt/boot
  fi
fi

if [ -f $conf_fic_ltsconf ]; then
		
	sed -i -e '/^#\\s*$comptemachine debut/,/^#\\s*$comptemachine fin/d' -e '/./,/^$/!d' $conf_fic_ltsconf

	echo \"$ltsconf\" >> $conf_fic_ltsconf
	chmod 644 $conf_fic_ltsconf
	if [ -d /opt/ltsp3/i386/etc ]; then
	  cp -a $conf_fic_ltsconf /opt/ltsp3/i386/etc/lts.conf || true
	fi

	if [ -d /opt/ltsp44/i386/etc ]; then
	  cp -a $conf_fic_ltsconf /opt/ltsp44/i386/etc/lts.conf || true
	fi

	if [ -d /opt/qooqv2/etc ]; then
	  cp -a $conf_fic_ltsconf /opt/qooqv2/etc/lts.conf || true
	fi

else
	echo \"configuration système incomplète, il manque LTSP et/ou son fichier de configuration.\"
	exit 1
fi

";

  if(trim($syslinux)!="") {
    $commande .= "
#on ecrase le syslinux dans tous les cas (nouveau ou modif)
echo \"$syslinux\" > $syslinuxfile || true

#le lien
if [ ! -e $syslinuxsymlink ]; then
   ln $syslinuxfile $syslinuxsymlink
fi
";
  }

$commande .= "

if [ -f $fic_bind ]; then

	  /etc/init.d/dhcp3-server stop

	TESTRYCKS=`grep -w $hostname $fic_bind || true`
	if [ -z \"\${TESTRYCKS}\" ]; then
	  echo \"$dns\" >> $fic_bind;
	  echo \"$dnsrev\" >> $fic_bind_rev;
	  /etc/init.d/bind9 restart
";

  if($ip_actuelle != "") {
    $commande .= "
		  cat $conf_fic_dhcp_leases | sed s/\"^}\$\"/\"}%\"/ | tr \"\\n%\" \"*\\n\" | grep -v \"$ip_actuelle\" | tr \"*\" \"\\n\" > $conf_fic_dhcp_leases.temp
";
  }
  $commande .= "  mv $conf_fic_dhcp_leases.temp $conf_fic_dhcp_leases

	fi # if -z TESTRYCKS

	TMPFILE=`mktemp`

	# remove host block from DHCPd.conf, insert the new block at the end
	# of the file, and supress multiple blank lines.
	# be carefull with poste-01 and poste-01w ... this is not the same ... $dhcphostname with space after
	sed -i -e '/./{H;$!d;}' -e 'x;/host $dhcphostname /d' -e 's/\(^.*ltsconfinsertend\)/$dhcp\\1/' -e '/./,/^$/!d' $fic_dhcp

	/etc/init.d/dhcp3-server start

else
	echo \"configuration système incomplète, il manque au moins BIND et DHCPd.\"
	exit 1
fi # if -f $fic_bind
";

  if( fichier_script($commande) == 0 ){
    $retour .= sprintf(_("%s Configuration enregistrée %s Merci d'attendre quelques instants, puis éteignez la machine et relancez-la pour qu'elle prenne en compte ses nouveaux paramètres de configuration."), "<div style=\"text-align: center; margin: 3em;\"><div style=\"color: #3A3;\">", "<br />&nbsp;</div>") . "</div>" ;
  }
  else {
    $retour .= $fichier_script_output . "<div style=\"text-align: center; margin: 3em;\"><div style=\"color: #A33;\">" . sprintf(_("Erreur d'exécution de la commande !%s Regardez le journal d'exécution et contactez le support."), "<br />&nbsp;</div>") . "</div>";
  }

  return $retour;
}

function config_tx($erreurflash, $modifier = false ) {
  global $mac, $tabconftx;

  if( ! $modifier && erreur_mac_addr($mac) != 0){
    $retour .= "<p>" . _("ERREUR: Cette adresse MAC est déjà présente dans les fichiers de configurations !") . "</p>\n";
  }
  else {
    $retour .= "<p style=\"text-align: justify;\">" . _("ATTENTION: pour le nom de vos terminaux, <u>n'utilisez pas de caractères spéciaux</u> ! Utilisez des noms comme <i>grincheux, patatix, silencieux, rapide, discrete, bordeaux, saintandredecubzac, larochelle</i> ... (sans espaces, sans accents). Vous pouvez aussi utiliser des chiffres et des tirets, mais pas en début de mot (exemple&nbsp;: poste-23).") . "</p>\n";

    // Si on ajoute un tx (normalement c'est le cas quand on est dans ce fichier ...)
    // sauf qu'on a un cas particulier qui est "si j'ai déjà vu cette page et rempli
    // des options mais que j'ai oublié le nom du tx" on est donc en mode "modif"
    if(trim($erreurflash) == "")
      $add = 1;
    else
      $add = 0;
    
    if ( $modifier ) {
      $add = 0;
    }
    $retour .= make_all_boxes($tabconftx, $add);
  }
  return $retour;
}
/*
 * END LTSConf functions
 */

/*
 * 2 functions converted from python code.
 * source : http://dev.zenoss.org/svn/trunk/Products/ZenUtils/IpUtil.py
 */
function ipstr2nb($ipstr) {
  $octs = explode('.', $ipstr);
  $octs = array_reverse($octs);
  $i = 0;
  $c = count($octs);
  for ($j=0; $j < $c ; $j++) {
    $i += pow(256, $j) * $octs[$j];
  }
  return $i;
}

function short_mask($mask) {

  if ($mask[0]=='0'){
    return 0;
  }
  $masknumb = ipstr2nb($mask);
  $test     = 0xffffffff;
  for ( $i=0; $i<32; $i++) {
    if ( $test == $masknumb ) {
      return 32 - $i;
    }
    $test = $test - pow(2, $i);
  }
  return FALSE;
}


/* 
 * Intranet functions
 */

function ListeApplications() {
  // Fonction qui retourne un tableau avec la liste des applications
  // disponibles pour l'intranet des utilisateurs.
  //
  // par exemple 
  // $tab = ListeApplications();
  // $tab[0]["title"] : Titre de l'application, exemple "NéoMail"
  // $tab[0]["url"]   : URL pour l'application, exemple "/cgi-bin/neomail/pl"
  // $tab[0]["desc"]  : Description, exemple "NéoMail est le logiciel de courriel pour l'in..."
  $fichier = "listeapplications";

  if ( ! file_exists($fichier) || filesize($fichier) < 10 ) {
    return false;
  }
  $fp = fopen($fichier,"r");
  if($fp = @fopen($fichier,"r")){
    $i = 0;
    while(!feof($fp)){
      $ligne = fgetcsv($fp,"1024","=");
      array_trim($ligne);
      if($ligne[0] == "TITLE") {
	if($retour[$i]["title"] != "")
	  $i++;
	$retour[$i]["title"] = $ligne[1];
      }
      else if($ligne[0] == "URL")
	$retour[$i]["url"] = $ligne[1];
      else if($ligne[0] == "DESC") {
	$desc = explode("!",$ligne[1]);
	$retour[$i]["desc"][$desc[0]] = $desc[1];
      }
    }
    fclose($fp);
  }
  return $retour;
}

/**
 * Code from wordpress - wordpress.org
 * Converts all accent characters to ASCII characters.
 *
 * If there are no accent characters, then the string given is just returned.
 *
 * @since 1.2.1
 *
 * @param string $string Text that might have accent characters
 * @return string Filtered string with replaced "nice" characters.
 */
function remove_accents($string) {
	if ( !preg_match('/[\x80-\xff]/', $string) )
		return $string;

	if (seems_utf8($string)) {
		$chars = array(
		// Decompositions for Latin-1 Supplement
		chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
		chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
		chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
		chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
		chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
		chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
		chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
		chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
		chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
		chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
		chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
		chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
		chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
		chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
		chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
		chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
		chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
		chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
		chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
		chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
		chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
		chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
		chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
		chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
		chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
		chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
		chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
		chr(195).chr(191) => 'y',
		// Decompositions for Latin Extended-A
		chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
		chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
		chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
		chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
		chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
		chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
		chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
		chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
		chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
		chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
		chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
		chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
		chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
		chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
		chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
		chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
		chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
		chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
		chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
		chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
		chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
		chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
		chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
		chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
		chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
		chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
		chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
		chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
		chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
		chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
		chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
		chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
		chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
		chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
		chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
		chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
		chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
		chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
		chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
		chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
		chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
		chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
		chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
		chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
		chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
		chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
		chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
		chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
		chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
		chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
		chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
		chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
		chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
		chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
		chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
		chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
		chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
		chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
		chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
		chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
		chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
		chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
		chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
		chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
		// Euro Sign
		chr(226).chr(130).chr(172) => 'E',
		// GBP (Pound) Sign
		chr(194).chr(163) => '');

		$string = strtr($string, $chars);
	} else {
		// Assume ISO-8859-1 if not UTF-8
		$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
			.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
			.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
			.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
			.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
			.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
			.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
			.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
			.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
			.chr(252).chr(253).chr(255);

		$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";

		$string = strtr($string, $chars['in'], $chars['out']);
		$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
		$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
		$string = str_replace($double_chars['in'], $double_chars['out'], $string);
	}

	return $string;
}

/**
 * Code from wordpress - wordpress.org
 * Checks to see if a string is utf8 encoded.
 *
 * NOTE: This function checks for 5-Byte sequences, UTF8
 *       has Bytes Sequences with a maximum length of 4.
 *
 * @author bmorel at ssi dot fr (modified)
 * @since 1.2.1
 *
 * @param string $str The string to be checked
 * @return bool True if $str fits a UTF-8 model, false otherwise.
 */
function seems_utf8($str) {
	$length = strlen($str);
	for ($i=0; $i < $length; $i++) {
		$c = ord($str[$i]);
		if ($c < 0x80) $n = 0; # 0bbbbbbb
		elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
		elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
		elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
		elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
		elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
		else return false; # Does not match any model
		for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
			if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
				return false;
		}
	}
	return true;
}

function slugify($text)
{ 
  // replace non letter or digits by -
  $text = preg_replace('~[^\\pL\d]+~u', '-', $text);
  
  // trim
  $text = trim($text, '-');
  
  // transliterate
  $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);

  // lowercase
  $text = strtolower($text);

  // remove unwanted characters
  $text = preg_replace('~[^-\w]+~', '', $text);

  if (empty($text))
  {
    return 'n-a';
  }
  return $text;
}

?>
