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