<?php
/*******************************************************************************
 * Copyright (C) 2007 Easter-eggs
 * http://ldapsaisie.labs.libre-entreprise.org
 *
 * Author: See AUTHORS file in top-level directory.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * 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.

******************************************************************************/

define('LS_DEFAULT_CONF_DIR','conf');

/**
 * Gestion des sessions
 *
 * Cette classe g�re les sessions d'utilisateurs.
 *
 * @author Benjamin Renard <brenard@easter-eggs.com>
 */
class LSsession {

  var $confDir = NULL;
  var $ldapServer = NULL;
  var $ldapServerId = NULL;
  var $topDn = NULL;
  var $LSuserObject = NULL;
  var $dn = NULL;
  var $rdn = NULL;
  var $JSscripts = array();
  var $CssFiles = array();
  var $template = NULL;
  var $LSrights = array (
    'topDn_admin' => array ()
  );
  var $LSaccess = array();

  /**
   * Constructeur
   *
   * @author Benjamin Renard <brenard@easter-eggs.com>
   *
   * @retval void
   */
  function LSsession ($configDir=LS_DEFAULT_CONF_DIR) {
    $this -> confDir = $configDir;
    if ($this -> loadConfig()) {
      $this -> startLSerror();
    }
    else {
      return;
    }
  }

 /*
  * Chargement de la configuration
  *
  * Chargement des fichiers de configuration et cr�ation de l'objet Smarty.
  *
  * @author Benjamin Renard <brenard@easter-eggs.com>
  *
  * @retval true si tout c'est bien pass�, false sinon
  */
  function loadConfig() {
    if (loadDir($this -> confDir, '^config\..*\.php$')) {
      if ( @include_once $GLOBALS['LSconfig']['Smarty'] ) {
        $GLOBALS['Smarty'] = new Smarty();
        return true;
      }
      else {
        $GLOBALS['LSerror'] -> addErrorCode(1008);
        return;
      }
      return true;
    }
    else {
      return;
    }
  }

 /*
  * Initialisation de la gestion des erreurs
  *
  * Cr�ation de l'objet LSerror
  *
  * @author Benjamin Renard <brenard@easter-eggs.com
  *
  * @retval boolean true si l'initialisation a r�ussi, false sinon.
  */
  function startLSerror() {
    if(!$this -> loadLSclass('LSerror'))
      return;
    $GLOBALS['LSerror'] = new LSerror();
    return true;
  }

 /*
  * Chargement d'une classe d'LdapSaisie
  *
  * @param[in] $class Nom de la classe � charger (Exemple : LSeepeople)
  * @param[in] $type (Optionnel) Type de classe � charger (Exemple : LSobjects)
  *
  * @author Benjamin Renard <brenard@easter-eggs.com
  * 
  * @retval boolean true si le chargement a r�ussi, false sinon.
  */
  function loadLSclass($class,$type='') {
    if (class_exists($class))
      return true;
    if($type!='')
      $type=$type.'.';
    return @include_once LS_CLASS_DIR .'class.'.$type.$class.'.php';
  }

 /*
  * Chargement d'un object LdapSaisie
  *
  * @param[in] $object Nom de l'objet � charger
  *
  * @retval boolean true si le chargement a r�ussi, false sinon.
  */
  function loadLSobject($object) {
    if (!$this -> loadLSclass($object,'LSobjects'))
      return;
    if (!require_once( LS_OBJECTS_DIR . 'config.LSobjects.'.$object.'.php' ))
      return;
    return true;
  }

 /*
  * Chargement des objects LdapSaisie
  *
  * Chargement des LSobjects contenue dans la variable
  * $GLOBALS['LSobjects_loads']
  *
  * @retval boolean true si le chargement a r�ussi, false sinon.
  */
  function loadLSobjects() {

    $this -> loadLSclass('LSldapObject');

    if(!is_array($GLOBALS['LSobjects_loads'])) {
      $GLOBALS['LSerror'] -> addErrorCode(1001,"LSobjects['loads']");
      return;
    }

    foreach ($GLOBALS['LSobjects_loads'] as $object) {
      if ( !$this -> loadLSobject($object) )
        return;
    }
    return true;
  }

