Aleksander Machniak
2016-05-16 0b7e26c1bf6bc7a684eb3a214d92d3927306cd8a
commit | author | age
203323 1 <?php
TB 2
a95874 3 /**
203323 4  +-----------------------------------------------------------------------+
TB 5  | Roundcube/rcube_ldap_generic.php                                      |
6  |                                                                       |
7  | This file is part of the Roundcube Webmail client                     |
6ac939 8  | Copyright (C) 2006-2014, The Roundcube Dev Team                       |
83eeec 9  | Copyright (C) 2012-2015, Kolab Systems AG                             |
203323 10  |                                                                       |
TB 11  | Licensed under the GNU General Public License version 3 or            |
12  | any later version with exceptions for skins & plugins.                |
13  | See the README file for a full license statement.                     |
14  |                                                                       |
15  | PURPOSE:                                                              |
16  |   Provide basic functionality for accessing LDAP directories          |
17  |                                                                       |
18  +-----------------------------------------------------------------------+
19  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
20  |         Aleksander Machniak <machniak@kolabsys.com>                   |
21  +-----------------------------------------------------------------------+
22 */
23
24 /**
25  * Model class to access an LDAP directories
26  *
27  * @package    Framework
28  * @subpackage LDAP
29  */
6ac939 30 class rcube_ldap_generic extends Net_LDAP3
203323 31 {
TB 32     /** private properties */
004f86 33     protected $cache = null;
203323 34     protected $attributes = array('dn');
028734 35     protected $error;
203323 36
6ac939 37     function __construct($config = null)
203323 38     {
6ac939 39         parent::__construct($config);
203323 40
6ac939 41         $this->config_set('log_hook', array($this, 'log'));
203323 42     }
TB 43
44     /**
6ac939 45      * Establish a connection to the LDAP server
203323 46      */
TB 47     public function connect($host = null)
48     {
6ac939 49         // Net_LDAP3 does not support IDNA yet
JVM(S 50         // also parse_host() here is very Roundcube specific
51         $host = rcube_utils::idn_to_ascii(rcube_utils::parse_host($host));
203323 52
6ac939 53         return parent::connect($host);
203323 54     }
TB 55
56     /**
57      * Get a specific LDAP entry, identified by its DN
58      *
59      * @param string $dn Record identifier
1b39d9 60      * @param array  $attributes Attributes to return
6ac939 61      *
JVM(S 62      * @return array Hash array
203323 63      */
1b39d9 64     function get_entry($dn, $attributes = array())
203323 65     {
1b39d9 66         return parent::get_entry($dn, !empty($attributes) ? $attributes : $this->attributes);
203323 67     }
TB 68
69     /**
6ac939 70      * Prints debug/error info to the log
203323 71      */
6ac939 72     public function log($level, $msg)
203323 73     {
6ac939 74         $msg = implode("\n", $msg);
203323 75
6ac939 76         switch ($level) {
JVM(S 77         case LOG_DEBUG:
78         case LOG_INFO:
79         case LOG_NOTICE:
80             if ($this->config['debug']) {
81                 rcube::write_log('ldap', $msg);
203323 82             }
6ac939 83             break;
858af7 84
6ac939 85         case LOG_EMERGE:
JVM(S 86         case LOG_ALERT:
87         case LOG_CRIT:
88             rcube::raise_error($msg, true, true);
89             break;
90
91         case LOG_ERR:
92         case LOG_WARNING:
028734 93             $this->error = $msg;
6ac939 94             rcube::raise_error($msg, true, false);
JVM(S 95             break;
858af7 96         }
028734 97     }
TB 98
99     /**
100      * Returns the last LDAP error occurred
101      *
102      * @return mixed Error message string or null if no error occured
103      */
104     function get_error()
105     {
106         return $this->error;
203323 107     }
TB 108
109     /**
6ac939 110      * @deprecated
203323 111      */
6ac939 112     public function set_debug($dbg = true)
203323 113     {
6ac939 114         $this->config['debug'] = (bool) $dbg;
203323 115     }
TB 116
117     /**
6ac939 118      * @deprecated
JVM(S 119      */
120     public function set_cache($cache_engine)
121     {
122         $this->config['cache'] = $cache_engine;
123     }
124
125     /**
126      * @deprecated
127      */
128     public static function scope2func($scope, &$ns_function = null)
129     {
130         return self::scope_to_function($scope, $ns_function);
131     }
132
133     /**
134      * @deprecated
135      */
136     public function set_config($opt, $val = null)
137     {
138         $this->config_set($opt, $val);
139     }
140
141     /**
142      * @deprecated
203323 143      */
TB 144     public function add($dn, $entry)
145     {
6ac939 146         return $this->add_entry($dn, $entry);
203323 147     }
TB 148
149     /**
6ac939 150      * @deprecated
203323 151      */
TB 152     public function delete($dn)
153     {
6ac939 154         return $this->delete_entry($dn);
203323 155     }
TB 156
157     /**
158      * Wrapper for ldap_mod_replace()
159      *
160      * @see ldap_mod_replace()
161      */
162     public function mod_replace($dn, $entry)
163     {
c64bee 164         $this->_debug("C: Replace $dn: ".print_r($entry, true));
203323 165
TB 166         if (!ldap_mod_replace($this->conn, $dn, $entry)) {
028734 167             $this->_error("ldap_mod_replace() failed with " . ldap_error($this->conn));
203323 168             return false;
TB 169         }
170
171         $this->_debug("S: OK");
172         return true;
173     }
174
175     /**
176      * Wrapper for ldap_mod_add()
177      *
178      * @see ldap_mod_add()
179      */
180     public function mod_add($dn, $entry)
181     {
c64bee 182         $this->_debug("C: Add $dn: ".print_r($entry, true));
203323 183
TB 184         if (!ldap_mod_add($this->conn, $dn, $entry)) {
028734 185             $this->_error("ldap_mod_add() failed with " . ldap_error($this->conn));
203323 186             return false;
TB 187         }
188
189         $this->_debug("S: OK");
190         return true;
191     }
192
193     /**
194      * Wrapper for ldap_mod_del()
195      *
196      * @see ldap_mod_del()
197      */
198     public function mod_del($dn, $entry)
199     {
c64bee 200         $this->_debug("C: Delete $dn: ".print_r($entry, true));
203323 201
TB 202         if (!ldap_mod_del($this->conn, $dn, $entry)) {
028734 203             $this->_error("ldap_mod_del() failed with " . ldap_error($this->conn));
203323 204             return false;
TB 205         }
206
207         $this->_debug("S: OK");
208         return true;
209     }
210
211     /**
212      * Wrapper for ldap_rename()
213      *
214      * @see ldap_rename()
215      */
216     public function rename($dn, $newrdn, $newparent = null, $deleteoldrdn = true)
217     {
c64bee 218         $this->_debug("C: Rename $dn to $newrdn");
203323 219
TB 220         if (!ldap_rename($this->conn, $dn, $newrdn, $newparent, $deleteoldrdn)) {
028734 221             $this->_error("ldap_rename() failed with " . ldap_error($this->conn));
203323 222             return false;
TB 223         }
224
225         $this->_debug("S: OK");
226         return true;
227     }
228
229     /**
230      * Wrapper for ldap_list() + ldap_get_entries()
231      *
232      * @see ldap_list()
233      * @see ldap_get_entries()
234      */
235     public function list_entries($dn, $filter, $attributes = array('dn'))
236     {
237         $list = array();
c64bee 238         $this->_debug("C: List $dn [{$filter}]");
203323 239
TB 240         if ($result = ldap_list($this->conn, $dn, $filter, $attributes)) {
241             $list = ldap_get_entries($this->conn, $result);
242
243             if ($list === false) {
028734 244                 $this->_error("ldap_get_entries() failed with " . ldap_error($this->conn));
203323 245                 return array();
TB 246             }
247
248             $count = $list['count'];
249             unset($list['count']);
250
251             $this->_debug("S: $count record(s)");
252         }
253         else {
028734 254             $this->_error("ldap_list() failed with " . ldap_error($this->conn));
203323 255         }
TB 256
257         return $list;
258     }
259
260     /**
261      * Wrapper for ldap_read() + ldap_get_entries()
262      *
263      * @see ldap_read()
264      * @see ldap_get_entries()
265      */
266     public function read_entries($dn, $filter, $attributes = null)
267     {
c64bee 268         $this->_debug("C: Read $dn [{$filter}]");
203323 269
TB 270         if ($this->conn && $dn) {
271             $result = @ldap_read($this->conn, $dn, $filter, $attributes, 0, (int)$this->config['sizelimit'], (int)$this->config['timelimit']);
272             if ($result === false) {
028734 273                 $this->_error("ldap_read() failed with " . ldap_error($this->conn));
203323 274                 return false;
TB 275             }
276
277             $this->_debug("S: OK");
278             return ldap_get_entries($this->conn, $result);
279         }
280
281         return false;
282     }
d8b26e 283
AM 284     /**
285      * Turn an LDAP entry into a regular PHP array with attributes as keys.
286      *
287      * @param array $entry Attributes array as retrieved from ldap_get_attributes() or ldap_get_entries()
1b39d9 288      * @param bool  $flat  Convert one-element-array values into strings (not implemented)
d8b26e 289      *
AM 290      * @return array Hash array with attributes as keys
291      */
1b39d9 292     public static function normalize_entry($entry, $flat = false)
d8b26e 293     {
AM 294         if (!isset($entry['count'])) {
295             return $entry;
296         }
297
298         $rec = array();
299
300         for ($i=0; $i < $entry['count']; $i++) {
301             $attr = $entry[$i];
302             if ($entry[$attr]['count'] == 1) {
303                 switch ($attr) {
304                     case 'objectclass':
305                         $rec[$attr] = array(strtolower($entry[$attr][0]));
306                         break;
307                     default:
308                         $rec[$attr] = $entry[$attr][0];
309                         break;
310                 }
311             }
312             else {
313                 for ($j=0; $j < $entry[$attr]['count']; $j++) {
314                     $rec[$attr][$j] = $entry[$attr][$j];
315                 }
316             }
317         }
318
319         return $rec;
320     }
83eeec 321
TB 322     /**
323      * Compose an LDAP filter string matching all words from the search string
324      * in the given list of attributes.
325      *
326      * @param string  $value    Search value
327      * @param mixed   $attrs    List of LDAP attributes to search
328      * @param int     $mode     Matching mode:
329      *                          0 - partial (*abc*),
330      *                          1 - strict (=),
331      *                          2 - prefix (abc*)
332      * @return string LDAP filter
333      */
334     public static function fulltext_search_filter($value, $attributes, $mode = 1)
335     {
336         if (empty($attributes)) {
337             $attributes = array('cn');
338         }
339
340         $groups = array();
36ee2c 341         $value = str_replace('*', '', $value);
TB 342         $words = $mode == 0 ? rcube_utils::tokenize_string($value, 1) : array($value);
83eeec 343
TB 344         // set wildcards
345         $wp = $ws = '';
346         if ($mode != 1) {
347             $ws = '*';
348             $wp = !$mode ? '*' : '';
349         }
350
351         // search each word in all listed attributes
352         foreach ($words as $word) {
353             $parts = array();
354             foreach ($attributes as $attr) {
355                 $parts[] = "($attr=$wp" . self::quote_string($word) . "$ws)";
356             }
357             $groups[] = '(|' . join('', $parts) . ')';
358         }
359
36ee2c 360         return count($groups) > 1 ? '(&' . join('', $groups) . ')' : join('', $groups);
83eeec 361     }
203323 362 }
6ac939 363
JVM(S 364 // for backward compat.
365 class rcube_ldap_result extends Net_LDAP3_Result {}