source: github/program/include/rcube_ldap.inc @ 6b603da

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 6b603da was 6b603da, checked in by thomascube <thomas@…>, 6 years ago

LDAP improvements

  • Property mode set to 100644
File size: 10.3 KB
RevLine 
[d1d2c4f]1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/rcube_ldap.inc                                        |
6 |                                                                       |
7 | This file is part of the RoundCube Webmail client                     |
[f115416]8 | Copyright (C) 2006-2007, RoundCube Dev. - Switzerland                 |
[d1d2c4f]9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
11 | PURPOSE:                                                              |
[f115416]12 |   Interface to an LDAP address directory                              |
[d1d2c4f]13 |                                                                       |
14 +-----------------------------------------------------------------------+
[f115416]15 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
[d1d2c4f]16 +-----------------------------------------------------------------------+
17
18 $Id$
19
20*/
21
[6d969b4]22
23/**
24 * Model class to access an LDAP address directory
25 *
26 * @package Addressbook
27 */
[d1d2c4f]28class rcube_ldap
[f115416]29{
[d1d2c4f]30  var $conn;
[f115416]31  var $prop = array();
32  var $fieldmap = array();
33 
34  var $filter = '';
35  var $result = null;
36  var $ldap_result = null;
37  var $sort_col = '';
38 
39  /** public properties */
40  var $primary_key = 'ID';
41  var $readonly = true;
42  var $list_page = 1;
43  var $page_size = 10;
44  var $ready = false;
45 
46 
47  /**
48   * Object constructor
49   *
50   * @param array LDAP connection properties
51   * @param integer User-ID
52   */
53  function __construct($p)
54  {
55    $this->prop = $p;
56   
57    foreach ($p as $prop => $value)
58      if (preg_match('/^(.+)_field$/', $prop, $matches))
59        $this->fieldmap[$matches[1]] = $value;
60   
61    $this->connect();
62  }
[d1d2c4f]63
[f115416]64  /**
65   * PHP 4 object constructor
66   *
[6d969b4]67   * @see  rcube_ldap::__construct()
[f115416]68   */
69  function rcube_ldap($p)
70  {
71    $this->__construct($p);
72  }
73 
[d1d2c4f]74
[f115416]75  /**
76   * Establish a connection to the LDAP server
77   */
78  function connect()
79  {
[d1d2c4f]80    if (!function_exists('ldap_connect'))
[f115416]81      raise_error(array('type' => 'ldap', 'message' => "No ldap support in this installation of PHP"), true);
[d1d2c4f]82
83    if (is_resource($this->conn))
[f115416]84      return true;
[d1d2c4f]85   
[f115416]86    if (!is_array($this->prop['hosts']))
87      $this->prop['hosts'] = array($this->prop['hosts']);
[d1d2c4f]88
[f115416]89    foreach ($this->prop['hosts'] as $host)
90    {
91      if ($lc = @ldap_connect($host, $this->prop['port']))
[d1d2c4f]92      {
[f115416]93        ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $this->prop['port']);
94        $this->prop['host'] = $host;
[d1d2c4f]95        $this->conn = $lc;
[f115416]96        break;
[d1d2c4f]97      }
98    }
[f115416]99   
100    if (is_resource($this->conn))
[e3caaf5]101    {
[f115416]102      $this->ready = true;
[e3caaf5]103      if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass']))
104        $this->ready = $this->bind($this->prop['bind_dn'], $this->prop['bind_pass']);
105    }
[f115416]106    else
107      raise_error(array('type' => 'ldap', 'message' => "Could not connect to any LDAP server, tried $host:{$this->prop[port]} last"), true);
108  }
[d1d2c4f]109
110
[f115416]111  /**
[e3caaf5]112   * Bind connection with DN and password
[6d969b4]113   *
114   * @param string Bind DN
115   * @param string Bind password
116   * @return boolean True on success, False on error
[f115416]117   */
[e3caaf5]118  function bind($dn, $pass)
[f115416]119  {
[e3caaf5]120    if (!$this->conn)
121      return false;
122   
123    if (@ldap_bind($this->conn, $dn, $pass))
124      return true;
125    else
[f115416]126    {
[e3caaf5]127      raise_error(array(
128        'code' => ldap_errno($this->conn),
129        'type' => 'ldap',
130        'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)),
131      true);
[f115416]132    }
[e3caaf5]133   
[f115416]134    return false;
[e3caaf5]135  }
[d1d2c4f]136
[f115416]137
138  /**
139   * Close connection to LDAP server
140   */
141  function close()
142  {
[d1d2c4f]143    if ($this->conn)
[6b603da]144    {
[f115416]145      @ldap_unbind($this->conn);
[6b603da]146      $this->conn = null;
147    }
[f115416]148  }
149
150
151  /**
152   * Set internal list page
153   *
154   * @param  number  Page number to list
155   * @access public
156   */
157  function set_page($page)
158  {
159    $this->list_page = (int)$page;
160  }
161
162
163  /**
164   * Set internal page size
165   *
166   * @param  number  Number of messages to display on one page
167   * @access public
168   */
169  function set_pagesize($size)
170  {
171    $this->page_size = (int)$size;
172  }
173
174
175  /**
176   * Save a search string for future listings
177   *
[6d969b4]178   * @param string Filter string
[f115416]179   */
180  function set_search_set($filter)
181  {
182    $this->filter = $filter;
183  }
184 
185 
186  /**
187   * Getter for saved search properties
188   *
189   * @return mixed Search properties used by this class
190   */
191  function get_search_set()
192  {
193    return $this->filter;
194  }
195
196
197  /**
198   * Reset all saved results and search parameters
199   */
200  function reset()
201  {
202    $this->result = null;
203    $this->ldap_result = null;
204    $this->filter = '';
205  }
206 
207 
208  /**
209   * List the current set of contact records
210   *
211   * @param  array  List of cols to show
[6d969b4]212   * @param  int    Only return this number of records (not implemented)
[f115416]213   * @return array  Indexed list of contact records, each a hash array
214   */
215  function list_records($cols=null, $subset=0)
216  {
[6b603da]217    // add general filter to query
218    if (!empty($this->prop['filter']))
219    {
220      $filter = $this->prop['filter'];
221      $this->set_search_set($filter);
222    }
223   
[f115416]224    // exec LDAP search if no result resource is stored
225    if ($this->conn && !$this->ldap_result)
226      $this->_exec_search();
227   
228    // count contacts for this user
229    $this->result = $this->count();
230   
231    // we have a search result resource
232    if ($this->ldap_result && $this->result->count > 0)
233    {
234      if ($this->sort_col && $this->prop['scope'] !== "base")
235        @ldap_sort($this->conn, $this->ldap_result, $this->sort_col);
236       
237      $entries = ldap_get_entries($this->conn, $this->ldap_result);
238      for ($i = $this->result->first; $i < min($entries['count'], $this->result->first + $this->page_size); $i++)
239        $this->result->add($this->_ldap2result($entries[$i]));
[d1d2c4f]240    }
241
[f115416]242    return $this->result;
243  }
244
245
246  /**
247   * Search contacts
248   *
249   * @param array   List of fields to search in
250   * @param string  Search value
251   * @param boolean True if results are requested, False if count only
[6d969b4]252   * @return array  Indexed list of contact records and 'count' value
[f115416]253   */
[3fc00e6]254  function search($fields, $value, $strict=false, $select=true)
[f115416]255  {
256    // special treatment for ID-based search
257    if ($fields == 'ID' || $fields == $this->primary_key)
[d1d2c4f]258    {
[f115416]259      $ids = explode(',', $value);
260      $result = new rcube_result_set();
261      foreach ($ids as $id)
262        if ($rec = $this->get_record($id, true))
[d1d2c4f]263        {
[f115416]264          $result->add($rec);
265          $result->count++;
[d1d2c4f]266        }
[f115416]267     
268      return $result;
[d1d2c4f]269    }
[f115416]270   
271    $filter = '(|';
[3fc00e6]272    $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : '';
[f115416]273    if (is_array($this->prop['search_fields']))
[d1d2c4f]274    {
[f115416]275      foreach ($this->prop['search_fields'] as $k => $field)
276        $filter .= "($field=$wc" . rcube_ldap::quote_string($value) . "$wc)";
277    }
[d1d2c4f]278    else
[f115416]279    {
280      foreach ((array)$fields as $field)
281        if ($f = $this->_map_field($field))
282          $filter .= "($f=$wc" . rcube_ldap::quote_string($value) . "$wc)";
[d1d2c4f]283    }
[f115416]284    $filter .= ')';
[e3caaf5]285   
286    // add general filter to query
287    if (!empty($this->prop['filter']))
288      $filter = '(&'.$this->prop['filter'] . $filter . ')';
[d1d2c4f]289
[f115416]290    // set filter string and execute search
291    $this->set_search_set($filter);
292    $this->_exec_search();
293   
294    if ($select)
295      $this->list_records();
[d1d2c4f]296    else
[f115416]297      $this->result = $this->count();
298   
299    return $this->result;
300  }
[d1d2c4f]301
302
[f115416]303  /**
304   * Count number of available contacts in database
305   *
[6d969b4]306   * @return object rcube_result_set Resultset with values for 'count' and 'first'
[f115416]307   */
308  function count()
309  {
310    $count = 0;
311    if ($this->conn && $this->ldap_result)
312      $count = ldap_count_entries($this->conn, $this->ldap_result);
313
314    return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
315  }
316
317
318  /**
319   * Return the last result set
320   *
[6d969b4]321   * @return object rcube_result_set Current resultset or NULL if nothing selected yet
[f115416]322   */
323  function get_result()
324  {
325    return $this->result;
326  }
327 
328 
329  /**
330   * Get a specific contact record
331   *
[6d969b4]332   * @param mixed   Record identifier
333   * @param boolean Return as associative array
334   * @return mixed  Hash array or rcube_result_set with all record fields
[f115416]335   */
336  function get_record($dn, $assoc=false)
337  {
338    $res = null;
339    if ($this->conn && $dn)
340    {
341      $this->ldap_result = @ldap_read($this->conn, base64_decode($dn), "(objectclass=*)", array_values($this->fieldmap));
342      $entry = @ldap_first_entry($this->conn, $this->ldap_result);
343     
344      if ($entry && ($rec = ldap_get_attributes($this->conn, $entry)))
[d1d2c4f]345      {
[f115416]346        $res = $this->_ldap2result($rec);
347        $this->result = new rcube_result_set(1);
348        $this->result->add($res);
[d1d2c4f]349      }
350    }
351
[f115416]352    return $assoc ? $res : $this->result;
353  }
354 
355 
356  /**
357   * Create a new contact record
358   *
[6d969b4]359   * @param array    Hash array with save data
360   * @return boolean The create record ID on success, False on error
[f115416]361   */
362  function insert($save_cols)
363  {
364    // TODO
365    return false;
366  }
367 
368 
369  /**
370   * Update a specific contact record
371   *
372   * @param mixed Record identifier
[6d969b4]373   * @param array Hash array with save data
374   * @return boolean True on success, False on error
[f115416]375   */
376  function update($id, $save_cols)
377  {
378    // TODO   
379    return false;
380  }
381 
382 
383  /**
384   * Mark one or more contact records as deleted
385   *
386   * @param array  Record identifiers
[6d969b4]387   * @return boolean True on success, False on error
[f115416]388   */
389  function delete($ids)
390  {
391    // TODO
392    return false;
393  }
394
395
396  /**
397   * Execute the LDAP search based on the stored credentials
398   *
[6d969b4]399   * @access private
[f115416]400   */
401  function _exec_search()
402  {
403    if ($this->conn && $this->filter)
[d1d2c4f]404    {
[f115416]405      $function = $this->prop['scope'] == 'sub' ? 'ldap_search' : ($this->prop['scope'] == 'base' ? 'ldap_read' : 'ldap_list');
[e3caaf5]406      $this->ldap_result = $function($this->conn, $this->prop['base_dn'], $this->filter, array_values($this->fieldmap), 0, 0);
[f115416]407      return true;
408    }
[d1d2c4f]409    else
[f115416]410      return false;
411  }
412 
413 
414  /**
[6d969b4]415   * @access private
[f115416]416   */
417  function _ldap2result($rec)
418  {
419    $out = array();
420   
421    if ($rec['dn'])
422      $out[$this->primary_key] = base64_encode($rec['dn']);
423   
424    foreach ($this->fieldmap as $rf => $lf)
425    {
426      if ($rec[$lf]['count'])
427        $out[$rf] = $rec[$lf][0];
[d1d2c4f]428    }
[f115416]429   
430    return $out;
431  }
432 
433 
434  /**
[6d969b4]435   * @access private
[f115416]436   */
437  function _map_field($field)
438  {
439    return $this->fieldmap[$field];
[d1d2c4f]440  }
[f115416]441 
442 
443  /**
444   * @static
445   */
446  function quote_string($str)
447  {
448    return strtr($str, array('*'=>'\2a', '('=>'\28', ')'=>'\29', '\\'=>'\5c'));
449  }
450
451
452}
[d1d2c4f]453
[6b603da]454?>
Note: See TracBrowser for help on using the repository browser.