 /*
  * Chargement d'un addons d'LdapSaisie
  *
  * @param[in] $addon Nom de l'addon � charger (Exemple : samba)
  *
  * @author Benjamin Renard <brenard@easter-eggs.com
  * 
  * @retval boolean true si le chargement a r�ussi, false sinon.
  */
  function loadLSaddon($addon) {
    return require_once LS_ADDONS_DIR .'LSaddons.'.$addon.'.php';
  }

 /*
  * Chargement des addons LdapSaisie
  *
  * Chargement des LSaddons contenue dans la variable
  * $GLOBALS['LSaddons']['loads']
  *
  * @retval boolean true si le chargement a r�ussi, false sinon.
  */
  function loadLSaddons() {
    if(!is_array($GLOBALS['LSaddons']['loads'])) {
      $GLOBALS['LSerror'] -> addErrorCode(1001,"LSaddons['loads']");
      return;
    }

    foreach ($GLOBALS['LSaddons']['loads'] as $addon) {
      $this -> loadLSaddon($addon);
      if (!call_user_func('LSaddon_'. $addon .'_support')) {
        $GLOBALS['LSerror'] -> addErrorCode(1002,$addon);
      }
    }
    return true;
  }

 /*
  * Initialisation de la session LdapSaisie
  *
  * Initialisation d'une LSsession :
  * - Authentification et activation du m�canisme de session de LdapSaisie
  * - ou Chargement des param�tres de la session � partir de la variable 
  *   $_SESSION['LSsession'].
  * - ou Destruction de la session en cas de $_GET['LSsession_logout'].
  *
  * @retval boolean True si l'initialisation � r�ussi (utilisateur authentifi�), false sinon.
  */
  function startLSsession() {
      $this -> loadLSobjects();
      $this -> loadLSaddons();
      session_start();

      // D�connexion
      if (isset($_GET['LSsession_logout'])) {
        session_destroy();
        unset($_SESSION['LSsession']);
      }

      if(isset($_SESSION['LSsession'])) {
        // Session existante
        $this -> confDir      = $_SESSION['LSsession'] -> confDir;
        $this -> topDn        = $_SESSION['LSsession'] -> topDn;
        //$this -> LSuserObject = $_SESSION['LSsession'] -> LSuserObject;
        $this -> dn           = $_SESSION['LSsession'] -> dn;
        $this -> rdn          = $_SESSION['LSsession'] -> rdn;
        $this -> ldapServerId = $_SESSION['LSsession'] -> ldapServerId;
        if ( ($GLOBALS['LSconfig']['cacheLSrights']) || ($this -> ldapServer['cacheLSrights']) ) {
          $this -> ldapServer = $_SESSION['LSsession'] -> ldapServer;
          $this -> LSrights   = $_SESSION['LSsession'] -> LSrights;
          $this -> LSaccess   = $_SESSION['LSsession'] -> LSaccess;
          if (!$this -> LSldapConnect())
            return;
        }
        else {
          $this -> setLdapServer($this -> ldapServerId);
          if (!$this -> LSldapConnect())
            return;
          $this -> loadLSrights();
          $this -> loadLSaccess();
        }
        $this -> LSuserObject = new $this -> ldapServer['authobject']();
        $this -> LSuserObject -> loadData($this -> dn);
        $GLOBALS['Smarty'] -> assign('LSsession_username',$this -> LSuserObject -> getDisplayValue());
        return true;
        
      }
      else {
        // Session inexistante

        if (isset($_POST['LSsession_user'])) {
          if (isset($_POST['LSsession_ldapserver'])) {
            $this -> setLdapServer($_POST['LSsession_ldapserver']);
          }
          else {
            $this -> setLdapServer(0);
          }

          // Connexion au serveur LDAP
              if ($this -> LSldapConnect()) {
            // topDn
            if ( $_POST['LSsession_topDn'] != '' ){
              $this -> topDn = $_POST['LSsession_topDn'];
            }
            else {
              $this -> topDn = $this -> ldapServer['ldap_config']['basedn'];
            }

            if ( $this -> loadLSobject($this -> ldapServer['authobject']) ) {
              $authobject = new $this -> ldapServer['authobject']();
              $result = $authobject -> searchObject($_POST['LSsession_user'],$this -> topDn);
              $nbresult=count($result);
              if ($nbresult==0) {
                // identifiant incorrect
                debug('identifiant incorrect');
                $GLOBALS['LSerror'] -> addErrorCode(1006);
              }
              else if ($nbresult>1) {
                // duplication d'authentit�
                $GLOBALS['LSerror'] -> addErrorCode(1007);
              }
              else {
                if ( $this -> checkUserPwd($result[0],$_POST['LSsession_pwd']) ) {
                  // Authentification r�ussi
                  $this -> LSuserObject = $result[0];
                  $this -> dn = $result[0]->getValue('dn');
                  $this -> rdn = $_POST['LSsession_user'];
                  $this -> loadLSrights();
                  $this -> loadLSaccess();
                  $GLOBALS['Smarty'] -> assign('LSsession_username',$this -> LSuserObject -> getDisplayValue());
                  $_SESSION['LSsession']=$this;
                  return true;
                }
                else {
                  $GLOBALS['LSerror'] -> addErrorCode(1006);
                  debug('mdp incorrect');
                }
              }
            }
            else {
              $GLOBALS['LSerror'] -> addErrorCode(1010);
            }
          }
          else {
            $GLOBALS['LSerror'] -> addErrorCode(1009);
          }
        }
        $this -> displayLoginForm();
        return;
      }
  }

