Source for file abook_ldap_server.php

Documentation is available at abook_ldap_server.php

  1. <?php
  2.  
  3. /**
  4.  * abook_ldap_server.php
  5.  *
  6.  * Address book backend for LDAP server
  7.  *
  8.  * @copyright 1999-2014 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: abook_ldap_server.php 14422 2014-01-01 20:59:43Z pdontthink $
  11.  * @package squirrelmail
  12.  * @subpackage addressbook
  13.  */
  14.  
  15. /**
  16.  * Address book backend for LDAP server
  17.  *
  18.  * An array with the following elements must be passed to
  19.  * the class constructor (elements marked ? are optional):
  20.  * <pre>
  21.  *    host      => LDAP server hostname/IP-address
  22.  *    base      => LDAP server root (base dn). Empty string allowed.
  23.  *  ? port      => LDAP server TCP port number (default: 389)
  24.  *  ? charset   => LDAP server charset (default: utf-8)
  25.  *  ? name      => Name for LDAP server (default "LDAP: hostname")
  26.  *                 Used to tag the result data
  27.  *  ? maxrows   => Maximum # of rows in search result
  28.  *  ? timeout   => Timeout for LDAP operations (in seconds, default: 30)
  29.  *                 Might not work for all LDAP libraries or servers.
  30.  *  ? binddn    => LDAP Bind DN.
  31.  *  ? bindpw    => LDAP Bind Password.
  32.  *  ? protocol  => LDAP Bind protocol.
  33.  * </pre>
  34.  * NOTE. This class should not be used directly. Use the
  35.  *       "AddressBook" class instead.
  36.  * @package squirrelmail
  37.  * @subpackage addressbook
  38.  */
  39.     /**
  40.      * @var string backend type
  41.      */
  42.     var $btype = 'remote';
  43.     /**
  44.      * @var string backend name
  45.      */
  46.     var $bname = 'ldap_server';
  47.  
  48.     /* Parameters changed by class */
  49.     /**
  50.      * @var string displayed name
  51.      */
  52.     var $sname   = 'LDAP';       /* Service name */
  53.     /**
  54.      * @var string LDAP server name or address or url
  55.      */
  56.     var $server  = '';
  57.     /**
  58.      * @var integer LDAP server port
  59.      */
  60.     var $port    = 389;
  61.     /**
  62.      * @var string LDAP base DN
  63.      */
  64.     var $basedn  = '';
  65.     /**
  66.      * @var string charset used for entries in LDAP server
  67.      */
  68.     var $charset = 'utf-8';
  69.     /**
  70.      * @var object PHP LDAP link ID
  71.      */
  72.     var $linkid  = false;
  73.     /**
  74.      * @var bool True if LDAP server is bound
  75.      */
  76.     var $bound   = false;
  77.     /**
  78.      * @var integer max rows in result
  79.      */
  80.     var $maxrows = 250;
  81.     /**
  82.      * @var integer timeout of LDAP operations (in seconds)
  83.      */
  84.     var $timeout = 30;
  85.     /**
  86.      * @var string DN to bind to (non-anonymous bind)
  87.      * @since 1.5.0 and 1.4.3
  88.      */
  89.     var $binddn = '';
  90.     /**
  91.      * @var string  password to bind with (non-anonymous bind)
  92.      * @since 1.5.0 and 1.4.3
  93.      */
  94.     var $bindpw = '';
  95.     /**
  96.      * @var integer protocol used to connect to ldap server
  97.      * @since 1.5.0 and 1.4.3
  98.      */
  99.     var $protocol = '';
  100.  
  101.     /**
  102.      * Constructor. Connects to database
  103.      * @param array connection options
  104.      */
  105.     function abook_ldap_server($param{
  106.         if(!function_exists('ldap_connect')) {
  107.             $this->set_error('LDAP support missing from PHP');
  108.             return;
  109.         }
  110.         if(is_array($param)) {
  111.             $this->server = $param['host'];
  112.             $this->basedn = $param['base'];
  113.             if(!empty($param['port'])) {
  114.                 $this->port = $param['port'];
  115.             }
  116.             if(!empty($param['charset'])) {
  117.                 $this->charset = strtolower($param['charset']);
  118.             }
  119.             if(isset($param['maxrows'])) {
  120.                 $this->maxrows = $param['maxrows'];
  121.             }
  122.             if(isset($param['timeout'])) {
  123.                 $this->timeout = $param['timeout'];
  124.             }
  125.             if(isset($param['binddn'])) {
  126.                 $this->binddn = $param['binddn'];
  127.             }
  128.             if(isset($param['bindpw'])) {
  129.                 $this->bindpw = $param['bindpw'];
  130.             }
  131.             if(isset($param['protocol'])) {
  132.                 $this->protocol = $param['protocol'];
  133.             }
  134.             if(empty($param['name'])) {
  135.                 $this->sname = 'LDAP: ' $param['host'];
  136.             }
  137.             else {
  138.                 $this->sname = $param['name'];
  139.             }
  140.  
  141.             $this->open(true);
  142.         else {
  143.             $this->set_error('Invalid argument to constructor');
  144.         }
  145.     }
  146.  
  147.  
  148.     /**
  149.      * Open the LDAP server.
  150.      * @param bool $new is it a new connection
  151.      * @return bool 
  152.      */
  153.     function open($new false{
  154.         $this->error = '';
  155.  
  156.         /* Connection is already open */
  157.         if($this->linkid != false && !$new{
  158.             return true;
  159.         }
  160.  
  161.         $this->linkid = @ldap_connect($this->server$this->port);
  162.         if(!$this->linkid{
  163.             if(function_exists('ldap_error')) {
  164.                 return $this->set_error(ldap_error($this->linkid));
  165.             else {
  166.                 return $this->set_error('ldap_connect failed');
  167.             }
  168.         }
  169.  
  170.         if(!empty($this->protocol)) {
  171.             if(!@ldap_set_option($this->linkidLDAP_OPT_PROTOCOL_VERSION$this->protocol)) {
  172.                 if(function_exists('ldap_error')) {
  173.                     return $this->set_error(ldap_error($this->linkid));
  174.                 else {
  175.                     return $this->set_error('ldap_set_option failed');
  176.                 }
  177.             }
  178.         }
  179.  
  180.         if(!empty($this->binddn)) {
  181.             if(!@ldap_bind($this->linkid$this->binddn$this->bindpw)) {
  182.                 if(function_exists('ldap_error')) {
  183.                     return $this->set_error(ldap_error($this->linkid));
  184.                 else {
  185.                     return $this->set_error('authenticated ldap_bind failed');
  186.                 }
  187.               }
  188.         else {
  189.             if(!@ldap_bind($this->linkid)) {
  190.                 if(function_exists('ldap_error')) {
  191.                     return $this->set_error(ldap_error($this->linkid));
  192.                 else {
  193.                     return $this->set_error('anonymous ldap_bind failed');
  194.                 }
  195.             }
  196.         }
  197.  
  198.         $this->bound = true;
  199.  
  200.         return true;
  201.     }
  202.  
  203.     /**
  204.      * Converts string to the charset used by LDAP server
  205.      * @param string string that has to be converted
  206.      * @return string converted string
  207.      */
  208.     function charset_encode($str{
  209.         global $default_charset;
  210.         if($this->charset != $default_charset{
  211.             return charset_convert($default_charset,$str,$this->charset,false);
  212.         else {
  213.             return $str;
  214.         }
  215.     }
  216.  
  217.     /**
  218.      * Convert from charset used by LDAP server to charset used by translation
  219.      *
  220.      * Output must be sanitized.
  221.      * @param string string that has to be converted
  222.      * @return string converted string
  223.      */
  224.     function charset_decode($str{
  225.         global $default_charset;
  226.         if ($this->charset != $default_charset{
  227.             return charset_convert($this->charset,$str,$default_charset,false);
  228.         else {
  229.             return $str;
  230.         }
  231.     }
  232.  
  233.     /**
  234.      * Sanitizes ldap search strings.
  235.      * See rfc2254
  236.      * @link http://www.faqs.org/rfcs/rfc2254.html
  237.      * @since 1.5.1 and 1.4.5
  238.      * @param string $string 
  239.      * @return string sanitized string
  240.      */
  241.     function ldapspecialchars($string{
  242.         $sanitized=array('\\' => '\5c',
  243.                          '*' => '\2a',
  244.                          '(' => '\28',
  245.                          ')' => '\29',
  246.                          "\x00" => '\00');
  247.  
  248.         return str_replace(array_keys($sanitized),array_values($sanitized),$string);
  249.     }
  250.  
  251.     /* ========================== Public ======================== */
  252.  
  253.     /**
  254.      * Search the LDAP server
  255.      * @param string $expr search expression
  256.      * @return array search results
  257.      */
  258.     function search($expr{
  259.         /* To be replaced by advanded search expression parsing */
  260.         if(is_array($expr)) return false;
  261.  
  262.         /* Encode the expression */
  263.         $expr $this->charset_encode($expr);
  264.  
  265.         /*
  266.          * allow use of one asterisk in search. 
  267.          * Don't allow any ldap special chars if search is different
  268.          */
  269.         if($expr!='*'{
  270.             $expr '*' $this->ldapspecialchars($expr'*';
  271.             /* Undo sanitizing of * symbol */
  272.             $expr str_replace('\2a','*',$expr);
  273.         }
  274.         $expr preg_replace('/\*+/''*'$expr)// LDAP chokes on more than one *
  275.         $expression "(|(cn=$expr)(sn=$expr)(givenname=$expr)(mail=$expr))";
  276.  
  277.         /* Make sure connection is there */
  278.         if(!$this->open()) {
  279.             return false;
  280.         }
  281.  
  282.         $sret @ldap_search($this->linkid$this->basedn$expression,
  283.             array('dn''o''ou''sn''givenname''cn''mail'),
  284.             0$this->maxrows$this->timeout);
  285.  
  286.         /* Should get error from server using the ldap_error() function,
  287.          * but it only exist in the PHP LDAP documentation. */
  288.         if(!$sret{
  289.             if(function_exists('ldap_error')) {
  290.                 return $this->set_error(ldap_error($this->linkid));
  291.             else {
  292.                 return $this->set_error('ldap_search failed');
  293.             }
  294.         }
  295.  
  296.         if(@ldap_count_entries($this->linkid$sret<= 0{
  297.             return array();
  298.         }
  299.  
  300.         /* Get results */
  301.         $ret array();
  302.         $returned_rows 0;
  303.         $res @ldap_get_entries($this->linkid$sret);
  304.         for($i $i $res['count'$i++{
  305.             $row $res[$i];
  306.  
  307.             /* Extract data common for all e-mail addresses
  308.              * of an object. Use only the first name */
  309.             $nickname $this->charset_decode($row['dn']);
  310.             $fullname $this->charset_decode($row['cn'][0]);
  311.  
  312.             if(!empty($row['ou'][0])) {
  313.                 $label $this->charset_decode($row['ou'][0]);
  314.             }
  315.             else if(!empty($row['o'][0])) {
  316.                 $label $this->charset_decode($row['o'][0]);
  317.             else {
  318.                 $label '';
  319.             }
  320.  
  321.             if(empty($row['givenname'][0])) {
  322.                 $firstname '';
  323.             else {
  324.                 $firstname $this->charset_decode($row['givenname'][0]);
  325.             }
  326.  
  327.             if(empty($row['sn'][0])) {
  328.                 $surname '';
  329.             else {
  330.                 $surname $this->charset_decode($row['sn'][0]);
  331.             }
  332.  
  333.             /* Add one row to result for each e-mail address */
  334.             if(isset($row['mail']['count'])) {
  335.                 for($j $j $row['mail']['count'$j++{
  336.                     array_push($retarray('nickname'  => $nickname,
  337.                    'name'      => $fullname,
  338.                    'firstname' => $firstname,
  339.                    'lastname'  => $surname,
  340.                    'email'     => $row['mail'][$j],
  341.                    'label'     => $label,
  342.                    'backend'   => $this->bnum,
  343.                    'source'    => &$this->sname));
  344.  
  345.                     // Limit number of hits
  346.                     $returned_rows++;
  347.                     if(($returned_rows >= $this->maxrows&&
  348.                        ($this->maxrows > 0) ) {
  349.                         ldap_free_result($sret);
  350.                         return $ret;
  351.                     }
  352.  
  353.                 // for($j ...)
  354.  
  355.             // isset($row['mail']['count'])
  356.  
  357.         }
  358.  
  359.         ldap_free_result($sret);
  360.         return $ret;
  361.     /* end search() */
  362.  
  363.  
  364.     /**
  365.      * List all entries present in LDAP server
  366.      *
  367.      * If you run a small-sized LDAP server and you want the "List all"
  368.      * button (found on the address book search screen that is accessed
  369.      * via the "Addresses" button on the compose screen) to show all
  370.      * addresses in the directory, add the following to config/config_local.php
  371.      *
  372.      *    $ldap_abook_allow_listing = TRUE;
  373.      *
  374.      * Remember that the "maxrows" configuration setting for the LDAP
  375.      * server backend might limit list of returned entries.
  376.      *
  377.      * NOTE: You should exercise caution enabling the listing of large
  378.      *       or public LDAP address book backends.
  379.      *
  380.      * @return array all entries in ldap server
  381.      *
  382.      */
  383.      function list_addr({
  384.          global $ldap_abook_allow_listing;
  385.          if ($ldap_abook_allow_listing)
  386.             return $this->search('*');
  387.          else
  388.             return array();
  389.      }
  390. }

Documentation generated on Sat, 25 Oct 2014 04:20:17 +0200 by phpDocumentor 1.4.3