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.  * LDAP filtering code by Tim Bell
  9.  *   <bhat at users.sourceforge.net> (#539534)
  10.  * ADS limit_scope code by Michael Brown
  11.  *   <mcb30 at users.sourceforge.net> (#1035454)
  12.  * StartTLS code by John Lane
  13.  *   <starfry at users.sourceforge.net> (#1197703)
  14.  * Code for remove, add, modify, lookup by David Härdeman
  15.  *   <david at 2gen.com> (#1495763)
  16.  *
  17.  * This backend uses LDAP person (RFC2256), organizationalPerson (RFC2256)
  18.  * and inetOrgPerson (RFC2798) objects and dn, description, sn, givenname,
  19.  * cn, mail attributes. Other attributes are ignored.
  20.  * 
  21.  * @copyright &copy; 1999-2006 The SquirrelMail Project Team
  22.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  23.  * @version $Id: abook_ldap_server.php,v 1.42 2006/06/04 12:42:24 tokul Exp $
  24.  * @package squirrelmail
  25.  * @subpackage addressbook
  26.  */
  27.  
  28. /**
  29.  * Address book backend for LDAP server
  30.  *
  31.  * An array with the following elements must be passed to
  32.  * the class constructor (elements marked ? are optional)
  33.  *
  34.  * Main settings:
  35.  * <pre>
  36.  *    host      => LDAP server hostname, IP-address or any other URI compatible
  37.  *                 with used LDAP library.
  38.  *    base      => LDAP server root (base dn). Empty string allowed.
  39.  *  ? port      => LDAP server TCP port number (default: 389)
  40.  *  ? charset   => LDAP server charset (default: utf-8)
  41.  *  ? name      => Name for LDAP server (default "LDAP: hostname")
  42.  *                 Used to tag the result data
  43.  *  ? maxrows   => Maximum # of rows in search result
  44.  *  ? timeout   => Timeout for LDAP operations (in seconds, default: 30)
  45.  *                 Might not work for all LDAP libraries or servers.
  46.  *  ? binddn    => LDAP Bind DN.
  47.  *  ? bindpw    => LDAP Bind Password.
  48.  *  ? protocol  => LDAP Bind protocol.
  49.  * </pre>
  50.  * Advanced settings:
  51.  * <pre>
  52.  *  ? filter    => Filter expression to limit ldap searches
  53.  *  ? limit_scope => Limits scope to base DN (Specific to Win2k3 ADS).
  54.  *  ? listing   => Controls listing of LDAP directory.
  55.  *  ? writeable => Controls write access to address book
  56.  *  ? search_tree => Controls subtree or one level search.
  57.  *  ? starttls  => Controls use of StartTLS on LDAP connections
  58.  * </pre>
  59.  * NOTE. This class should not be used directly. Use addressbook_init()
  60.  *       function instead.
  61.  * @package squirrelmail
  62.  * @subpackage addressbook
  63.  */
  64.     /**
  65.      * @var string backend type
  66.      */
  67.     var $btype = 'remote';
  68.     /**
  69.      * @var string backend name
  70.      */
  71.     var $bname = 'ldap_server';
  72.  
  73.     /* Parameters changed by class */
  74.     /**
  75.      * @var string displayed name
  76.      */
  77.     var $sname   = 'LDAP';       /* Service name */
  78.     /**
  79.      * @var string LDAP server name or address or url
  80.      */
  81.     var $server  = '';
  82.     /**
  83.      * @var integer LDAP server port
  84.      */
  85.     var $port    = 389;
  86.     /**
  87.      * @var string LDAP base DN
  88.      */
  89.     var $basedn  = '';
  90.     /**
  91.      * @var string charset used for entries in LDAP server
  92.      */
  93.     var $charset = 'utf-8';
  94.     /**
  95.      * @var object PHP LDAP link ID
  96.      */
  97.     var $linkid  = false;
  98.     /**
  99.      * @var bool True if LDAP server is bound
  100.      */
  101.     var $bound   = false;
  102.     /**
  103.      * @var integer max rows in result
  104.      */
  105.     var $maxrows = 250;
  106.     /**
  107.      * @var string ldap filter
  108.      * @since 1.5.1
  109.      */
  110.     var $filter = '';
  111.     /**
  112.      * @var integer timeout of LDAP operations (in seconds)
  113.      */
  114.     var $timeout = 30;
  115.     /**
  116.      * @var string DN to bind to (non-anonymous bind)
  117.      * @since 1.5.0 and 1.4.3
  118.      */
  119.     var $binddn = '';
  120.     /**
  121.      * @var string  password to bind with (non-anonymous bind)
  122.      * @since 1.5.0 and 1.4.3
  123.      */
  124.     var $bindpw = '';
  125.     /**
  126.      * @var integer protocol used to connect to ldap server
  127.      * @since 1.5.0 and 1.4.3
  128.      */
  129.     var $protocol = '';
  130.     /**
  131.      * @var boolean limits scope to base dn
  132.      * @since 1.5.1
  133.      */
  134.     var $limit_scope = false;
  135.     /**
  136.      * @var boolean controls listing of directory
  137.      * @since 1.5.1
  138.      */
  139.     var $listing = false;
  140.     /**
  141.      * @var boolean true if removing/adding/modifying entries is allowed
  142.      * @since 1.5.2
  143.      */
  144.     var $writeable = false;
  145.     /**
  146.      * @var boolean controls ldap search type.
  147.      *  only first level entries are displayed if set to false
  148.      * @since 1.5.1
  149.      */
  150.     var $search_tree = true;
  151.     /**
  152.      * @var boolean controls use of StartTLS on ldap
  153.      *  connections. Requires php 4.2+ and protocol >= 3
  154.      * @since 1.5.1
  155.      */
  156.     var $starttls = false;
  157.  
  158.     /**
  159.      * Constructor. Connects to database
  160.      * @param array connection options
  161.      */
  162.     function abook_ldap_server($param{
  163.         if(!function_exists('ldap_connect')) {
  164.             $this->set_error(_("PHP install does not have LDAP support."));
  165.             return;
  166.         }
  167.         if(is_array($param)) {
  168.             $this->server = $param['host'];
  169.             // remove whitespace from basedn
  170.             $this->basedn = preg_replace('/,\s*/',',',trim($param['base']));
  171.  
  172.             if(!empty($param['port']))
  173.                 $this->port = $param['port'];
  174.  
  175.             if(!empty($param['charset']))
  176.                 $this->charset = strtolower($param['charset']);
  177.  
  178.             if(isset($param['maxrows']))
  179.                 $this->maxrows = $param['maxrows'];
  180.  
  181.             if(isset($param['timeout']))
  182.                 $this->timeout = $param['timeout'];
  183.  
  184.             if(isset($param['binddn']))
  185.                 $this->binddn = $param['binddn'];
  186.  
  187.             if(isset($param['bindpw']))
  188.                 $this->bindpw = $param['bindpw'];
  189.  
  190.             if(isset($param['protocol']))
  191.                 $this->protocol = (int) $param['protocol'];
  192.  
  193.             if(isset($param['filter']))
  194.                 $this->filter = trim($param['filter']);
  195.  
  196.             if(isset($param['limit_scope']))
  197.                 $this->limit_scope = (bool) $param['limit_scope'];
  198.  
  199.             if(isset($param['listing']))
  200.                 $this->listing = (bool) $param['listing'];
  201.  
  202.             if(isset($param['writeable'])) {
  203.                 $this->writeable = (bool) $param['writeable'];
  204.                 // switch backend type to local, if it is writable
  205.                 if($this->writeable$this->btype = 'local';
  206.             }
  207.  
  208.             if(isset($param['search_tree']))
  209.                 $this->search_tree = (bool) $param['search_tree'];
  210.  
  211.             if(isset($param['starttls']))
  212.                 $this->starttls = (bool) $param['starttls'];
  213.  
  214.             if(empty($param['name'])) {
  215.                 $this->sname = 'LDAP: ' $param['host'];
  216.             else {
  217.                 $this->sname = $param['name'];
  218.             }
  219.  
  220.             /*
  221.              * don't open LDAP server on addressbook_init(),
  222.              * open ldap connection only on search. Speeds up
  223.              * addressbook_init() call.
  224.              */
  225.             // $this->open(true);
  226.         else {
  227.             $this->set_error('Invalid argument to constructor');
  228.         }
  229.     }
  230.  
  231.  
  232.     /**
  233.      * Open the LDAP server.
  234.      * @param bool $new is it a new connection
  235.      * @return bool 
  236.      */
  237.     function open($new false{
  238.         $this->error = '';
  239.  
  240.         /* Connection is already open */
  241.         if($this->linkid != false && !$new{
  242.             return true;
  243.         }
  244.  
  245.         $this->linkid = @ldap_connect($this->server$this->port);
  246.         /**
  247.          * check if connection was successful
  248.          * It does not work with OpenLDAP 2.x libraries. Connect error will be 
  249.          * displayed only on ldap command that tries to make connection 
  250.          * (ldap_start_tls or ldap_bind). 
  251.          */
  252.         if(!$this->linkid{
  253.             return $this->set_error($this->ldap_error('ldap_connect failed'));
  254.         }
  255.  
  256.         if(!empty($this->protocol)) {
  257.             // make sure that ldap_set_option() is available before using it
  258.             if(function_exists('ldap_set_option'||
  259.                !@ldap_set_option($this->linkidLDAP_OPT_PROTOCOL_VERSION$this->protocol)) {
  260.                 return $this->set_error('unable to set ldap protocol number');
  261.             }
  262.         }
  263.  
  264.         /**
  265.          * http://www.php.net/ldap-start-tls
  266.          * Check if v3 or newer protocol is used,
  267.          * check if ldap_start_tls function is available.
  268.          * Silently ignore setting, if these requirements are not satisfied.
  269.          * Break with error message if somebody tries to start TLS on
  270.          * ldaps or socket connection.
  271.          */
  272.         if($this->starttls && 
  273.            !empty($this->protocol&& $this->protocol >= &&
  274.            function_exists('ldap_start_tls') ) {
  275.             // make sure that $this->server is not ldaps:// or ldapi:// URL.
  276.             if (preg_match("/^ldap[si]:\/\/.+/i",$this->server)) {
  277.                 return $this->set_error("you can't enable starttls on ldaps and ldapi connections.");
  278.             }
  279.             
  280.             // try starting tls
  281.             if (@ldap_start_tls($this->linkid)) {
  282.                 // set error if call fails
  283.                 return $this->set_error($this->ldap_error('ldap_start_tls failed'));
  284.             }
  285.         }
  286.  
  287.         if(!empty($this->limit_scope&& $this->limit_scope{
  288.             if(empty($this->protocol|| intval($this->protocol3{
  289.                 return $this->set_error('limit_scope requires protocol >= 3');
  290.             }
  291.             // See http://msdn.microsoft.com/library/en-us/ldap/ldap/ldap_server_domain_scope_oid.asp
  292.             $ctrl array "oid" => "1.2.840.113556.1.4.1339""iscritical" => TRUE );
  293.             /*
  294.              * Option is set only during connection.
  295.              * It does not cause immediate errors with OpenLDAP 2.x libraries.
  296.              */
  297.             if(function_exists('ldap_set_option'||
  298.                !@ldap_set_option($this->linkidLDAP_OPT_SERVER_CONTROLSarray($ctrl))) {
  299.                 return $this->set_error($this->ldap_error('limit domain scope failed'));
  300.             }
  301.         }
  302.  
  303.         // authenticated bind
  304.         if(!empty($this->binddn)) {
  305.             if(!@ldap_bind($this->linkid$this->binddn$this->bindpw)) {
  306.                 return $this->set_error($this->ldap_error('authenticated ldap_bind failed'));
  307.             }
  308.         else {
  309.             // anonymous bind
  310.             if(!@ldap_bind($this->linkid)) {
  311.                 return $this->set_error($this->ldap_error('anonymous ldap_bind failed'));
  312.             }
  313.         }
  314.  
  315.         $this->bound = true;
  316.  
  317.         return true;
  318.     }
  319.  
  320.     /**
  321.      * Encode string to the charset used by this LDAP server
  322.      * @param string string that has to be encoded
  323.      * @return string encoded string
  324.      */
  325.     function charset_encode($str{
  326.         global $default_charset;
  327.         if($this->charset != $default_charset{
  328.             return charset_convert($default_charset,$str,$this->charset,false);
  329.         else {
  330.             return $str;
  331.         }
  332.     }
  333.  
  334.     /**
  335.      * Decode from charset used by this LDAP server to charset used by translation
  336.      *
  337.      * Uses SquirrelMail charset_decode functions
  338.      * @param string string that has to be decoded
  339.      * @return string decoded string
  340.      */
  341.     function charset_decode($str{
  342.         global $default_charset;
  343.         if ($this->charset != $default_charset{
  344.             return charset_convert($this->charset,$str,$default_charset,false);
  345.         else {
  346.             return $str;
  347.         }
  348.     }
  349.  
  350.     /**
  351.      * Sanitizes ldap search strings.
  352.      * See rfc2254
  353.      * @link http://www.faqs.org/rfcs/rfc2254.html
  354.      * @since 1.5.1 and 1.4.5
  355.      * @param string $string 
  356.      * @return string sanitized string
  357.      */
  358.     function ldapspecialchars($string{
  359.         $sanitized=array('\\' => '\5c',
  360.                          '*' => '\2a',
  361.                          '(' => '\28',
  362.                          ')' => '\29',
  363.                          "\x00" => '\00');
  364.  
  365.         return str_replace(array_keys($sanitized),array_values($sanitized),$string);
  366.     }
  367.  
  368.     /**
  369.      * Prepares user input for use in a ldap query.
  370.      *
  371.      * Function converts input string to character set used in LDAP server
  372.      * (charset_encode() method) and sanitizes it (ldapspecialchars()).
  373.      *
  374.      * @param string $string string to encode
  375.      * @return string ldap encoded string
  376.      * @since 1.5.2
  377.      */
  378.     function quotevalue($string{
  379.         $sanitized $this->charset_encode($string);
  380.         return $this->ldapspecialchars($sanitized);
  381.     }
  382.  
  383.     /**
  384.      * Search LDAP server.
  385.      *
  386.      * Warning: You must make sure that ldap query is correctly formated and
  387.      * sanitize use of special ldap keywords.
  388.      * @param string $expression ldap query
  389.      * @param boolean $singleentry (since 1.5.2) whether we are looking for a
  390.      *   single entry. Boolean true forces LDAP_SCOPE_BASE search.
  391.      * @return array search results (false on error)
  392.      * @since 1.5.1
  393.      */
  394.     function ldap_search($expression$singleentry false{
  395.         /* Make sure connection is there */
  396.         if(!$this->open()) {
  397.             return false;
  398.         }
  399.  
  400.         $attributes array('dn''description''sn''givenname''cn''mail');
  401.  
  402.         if ($singleentry{
  403.             // ldap_read - search for one single entry
  404.             $sret @ldap_read($this->linkid$expression"objectClass=*",
  405.                                $attributes0$this->maxrows$this->timeout);
  406.         elseif ($this->search_tree{
  407.             // ldap_search - search subtree
  408.             $sret @ldap_search($this->linkid$this->basedn$expression,
  409.                 $attributes0$this->maxrows$this->timeout);
  410.         else {
  411.             // ldap_list - search one level
  412.             $sret @ldap_list($this->linkid$this->basedn$expression,
  413.                 $attributes0$this->maxrows$this->timeout);
  414.         }
  415.  
  416.         /* Return error if search failed */
  417.         if(!$sret{
  418.             // Check for LDAP_NO_SUCH_OBJECT (0x20 or 32) error
  419.             if (ldap_errno($this->linkid)==32{
  420.                 return array();
  421.             else {
  422.                 return $this->set_error($this->ldap_error('ldap_search failed'));
  423.             }
  424.         }
  425.  
  426.         if(@ldap_count_entries($this->linkid$sret<= 0{
  427.             return array();
  428.         }
  429.  
  430.         /* Get results */
  431.         $ret array();
  432.         $returned_rows 0;
  433.         $res @ldap_get_entries($this->linkid$sret);
  434.         for($i $i $res['count'$i++{
  435.             $row $res[$i];
  436.  
  437.             /* Extract data common for all e-mail addresses
  438.              * of an object. Use only the first name */      
  439.             $nickname $this->charset_decode($row['dn']);
  440.  
  441.             /**
  442.              * remove trailing basedn
  443.              * remove whitespaces between RDNs
  444.              * remove leading "cn="
  445.              * which gives nicknames which are shorter while still unique
  446.              */
  447.             $nickname preg_replace('/,\s*/',','trim($nickname));
  448.             $offset strlen($nicknamestrlen($this->basedn);
  449.  
  450.             if($offset && substr($nickname$offset== $this->basedn{
  451.                 $nickname substr($nickname0$offset);
  452.                 if(substr($nickname-1== ",")
  453.                     $nickname substr($nickname0-1);
  454.             }
  455.             if(strncasecmp($nickname"cn="3== 0)
  456.                 $nickname=substr($nickname3);         
  457.  
  458.             if(empty($row['description'][0])) {
  459.                 $label '';
  460.             else {
  461.                 $label $this->charset_decode($row['description'][0]);
  462.             }
  463.  
  464.             if(empty($row['givenname'][0])) {
  465.                 $firstname '';
  466.             else {
  467.                 $firstname $this->charset_decode($row['givenname'][0]);
  468.             }
  469.  
  470.             if(empty($row['sn'][0])) {
  471.                 $surname '';
  472.             else {
  473.                 // remove whitespace in order to handle sn set to empty string
  474.                 $surname trim($this->charset_decode($row['sn'][0]));
  475.             }
  476.  
  477.             $fullname $this->fullname($firstname,$surname);
  478.  
  479.             /* Add one row to result for each e-mail address */
  480.             if(isset($row['mail']['count'])) {
  481.                 for($j $j $row['mail']['count'$j++{
  482.                     array_push($retarray('nickname'  => $nickname,
  483.                    'name'      => $fullname,
  484.                    'firstname' => $firstname,
  485.                    'lastname'  => $surname,
  486.                    'email'     => $row['mail'][$j],
  487.                    'label'     => $label,
  488.                    'backend'   => $this->bnum,
  489.                    'source'    => &$this->sname));
  490.  
  491.                     // Limit number of hits
  492.                     $returned_rows++;
  493.                     if(($returned_rows >= $this->maxrows&&
  494.                        ($this->maxrows > 0) ) {
  495.                         ldap_free_result($sret);
  496.                         return $ret;
  497.                     }
  498.  
  499.                 // for($j ...)
  500.  
  501.             // isset($row['mail']['count'])
  502.  
  503.         }
  504.  
  505.         ldap_free_result($sret);
  506.         return $ret;
  507.     }
  508.  
  509.     /**
  510.      * Add an entry to LDAP server.
  511.      *
  512.      * Warning: You must make sure that the arguments are correctly formated and
  513.      * sanitize use of special ldap keywords.
  514.      * @param string $dn the dn of the entry to be added
  515.      * @param array $data the values of the entry to be added
  516.      * @return boolean result (false on error)
  517.      * @since 1.5.2
  518.      */
  519.     function ldap_add($dn$data{
  520.         /* Make sure connection is there */
  521.         if(!$this->open()) {
  522.             return false;
  523.         }
  524.  
  525.         if(!@ldap_add($this->linkid$dn$data)) {
  526.             $this->set_error(_("Write to address book failed"));
  527.             return false;
  528.         }
  529.         
  530.         return true;
  531.     }
  532.  
  533.     /**
  534.      * Remove an entry from LDAP server.
  535.      *
  536.      * Warning: You must make sure that the argument is correctly formated and
  537.      * sanitize use of special ldap keywords.
  538.      * @param string $dn the dn of the entry to remove
  539.      * @return boolean result (false on error)
  540.      * @since 1.5.2
  541.      */
  542.     function ldap_remove($dn{
  543.         /* Make sure connection is there */
  544.         if(!$this->open()) {
  545.             return false;
  546.         }
  547.  
  548.         if(!@ldap_delete($this->linkid$dn)) {
  549.             $this->set_error(_("Removing entry from address book failed"));
  550.             return false;
  551.         }
  552.  
  553.         return true;
  554.     }
  555.  
  556.     /**
  557.      * Rename an entry on LDAP server.
  558.      *
  559.      * Warning: You must make sure that the arguments are correctly formated and
  560.      * sanitize use of special ldap keywords.
  561.      * @param string $sourcedn the dn of the entry to be renamed
  562.      * @param string $targetdn the dn which $sourcedn should be renamed to
  563.      * @param string $parent the dn of the parent entry
  564.      * @return boolean result (false on error)
  565.      * @since 1.5.2
  566.      */
  567.     function ldap_rename($sourcedn$targetdn$parent{
  568.         /* Make sure connection is there */
  569.         if(!$this->open()) {
  570.             return false;
  571.         }
  572.  
  573.         /* Make sure that the protocol version supports rename */
  574.         if($this->protocol < 3{
  575.             $this->set_error(_("LDAP rename is not supported by used protocol version"));
  576.             return false;
  577.         }
  578.         /**
  579.          * Function is available only in OpenLDAP 2.x.x or Netscape Directory 
  580.          * SDK x.x, and was added in PHP 4.0.5
  581.          * @todo maybe we can use copy + delete instead of ldap_rename()
  582.          */
  583.         if(!function_exists('ldap_rename')) {
  584.             $this->set_error(_("LDAP rename is not supported by used LDAP library. You can't change nickname"));
  585.             return false;
  586.         }
  587.  
  588.         /* OK, go for it */
  589.         if(!@ldap_rename($this->linkid$sourcedn$targetdn$parenttrue)) {
  590.             $this->set_error(_("LDAP rename failed"));
  591.             return false;
  592.         }
  593.  
  594.         return true;
  595.     }
  596.  
  597.     /**
  598.      * Modify the values of an entry on LDAP server.
  599.      *
  600.      * Warning: You must make sure that the arguments are correctly formated and
  601.      * sanitize use of special ldap keywords.
  602.      * @param string $dn the dn of the entry to be modified
  603.      * @param array $data the new values of the entry
  604.      * @param array $deleted_attribs attributes that should be deleted.
  605.      * @return bool result (false on error)
  606.      * @since 1.5.2
  607.      */
  608.     function ldap_modify($dn$data$deleted_attribs{
  609.         /* Make sure connection is there */
  610.         if(!$this->open()) {
  611.             return false;
  612.         }
  613.  
  614.         if(!@ldap_modify($this->linkid$dn$data)) {
  615.             $this->set_error(_("Write to address book failed"));
  616.             return false;
  617.         }
  618.  
  619.         if (!@ldap_mod_del($this->linkid$dn$deleted_attribs)) {
  620.             $this->set_error(_("Unable to remove some field values"));
  621.             return false;
  622.         }
  623.  
  624.         return true;
  625.     }
  626.  
  627.     /**
  628.      * Get error from LDAP resource if possible
  629.      *
  630.      * Should get error from server using the ldap_errno() and ldap_err2str() functions
  631.      * @param string $sError error message used when ldap error functions
  632.      *  and connection resource are unavailable
  633.      * @return string error message
  634.      * @since 1.5.1
  635.      */
  636.     function ldap_error($sError{
  637.         // it is possible that function_exists() tests are not needed
  638.         if(function_exists('ldap_err2str'&& 
  639.            function_exists('ldap_errno'&& 
  640.            is_resource($this->linkid)) {
  641.             return ldap_err2str(ldap_errno($this->linkid));
  642.             // return ldap_error($this->linkid);
  643.         else {
  644.             return $sError;
  645.         }
  646.     }
  647.  
  648.     /* ========================== Public ======================== */
  649.  
  650.     /**
  651.      * Search the LDAP server
  652.      * @param string $expr search expression
  653.      * @return array search results
  654.      */
  655.     function search($expr{
  656.         /* To be replaced by advanded search expression parsing */
  657.         if(is_array($expr)) return false;
  658.  
  659.         // don't allow wide search when listing is disabled.
  660.         if ($expr=='*' && $this->listing{
  661.             return array();
  662.         elseif ($expr=='*'{
  663.             // allow use of wildcard when listing is enabled.
  664.             $expression '(cn=*)';
  665.         else {
  666.             /* Convert search from user's charset to the one used in ldap and sanitize */
  667.             $expr $this->quotevalue($expr);
  668.  
  669.             /* Search for same string in cn, main and sn */
  670.             $expression '(|(cn=*'.$expr.'*)(mail=*'.$expr.'*)(sn=*'.$expr.'*))';
  671.  
  672.             /* Undo sanitizing of * symbol */
  673.             $expression str_replace('\2a','*',$expression);
  674.         }
  675.  
  676.         /* Add search filtering */
  677.         if ($this->filter!='')
  678.             $expression '(&' $this->filter . $expression ')';
  679.  
  680.         /* Use internal search function and return search results */
  681.         return $this->ldap_search($expression);
  682.     }
  683.  
  684.     /**
  685.      * Lookup an alias
  686.      * @param string $alias alias
  687.      * @return array search results
  688.      * @since 1.5.2
  689.      */
  690.     function lookup($alias{
  691.         /* Generate the dn and try to retrieve that single entry */
  692.         $cn $this->quotevalue($alias);
  693.         $dn 'cn=' $cn ',' $this->basedn;
  694.  
  695.         /* Do the search */
  696.         $result $this->ldap_search($dntrue);
  697.         if (!is_array($result|| count($result1)
  698.             return array();
  699.  
  700.         return $result[0];
  701.     }
  702.  
  703.     /**
  704.      * List all entries present in LDAP server
  705.      *
  706.      * maxrows setting might limit list of returned entries.
  707.      * Careful with this -- it could get quite large for big sites.
  708.      * @return array all entries in ldap server
  709.      */
  710.      function list_addr({
  711.          if ($this->listing)
  712.              return array();
  713.  
  714.          /* set wide search expression */
  715.          $expression '(cn=*)';
  716.  
  717.          /* add filtering */
  718.          if ($this->filter!='')
  719.              $expression '(&' $this->filter . $expression .')';
  720.  
  721.          /* use internal search function and return search results */
  722.          return $this->ldap_search($expression);
  723.      }
  724.  
  725.     /**
  726.      * Add address
  727.      * @param array $userdata new data
  728.      * @return boolean 
  729.      * @since 1.5.2
  730.      */
  731.     function add($userdata{
  732.         if(!$this->writeable{
  733.             return $this->set_error(_("Address book is read-only"));
  734.         }
  735.  
  736.         /* Convert search from user's charset to the one used in ldap and sanitize */
  737.         $cn $this->quotevalue($userdata['nickname']);
  738.         $dn 'cn=' $cn ',' trim($this->basedn);
  739.  
  740.         /* See if user exists already */
  741.         $user $this->ldap_search($dntrue);
  742.         if (!is_array($user)) {
  743.             return false;
  744.         elseif (count($user0{
  745.             return $this->set_error(sprintf(_("User \"%s\" already exists")$userdata['nickname']));
  746.         }
  747.  
  748.         /* init variable */
  749.         $data array();
  750.  
  751.         /* Prepare data */
  752.         $data['cn'$cn;
  753.         $data['mail'$this->quotevalue($userdata['email']);
  754.         $data["objectclass"][0"top";
  755.         $data["objectclass"][1"person";
  756.         $data["objectclass"][2"organizationalPerson";
  757.         $data["objectclass"][3"inetOrgPerson";
  758.         /* sn is required in person object */
  759.         if(!empty($userdata['lastname'])) {
  760.             $data['sn'$this->quotevalue($userdata['lastname']);
  761.         else {
  762.             $data['sn'' ';
  763.         }
  764.         /* optional fields */
  765.         if(!empty($userdata['firstname']))
  766.             $data['givenName'$this->quotevalue($userdata['firstname']);
  767.         if(!empty($userdata['label'])) {
  768.             $data['description'$this->quotevalue($userdata['label']);
  769.         }
  770.         return $this->ldap_add($dn$data);
  771.     }
  772.  
  773.     /**
  774.      * Delete address
  775.      * @param array $aliases array of entries that have to be removed.
  776.      * @return boolean 
  777.      * @since 1.5.2
  778.      */
  779.     function remove($aliases{
  780.         if(!$this->writeable{
  781.             return $this->set_error(_("Address book is read-only"));
  782.         }
  783.  
  784.         foreach ($aliases as $alias{
  785.             /* Convert nickname from user's charset and derive cn/dn */
  786.             $cn $this->quotevalue($alias);
  787.             $dn 'cn=' $cn ',' $this->basedn;
  788.  
  789.             if (!$this->ldap_remove($dn))
  790.                 return false;
  791.         }
  792.  
  793.         return true;
  794.     }
  795.  
  796.     /**
  797.      * Modify address
  798.      * @param string $alias modified alias
  799.      * @param array $userdata new data
  800.      * @return boolean 
  801.      * @since 1.5.2
  802.      */
  803.     function modify($alias$userdata{
  804.         if(!$this->writeable{
  805.             return $this->set_error(_("Address book is read-only"));
  806.         }
  807.  
  808.         /* Convert search from user's charset to the one used in ldap and sanitize */
  809.         $sourcecn $this->quotevalue($alias);
  810.         $sourcedn 'cn=' $sourcecn ',' trim($this->basedn);
  811.         $targetcn $this->quotevalue($userdata['nickname']);
  812.         $targetdn 'cn=' $targetcn ',' trim($this->basedn);
  813.  
  814.         /* Check that the dn to modify exists */
  815.         $sourceuser $this->lookup($alias);
  816.         if (!is_array($sourceuser|| count($sourceuser1)
  817.             return false;
  818.  
  819.         /* Check if dn is going to change */
  820.         if ($alias != $userdata['nickname']{
  821.  
  822.             /* Check that the target dn doesn't exist */
  823.             $targetuser $this->lookup($userdata['nickname']);
  824.             if (is_array($targetuser&& count($targetuser0)
  825.                 return $this->set_error(sprintf(_("User \"%s\" already exists")$userdata['nickname']));
  826.  
  827.             /* Rename from the source dn to target dn */
  828.             if (!$this->ldap_rename($sourcedn'cn=' $targetcn$this->basedn))
  829.                     return $this->set_error(sprintf(_("Unable to rename user \"%s\" to \"%s\"")$alias$userdata['nickname']));
  830.         }
  831.  
  832.         // initial vars
  833.         $data array();
  834.         $deleted_attribs array();
  835.  
  836.         /* Prepare data */
  837.         $data['cn'$this->quotevalue($targetcn);
  838.         $data['mail'$this->quotevalue($userdata['email']);
  839.         $data["objectclass"][0"top";
  840.         $data["objectclass"][1"person";
  841.         $data["objectclass"][2"organizationalPerson";
  842.         $data["objectclass"][3"inetOrgPerson";
  843.  
  844.         if(!empty($userdata['firstname'])) {
  845.             $data['givenName'$this->quotevalue($userdata['firstname']);
  846.         elseif (!empty($sourceuser['firstname'])) {
  847.             $deleted_attribs['givenName'$this->quotevalue($sourceuser['firstname']);
  848.         }
  849.  
  850.         if(!empty($userdata['lastname'])) {
  851.             $data['sn'$this->quotevalue($userdata['lastname']);
  852.         else {
  853.             // sn is required attribute in LDAP person object.
  854.             // SquirrelMail requires givenName or Surname 
  855.             $data['sn'' ';
  856.         }
  857.  
  858.         if(!empty($userdata['label'])) {
  859.             $data['description'$this->quotevalue($userdata['label']);
  860.         elseif (!empty($sourceuser['label'])) {
  861.             $deleted_attribs['description'$this->quotevalue($sourceuser['label']);
  862.         }
  863.  
  864.         return $this->ldap_modify($targetdn$data$deleted_attribs);
  865.     }
  866. }

Documentation generated on Sat, 07 Oct 2006 16:08:31 +0300 by phpDocumentor 1.3.0RC6