 /*
  * D�finition du serveur Ldap de la session
  *
  * D�finition du serveur Ldap de la session � partir de son ID dans 
  * le tableau $GLOBALS['LSconfig']['ldap_servers'].
  *
  * @param[in] integer Index du serveur Ldap
  *
  * @retval boolean True sinon false.
  */
  function setLdapServer($id) {
    if ( isset($GLOBALS['LSconfig']['ldap_servers'][$id]) ) {
      $this -> ldapServerId = $id;
      $this -> ldapServer=$GLOBALS['LSconfig']['ldap_servers'][$id];
      return true;
    }
    else {
      return;
    }
  }

 /*
  * Connexion au serveur Ldap
  *
  * @retval boolean True sinon false.
  */
  function LSldapConnect() {
    if ($this -> ldapServer) {
      include_once($GLOBALS['LSconfig']['NetLDAP']);
      if (!$this -> loadLSclass('LSldap'))
        return;
        $GLOBALS['LSldap'] = new LSldap($this -> ldapServer['ldap_config']);
        if ($GLOBALS['LSldap'] -> isConnected())
          return true;
        else
          return;
      return $GLOBALS['LSldap'] = new LSldap($this -> ldapServer['ldap_config']);
    }
    else {
      $GLOBALS['LSerror'] -> addErrorCode(1003);
      return;
    }
  }


  function getSubDnLdapServer() {
    if ( isset($this ->ldapServer['subdnobject']) ) {
      if( $this -> loadLSobject($this ->ldapServer['subdnobject']) ) {
        if ($subdnobject = new $this ->ldapServer['subdnobject']()) {
          return $subdnobject -> getSelectArray();
        }
        else {
          return;
        }
      }
      else {
        $GLOBALS['LSerror'] -> addErrorCode(1004,$this ->ldapServer['subdnobject']);
        return;
      }
    }
    else {
      return;
    }
  }

 /*
  * Retourne les options d'une liste d�roulante pour le choix du topDn
  * de connexion au serveur Ldap
  *
  * Liste les subdnobject ($this ->ldapServer['subdnobject'])
  *
  * @retval string Les options (<option>) pour la s�lection du topDn.
  */
  function getSubDnLdapServerOptions() {
    if ( isset($this ->ldapServer['subdnobject']) ) {
      
      if( $this -> loadLSobject($this ->ldapServer['subdnobject']) ) {
        if ($subdnobject = new $this ->ldapServer['subdnobject']()) {
          return $subdnobject -> getSelectOptions();
        }
        else {
          return;
        }
      }
      else {
        $GLOBALS['LSerror'] -> addErrorCode(1004,$this ->ldapServer['subdnobject']);
        return;
      }
    }
    else {
      return;
    }
  }

 /*
  * Test un couple LSobject/pwd
  *
  * Test un bind sur le serveur avec le dn de l'objet et le mot de passe fourni.
  *
  * @param[in] LSobject L'object "user" pour l'authentification
  * @param[in] string Le mot de passe � tester
  *
  * @retval boolean True si l'authentification � r�ussi, false sinon.
  */
  function checkUserPwd($object,$pwd) {
    return $GLOBALS['LSldap'] -> checkBind($object -> getValue('dn'),$pwd);
  }

