- Utilisation de LSconfig dans tout le projet
[ldapsaisie.git] / trunk / includes / class / class.LSsession.php
1 <?php
2 /*******************************************************************************
3  * Copyright (C) 2007 Easter-eggs
4  * http://ldapsaisie.labs.libre-entreprise.org
5  *
6  * Author: See AUTHORS file in top-level directory.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 ******************************************************************************/
22
23 /**
24  * Gestion des sessions
25  *
26  * Cette classe gère les sessions d'utilisateurs.
27  *
28  * @author Benjamin Renard <brenard@easter-eggs.com>
29  */
30 class LSsession {
31
32   // La configuration du serveur Ldap utilisé
33   public static $ldapServer = NULL;
34   
35   // L'id du serveur Ldap utilisé
36   private static $ldapServerId = NULL;
37   
38   // Le topDn courant
39   private static $topDn = NULL;
40   
41   // Le DN de l'utilisateur connecté
42   private static $dn = NULL;
43   
44   // Le RDN de l'utilisateur connecté (son identifiant)
45   private static $rdn = NULL;
46   
47   // Les LSprofiles de l'utilisateur
48   private static $LSprofiles = array();
49   
50   // Les droits d'accès de l'utilisateur
51   private static $LSaccess = array();
52   
53   // Les fichiers temporaires
54   private static $tmp_file = array();
55   
56   // Langue et encodage actuel
57   private static $lang = NULL;
58   private static $encoding = NULL;
59   
60   /*
61    * Constante de classe non stockée en session
62    */
63   // Le template à afficher
64   private static $template = NULL;
65   
66   // Les subDn des serveurs Ldap
67   private static $_subDnLdapServer = array();
68   
69   // Affichage Ajax
70   private static $ajaxDisplay = false;
71
72   // Les fichiers JS à charger dans la page
73   private static $JSscripts = array();
74   
75   // Les paramètres JS à communiquer dans la page
76   private static $_JSconfigParams = array();
77   
78   // Les fichiers CSS à charger dans la page
79   private static $CssFiles = array();
80
81   // L'objet de l'utilisateur connecté
82   private static $LSuserObject = NULL;
83
84  /**
85   * Include un fichier PHP
86   *
87   * @author Benjamin Renard <brenard@easter-eggs.com>
88   *
89   * @retval true si tout c'est bien passé, false sinon
90   */
91   public static function includeFile($file) {
92     if (!file_exists($file)) {
93       return;
94     }
95     if (LSdebug) {
96       return include_once($file);
97     }
98     else {
99       return @include_once($file);
100     }
101     return;
102   }
103
104  /**
105   * Lancement de LSconfig
106   *
107   * @author Benjamin Renard <brenard@easter-eggs.com>
108   *
109   * @retval true si tout c'est bien passé, false sinon
110   */
111   private static function startLSconfig() {
112     if (self :: loadLSclass('LSconfig')) {
113       if (LSconfig :: start()) {
114         return true;
115       }
116     }
117     die("ERROR : Can't load configuration files.");
118     return;
119   }
120
121  /**
122   * Lancement et initialisation de Smarty
123   *
124   * @author Benjamin Renard <brenard@easter-eggs.com>
125   *
126   * @retval true si tout c'est bien passé, false sinon
127   */  
128   private static function startLStemplate() {
129     if ( self :: includeFile(LSconfig :: get('Smarty')) ) {
130       $GLOBALS['Smarty'] = new Smarty();
131       $GLOBALS['Smarty'] -> template_dir = LS_TEMPLATES_DIR;
132       $GLOBALS['Smarty'] -> compile_dir = LS_TMP_DIR;
133       
134       if (LSdebug) {
135         $GLOBALS['Smarty'] -> caching = 0;
136         // cache files are always regenerated
137         $GLOBALS['Smarty'] -> force_compile = TRUE;
138         // recompile template if it is changed
139         $GLOBALS['Smarty'] -> compile_check = TRUE;
140       }
141       
142       $GLOBALS['Smarty'] -> assign('LS_CSS_DIR',LS_CSS_DIR);
143       $GLOBALS['Smarty'] -> assign('LS_IMAGES_DIR',LS_IMAGES_DIR);
144       
145       self :: addJSconfigParam('LS_IMAGES_DIR',LS_IMAGES_DIR);
146       return true;
147     }
148     die("ERROR : Can't load Smarty.");
149     return;
150   }
151   
152  /**
153   * Retourne le topDn de la session
154   *
155   * @author Benjamin Renard <brenard@easter-eggs.com>
156   *
157   * @retval string le topDn de la session
158   */
159   public static function getTopDn() {
160     return self :: $topDn;
161   }
162
163  /**
164   * Initialisation de la gestion des erreurs
165   *
166   * Création de l'objet LSerror
167   *
168   * @author Benjamin Renard <brenard@easter-eggs.com
169   *
170   * @retval boolean true si l'initialisation a réussi, false sinon.
171   */
172   private static function startLSerror() {
173     if(!self :: loadLSclass('LSerror')) {
174       return;
175     }
176     self :: defineLSerrors();
177     return true;
178   }
179
180  /**
181   * Chargement d'une classe d'LdapSaisie
182   *
183   * @param[in] $class Nom de la classe Ã  charger (Exemple : LSeepeople)
184   * @param[in] $type (Optionnel) Type de classe Ã  charger (Exemple : LSobjects)
185   *
186   * @author Benjamin Renard <brenard@easter-eggs.com
187   * 
188   * @retval boolean true si le chargement a réussi, false sinon.
189   */
190   public static function loadLSclass($class,$type='') {
191     if (class_exists($class))
192       return true;
193     if($type!='')
194       $type=$type.'.';
195     return self :: includeFile(LS_CLASS_DIR .'class.'.$type.$class.'.php');
196   }
197
198  /**
199   * Chargement d'un object LdapSaisie
200   *
201   * @param[in] $object Nom de l'objet Ã  charger
202   *
203   * @retval boolean true si le chargement a réussi, false sinon.
204   */
205   public static function loadLSobject($object) {
206     if(class_exists($object)) {
207       return true;
208     }
209     $error = 0;
210     self :: loadLSclass('LSldapObject');
211     if (!self :: loadLSclass($object,'LSobjects')) {
212       $error = 1;
213     }
214     if (!self :: includeFile( LS_OBJECTS_DIR . 'config.LSobjects.'.$object.'.php' )) {
215       $error = 1;
216     }
217     else {
218       if (!LSconfig :: set("LSobjects.$object",$GLOBALS['LSobjects'][$object])) {
219         $error = 1;
220       }
221     }
222     if ($error) {
223       LSerror :: addErrorCode('LSsession_04',$object);
224       return;
225     }
226     return true;
227   }
228
229  /**
230   * Chargement d'un addons d'LdapSaisie
231   *
232   * @param[in] $addon Nom de l'addon Ã  charger (Exemple : samba)
233   *
234   * @author Benjamin Renard <brenard@easter-eggs.com
235   * 
236   * @retval boolean true si le chargement a réussi, false sinon.
237   */
238   public static function loadLSaddon($addon) {
239     if(self :: includeFile(LS_ADDONS_DIR .'LSaddons.'.$addon.'.php')) {
240       self :: includeFile(LS_CONF_DIR."LSaddons/config.LSaddons.".$addon.".php");
241       if (!call_user_func('LSaddon_'. $addon .'_support')) {
242         LSerror :: addErrorCode('LSsession_02',$addon);
243         return;
244       }
245       return true;
246     }
247     return;
248   }
249
250  /**
251   * Chargement des addons LdapSaisie
252   *
253   * Chargement des LSaddons contenue dans la variable
254   * $GLOBALS['LSaddons']['loads']
255   *
256   * @retval boolean true si le chargement a réussi, false sinon.
257   */
258   public static function loadLSaddons() {
259     $conf=LSconfig :: get('LSaddons.loads');
260     if(!is_array($conf)) {
261       LSerror :: addErrorCode('LSsession_01',"LSaddons['loads']");
262       return;
263     }
264
265     foreach ($conf as $addon) {
266       self :: loadLSaddon($addon);
267     }
268     return true;
269   }
270
271  /**
272   * Défini la locale
273   * 
274   * @retval void
275   */
276   public static function setLocale() {
277     if (isset($_REQUEST['lang'])) {
278       $lang = $_REQUEST['lang'];
279     }
280     elseif (isset($_SESSION['LSlang'])) {
281       $lang = $_SESSION['LSlang'];
282     }
283     elseif (isset(self :: $ldapServer['lang'])) {
284       $lang = self :: $ldapServer['lang'];
285     }
286     else {
287       $lang = LSconfig :: get('lang');
288     }
289     
290     if (isset($_REQUEST['encoding'])) {
291       $encoding = $_REQUEST['encoding'];
292     }
293     elseif (isset($_SESSION['LSencoding'])) {
294       $encoding = $_SESSION['LSencoding'];
295     }
296     elseif (isset(self :: $ldapServer['encoding'])) {
297       $encoding = self :: $ldapServer['encoding'];
298     }
299     else {
300       $encoding = LSconfig :: get('encoding');
301     }
302     
303     $_SESSION['LSlang']=$lang;
304     self :: $lang=$lang;
305     $_SESSION['LSencoding']=$encoding;
306     self :: $encoding=$encoding;
307     
308
309     if (self :: localeExist($lang,$encoding)) {
310       if ($encoding) {
311         $lang.='.'.$encoding;
312       }
313       setlocale(LC_ALL, $lang);
314       bindtextdomain(LS_TEXT_DOMAIN, LS_I18N_DIR);
315       textdomain(LS_TEXT_DOMAIN);
316       
317       if (is_file(LS_I18N_DIR.'/'.$lang.'/lang.php')) {
318         include(LS_I18N_DIR.'/'.$lang.'/lang.php');
319       }
320     }
321     else {
322       if ($encoding && $lang) {
323         $lang.='.'.$encoding;
324       }
325       LSdebug('La locale "'.$lang.'" n\'existe pas, utilisation de la locale par défaut.');
326     }
327   }
328   
329  /**
330   * Retourne la liste des langues disponibles
331   * 
332   * @retval array Tableau/Liste des langues disponibles
333   **/ 
334   public static function getLangList() {
335     $list=array('en_US');
336     if (self :: $encoding) {
337       $regex = '^([a-zA-Z_]*)\.'.self :: $encoding.'$';
338     }
339     else {
340       $regex = '^([a-zA-Z_]*)$';
341     }
342     if ($handle = opendir(LS_I18N_DIR)) {
343       while (false !== ($file = readdir($handle))) {
344         if(is_dir(LS_I18N_DIR.'/'.$file)) {
345           if (ereg($regex,$file,$regs)) {
346             if (!in_array($regs[1],$list)) {
347               $list[]=$regs[1];
348             }
349           }
350         }
351       }
352     }
353     return $list;
354   }
355
356  /**
357   * Retourne la langue courante de la session
358   * 
359   * @param[in] boolean Si true, le code langue retourné sera court
360   * 
361   * @retval string La langue de la session
362   **/
363   public static function getLang($short=false) {
364     if ($short) {
365       return strtolower(self :: $lang[0].self :: $lang[1]);
366     }
367     return self :: $lang;
368   }
369   
370  /**
371   * Vérifie si une locale est disponible
372   * 
373   * @param[in] $lang string La langue (Ex : fr_FR)
374   * @param[in] $encoding string L'encodage de caractère (Ex : UTF8)
375   * 
376   * @retval boolean True si la locale est disponible, False sinon
377   **/
378   public static function localeExist($lang,$encoding) {
379     if ( !$lang && !$encoding ) {
380       return;
381     }
382     $locale=$lang.(($encoding)?'.'.$encoding:'');
383     if ($locale=='en_US.UTF8') {
384       return true;
385     }
386     return (is_dir(LS_I18N_DIR.'/'.$locale));
387   }
388
389  /**
390   * Initialisation LdapSaisie
391   *
392   * @retval boolean True si l'initialisation à réussi, false sinon.
393   */
394   public static function initialize() {
395     if (!self :: startLSconfig()) {
396       return;
397     }
398     
399     self :: startLStemplate();
400     
401     session_start();
402     
403     self :: setLocale();
404     
405     self :: startLSerror();
406     self :: loadLSaddons();
407     return true;
408   }
409
410  /**
411   * Initialisation de la session LdapSaisie
412   *
413   * Initialisation d'une LSsession :
414   * - Authentification et activation du mécanisme de session de LdapSaisie
415   * - ou Chargement des paramètres de la session Ã  partir de la variable 
416   *   $_SESSION['LSsession'].
417   * - ou Destruction de la session en cas de $_GET['LSsession_logout'].
418   *
419   * @retval boolean True si l'initialisation Ã  réussi (utilisateur authentifié), false sinon.
420   */
421   public static function startLSsession() {
422     if (!self :: initialize()) {
423       return;
424     }   
425
426     // Déconnexion
427     if (isset($_GET['LSsession_logout'])||isset($_GET['LSsession_recoverPassword'])) {
428       session_destroy();
429       
430       if (is_array($_SESSION['LSsession']['tmp_file'])) {
431         self :: $tmp_file = $_SESSION['LSsession']['tmp_file'];
432       }
433       self :: deleteTmpFile();
434       unset($_SESSION['LSsession']);
435     }
436     
437     // Récupération de mot de passe
438     if (isset($_GET['recoveryHash'])) {
439       $_POST['LSsession_user'] = 'a determiner plus tard';
440     }
441     
442     if(isset($_SESSION['LSsession'])) {
443       // Session existante
444       self :: $topDn        = $_SESSION['LSsession']['topDn'];
445       self :: $dn           = $_SESSION['LSsession']['dn'];
446       self :: $rdn          = $_SESSION['LSsession']['rdn'];
447       self :: $ldapServerId = $_SESSION['LSsession']['ldapServerId'];
448       self :: $tmp_file     = $_SESSION['LSsession']['tmp_file'];
449       
450       if ( self :: cacheLSprofiles() && !isset($_REQUEST['LSsession_refresh']) ) {
451         self :: setLdapServer(self :: $ldapServerId);
452         self :: $LSprofiles   = $_SESSION['LSsession']['LSprofiles'];
453         self :: $LSaccess   = $_SESSION['LSsession']['LSaccess'];
454         if (!self :: LSldapConnect())
455           return;
456       }
457       else {
458         self :: setLdapServer(self :: $ldapServerId);
459         if (!self :: LSldapConnect())
460           return;
461         self :: loadLSprofiles();
462       }
463       
464       if ( self :: cacheSudDn() && (!isset($_REQUEST['LSsession_refresh'])) ) {
465         self :: $_subDnLdapServer = $_SESSION['LSsession_subDnLdapServer'];
466       }
467       
468       if (!self :: loadLSobject(self :: $ldapServer['authObjectType'])) {
469         return;
470       }
471       
472       self :: getLSuserObject();
473       
474       if ( !self :: cacheLSprofiles() || isset($_REQUEST['LSsession_refresh']) ) {
475         self :: loadLSaccess();
476       }
477       
478       $GLOBALS['Smarty'] -> assign('LSsession_username',self :: getLSuserObject() -> getDisplayName());
479       
480       if ($_POST['LSsession_topDn']) {
481         if (self :: validSubDnLdapServer($_POST['LSsession_topDn'])) {
482           self :: $topDn = $_POST['LSsession_topDn'];
483           $_SESSION['LSsession']['topDn'] = $_POST['LSsession_topDn'];
484         } // end if
485       } // end if
486       
487       return true;
488       
489     }
490     else {
491       // Session inexistante
492       $recoveryPasswordInfos=array();
493
494       if (isset($_POST['LSsession_user'])) {
495         if (isset($_POST['LSsession_ldapserver'])) {
496           self :: setLdapServer($_POST['LSsession_ldapserver']);
497         }
498         else {
499           self :: setLdapServer(0);
500         }
501         
502         // Connexion au serveur LDAP
503         if (self :: LSldapConnect()) {
504
505           // topDn
506           if ( $_POST['LSsession_topDn'] != '' ){
507             self :: $topDn = $_POST['LSsession_topDn'];
508           }
509           else {
510             self :: $topDn = self :: $ldapServer['ldap_config']['basedn'];
511           }
512           $_SESSION['LSsession_topDn']=self :: $topDn;
513
514           if ( self :: loadLSobject(self :: $ldapServer['authObjectType']) ) {
515             $authobject = new self :: $ldapServer['authObjectType']();
516             $find=true;
517             if (isset($_GET['recoveryHash'])) {
518               $filter=self :: $ldapServer['recoverPassword']['recoveryHashAttr']."=".$_GET['recoveryHash'];
519               $result = $authobject -> listObjects($filter,self :: $topDn);
520               $nbresult=count($result);
521               if ($nbresult==1) {
522                 $rdn = $result[0] -> getValue('rdn');
523                 $rdn = $rdn[0];
524                 $_POST['LSsession_user'] = $rdn;
525                 $find=false;
526               }
527             }
528             if ($find) {
529               $result = $authobject -> searchObject($_POST['LSsession_user'],self :: $topDn,self :: $ldapServer['authObjectFilter']);
530               $nbresult=count($result);
531             }
532             if ($nbresult==0) {
533               // identifiant incorrect
534               LSdebug('identifiant incorrect');
535               LSerror :: addErrorCode('LSsession_06');
536             }
537             else if ($nbresult>1) {
538               // duplication d'authentité
539               LSerror :: addErrorCode('LSsession_07');
540             }
541             else {
542               if (isset($_GET['LSsession_recoverPassword'])) {
543                 LSdebug('Recover : Id trouvé');
544                 if (self :: $ldapServer['recoverPassword']) {
545                   if (self :: loadLSaddon('mail')) {
546                     LSdebug('Récupération active');
547                     $user=$result[0];
548                     $emailAddress = $user -> getValue(self :: $ldapServer['recoverPassword']['mailAttr']);
549                     $emailAddress = $emailAddress[0];
550                     
551                     // Header des mails
552                     $sendParams=array();
553                     if (self :: $ldapServer['recoverPassword']['recoveryEmailSender']) {
554                       $sendParams['From']=self :: $ldapServer['recoverPassword']['recoveryEmailSender'];
555                     }
556                     
557                     if (checkEmail($emailAddress)) {
558                       LSdebug('Email : '.$emailAddress);
559                       self :: $dn = $user -> getDn();
560                       // 1ère étape : envoie du recoveryHash
561                       if (!isset($_GET['recoveryHash'])) {
562                         // Generer un hash
563                         $rdn=$user -> getValue('rdn');
564                         $rdn = $rdn[0];
565                         $recovery_hash = md5($rdn . strval(time()) . strval(rand()));
566                         
567                         $lostPasswdForm = $user -> getForm('lostPassword');
568                         $lostPasswdForm -> setPostData(
569                           array(
570                             self :: $ldapServer['recoverPassword']['recoveryHashAttr'] => $recovery_hash
571                           )
572                           ,true
573                         );
574                         
575                         if($lostPasswdForm -> validate()) {
576                           if ($user -> updateData('lostPassword')) {
577                             // recoveryHash de l'utilisateur mis à jour
578                             if ($_SERVER['HTTPS']=='on') {
579                               $recovery_url='https://';
580                             }
581                             else {
582                               $recovery_url='http://';
583                             }
584                             $recovery_url .= $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'&recoveryHash='.$recovery_hash;
585
586                             if (
587                               sendMail(
588                                 $emailAddress,
589                                 self :: $ldapServer['recoverPassword']['recoveryHashMail']['subject'],
590                                 getFData(self :: $ldapServer['recoverPassword']['recoveryHashMail']['msg'],$recovery_url),
591                                 $sendParams
592                               )
593                             ){
594                               // Mail a bien été envoyé
595                               $recoveryPasswordInfos['recoveryHashMail']=$emailAddress;
596                             }
597                             else {
598                               // Problème durant l'envoie du mail
599                               LSdebug("Problème durant l'envoie du mail");
600                               LSerror :: addErrorCode('LSsession_20',7);
601                             }
602                           }
603                           else {
604                             // Erreur durant la mise à jour de l'objet
605                             LSdebug("Erreur durant la mise à jour de l'objet");
606                             LSerror :: addErrorCode('LSsession_20',6);
607                           }
608                         }
609                         else {
610                           // Erreur durant la validation du formulaire de modification de perte de password
611                           LSdebug("Erreur durant la validation du formulaire de modification de perte de password");
612                           LSerror :: addErrorCode('LSsession_20',5);
613                         }
614                       }
615                       // 2nd étape : génération du mot de passe + envoie par mail
616                       else {
617                         $attr=$user -> attrs[self :: $ldapServer['authObjectTypeAttrPwd']];
618                         if ($attr instanceof LSattribute) {
619                           $mdp = generatePassword($attr -> config['html_options']['chars'],$attr -> config['html_options']['lenght']);
620                           LSdebug('Nvx mpd : '.$mdp);
621                           $lostPasswdForm = $user -> getForm('lostPassword');
622                           $lostPasswdForm -> setPostData(
623                             array(
624                               self :: $ldapServer['recoverPassword']['recoveryHashAttr'] => array(''),
625                               self :: $ldapServer['authObjectTypeAttrPwd'] => array($mdp)
626                             )
627                             ,true
628                           );
629                           if($lostPasswdForm -> validate()) {
630                             if ($user -> updateData('lostPassword')) {
631                               if (
632                                 sendMail(
633                                   $emailAddress,
634                                   self :: $ldapServer['recoverPassword']['newPasswordMail']['subject'],
635                                   getFData(self :: $ldapServer['recoverPassword']['newPasswordMail']['msg'],$mdp),
636                                   $sendParams
637                                 )
638                               ){
639                                 // Mail a bien été envoyé
640                                 $recoveryPasswordInfos['newPasswordMail']=$emailAddress;
641                               }
642                               else {
643                                 // Problème durant l'envoie du mail
644                                 LSdebug("Problème durant l'envoie du mail");
645                                 LSerror :: addErrorCode('LSsession_20',4);
646                               }
647                             }
648                             else {
649                               // Erreur durant la mise à jour de l'objet
650                               LSdebug("Erreur durant la mise à jour de l'objet");
651                               LSerror :: addErrorCode('LSsession_20',3);
652                             }
653                           }
654                           else {
655                             // Erreur durant la validation du formulaire de modification de perte de password
656                             LSdebug("Erreur durant la validation du formulaire de modification de perte de password");
657                             LSerror :: addErrorCode('LSsession_20',2);
658                           }
659                         }
660                         else {
661                           // l'attribut password n'existe pas
662                           LSdebug("L'attribut password n'existe pas");
663                           LSerror :: addErrorCode('LSsession_20',1);
664                         }
665                       }
666                     }
667                     else {
668                       LSerror :: addErrorCode('LSsession_19');
669                     }
670                   }
671                 }
672                 else {
673                   LSerror :: addErrorCode('LSsession_18');
674                 }
675               }
676               else {
677                 if ( self :: checkUserPwd($result[0],$_POST['LSsession_pwd']) ) {
678                   // Authentification réussi
679                   self :: $LSuserObject = $result[0];
680                   self :: $dn = $result[0]->getValue('dn');
681                   self :: $rdn = $result[0]->getValue('rdn');
682                   self :: loadLSprofiles();
683                   self :: loadLSaccess();
684                   $GLOBALS['Smarty'] -> assign('LSsession_username',self :: getLSuserObject() -> getDisplayName());
685                   $_SESSION['LSsession']=self :: getContextInfos();
686                   return true;
687                 }
688                 else {
689                   LSerror :: addErrorCode('LSsession_06');
690                   LSdebug('mdp incorrect');
691                 }
692               }
693             }
694           }
695           else {
696             LSerror :: addErrorCode('LSsession_10');
697           }
698         }
699         else {
700           LSerror :: addErrorCode('LSsession_09');
701         }
702       }
703       if (self :: $ldapServerId) {
704         $GLOBALS['Smarty'] -> assign('ldapServerId',self :: $ldapServerId);
705       }
706       $GLOBALS['Smarty'] -> assign('topDn',self :: $topDn);
707       if (isset($_GET['LSsession_recoverPassword'])) {
708         self :: displayRecoverPasswordForm($recoveryPasswordInfos);
709       }
710       else {
711         self :: displayLoginForm();
712       }
713       return;
714     }
715   }
716   
717  /**
718   * Retourne les informations du contexte
719   *
720   * @author Benjamin Renard <brenard@easter-eggs.com
721   * 
722   * @retval array Tableau associatif des informations du contexte
723   */
724   private static function getContextInfos() {
725     return array(
726       'tmp_file' => self :: $tmp_file,
727       'topDn' => self :: $topDn,
728       'dn' => self :: $dn,
729       'rdn' => self :: $rdn,
730       'ldapServerId' => self :: $ldapServerId,
731       'ldapServer' => self :: $ldapServer,
732       'LSprofiles' => self :: $LSprofiles,
733       'LSaccess' => self :: $LSaccess
734     );
735   }
736   
737   /**
738   * Retourne l'objet de l'utilisateur connecté
739   *
740   * @author Benjamin Renard <brenard@easter-eggs.com
741   * 
742   * @retval mixed L'objet de l'utilisateur connecté ou false si il n'a pas put
743   *               être créé
744   */
745   public static function getLSuserObject($dn=null) {
746     if ($dn) {
747       self :: $dn = $dn;
748     }
749     if (!self :: $LSuserObject) {
750       if (self :: loadLSobject(self :: $ldapServer['authObjectType'])) {
751         self :: $LSuserObject = new self :: $ldapServer['authObjectType']();
752         self :: $LSuserObject -> loadData(self :: $dn);
753       }
754       else {
755         return;
756       }
757     }
758     return self :: $LSuserObject;
759   }
760   
761  /**
762   * Retourne le DN de l'utilisateur connecté
763   *
764   * @author Benjamin Renard <brenard@easter-eggs.com
765   * 
766   * @retval string Le DN de l'utilisateur connecté
767   */
768   public static function getLSuserObjectDn() {
769     return self :: $dn;
770   }
771
772  /**
773   * Modifie l'utilisateur connecté à la volé
774   * 
775   * @param[in] $object Mixed  L'objet Ldap du nouvel utilisateur
776   *                           le type doit correspondre à
777   *                           self :: $ldapServer['authObjectType']
778   * 
779   * @retval boolean True en cas de succès, false sinon
780   */
781  public static function changeAuthUser($object) {
782   if ($object instanceof self :: $ldapServer['authObjectType']) {
783     self :: $dn = $object -> getDn();
784     $rdn = $object -> getValue('rdn');
785     if(is_array($rdn)) {
786       $rdn = $rdn[0];
787     }
788     self :: $rdn = $rdn;
789     self :: $LSuserObject = $object;
790     
791     if(self :: loadLSprofiles()) {
792       self :: loadLSaccess();
793       $_SESSION['LSsession']=self :: getContextInfos();
794       return true;
795     }
796   }
797   return;
798  }
799
800  /**
801   * Définition du serveur Ldap de la session
802   *
803   * Définition du serveur Ldap de la session Ã  partir de son ID dans 
804   * le tableau LSconfig :: get('ldap_servers').
805   *
806   * @param[in] integer Index du serveur Ldap
807   *
808   * @retval boolean True sinon false.
809   */
810   public static function setLdapServer($id) {
811     $conf = LSconfig :: get("ldap_servers.$id");
812     if ( is_array($conf) ) {
813       self :: $ldapServerId = $id;
814       self :: $ldapServer = $conf;
815       self :: setLocale();
816       return true;
817     }
818     else {
819       return;
820     }
821   }
822
823  /**
824   * Connexion au serveur Ldap
825   *
826   * @retval boolean True sinon false.
827   */
828   public static function LSldapConnect() {
829     if (self :: $ldapServer) {
830       self :: includeFile(LSconfig :: get('NetLDAP2'));
831       if (!self :: loadLSclass('LSldap')) {
832         return;
833       }
834       LSldap :: connect(self :: $ldapServer['ldap_config']);
835       if (LSldap :: isConnected()) {
836         return true;
837       }
838       else {
839         return;
840       }
841     }
842     else {
843       LSerror :: addErrorCode('LSsession_03');
844       return;
845     }
846   }
847
848  /**
849   * Retourne les sous-dns du serveur Ldap courant
850   *
851   * @retval mixed Tableau des subDn, false si une erreur est survenue.
852   */
853   public static function getSubDnLdapServer() {
854     if (self :: cacheSudDn() && isset(self :: $_subDnLdapServer[self :: $ldapServerId])) {
855       return self :: $_subDnLdapServer[self :: $ldapServerId];
856     }
857     if (!isset(self :: $ldapServer['subDn'])) {
858       return;
859     }
860     if ( !is_array(self :: $ldapServer['subDn']) ) {
861       return;
862     }
863     $return=array();
864     foreach(self :: $ldapServer['subDn'] as $subDn_name => $subDn_config) {
865       if ($subDn_name == 'LSobject') {
866         if (is_array($subDn_config)) {
867           foreach($subDn_config as $LSobject_name => $LSoject_config) {
868             if ($LSoject_config['basedn']) {
869               $basedn = $LSoject_config['basedn'];
870             }
871             else {
872               $basedn = NULL;
873             }
874             if ($LSoject_config['displayName']) {
875               $displayName = $LSoject_config['displayName'];
876             }
877             else {
878               $displayName = NULL;
879             }
880             if( self :: loadLSobject($LSobject_name) ) {
881               if ($subdnobject = new $LSobject_name()) {
882                 $tbl_return = $subdnobject -> getSelectArray(NULL,$basedn,$displayName);
883                 if (is_array($tbl_return)) {
884                   $return=array_merge($return,$tbl_return);
885                 }
886                 else {
887                   LSerror :: addErrorCode('LSsession_17',3);
888                 }
889               }
890               else {
891                 LSerror :: addErrorCode('LSsession_17',2);
892               }
893             }
894           }
895         }
896         else {
897           LSerror :: addErrorCode('LSsession_17',1);
898         }
899       }
900       else {
901         if ((isCompatibleDNs($subDn_config['dn'],self :: $ldapServer['ldap_config']['basedn']))&&($subDn_config['dn']!="")) {
902           $return[$subDn_config['dn']] = __($subDn_name);
903         }
904       }
905     }
906     if (self :: cacheSudDn()) {
907       self :: $_subDnLdapServer[self :: $ldapServerId]=$return;
908       $_SESSION['LSsession_subDnLdapServer'] = self :: $_subDnLdapServer;
909     }
910     return $return;
911   }
912   
913   /**
914    * Retourne la liste de subDn du serveur Ldap utilise
915    * trié par la profondeur dans l'arboressence (ordre décroissant)
916    * 
917    * @return array() Tableau des subDn trié
918    */  
919   public static function getSortSubDnLdapServer() {
920     $subDnLdapServer = self :: getSubDnLdapServer();
921     if (!$subDnLdapServer) {
922       return array();
923     }
924     uksort($subDnLdapServer,"compareDn");
925     return $subDnLdapServer;
926   }
927
928  /**
929   * Retourne les options d'une liste déroulante pour le choix du topDn
930   * de connexion au serveur Ldap
931   *
932   * Liste les subdn (self :: $ldapServer['subDn'])
933   *
934   * @retval string Les options (<option>) pour la sélection du topDn.
935   */
936   public static function getSubDnLdapServerOptions($selected=NULL) {
937     $list = self :: getSubDnLdapServer();
938     if ($list) {
939       asort($list);
940       $display='';
941       foreach($list as $dn => $txt) {
942         if ($selected && ($selected==$dn)) {
943           $selected_txt = ' selected';
944         }
945         else {
946           $selected_txt = '';
947         }
948         $display.="<option value=\"".$dn."\"$selected_txt>".$txt."</option>\n"; 
949       }
950       return $display;
951     }
952     return;
953   }
954
955  /**
956   * Vérifie qu'un subDn est déclaré
957   *
958   * @param[in] string Un subDn
959   * 
960   * @retval boolean True si le subDn existe, False sinon
961   */
962   public static function validSubDnLdapServer($subDn) {
963     $listTopDn = self :: getSubDnLdapServer();
964     if(is_array($listTopDn)) {
965       foreach($listTopDn as $dn => $txt) {
966         if ($subDn==$dn) {
967           return true;
968         } // end if
969       } // end foreach
970     } // end if
971     return;
972   }
973
974  /**
975   * Test un couple LSobject/pwd
976   *
977   * Test un bind sur le serveur avec le dn de l'objet et le mot de passe fourni.
978   *
979   * @param[in] LSobject L'object "user" pour l'authentification
980   * @param[in] string Le mot de passe Ã  tester
981   *
982   * @retval boolean True si l'authentification Ã  réussi, false sinon.
983   */
984   public static function checkUserPwd($object,$pwd) {
985     return LSldap :: checkBind($object -> getValue('dn'),$pwd);
986   }
987
988  /**
989   * Affiche le formulaire de login
990   *
991   * Défini les informations pour le template Smarty du formulaire de login.
992   *
993   * @retval void
994   */
995   public static function displayLoginForm() {
996     $GLOBALS['Smarty'] -> assign('pagetitle',_('Connection'));
997     if (isset($_GET['LSsession_logout'])) {
998       $GLOBALS['Smarty'] -> assign('loginform_action','index.php');
999     }
1000     else {
1001       $GLOBALS['Smarty'] -> assign('loginform_action',$_SERVER['REQUEST_URI']);
1002     }
1003     if (count(LSconfig :: get('ldap_servers'))==1) {
1004       $GLOBALS['Smarty'] -> assign('loginform_ldapserver_style','style="display: none"');
1005     }
1006     $GLOBALS['Smarty'] -> assign('loginform_label_ldapserver',_('LDAP server'));
1007     $ldapservers_name=array();
1008     $ldapservers_index=array();
1009     foreach(LSconfig :: get('ldap_servers') as $id => $infos) {
1010       $ldapservers_index[]=$id;
1011       $ldapservers_name[]=__($infos['name']);
1012     }
1013     $GLOBALS['Smarty'] -> assign('loginform_ldapservers_name',$ldapservers_name);
1014     $GLOBALS['Smarty'] -> assign('loginform_ldapservers_index',$ldapservers_index);
1015
1016     $GLOBALS['Smarty'] -> assign('loginform_label_level',_('Level'));
1017     $GLOBALS['Smarty'] -> assign('loginform_label_user',_('Identifier'));
1018     $GLOBALS['Smarty'] -> assign('loginform_label_pwd',_('Password'));
1019     $GLOBALS['Smarty'] -> assign('loginform_label_submit',_('Connect'));
1020     $GLOBALS['Smarty'] -> assign('loginform_label_recoverPassword',_('Forgot your password ?'));
1021     
1022     self :: setTemplate('login.tpl');
1023     self :: addJSscript('LSsession_login.js');
1024   }
1025
1026  /**
1027   * Affiche le formulaire de récupération de mot de passe
1028   *
1029   * Défini les informations pour le template Smarty du formulaire de 
1030   * récupération de mot de passe
1031   * 
1032   * @param[in] $infos array() Information sur le status du processus de 
1033   *                           recouvrement de mot de passe
1034   *
1035   * @retval void
1036   */
1037   public static function displayRecoverPasswordForm($recoveryPasswordInfos) {
1038     $GLOBALS['Smarty'] -> assign('pagetitle',_('Recovery of your credentials'));
1039     $GLOBALS['Smarty'] -> assign('recoverpasswordform_action','index.php?LSsession_recoverPassword');
1040     
1041     if (count(LSconfig :: get('ldap_servers'))==1) {
1042       $GLOBALS['Smarty'] -> assign('recoverpasswordform_ldapserver_style','style="display: none"');
1043     }
1044     
1045     $GLOBALS['Smarty'] -> assign('recoverpasswordform_label_ldapserver',_('LDAP server'));
1046     $ldapservers_name=array();
1047     $ldapservers_index=array();
1048     foreach(LSconfig :: get('ldap_servers') as $id => $infos) {
1049       $ldapservers_index[]=$id;
1050       $ldapservers_name[]=$infos['name'];
1051     }
1052     $GLOBALS['Smarty'] -> assign('recoverpasswordform_ldapservers_name',$ldapservers_name);
1053     $GLOBALS['Smarty'] -> assign('recoverpasswordform_ldapservers_index',$ldapservers_index);
1054
1055     $GLOBALS['Smarty'] -> assign('recoverpasswordform_label_user',_('Identifier'));
1056     $GLOBALS['Smarty'] -> assign('recoverpasswordform_label_submit',_('Validate'));
1057     $GLOBALS['Smarty'] -> assign('recoverpasswordform_label_back',_('Back'));
1058     
1059     $recoverpassword_msg = _('Please fill the identifier field to proceed recovery procedure');
1060     
1061     if (isset($recoveryPasswordInfos['recoveryHashMail'])) {
1062       $recoverpassword_msg = getFData(
1063         _("An email has been sent to  %{mail}. " .
1064         "Please follow the instructions on it."),
1065         $recoveryPasswordInfos['recoveryHashMail']
1066       );
1067     }
1068     
1069     if (isset($recoveryPasswordInfos['newPasswordMail'])) {
1070       $recoverpassword_msg = getFData(
1071         _("Your new password has been sent to %{mail}. "),
1072         $recoveryPasswordInfos['newPasswordMail']
1073       );
1074     }
1075     
1076     $GLOBALS['Smarty'] -> assign('recoverpassword_msg',$recoverpassword_msg);
1077     
1078     self :: setTemplate('recoverpassword.tpl');
1079     self :: addJSscript('LSsession_recoverPassword.js');
1080   }
1081
1082  /**
1083   * Défini le template Smarty Ã  utiliser
1084   *
1085   * Remarque : les fichiers de templates doivent se trouver dans le dossier 
1086   * templates/.
1087   *
1088   * @param[in] string Le nom du fichier de template
1089   *
1090   * @retval void
1091   */
1092   public static function setTemplate($template) {
1093     self :: $template = $template;
1094   }
1095
1096  /**
1097   * Ajoute un script JS au chargement de la page
1098   *
1099   * Remarque : les scripts doivents Ãªtre dans le dossier LS_JS_DIR.
1100   *
1101   * @param[in] $script Le nom du fichier de script Ã  charger.
1102   *
1103   * @retval void
1104   */
1105   public static function addJSscript($file,$path=NULL) {
1106     $script=array(
1107       'file' => $file,
1108       'path' => $path
1109     );
1110     self :: $JSscripts[$path.$file]=$script;
1111   }
1112
1113  /**
1114   * Ajouter un paramètre de configuration Javascript
1115   * 
1116   * @param[in] $name string Nom de la variable de configuration
1117   * @param[in] $val mixed Valeur de la variable de configuration
1118   *
1119   * @retval void
1120   */
1121   public static function addJSconfigParam($name,$val) {
1122     self :: $_JSconfigParams[$name]=$val;
1123   }
1124
1125  /**
1126   * Ajoute une feuille de style au chargement de la page
1127   *
1128   * Remarque : les scripts doivents Ãªtre dans le dossier LS_CSS_DIR.
1129   *
1130   * @param[in] $script Le nom du fichier css Ã  charger.
1131   *
1132   * @retval void
1133   */
1134   public static function addCssFile($file,$path=NULL) {
1135     $cssFile=array(
1136       'file' => $file,
1137       'path' => $path
1138     );
1139     self :: $CssFiles[$path.$file]=$cssFile;
1140   }
1141
1142  /**
1143   * Affiche le template Smarty
1144   *
1145   * Charge les dépendances et affiche le template Smarty
1146   *
1147   * @retval void
1148   */
1149   public static function displayTemplate() {
1150     // JS
1151     $JSscript_txt='';
1152     foreach ($GLOBALS['defaultJSscipts'] as $script) {
1153       $JSscript_txt.="<script src='".LS_JS_DIR.$script."' type='text/javascript'></script>\n";
1154     }
1155
1156     foreach (self :: $JSscripts as $script) {
1157       if (!$script['path']) {
1158         $script['path']=LS_JS_DIR;
1159       }
1160       else {
1161         $script['path'].='/';
1162       }
1163       $JSscript_txt.="<script src='".$script['path'].$script['file']."' type='text/javascript'></script>\n";
1164     }
1165
1166     $KAconf = LSconfig :: get('keepLSsessionActive');
1167     if ( 
1168           (
1169             (!isset(self :: $ldapServer['keepLSsessionActive']))
1170             &&
1171             (!($KAconf === false))
1172           )
1173           ||
1174           (self :: $ldapServer['keepLSsessionActive'])
1175         ) {
1176       self :: addJSconfigParam('keepLSsessionActive',ini_get('session.gc_maxlifetime'));
1177     }
1178
1179     $GLOBALS['Smarty'] -> assign('LSjsConfig',json_encode(self :: $_JSconfigParams));
1180     
1181     if (LSdebug) {
1182       $JSscript_txt.="<script type='text/javascript'>LSdebug_active = 1;</script>\n";
1183     }
1184     else {
1185       $JSscript_txt.="<script type='text/javascript'>LSdebug_active = 0;</script>\n";
1186     }
1187     
1188     $GLOBALS['Smarty'] -> assign('LSsession_js',$JSscript_txt);
1189
1190     // Css
1191     self :: addCssFile("LSdefault.css");
1192     $Css_txt='';
1193     foreach (self :: $CssFiles as $file) {
1194       if (!$file['path']) {
1195         $file['path']=LS_CSS_DIR.'/';
1196       }
1197       $Css_txt.="<link rel='stylesheet' type='text/css' href='".$file['path'].$file['file']."' />\n";
1198     }
1199     $GLOBALS['Smarty'] -> assign('LSsession_css',$Css_txt);
1200   
1201     if (isset(self :: $LSaccess[self :: $topDn])) {
1202       $GLOBALS['Smarty'] -> assign('LSaccess',self :: $LSaccess[self :: $topDn]);
1203     }
1204     
1205     // Niveau
1206     $listTopDn = self :: getSubDnLdapServer();
1207     if (is_array($listTopDn)) {
1208       asort($listTopDn);
1209       $GLOBALS['Smarty'] -> assign('label_level',self :: getSubDnLabel());
1210       $GLOBALS['Smarty'] -> assign('_refresh',_('Refresh'));
1211       $LSsession_topDn_index = array();
1212       $LSsession_topDn_name = array();
1213       foreach($listTopDn as $index => $name) {
1214         $LSsession_topDn_index[]  = $index;
1215         $LSsession_topDn_name[]   = $name;
1216       }
1217       $GLOBALS['Smarty'] -> assign('LSsession_subDn_indexes',$LSsession_topDn_index);
1218       $GLOBALS['Smarty'] -> assign('LSsession_subDn_names',$LSsession_topDn_name);
1219       $GLOBALS['Smarty'] -> assign('LSsession_subDn',self :: $topDn);
1220       $GLOBALS['Smarty'] -> assign('LSsession_subDnName',self :: getSubDnName());
1221     }
1222     
1223     $GLOBALS['Smarty'] -> assign('LSlanguages',self :: getLangList());
1224     $GLOBALS['Smarty'] -> assign('LSlang',self :: $lang);
1225     $GLOBALS['Smarty'] -> assign('LSencoding',self :: $encoding);
1226     $GLOBALS['Smarty'] -> assign('lang_label',_('Language'));
1227
1228     // Infos
1229     if((!empty($_SESSION['LSsession_infos']))&&(is_array($_SESSION['LSsession_infos']))) {
1230       $txt_infos="<ul>\n";
1231       foreach($_SESSION['LSsession_infos'] as $info) {
1232         $txt_infos.="<li>$info</li>\n";
1233       }
1234       $txt_infos.="</ul>\n";
1235       $GLOBALS['Smarty'] -> assign('LSinfos',$txt_infos);
1236       $_SESSION['LSsession_infos']=array();
1237     }
1238     
1239     if (self :: $ajaxDisplay) {
1240       $GLOBALS['Smarty'] -> assign('LSerror_txt',LSerror :: getErrors());
1241       $GLOBALS['Smarty'] -> assign('LSdebug_txt',LSdebug_print(true));
1242     }
1243     else {
1244       LSerror :: display();
1245       LSdebug_print();
1246     }
1247     if (!self :: $template)
1248       self :: setTemplate('empty.tpl');
1249       
1250     $GLOBALS['Smarty'] -> assign('connected_as',_("Connected as"));
1251     
1252     $GLOBALS['Smarty'] -> display(self :: $template);
1253   }
1254   
1255  /**
1256   * Défini que l'affichage se fera ou non via un retour Ajax
1257   * 
1258   * @param[in] $val boolean True pour que l'affichage se fasse par un retour
1259   *                         Ajax, false sinon
1260   * @retval void
1261   */
1262   public static function setAjaxDisplay($val=true) {
1263     self :: $ajaxDisplay = (boolean)$val;
1264   }
1265   
1266  /**
1267   * Affiche un retour Ajax
1268   *
1269   * @retval void
1270   */
1271   public static function displayAjaxReturn($data=array()) {
1272     if (isset($data['LSredirect']) && (!LSdebugDefined()) ) {
1273       echo json_encode($data);
1274       return;
1275     }
1276     
1277     $data['LSjsConfig'] = self :: $_JSconfigParams;
1278     
1279     // Infos
1280     if((!empty($_SESSION['LSsession_infos']))&&(is_array($_SESSION['LSsession_infos']))) {
1281       $txt_infos="<ul>\n";
1282       foreach($_SESSION['LSsession_infos'] as $info) {
1283         $txt_infos.="<li>$info</li>\n";
1284       }
1285       $txt_infos.="</ul>\n";
1286       $data['LSinfos'] = $txt_infos;
1287       $_SESSION['LSsession_infos']=array();
1288     }
1289     
1290     if (LSerror :: errorsDefined()) {
1291       $data['LSerror'] = LSerror :: getErrors();
1292     }
1293
1294     if (isset($_REQUEST['imgload'])) {
1295       $data['imgload'] = $_REQUEST['imgload'];
1296     }
1297
1298     if (LSdebugDefined()) {
1299       $data['LSdebug'] = LSdebug_print(true);
1300     }
1301
1302     echo json_encode($data);  
1303   }
1304  
1305  /**
1306   * Retournne un template Smarty compilé
1307   *
1308   * @param[in] string $template Le template à retourner
1309   * @param[in] array $variables Variables Smarty à assigner avant l'affichage
1310   * 
1311   * @retval string Le HTML compilé du template
1312   */
1313   public static function fetchTemplate($template,$variables=array()) {
1314     foreach($variables as $name => $val) {
1315       $GLOBALS['Smarty'] -> assign($name,$val);
1316     }
1317     return $GLOBALS['Smarty'] -> fetch($template);
1318   }
1319   
1320   /**
1321    * Charge les droits LS de l'utilisateur
1322    * 
1323    * @retval boolean True si le chargement Ã  réussi, false sinon.
1324    **/
1325   private static function loadLSprofiles() {
1326     if (is_array(self :: $ldapServer['LSprofiles'])) {
1327       foreach (self :: $ldapServer['LSprofiles'] as $profile => $profileInfos) {
1328         if (is_array($profileInfos)) {
1329           foreach ($profileInfos as $topDn => $rightsInfos) {
1330             /*
1331              * If $topDn == 'LSobject', we search for each LSobject type to find
1332              * all items on witch the user will have powers.
1333              */
1334             if ($topDn == 'LSobjects') {
1335               if (is_array($rightsInfos)) {
1336                 foreach ($rightsInfos as $LSobject => $listInfos) {
1337                   if (self :: loadLSobject($LSobject)) {
1338                     if ($object = new $LSobject()) {
1339                       if ($listInfos['filter']) {
1340                         $filter = self :: getLSuserObject() -> getFData($listInfos['filter']);
1341                       }
1342                       else {
1343                         $filter = $listInfos['attr'].'='.self :: getLSuserObject() -> getFData($listInfos['attr_value']);
1344                       }
1345                       $list = $object -> search($filter,$listInfos['basedn'],$listInfos['params']);
1346                       foreach($list as $obj) {
1347                         self :: $LSprofiles[$profile][] = $obj['dn'];
1348                       }
1349                     }
1350                     else {
1351                       LSdebug('Impossible de créer l\'objet de type : '.$LSobject);
1352                     }
1353                   }
1354                 }
1355               }
1356               else {
1357                 LSdebug('LSobjects => [] doit etre un tableau');
1358               }
1359             }
1360             else {
1361               if (is_array($rightsInfos)) {
1362                 foreach($rightsInfos as $dn => $conf) {
1363                   if ((isset($conf['attr'])) && (isset($conf['LSobject']))) {
1364                     if( self :: loadLSobject($conf['LSobject']) ) {
1365                       if ($object = new $conf['LSobject']()) {
1366                         if ($object -> loadData($dn)) {
1367                           $listDns=$object -> getValue($conf['attr']);
1368                           $valKey = (isset($conf['attr_value']))?$conf['attr_value']:'%{dn}';
1369                           $val = self :: getLSuserObject() -> getFData($valKey);
1370                           if (is_array($listDns)) {
1371                             if (in_array($val,$listDns)) {
1372                               self :: $LSprofiles[$profile][] = $topDn;
1373                             }
1374                           }
1375                         }
1376                         else {
1377                           LSdebug('Impossible de chargé le dn : '.$dn);
1378                         }
1379                       }
1380                       else {
1381                         LSdebug('Impossible de créer l\'objet de type : '.$conf['LSobject']);
1382                       }
1383                     }
1384                   }
1385                   else {
1386                     if (self :: $dn == $dn) {
1387                       self :: $LSprofiles[$profile][] = $topDn;
1388                     }
1389                   }
1390                 }
1391               }
1392               else {
1393                 if ( self :: $dn == $rightsInfos ) {
1394                   self :: $LSprofiles[$profile][] = $topDn;
1395                 }
1396               }
1397             } // fin else ($topDn == 'LSobjects')
1398           } // fin foreach($profileInfos)
1399         } // fin is_array($profileInfos)
1400       } // fin foreach LSprofiles
1401       LSdebug(self :: $LSprofiles);
1402       return true;
1403     }
1404     else {
1405       return;
1406     }
1407   }
1408   
1409   /**
1410    * Charge les droits d'accès de l'utilisateur pour construire le menu de l'interface
1411    *
1412    * @retval void
1413    */
1414   private static function loadLSaccess() {
1415     $LSaccess=array();
1416     if (is_array(self :: $ldapServer['subDn'])) {
1417       foreach(self :: $ldapServer['subDn'] as $name => $config) {
1418         if ($name=='LSobject') {
1419           if (is_array($config)) {
1420
1421             // Définition des subDns 
1422             foreach($config as $objectType => $objectConf) {
1423               if (self :: loadLSobject($objectType)) {
1424                 if ($subdnobject = new $objectType()) {
1425                   $tbl = $subdnobject -> getSelectArray();
1426                   if (is_array($tbl)) {
1427                     // Définition des accès
1428                     $access=array();
1429                     if (is_array($objectConf['LSobjects'])) {
1430                       foreach($objectConf['LSobjects'] as $type) {
1431                         if (self :: loadLSobject($type)) {
1432                           if (self :: canAccess($type)) {
1433                             $access[$type] = LSconfig :: get('LSobjects.'.$type.'.label');
1434                           }
1435                         }
1436                       }
1437                     }
1438                     foreach($tbl as $dn => $dn_name) {
1439                       $LSaccess[$dn]=$access;
1440                     }
1441                   }
1442                 }
1443               }
1444             }
1445           }
1446         }
1447         else {
1448           if ((isCompatibleDNs(self :: $ldapServer['ldap_config']['basedn'],$config['dn']))&&($config['dn']!='')) {
1449             $access=array();
1450             if (is_array($config['LSobjects'])) {
1451               foreach($config['LSobjects'] as $objectType) {
1452                 if (self :: loadLSobject($objectType)) {
1453                   if (self :: canAccess($objectType)) {
1454                     $access[$objectType] = LSconfig :: get('LSobjects.'.$objectType.'.label');
1455                   }
1456                 }
1457               }
1458             }
1459             $LSaccess[$config['dn']]=$access;
1460           }
1461         }
1462       }
1463     }
1464     else {
1465       if(is_array(self :: $ldapServer['LSaccess'])) {
1466         $access=array();
1467         foreach(self :: $ldapServer['LSaccess'] as $objectType) {
1468           if (self :: loadLSobject($objectType)) {
1469             if (self :: canAccess($objectType)) {
1470                 $access[$objectType] = LSconfig :: get('LSobjects.'.$objectType.'.label');
1471             }
1472           }
1473         }
1474         $LSaccess[self :: $topDn] = $access;
1475       }
1476     }
1477     foreach($LSaccess as $dn => $access) {
1478       $LSaccess[$dn] = array_merge(
1479         array(
1480           'SELF' => 'My account'
1481         ),
1482         $access
1483       );
1484     }
1485     
1486     self :: $LSaccess = $LSaccess;
1487     $_SESSION['LSsession']['LSaccess'] = $LSaccess;
1488   }
1489   
1490   /**
1491    * Dit si l'utilisateur est du profil pour le DN spécifié
1492    *
1493    * @param[in] string $profile de l'objet
1494    * @param[in] string $dn DN de l'objet
1495    * 
1496    * @retval boolean True si l'utilisateur est du profil sur l'objet, false sinon.
1497    */
1498   public static function isLSprofile($dn,$profile) {
1499     if (is_array(self :: $LSprofiles[$profile])) {
1500       foreach(self :: $LSprofiles[$profile] as $topDn) {
1501         if($dn == $topDn) {
1502           return true;
1503         }
1504         else if ( isCompatibleDNs($dn,$topDn) ) {
1505           return true;
1506         }
1507       }
1508     }
1509     return;
1510   }
1511   
1512   /**
1513    * Retourne qui est l'utilisateur par rapport Ã  l'object
1514    *
1515    * @param[in] string Le DN de l'objet
1516    * 
1517    * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
1518    */
1519   public static function whoami($dn) {
1520     $retval = array('user');
1521     
1522     foreach(self :: $LSprofiles as $profile => $infos) {
1523       if(self :: isLSprofile($dn,$profile)) {
1524        $retval[]=$profile;
1525       }
1526     }
1527     
1528     if (self :: $dn == $dn) {
1529       $retval[]='self';
1530     }
1531     
1532     return $retval;
1533   }
1534   
1535   /**
1536    * Retourne le droit de l'utilisateur Ã  accèder Ã  un objet
1537    * 
1538    * @param[in] string $LSobject Le type de l'objet
1539    * @param[in] string $dn Le DN de l'objet (le container_dn du type de l'objet par défaut)
1540    * @param[in] string $right Le type de droit d'accès Ã  tester ('r'/'w')
1541    * @param[in] string $attr Le nom de l'attribut auquel on test l'accès
1542    *
1543    * @retval boolean True si l'utilisateur a accès, false sinon
1544    */
1545   public static function canAccess($LSobject,$dn=NULL,$right=NULL,$attr=NULL) {
1546     if (!self :: loadLSobject($LSobject)) {
1547       return;
1548     }
1549     if ($dn) {
1550       $whoami = self :: whoami($dn);
1551       if ($dn==self :: getLSuserObject() -> getValue('dn')) {
1552         if (!self :: in_menu('SELF')) {
1553           return;
1554         }
1555       }
1556       else {
1557         $obj = new $LSobject();
1558         $obj -> dn = $dn;
1559         if (!self :: in_menu($LSobject,$obj -> getSubDnValue())) {
1560           return;
1561         }
1562       }
1563     }
1564     else {
1565       $objectdn=LSconfig :: get('LSobjects.'.$LSobject.'.container_dn').','.self :: $topDn;
1566       $whoami = self :: whoami($objectdn);
1567     }
1568     
1569     // Pour un attribut particulier
1570     if ($attr) {
1571       if ($attr=='rdn') {
1572         $attr=LSconfig :: get('LSobjects.'.$LSobject.'.rdn');
1573       }
1574       if (!is_array(LSconfig :: get('LSobjects.'.$LSobject.'.attrs.'.$attr))) {
1575         return;
1576       }
1577
1578       $r = 'n';
1579       foreach($whoami as $who) {
1580         $nr = LSconfig :: get('LSobjects.'.$LSobject.'.attrs.'.$attr.'.rights.'.$who);
1581         if($nr == 'w') {
1582           $r = 'w';
1583         }
1584         else if($nr == 'r') {
1585           if ($r=='n') {
1586             $r='r';
1587           }
1588         }
1589       }
1590       
1591       if (($right=='r')||($right=='w')) {
1592         if ($r==$right) {
1593           return true;
1594         }
1595         return;
1596       }
1597       else {
1598         if ( ($r=='r') || ($r=='w') ) {
1599           return true;
1600         }
1601         return;
1602       }
1603     }
1604     
1605     // Pour un attribut quelconque
1606     $attrs_conf=LSconfig :: get('LSobjects.'.$LSobject.'.attrs');
1607     if (is_array($attrs_conf)) {
1608       if (($right=='r')||($right=='w')) {
1609         foreach($whoami as $who) {
1610           foreach ($attrs_conf as $attr_name => $attr_config) {
1611             if ($attr_config['rights'][$who]==$right) {
1612               return true;
1613             }
1614           }
1615         }
1616       }
1617       else {
1618         foreach($whoami as $who) {
1619           foreach ($attrs_conf as $attr_name => $attr_config) {
1620             if ( ($attr_config['rights'][$who]=='r') || ($attr_config['rights'][$who]=='w') ) {
1621               return true;
1622             }
1623           }
1624         }
1625       }
1626     }
1627     return;
1628   }
1629   
1630   /**
1631    * Retourne le droit de l'utilisateur Ã  editer Ã  un objet
1632    * 
1633    * @param[in] string $LSobject Le type de l'objet
1634    * @param[in] string $dn Le DN de l'objet (le container_dn du type de l'objet par défaut)
1635    * @param[in] string $attr Le nom de l'attribut auquel on test l'accès
1636    *
1637    * @retval boolean True si l'utilisateur a accès, false sinon
1638    */
1639   public static function canEdit($LSobject,$dn=NULL,$attr=NULL) {
1640     return self :: canAccess($LSobject,$dn,'w',$attr);
1641   }
1642
1643   /**
1644    * Retourne le droit de l'utilisateur Ã  supprimer un objet
1645    * 
1646    * @param[in] string $LSobject Le type de l'objet
1647    * @param[in] string $dn Le DN de l'objet (le container_dn du type de l'objet par défaut)
1648    *
1649    * @retval boolean True si l'utilisateur a accès, false sinon
1650    */  
1651   public static function canRemove($LSobject,$dn) {
1652     return self :: canAccess($LSobject,$dn,'w','rdn');
1653   }
1654   
1655   /**
1656    * Retourne le droit de l'utilisateur Ã  créer un objet
1657    * 
1658    * @param[in] string $LSobject Le type de l'objet
1659    *
1660    * @retval boolean True si l'utilisateur a accès, false sinon
1661    */    
1662   public static function canCreate($LSobject) {
1663     return self :: canAccess($LSobject,NULL,'w','rdn');
1664   }
1665   
1666   /**
1667    * Retourne le droit de l'utilisateur Ã  gérer la relation d'objet
1668    * 
1669    * @param[in] string $dn Le DN de l'objet (le container_dn du type de l'objet par défaut)
1670    * @param[in] string $LSobject Le type de l'objet
1671    * @param[in] string $relationName Le nom de la relation avec l'objet
1672    * @param[in] string $right Le type de droit a vérifier ('r' ou 'w')
1673    *
1674    * @retval boolean True si l'utilisateur a accès, false sinon
1675    */
1676   public static function relationCanAccess($dn,$LSobject,$relationName,$right=NULL) {
1677     $relConf=LSconfig :: get('LSobjects.'.$LSobject.'.LSrelation.'.$relationName);
1678     if (!is_array($relConf))
1679       return;
1680     $whoami = self :: whoami($dn);
1681
1682     if (($right=='w') || ($right=='r')) {
1683       $r = 'n';
1684       foreach($whoami as $who) {
1685         $nr = $relConf['rights'][$who];
1686         if($nr == 'w') {
1687           $r = 'w';
1688         }
1689         else if($nr == 'r') {
1690           if ($r=='n') {
1691             $r='r';
1692           }
1693         }
1694       }
1695       
1696       if ($r == $right) {
1697         return true;
1698       }
1699     }
1700     else {
1701       foreach($whoami as $who) {
1702         if (($relConf['rights'][$who] == 'w') || ($relConf['rights'][$who] == 'r')) {
1703           return true;
1704         }
1705       }
1706     }
1707     return;
1708   }
1709
1710   /**
1711    * Retourne le droit de l'utilisateur Ã  modifier la relation d'objet
1712    * 
1713    * @param[in] string $dn Le DN de l'objet (le container_dn du type de l'objet par défaut)
1714    * @param[in] string $LSobject Le type de l'objet
1715    * @param[in] string $relationName Le nom de la relation avec l'objet
1716    *
1717    * @retval boolean True si l'utilisateur a accès, false sinon
1718    */  
1719   public static function relationCanEdit($dn,$LSobject,$relationName) {
1720     return self :: relationCanAccess($dn,$LSobject,$relationName,'w');
1721   }
1722
1723   /**
1724    * Ajoute un fichier temporaire
1725    * 
1726    * @author Benjamin Renard <brenard@easter-eggs.com>
1727    * 
1728    * @retval void
1729    **/
1730   public static function addTmpFile($value,$filePath) {
1731     $hash = mhash(MHASH_MD5,$value);
1732     self :: $tmp_file[$filePath] = $hash;
1733     $_SESSION['LSsession']['tmp_file'][$filePath] = $hash;
1734   }
1735   
1736   /**
1737    * Retourne le chemin du fichier temporaire si l'existe
1738    * 
1739    * @author Benjamin Renard <brenard@easter-eggs.com>
1740    * 
1741    * @param[in] $value La valeur du fichier
1742    * 
1743    * @retval mixed 
1744    **/
1745   public static function tmpFileExist($value) {
1746     $hash = mhash(MHASH_MD5,$value);
1747     foreach(self :: $tmp_file as $filePath => $contentHash) {
1748       if ($hash == $contentHash) {
1749         return $filePath;
1750       }
1751     }
1752     return false;
1753   }
1754   
1755   /**
1756    * Retourne le chemin du fichier temporaire
1757    * 
1758    * Retourne le chemin du fichier temporaire qu'il créera Ã  partir de la valeur
1759    * s'il n'existe pas déjà.
1760    * 
1761    * @author Benjamin Renard <brenard@easter-eggs.com>
1762    * 
1763    * @param[in] $value La valeur du fichier
1764    * 
1765    * @retval mixed 
1766    **/
1767   public static function getTmpFile($value) {
1768     $exist = self :: tmpFileExist($value);
1769     if (!$exist) {
1770       $img_path = LS_TMP_DIR .rand().'.tmp';
1771       $fp = fopen($img_path, "w");
1772       fwrite($fp, $value);
1773       fclose($fp);
1774       self :: addTmpFile($value,$img_path);
1775       return $img_path;
1776     }
1777     else {
1778       return $exist;
1779     }
1780   }
1781   
1782   /**
1783    * Supprime les fichiers temporaires
1784    * 
1785    * @author Benjamin Renard <brenard@easter-eggs.com>
1786    * 
1787    * @retval void
1788    **/
1789   public static function deleteTmpFile($filePath=NULL) {
1790     if ($filePath) {
1791         @unlink($filePath);
1792         unset(self :: $tmp_file[$filePath]);
1793         unset($_SESSION['LSsession']['tmp_file'][$filePath]);
1794     }
1795     else {
1796       foreach(self :: $tmp_file as $file => $content) {
1797         @unlink($file);
1798       }
1799       self :: $tmp_file = array();
1800       $_SESSION['LSsession']['tmp_file'] = array();
1801     }
1802   }
1803
1804   /**
1805    * Retourne true si le cache des droits est activé
1806    *
1807    * @author Benjamin Renard <brenard@easter-eggs.com>
1808    * 
1809    * @retval boolean True si le cache des droits est activé, false sinon.
1810    */
1811   public static function cacheLSprofiles() {
1812     return ( (LSconfig :: get('cacheLSprofiles')) || (self :: $ldapServer['cacheLSprofiles']) );
1813   }
1814
1815   /**
1816    * Retourne true si le cache des subDn est activé
1817    *
1818    * @author Benjamin Renard <brenard@easter-eggs.com>
1819    * 
1820    * @retval boolean True si le cache des subDn est activé, false sinon.
1821    */
1822   public static function cacheSudDn() {
1823     return ( (LSconfig :: get('cacheSubDn')) || (self :: $ldapServer['cacheSubDn']));
1824   }
1825   
1826   /**
1827    * Retourne true si le cache des recherches est activé
1828    *
1829    * @author Benjamin Renard <brenard@easter-eggs.com>
1830    * 
1831    * @retval boolean True si le cache des recherches est activé, false sinon.
1832    */
1833   public static function cacheSearch() {
1834     return ( (LSconfig :: get('cacheSearch')) || (self :: $ldapServer['cacheSearch']));
1835   }
1836   
1837   /**
1838    * Retourne le label des niveaux pour le serveur ldap courant
1839    * 
1840    * @author Benjamin Renard <brenard@easter-eggs.com>
1841    * 
1842    * @retval string Le label des niveaux pour le serveur ldap dourant
1843    */
1844   public static function getSubDnLabel() {
1845     return (self :: $ldapServer['subDnLabel']!='')?__(self :: $ldapServer['subDnLabel']):_('Level');
1846   }
1847   
1848   /**
1849    * Retourne le nom du subDn
1850    * 
1851    * @param[in] $subDn string subDn
1852    * 
1853    * @retval string Le nom du subDn ou '' sinon
1854    */
1855   public static function getSubDnName($subDn=false) {
1856     if (!$subDn) {
1857       $subDn = self :: $topDn;
1858     }
1859     if (self :: getSubDnLdapServer()) {
1860       if (isset(self :: $_subDnLdapServer[self :: $ldapServerId][$subDn])) {
1861         return self :: $_subDnLdapServer[self :: $ldapServerId][$subDn];
1862       }
1863     }
1864     return '';
1865   }
1866
1867   /**
1868    * L'objet est t-il utilisé pour listé les subDnS
1869    * 
1870    * @param[in] $type string Le type d'objet
1871    * 
1872    * @retval boolean true si le type d'objet est un subDnObject, false sinon
1873    */
1874   public static function isSubDnLSobject($type) {
1875     $result = false;
1876     if (is_array(self :: $ldapServer['subDn']['LSobject'])) {
1877       foreach(self :: $ldapServer['subDn']['LSobject'] as $key => $value) {
1878         if ($key==$type) {
1879           $result=true;
1880         }
1881       }
1882     }
1883     return $result;
1884   }
1885   
1886   /**
1887    * Indique si un type d'objet est dans le menu courant
1888    * 
1889    * @retval boolean true si le type d'objet est dans le menu, false sinon
1890    */
1891   public static function in_menu($LSobject,$topDn=NULL) {
1892     if (!$topDn) {
1893       $topDn=self :: $topDn;
1894     }
1895     return isset(self :: $LSaccess[$topDn][$LSobject]);
1896   }
1897   
1898   /**
1899    * Indique si le serveur LDAP courant a des subDn
1900    * 
1901    * @retval boolean true si le serveur LDAP courant a des subDn, false sinon
1902    */
1903   public static function haveSubDn() {
1904     return (is_array(self :: $ldapServer['subDn']));
1905   }
1906
1907   /**
1908    * Ajoute une information à afficher
1909    * 
1910    * @param[in] $msg string Le message à afficher
1911    * 
1912    * @retval void
1913    */
1914   public static function addInfo($msg) {
1915     $_SESSION['LSsession_infos'][]=$msg;
1916   }
1917   
1918   /**
1919    * Redirection de l'utilisateur vers une autre URL
1920    * 
1921    * @param[in] $url string L'URL
1922    * @param[in] $exit boolean Si true, l'execution script s'arrête après la redirection
1923    * 
1924    * @retval void
1925    */  
1926   public static function redirect($url,$exit=true) {
1927     $GLOBALS['Smarty'] -> assign('url',$url);
1928     $GLOBALS['Smarty'] -> display('redirect.tpl');
1929     if ($exit) {
1930       exit();
1931     }
1932   }
1933   
1934   /**
1935    * Retourne l'adresse mail d'emission configurée pour le serveur courant
1936    * 
1937    * @retval string Adresse mail d'emission
1938    */
1939   public static function getEmailSender() {
1940     return self :: $ldapServer['emailSender'];  
1941   }
1942   
1943   /**
1944    * Ajout d'une information d'aide
1945    * 
1946    * @param[in] $group string Le nom du groupe d'infos dans lequels ajouter
1947    *                          celle-ci
1948    * @param[in] $infos array  Tableau array(name => value) des infos
1949    * 
1950    * @retval void
1951    */
1952   public static function addHelpInfos($group,$infos) {
1953     if (is_array($infos)) {
1954       if (is_array(self :: $_JSconfigParams['helpInfos'][$group])) {
1955         self :: $_JSconfigParams['helpInfos'][$group] = array_merge(self :: $_JSconfigParams['helpInfos'][$group],$infos);
1956       }
1957       else {
1958         self :: $_JSconfigParams['helpInfos'][$group] = $infos;
1959       }
1960     }
1961   }
1962   
1963  /**
1964   * Défini les codes erreur relative à la classe LSsession
1965   * 
1966   * @retval void
1967   */  
1968   private static function defineLSerrors() {
1969     /*
1970      * Error Codes
1971      */
1972     LSerror :: defineError('LSsession_01',
1973     _("LSsession : The constant %{const} is not defined.")
1974     );
1975     LSerror :: defineError('LSsession_02',
1976     _("LSsession : The %{addon} support is uncertain. Verify system compatibility and the add-on configuration.")
1977     );
1978     LSerror :: defineError('LSsession_03',
1979     _("LSsession : LDAP server's configuration data are invalid. Can't connect.")
1980     );
1981     LSerror :: defineError('LSsession_04',
1982     _("LSsession : Failed to load LSobject type %{type} : unknon type.")
1983     );
1984     // no longer used
1985     /*LSerror :: defineError(1005,
1986     _("LSsession : Object type use for authentication is unknow (%{type}).")
1987     );*/
1988     LSerror :: defineError('LSsession_06',
1989     _("LSsession : Login or password incorrect.")
1990     );
1991     LSerror :: defineError('LSsession_07',
1992     _("LSsession : Impossible to identify you : Duplication of identities.")
1993     );
1994     LSerror :: defineError('LSsession_08',
1995     _("LSsession : Can't load Smarty template engine.")
1996     );
1997     LSerror :: defineError('LSsession_09',
1998     _("LSsession : Can't connect to LDAP server.")
1999     );
2000     LSerror :: defineError('LSsession_10',
2001     _("LSsession : Could not load type of identifiable objects.")
2002     );
2003     LSerror :: defineError('LSsession_11',
2004     _("LSsession : Your are not authorized to do this action.")
2005     );
2006     LSerror :: defineError('LSsession_12',
2007     _("LSsession : Some informations are missing to display this page.")
2008     );
2009     // 13 -> 16 : not yet used
2010     LSerror :: defineError('LSsession_17',
2011     _("LSsession : Error during creation of list of levels. Contact administrators. (Code : %{code})")
2012     );
2013     LSerror :: defineError('LSsession_18',
2014     _("LSsession : The password recovery is disabled for this LDAP server.")
2015     );
2016     LSerror :: defineError('LSsession_19',
2017     _("LSsession : Some informations are missing to recover your password. Contact administrators.")
2018     );
2019     LSerror :: defineError('LSsession_20',
2020     _("LSsession : Error during password recovery. Contact administrators.(Step : %{step})")
2021     );
2022     // 21 : not yet used
2023     LSerror :: defineError('LSsession_22',
2024     _("LSsession : problem during initialisation.")
2025     );
2026
2027
2028     // LSrelations
2029     LSerror :: defineError('LSrelations_01',
2030     _("LSrelation : The listing function for the relation %{relation} is unknow.")
2031     );
2032     LSerror :: defineError('LSrelations_02',
2033     _("LSrelation : The update function of the relation %{relation} is unknow.")
2034     );
2035     LSerror :: defineError('LSrelations_03',
2036     _("LSrelation : Error during relation update of the relation %{relation}.")
2037     );
2038     LSerror :: defineError('LSrelations_04',
2039     _("LSrelation : Object type %{LSobject} unknow (Relation : %{relation}).")
2040     );
2041     LSerror :: defineError('LSrelations_05',
2042     _("LSrelation : Some parameters are missing in the call of methods to handle standard relations (Method : %{meth}).")
2043     );
2044   }
2045
2046   public static function ajax_onLdapServerChangedLogin(&$data) {  
2047     if ( isset($_REQUEST['server']) ) {
2048       self :: setLdapServer($_REQUEST['server']);
2049       $data = array();
2050       if ( self :: LSldapConnect() ) {
2051         session_start();
2052         if (isset($_SESSION['LSsession_topDn'])) {
2053           $sel = $_SESSION['LSsession_topDn'];
2054         }
2055         else {
2056           $sel = NULL;
2057         }
2058         $list = self :: getSubDnLdapServerOptions($sel);
2059         if (is_string($list)) {
2060           $data['list_topDn'] = "<select name='LSsession_topDn' id='LSsession_topDn'>".$list."</select>";
2061           $data['subDnLabel'] = self :: getSubDnLabel();
2062         }
2063       }
2064       $data['recoverPassword'] = isset(self :: $ldapServer['recoverPassword']);
2065     }
2066   }
2067   
2068   public static function ajax_onLdapServerChangedRecoverPassword(&$data) {  
2069     if ( isset($_REQUEST['server']) ) {
2070       self :: setLdapServer($_REQUEST['server']);
2071       $data=array('recoverPassword' => isset(self :: $ldapServer['recoverPassword']));
2072     }
2073   }
2074 }
2075
2076 ?>