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-2020 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: abook_ldap_server.php 14840 2020-01-07 07:42:38Z 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 (PHP5 style, required in some future version of PHP)
  103.      * Connects to the database
  104.      * @param array connection options
  105.      */
  106.     function __construct($param{
  107.         if(!function_exists('ldap_connect')) {
  108.             $this->set_error('LDAP support missing from PHP');
  109.             return;
  110.         }
  111.         if(is_array($param)) {
  112.             $this->server = $param['host'];
  113.             $this->basedn = $param['base'];
  114.             if(!empty($param['port'])) {
  115.                 $this->port = $param['port'];
  116.             }
  117.             if(!empty($param['charset'])) {
  118.                 $this->charset = strtolower($param['charset']);
  119.             }
  120.             if(isset($param['maxrows'])) {
  121.                 $this->maxrows = $param['maxrows'];
  122.             }
  123.             if(isset($param['timeout'])) {
  124.                 $this->timeout = $param['timeout'];
  125.             }
  126.             if(isset($param['binddn'])) {
  127.                 $this->binddn = $param['binddn'];
  128.             }
  129.             if(isset($param['bindpw'])) {
  130.                 $this->bindpw = $param['bindpw'];
  131.             }
  132.             if(isset($param['protocol'])) {
  133.                 $this->protocol = $param['protocol'];
  134.             }
  135.             if(empty($param['name'])) {
  136.                 $this->sname = 'LDAP: ' $param['host'];
  137.             }
  138.             else {
  139.                 $this->sname = $param['name'];
  140.             }
  141.  
  142.             $this->open(true);
  143.         else {
  144.             $this->set_error('Invalid argument to constructor');
  145.         }
  146.     }
  147.  
  148.     /**
  149.      * Constructor (PHP4 style, kept for compatibility reasons)
  150.      * Connects to the database
  151.      * @param array connection options
  152.      */
  153.     function abook_ldap_server($param{
  154.         return self::__construct($param);
  155.     }
  156.  
  157.     /**
  158.      * Open the LDAP server.
  159.      * @param bool $new is it a new connection
  160.      * @return bool 
  161.      */
  162.     function open($new false{
  163.         $this->error = '';
  164.  
  165.         /* Connection is already open */
  166.         if($this->linkid != false && !$new{
  167.             return true;
  168.         }
  169.  
  170.         $this->linkid = @ldap_connect($this->server$this->port);
  171.         if(!$this->linkid{
  172.             if(function_exists('ldap_error')) {
  173.                 return $this->set_error(ldap_error($this->linkid));
  174.             else {
  175.                 return $this->set_error('ldap_connect failed');
  176.             }
  177.         }
  178.  
  179.         if(!empty($this->protocol)) {
  180.             if(!@ldap_set_option($this->linkidLDAP_OPT_PROTOCOL_VERSION$this->protocol)) {
  181.                 if(function_exists('ldap_error')) {
  182.                     return $this->set_error(ldap_error($this->linkid));
  183.                 else {
  184.                     return $this->set_error('ldap_set_option failed');
  185.                 }
  186.             }
  187.         }
  188.  
  189.         if(!empty($this->binddn)) {
  190.             if(!@ldap_bind($this->linkid$this->binddn$this->bindpw)) {
  191.                 if(function_exists('ldap_error')) {
  192.                     return $this->set_error(ldap_error($this->linkid));
  193.                 else {
  194.                     return $this->set_error('authenticated ldap_bind failed');
  195.                 }
  196.               }
  197.         else {
  198.             if(!@ldap_bind($this->linkid)) {
  199.                 if(function_exists('ldap_error')) {
  200.                     return $this->set_error(ldap_error($this->linkid));
  201.                 else {
  202.                     return $this->set_error('anonymous ldap_bind failed');
  203.                 }
  204.             }
  205.         }
  206.  
  207.         $this->bound = true;
  208.  
  209.         return true;
  210.     }
  211.  
  212.     /**
  213.      * Converts string to the charset used by LDAP server
  214.      * @param string string that has to be converted
  215.      * @return string converted string
  216.      */
  217.     function charset_encode($str{
  218.         global $default_charset;
  219.         if($this->charset != $default_charset{
  220.             return charset_convert($default_charset,$str,$this->charset,false);
  221.         else {
  222.             return $str;
  223.         }
  224.     }
  225.  
  226.     /**
  227.      * Convert from charset used by LDAP server to charset used by translation
  228.      *
  229.      * Output must be sanitized.
  230.      * @param string string that has to be converted
  231.      * @return string converted string
  232.      */
  233.     function charset_decode($str{
  234.         global $default_charset;
  235.         if ($this->charset != $default_charset{
  236.             return charset_convert($this->charset,$str,$default_charset,false);
  237.         else {
  238.             return $str;
  239.         }
  240.     }
  241.  
  242.     /**
  243.      * Sanitizes ldap search strings.
  244.      * See rfc2254
  245.      * @link http://www.faqs.org/rfcs/rfc2254.html
  246.      * @since 1.5.1 and 1.4.5
  247.      * @param string $string 
  248.      * @return string sanitized string
  249.      */
  250.     function ldapspecialchars($string{
  251.         $sanitized=array('\\' => '\5c',
  252.                          '*' => '\2a',
  253.                          '(' => '\28',
  254.                          ')' => '\29',
  255.                          "\x00" => '\00');
  256.  
  257.         return str_replace(array_keys($sanitized),array_values($sanitized),$string);
  258.     }
  259.  
  260.     /* ========================== Public ======================== */
  261.  
  262.     /**
  263.      * Search the LDAP server
  264.      * @param string $expr search expression
  265.      * @return array search results
  266.      */
  267.     function search($expr{
  268.         /* To be replaced by advanded search expression parsing */
  269.         if(is_array($expr)) return false;
  270.  
  271.         /* Encode the expression */
  272.         $expr $this->charset_encode($expr);
  273.  
  274.         /*
  275.          * allow use of one asterisk in search. 
  276.          * Don't allow any ldap special chars if search is different
  277.          */
  278.         if($expr!='*'{
  279.             $expr '*' $this->ldapspecialchars($expr'*';
  280.             /* Undo sanitizing of * symbol */
  281.             $expr str_replace('\2a','*',$expr);
  282.         }
  283.         $expr preg_replace('/\*+/''*'$expr)// LDAP chokes on more than one *
  284.         $expression "(|(cn=$expr)(sn=$expr)(givenname=$expr)(mail=$expr))";
  285.  
  286.         /* Make sure connection is there */
  287.         if(!$this->open()) {
  288.             return false;
  289.         }
  290.  
  291.         $sret @ldap_search($this->linkid$this->basedn$expression,
  292.             array('dn''o''ou''sn''givenname''cn''mail'),
  293.             0$this->maxrows$this->timeout);
  294.  
  295.         /* Should get error from server using the ldap_error() function,
  296.          * but it only exist in the PHP LDAP documentation. */
  297.         if(!$sret{
  298.             if(function_exists('ldap_error')) {
  299.                 return $this->set_error(ldap_error($this->linkid));
  300.             else {
  301.                 return $this->set_error('ldap_search failed');
  302.             }
  303.         }
  304.  
  305.         if(@ldap_count_entries($this->linkid$sret<= 0{
  306.             return array();
  307.         }
  308.  
  309.         /* Get results */
  310.         $ret array();
  311.         $returned_rows 0;
  312.         $res @ldap_get_entries($this->linkid$sret);
  313.         for($i $i $res['count'$i++{
  314.             $row $res[$i];
  315.  
  316.             /* Extract data common for all e-mail addresses
  317.              * of an object. Use only the first name */
  318.             $nickname $this->charset_decode($row['dn']);
  319.             $fullname $this->charset_decode($row['cn'][0]);
  320.  
  321.             if(!empty($row['ou'][0])) {
  322.                 $label $this->charset_decode($row['ou'][0]);
  323.             }
  324.             else if(!empty($row['o'][0])) {
  325.                 $label $this->charset_decode($row['o'][0]);
  326.             else {
  327.                 $label '';
  328.             }
  329.  
  330.             if(empty($row['givenname'][0])) {
  331.                 $firstname '';
  332.             else {
  333.                 $firstname $this->charset_decode($row['givenname'][0]);
  334.             }
  335.  
  336.             if(empty($row['sn'][0])) {
  337.                 $surname '';
  338.             else {
  339.                 $surname $this->charset_decode($row['sn'][0]);
  340.             }
  341.  
  342.             /* Add one row to result for each e-mail address */
  343.             if(isset($row['mail']['count'])) {
  344.                 for($j $j $row['mail']['count'$j++{
  345.                     array_push($retarray('nickname'  => $nickname,
  346.                    'name'      => $fullname,
  347.                    'firstname' => $firstname,
  348.                    'lastname'  => $surname,
  349.                    'email'     => $row['mail'][$j],
  350.                    'label'     => $label,
  351.                    'backend'   => $this->bnum,
  352.                    'source'    => &$this->sname));
  353.  
  354.                     // Limit number of hits
  355.                     $returned_rows++;
  356.                     if(($returned_rows >= $this->maxrows&&
  357.                        ($this->maxrows > 0) ) {
  358.                         ldap_free_result($sret);
  359.                         return $ret;
  360.                     }
  361.  
  362.                 // for($j ...)
  363.  
  364.             // isset($row['mail']['count'])
  365.  
  366.         }
  367.  
  368.         ldap_free_result($sret);
  369.         return $ret;
  370.     /* end search() */
  371.  
  372.  
  373.     /**
  374.      * List all entries present in LDAP server
  375.      *
  376.      * If you run a small-sized LDAP server and you want the "List all"
  377.      * button (found on the address book search screen that is accessed
  378.      * via the "Addresses" button on the compose screen) to show all
  379.      * addresses in the directory, add the following to config/config_local.php
  380.      *
  381.      *    $ldap_abook_allow_listing = TRUE;
  382.      *
  383.      * Remember that the "maxrows" configuration setting for the LDAP
  384.      * server backend might limit list of returned entries.
  385.      *
  386.      * NOTE: You should exercise caution enabling the listing of large
  387.      *       or public LDAP address book backends.
  388.      *
  389.      * @return array all entries in ldap server
  390.      *
  391.      */
  392.      function list_addr({
  393.          global $ldap_abook_allow_listing;
  394.          if ($ldap_abook_allow_listing)
  395.             return $this->search('*');
  396.          else
  397.             return array();
  398.      }
  399. }

Documentation generated on Mon, 13 Jan 2020 04:24:10 +0100 by phpDocumentor 1.4.3