 /*
  * Affiche le formulaire de login
  *
  * D�fini les informations pour le template Smarty du formulaire de login.
  *
  * @retval void
  */
  function displayLoginForm() {
    $GLOBALS['Smarty'] -> assign('pagetitle',_('Connexion'));
    $GLOBALS['Smarty'] -> assign('loginform_action',$_SERVER['PHP_SELF']);
    if (count($GLOBALS['LSconfig']['ldap_servers'])==1) {
      $GLOBALS['Smarty'] -> assign('loginform_ldapserver_style','style="display: none"');
    }
    $GLOBALS['Smarty'] -> assign('loginform_label_ldapserver',_('Serveur LDAP'));
    $ldapservers_name=array();
    $ldapservers_index=array();
    foreach($GLOBALS['LSconfig']['ldap_servers'] as $id => $infos) {
      $ldapservers_index[]=$id;
      $ldapservers_name[]=$infos['name'];
    }
    $GLOBALS['Smarty'] -> assign('loginform_ldapservers_name',$ldapservers_name);
    $GLOBALS['Smarty'] -> assign('loginform_ldapservers_index',$ldapservers_index);

    $this -> setLdapServer(0);
    if ( $this -> LSldapConnect() ) {
      $topDn_array = $this -> getSubDnLdapServer();
      if ( $topDn_array ) {
        $GLOBALS['Smarty'] -> assign('loginform_topdn_name',$topDn_array['display']);
        $GLOBALS['Smarty'] -> assign('loginform_topdn_index',$topDn_array['dn']);
      }
    }

    $GLOBALS['Smarty'] -> assign('loginform_label_level',_('Niveau'));
    $GLOBALS['Smarty'] -> assign('loginform_label_user',_('Identifiant'));
    $GLOBALS['Smarty'] -> assign('loginform_label_pwd',_('Mot de passe'));
    $GLOBALS['Smarty'] -> assign('loginform_label_submit',_('Connexion'));

    $this -> addJSscript('LSsession_login.js');
  }

 /*
  * D�fini le template Smarty � utiliser
  *
  * Remarque : les fichiers de templates doivent se trouver dans le dossier 
  * templates/.
  *
  * @param[in] string Le nom du fichier de template
  *
  * @retval void
  */
  function setTemplate($template) {
    $this -> template = $template;
  }

 /*
  * Ajoute un script JS au chargement de la page
  *
  * Remarque : les scripts doivents �tre dans le dossier LS_JS_DIR.
  *
  * @param[in] $script Le nom du fichier de script � charger.
  *
  * @retval void
  */
  function addJSscript($script) {
    $this -> JSscripts[]=$script;
  }

 /*
  * Ajoute une feuille de style au chargement de la page
  *
  * Remarque : les scripts doivents �tre dans le dossiers templates/css/.
  *
  * @param[in] $script Le nom du fichier css � charger.
  *
  * @retval void
  */
  function addCssFile($file) {
    $this -> CssFiles[]=$file;
  }

 /*
  * Affiche le template Smarty
  *
  * Charge les d�pendances et affiche le template Smarty
  *
  * @retval void
  */
  function displayTemplate() {
    // JS
    $JSscript_txt='';
    foreach ($GLOBALS['defaultJSscipts'] as $script) {
      $JSscript_txt.="<script src='".LS_JS_DIR.$script."' type='text/javascript'></script>\n";
    }
    foreach ($this -> JSscripts as $script) {
      $JSscript_txt.="<script src='".LS_JS_DIR.$script."' type='text/javascript'></script>\n";
    }
    $GLOBALS['Smarty'] -> assign('LSsession_js',$JSscript_txt);

    // Css
    $Css_txt="<link rel='stylesheet' type='text/css' href='templates/css/LSdefault.css' media='screen' />\n";
    foreach ($this -> CssFiles as $file) {
      $Css_txt.="<link rel='stylesheet' type='text/css' href='templates/css/$file' media='screen' />\n";
    }
    $GLOBALS['Smarty'] -> assign('LSsession_css',$Css_txt);

    $GLOBALS['Smarty'] -> assign('LSaccess',$this -> LSaccess);

    $GLOBALS['LSerror'] -> display();
    debug_print();
    if (!$this -> template)
      $this -> setTemplate('empty.tpl');
    $GLOBALS['Smarty'] -> display($this -> template);
  }
  
  /**
   * Charge les droits LS de l'utilisateur
   * 
   * @retval boolean True si le chargement � r�ussi, false sinon.
   **/
  function loadLSrights() {
    if (is_array($this -> ldapServer['LSadmins'])) {
      foreach ($this -> ldapServer['LSadmins'] as $topDn => $adminsInfos) {
        if (is_array($adminsInfos)) {
          foreach($adminsInfos as $dn => $conf) {
            if ((isset($conf['attr'])) && (isset($conf['LSobject']))) {
              if( $this -> loadLSobject($conf['LSobject']) ) {
                if ($object = new $conf['LSobject']()) {
                  if ($object -> loadData($dn)) {
                    $listDns=$object -> getValue($conf['attr']);
                    if (is_array($listDns)) {
                      if (in_array($this -> dn,$listDns)) {
                        $this -> LSrights['topDn_admin'][] = $topDn;
                      }
                    }
                  }
                  else {
                    debug('Impossible de charg� le dn : '.$dn);
                  }
                }
                else {
                  debug('Impossible de cr�er l\'objet de type : '.$conf['LSobject']);
                }
              }
              else {
                $GLOBALS['LSerror'] -> addErrorCode(1004,$conf['LSobject']);
              }
            }
            else {
              if ($this -> dn == $dn) {
                $this -> LSrights['topDn_admin'][] = $topDn;
              }
            }
          }
        }
        else {
          if ( $this -> dn == $adminsInfos ) {
            $this -> LSrights['topDn_admin'][] = $topDn;
          }
        }
      }
      debug($this -> LSrights['topDn_admin']);
      return true;
    }
    else {
      return;
    }
  }
  
  function loadLSaccess() {
    $LSaccess = array(
      'SELF' => array(
        'label' => _('Mon compte'),
        'DNs' => $this -> dn
      )
    );
    foreach ($GLOBALS['LSobjects'] as $objecttype => $objectconf) {
      $objectdn = $objectconf['container_dn'].','.$this -> topDn;
      if ($this -> isAdmin($objectdn) ) {
        $LSaccess[$objecttype] = array (
          'label' => $objectconf['label'],
          'Dns' => 'All'
        );
      }
    }
    $this -> LSaccess = $LSaccess;
  }
  
  function isAdmin($dn) {
    foreach($this -> LSrights['topDn_admin'] as $topDn_admin) {
      if($dn == $topDn_admin) {
        return true;
      }
      else if ( isCompatibleDNs($dn,$topDn_admin) ) {
        return true;
      }
    }
    return;
  }
  
  function whoami($dn) {
    if ($this -> isAdmin($dn)) {
      return 'admin';
    }
    
    if ($this -> dn == $dn) {
      return 'self';
    }
    
    return 'user';
  }
  
  function canAccess($LSobject,$dn=NULL,$right=NULL) {
    if (!$this -> loadLSobject($LSobject))
      return;
    if ($dn) {
      $whoami = $this -> whoami($dn);
    }
    else {
      $whoami = 'user';
    }
    if (is_array($GLOBALS['LSobjects'][$LSobject]['attrs'])) {
      if (($right=='r')||($right=='w')) {
        foreach ($GLOBALS['LSobjects'][$LSobject]['attrs'] as $attr_name => $attr_config) {
          if ($attr_config['rights'][$whoami]==$right) {
            return true;
          }
        }
      }
      else {
        foreach ($GLOBALS['LSobjects'][$LSobject]['attrs'] as $attr_name => $attr_config) {
          if ( ($attr_config['rights'][$whoami]=='r') || ($attr_config['rights'][$whoami]=='w') ) {
            return true;
          }
        }
      }
    }
    return;
  }
  
  function canEdit($LSobject,$dn=NULL) {
    return $this -> canAccess($LSobject,$dn,'w');
  }
  
  function __sleep() {
    return ( array_keys( get_object_vars( &$this ) ) );
  }
  
  function __wakeup() {
    return true;
  }
}

?>