Code

Fixed encoding detection of the ldap backend.
[gosa.git] / gosa-core / include / class_ldap.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  * Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
6  * Copyright (C) 1998  Eric Kilfoil <eric@ipass.net>
7  *
8  * ID: $$Id$$
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
25 define("ALREADY_EXISTING_ENTRY",-10001);
26 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
27 define("NO_FILE_UPLOADED",10003);
28 define("INSERT_OK",10000);
29 define("SPECIALS_OVERRIDE", TRUE);
31 class LDAP
32 {
33     public static $characterMap = NULL;
34     public static $characterMapRegFrom = NULL;
35     public static $characterMapRegTo = NULL;
36     public static $readableMapRegFrom = NULL;
37     public static $readableMapRegTo = NULL;
39     var $hascon   =false;
40     var $reconnect=false;
41     var $tls      = false;
42     var $cid;
43     var $hasres   = array();
44     var $sr       = array();
45     var $re       = array();
46     var $basedn   ="";
47     var $start    = array(); // 0 if we are fetching the first entry, otherwise 1
48     var $error    = ""; // Any error messages to be returned can be put here
49     var $srp      = 0;
50     var $objectClasses = array(); // Information read from slapd.oc.conf
51     var $binddn   = "";
52     var $bindpw   = "";
53     var $hostname = "";
54     var $follow_referral = FALSE;
55     var $referrals= array();
56     var $max_ldap_query_time = 0;   // 0, empty or negative values will disable this check 
58     function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
59     {
60         global $config;
61         $this->follow_referral= $follow_referral;
62         $this->tls=$tls;
63         $this->binddn=LDAP::convert($binddn);
65         $this->bindpw=$bindpw;
66         $this->hostname=$hostname;
68         /* Check if MAX_LDAP_QUERY_TIME is defined */ 
69         if(is_object($config) && $config->get_cfg_value("core","ldapMaxQueryTime") != ""){
70             $str = $config->get_cfg_value("core","ldapMaxQueryTime");
71             $this->max_ldap_query_time = (float)($str);
72         }
74         $this->connect();
75     }
78     function getSearchResource()
79     {
80         $this->sr[$this->srp]= NULL;
81         $this->start[$this->srp]= 0;
82         $this->hasres[$this->srp]= false;
83         return $this->srp++;
84     }
87     /* Function to replace all problematic characters inside a DN by \001XX, where
88        \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
89        that this character is inside a DN.
91        Currently used codes:
92        ,   => CO
93        \2C => CO
94        (   => OB
95        )   => CB
96        /   => SL                                                                  
97        \22 => DQ                                                                  */
98     static function convert($dn)
99     {
100         if (SPECIALS_OVERRIDE == TRUE){
101             $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//", "/\\\\22/", '/\\\\"/'),
102                     array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL", "\001DQ", "\001DQ"),
103                     $dn);
104             return (preg_replace('/,\s+/', ',', $tmp));
105         } else {
106             return ($dn);
107         }
108     }
111     /* \brief     Tests for the special-char handling of the currently used ldap database 
112      *             and updates the LDAP class correspondingly.
113      *            This affects the LDAP::fix function and allows us to write 
114      *             dns containing  , " ( )
115      */
116     static function updateSpecialCharHandling()
117     {
118         global $config;
120         // Load results from the session if they exists.
121         if(session::is_set('LDAP::updateSpecialCharHandling')){
122             $data = session::get('LDAP::updateSpecialCharHandling');
123             $attrs = array("characterMap", "characterMapRegFrom", "characterMapRegTo", "readableMapRegFrom", "readableMapRegTo");
124             foreach($attrs as $attr){
125                 LDAP::$$attr = $data[$attr];
126             }
127             
128             $attrs = array(implode(LDAP::$characterMapRegFrom,', '),implode(LDAP::$characterMapRegTo,', '));
129             @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,$attrs,"(Cached) Detected special-char handling for LDAP actions");
130             return;
131         }
133         // Set a default character handling.
134         LDAP::$characterMapRegFrom = array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/", "/\001DQ/");
135         LDAP::$characterMapRegTo = array("\,", "(", ")", "/", '\"');
137         // Set a default mapping to make readable strings out of dns.
138         LDAP::$readableMapRegFrom = array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/", "/\001DQ/",
139                 "/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//", "/\\\\22/", '/\\\\"/');
140         LDAP::$readableMapRegTo = array(",", "(", ")", "/", '"',",", ",", "(", ")", "/", '"', '"');
142         if(LDAP::$characterMap === NULL){
143             LDAP::$characterMap = detectLdapSpecialCharHandling();
145             // Check if character-detection was successfull, if it wasn't use a fallback.
146             if(LDAP::$characterMap !== NULL){
147                 LDAP::$characterMapRegFrom = array();
148                 LDAP::$characterMapRegTo = array();
149                 LDAP::$readableMapRegFrom = array();
150                 LDAP::$readableMapRegTo = array();
151                 foreach(LDAP::$characterMap as $from => $to){
152                    
153                     // Append entry character conversion  
154                     LDAP::$characterMapRegFrom[] = "/".preg_quote(LDAP::convert($from),'/')."/";
155                     LDAP::$characterMapRegTo[] = addslashes($to);
157                     // Append entry make readable array
158                     LDAP::$readableMapRegFrom[] = "/".preg_quote($to,'/')."/";
159                     LDAP::$readableMapRegFrom[] = "/".preg_quote(LDAP::convert($to),'/')."/";
160                     LDAP::$readableMapRegTo[] = stripslashes($from);
161                     LDAP::$readableMapRegTo[] = stripslashes($from);
162                 }
163             }else{
164                 // To avoid querying a hundred times without any success, stop here.
165                 LDAP::$characterMap = array();
166             }
167         }
168         
169         // Store results in the session.
170         $attrs = array("characterMap", "characterMapRegFrom", "characterMapRegTo", "readableMapRegFrom", "readableMapRegTo");
171         $data = array();
172         foreach($attrs as $attr){
173             $data[$attr] = LDAP::$$attr;
174         }
175         session::set("LDAP::updateSpecialCharHandling", $data);
177         $attrs = array(implode(LDAP::$characterMapRegFrom,', '),implode(LDAP::$characterMapRegTo,', '));
178         @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,$attrs,"Detected special-char handling for LDAP actions");
179     }
182     /*!  \brief     This methods replaces non-readable characters be readable ones, 
183      *               depends on what were detect in 'updateSpecialCharHandling'.
184      * @param       String  The DN/string to cleanup 
185      * @return      String  The readable string.
186      */
187     static function makeReadable($dn)
188     {
189         if(LDAP::$characterMap === NULL) LDAP::updateSpecialCharHandling();
190         return (preg_replace(LDAP::$readableMapRegFrom,LDAP::$readableMapRegTo,$dn));
191     }
194     /* \brief   Function to fix all problematic characters inside a DN by replacing \001XX
195      *           codes to their original values. See "convert" for more information. 
196      *          The ',' characters are always expanded to \, (not \2C), since all tested LDAP
197      *           servers seem to take it the correct way.                                 
198      * @param String  The DN to convert characters in. 
199      * @param String  The converted dn.
200      */
201     static function fix($dn)
202     {
203         if (SPECIALS_OVERRIDE == TRUE){
205             // Update the conversion instruction set.
206             if(LDAP::$characterMap === NULL) LDAP::updateSpecialCharHandling();
208             return (preg_replace(LDAP::$characterMapRegFrom,LDAP::$characterMapRegTo,$dn));
209         } else {
210             return ($dn);
211         }
212     }
215     /* Function to fix problematic characters in DN's that are used for search
216        requests. I.e. member=....                                               */
217     static function prepare4filter($dn)
218     {
219         $fixed= normalizeLdap(str_replace('\\\\', '\\\\\\', LDAP::fix($dn)));
220         return str_replace('\\,', '\\\\,', $fixed);
221     }
224     function connect()
225     {
226         $this->hascon=false;
227         $this->reconnect=false;
228         if ($this->cid= @ldap_connect($this->hostname)) {
229             @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
230             if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
231                 @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
232                 @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
233             }
234             if (function_exists("ldap_start_tls") && $this->tls){
235                 @ldap_start_tls($this->cid);
236             }
238             $this->error = "No Error";
239             if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
240                 $this->error = "Success";
241                 $this->hascon=true;
242             } else {
243                 if ($this->reconnect){
244                     if ($this->error != "Success"){
245                         $this->error = "Could not rebind to " . $this->binddn;
246                     }
247                 } else {
248                     $this->error = "Could not bind to " . $this->binddn;
249                 }
250             }
251         } else {
252             $this->error = "Could not connect to LDAP server";
253         }
254     }
256     function rebind($ldap, $referral)
257     {
258         $credentials= $this->get_credentials($referral);
259         if (@ldap_bind($ldap, LDAP::fix($credentials['ADMINDN']), $credentials['ADMINPASSWORD'])) {
260             $this->error = "Success";
261             $this->hascon=true;
262             $this->reconnect= true;
263             return (0);
264         } else {
265             $this->error = "Could not bind to " . $credentials['ADMINDN'];
266             return NULL;
267         }
268     }
270     function reconnect()
271     {
272         if ($this->reconnect){
273             @ldap_unbind($this->cid);
274             $this->cid = NULL;
275         }
276     }
278     function unbind()
279     {
280         @ldap_unbind($this->cid);
281         $this->cid = NULL;
282     }
284     function disconnect()
285     {
286         if($this->hascon){
287             @ldap_close($this->cid);
288             $this->hascon=false;
289         }
290     }
292     function cd($dir)
293     {
294         if ($dir == ".."){
295             $this->basedn = $this->getParentDir();
296         } else {
297             $this->basedn = LDAP::convert($dir);
298         }
299     }
301     function getParentDir($basedn = "")
302     {
303         if ($basedn==""){
304             $basedn = $this->basedn;
305         } else {
306             $basedn = LDAP::convert($basedn);
307         }
308         return(preg_replace("/[^,]*[,]*[ ]*(.*)/", "$1", $basedn));
309     }
312     function search($srp, $filter, $attrs= array())
313     {
314         if($this->hascon){
315             if ($this->reconnect) $this->connect();
317             $start = microtime(true);
318             $this->clearResult($srp);
319             $this->sr[$srp] = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
320             $this->error = @ldap_error($this->cid);
321             $this->resetResult($srp);
322             $this->hasres[$srp]=true;
324             /* Check if query took longer as specified in max_ldap_query_time */
325             if($this->max_ldap_query_time){
326                 $diff = microtime(true) - $start;
327                 if($diff > $this->max_ldap_query_time){
328                     msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took %.2fs!"), $diff), WARNING_DIALOG);
329                 }
330             }
332             $this->log("LDAP operation: time=".(microtime(true)-$start)." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
334             // Create statistic table entry 
335             stats::log('ldap', $class = get_class($this), $category = array(),  $action = __FUNCTION__, 
336                     $amount = 1, $duration = (microtime(TRUE) - $start));
337             return($this->sr[$srp]);
338         }else{
339             $this->error = "Could not connect to LDAP server";
340             return("");
341         }
342     }
344     function ls($srp, $filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
345     {
346         if($this->hascon){
347             if ($this->reconnect) $this->connect();
349             $this->clearResult($srp);
350             if ($basedn == "")
351                 $basedn = $this->basedn;
352             else
353                 $basedn= LDAP::convert($basedn);
355             $start = microtime(true);
356             $this->sr[$srp] = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
357             $this->error = @ldap_error($this->cid);
358             $this->resetResult($srp);
359             $this->hasres[$srp]=true;
361             /* Check if query took longer as specified in max_ldap_query_time */
362             if($this->max_ldap_query_time){
363                 $diff = microtime(true) - $start;
364                 if($diff > $this->max_ldap_query_time){
365                     msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took %.2fs!"), $diff), WARNING_DIALOG);
366                 }
367             }
369             $this->log("LDAP operation: time=".(microtime(true) - $start)." operation=ls('".LDAP::fix($basedn)."', '$filter')");
371             // Create statistic table entry 
372             stats::log('ldap', $class = get_class($this), $category = array(),  $action = __FUNCTION__, 
373                     $amount = 1, $duration = (microtime(TRUE) - $start));
375             return($this->sr[$srp]);
376         }else{
377             $this->error = "Could not connect to LDAP server";
378             return("");
379         }
380     }
382     function cat($srp, $dn,$attrs= array("*"), $filter = "(objectclass=*)")
383     {
384         if($this->hascon){
385             if ($this->reconnect) $this->connect();
387             $this->clearResult($srp);
388             $this->sr[$srp] = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
389             $this->error = @ldap_error($this->cid);
390             $this->resetResult($srp);
391             $this->hasres[$srp]=true;
392             return($this->sr[$srp]);
393         }else{
394             $this->error = "Could not connect to LDAP server";
395             return("");
396         }
397     }
399     function object_match_filter($dn,$filter)
400     {
401         if($this->hascon){
402             if ($this->reconnect) $this->connect();
403             $res =  @ldap_read($this->cid, LDAP::fix($dn), $filter, array("objectClass"));
404             $rv =   @ldap_count_entries($this->cid, $res);
405             return($rv);
406         }else{
407             $this->error = "Could not connect to LDAP server";
408             return(FALSE);
409         }
410     }
412     function set_size_limit($size)
413     {
414         /* Ignore zero settings */
415         if ($size == 0){
416             @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
417         }
418         if($this->hascon){
419             @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
420         } else {
421             $this->error = "Could not connect to LDAP server";
422         }
423     }
425     function fetch($srp)
426     {
427         $att= array();
428         if($this->hascon){
429             if($this->hasres[$srp]){
430                 if ($this->start[$srp] == 0)
431                 {
432                     if ($this->sr[$srp]){
433                         $this->start[$srp] = 1;
434                         $this->re[$srp]= @ldap_first_entry($this->cid, $this->sr[$srp]);
435                     } else {
436                         return array();
437                     }
438                 } else {
439                     $this->re[$srp]= @ldap_next_entry($this->cid, $this->re[$srp]);
440                 }
441                 if ($this->re[$srp])
442                 {
443                     $att= @ldap_get_attributes($this->cid, $this->re[$srp]);
444                     $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re[$srp])));
445                 }
446                 $this->error = @ldap_error($this->cid);
447                 if (!isset($att)){
448                     $att= array();
449                 }
450                 return($att);
451             }else{
452                 $this->error = "Perform a fetch with no search";
453                 return("");
454             }
455         }else{
456             $this->error = "Could not connect to LDAP server";
457             return("");
458         }
459     }
461     function resetResult($srp)
462     {
463         $this->start[$srp] = 0;
464     }
466     function clearResult($srp)
467     {
468         if($this->hasres[$srp]){
469             $this->hasres[$srp] = false;
470             @ldap_free_result($this->sr[$srp]);
471         }
472     }
474     function getDN($srp)
475     {
476         if($this->hascon){
477             if($this->hasres[$srp]){
479                 if(!$this->re[$srp])
480                 {
481                     $this->error = "Perform a Fetch with no valid Result";
482                 }
483                 else
484                 {
485                     $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
487                     $this->error = @ldap_error($this->cid);
488                     return(trim(LDAP::convert($rv)));
489                 }
490             }else{
491                 $this->error = "Perform a Fetch with no Search";
492                 return("");
493             }
494         }else{
495             $this->error = "Could not connect to LDAP server";
496             return("");
497         }
498     }
500     function count($srp)
501     {
502         if($this->hascon){
503             if($this->hasres[$srp]){
504                 $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
505                 $this->error = @ldap_error($this->cid);
506                 return($rv);
507             }else{
508                 $this->error = "Perform a Fetch with no Search";
509                 return("");
510             }
511         }else{
512             $this->error = "Could not connect to LDAP server";
513             return("");
514         }
515     }
517     function rm($attrs = "", $dn = "")
518     {
519         if($this->hascon){
520             if ($this->reconnect) $this->connect();
521             if ($dn == "")
522                 $dn = $this->basedn;
524             $r = ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
525             $this->error = @ldap_error($this->cid);
526             return($r);
527         }else{
528             $this->error = "Could not connect to LDAP server";
529             return("");
530         }
531     }
533     function mod_add($attrs = "", $dn = "")
534     {
535         if($this->hascon){
536             if ($this->reconnect) $this->connect();
537             if ($dn == "")
538                 $dn = $this->basedn;
540             $r = @ldap_mod_add($this->cid, LDAP::fix($dn), $attrs);
541             $this->error = @ldap_error($this->cid);
542             return($r);
543         }else{
544             $this->error = "Could not connect to LDAP server";
545             return("");
546         }
547     }
549     function rename($attrs, $dn = "")
550     {
551         if($this->hascon){
552             if ($this->reconnect) $this->connect();
553             if ($dn == "")
554                 $dn = $this->basedn;
556             $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
557             $this->error = @ldap_error($this->cid);
558             return($r);
559         }else{
560             $this->error = "Could not connect to LDAP server";
561             return("");
562         }
563     }
565     function rmdir($deletedn)
566     {
567         if($this->hascon){
568             if ($this->reconnect) $this->connect();
569             $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
570             $this->error = @ldap_error($this->cid);
571             return($r ? $r : 0);
572         }else{
573             $this->error = "Could not connect to LDAP server";
574             return("");
575         }
576     }
579     /*! \brief Move the given Ldap entry from $source to $dest
580       @param  String  $source The source dn.
581       @param  String  $dest   The destination dn.
582       @return Boolean TRUE on success else FALSE.
583      */
584     function rename_dn($source,$dest)
585     {
586         /* Check if source and destination are the same entry */
587         if(strtolower($source) == strtolower($dest)){
588             trigger_error("Source and destination can't be the same entry.");
589             $this->error = "Source and destination can't be the same entry.";
590             return(FALSE);
591         }
593         /* Check if destination entry exists */    
594         if($this->dn_exists($dest)){
595             trigger_error("Destination '$dest' already exists.");
596             $this->error = "Destination '$dest' already exists.";
597             return(FALSE);
598         }
600         /* Extract the name and the parent part out ouf source dn.
601            e.g.  cn=herbert,ou=department,dc=... 
602            parent   =>  ou=department,dc=...
603            dest_rdn =>  cn=herbert
604          */
605         $parent   = preg_replace("/^[^,]+,/","", $dest);
606         $dest_rdn = preg_replace("/,.*$/","",$dest);
608         if($this->hascon){
609             if ($this->reconnect) $this->connect();
610             $r= ldap_rename($this->cid,@LDAP::fix($source), @LDAP::fix($dest_rdn),@LDAP::fix($parent),TRUE); 
611             $this->error = ldap_error($this->cid);
613             /* Check if destination dn exists, if not the 
614                server may not support this operation */
615             $r &= is_resource($this->dn_exists($dest));
616             return($r);
617         }else{
618             $this->error = "Could not connect to LDAP server";
619             return(FALSE);
620         }
621     }
624     /**
625      *  Function rmdir_recursive
626      *
627      *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
628      *  Parameters:  The dn to delete
629      *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
630      *
631      */
632     function rmdir_recursive($srp, $deletedn)
633     {
634         if($this->hascon){
635             if ($this->reconnect) $this->connect();
636             $delarray= array();
638             /* Get sorted list of dn's to delete */
639             $this->ls ($srp, "(objectClass=*)",$deletedn);
640             while ($this->fetch($srp)){
641                 $deldn= $this->getDN($srp);
642                 $delarray[$deldn]= strlen($deldn);
643             }
644             arsort ($delarray);
645             reset ($delarray);
647             /* Really Delete ALL dn's in subtree */
648             foreach ($delarray as $key => $value){
649                 $this->rmdir_recursive($srp, $key);
650             }
652             /* Finally Delete own Node */
653             $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
654             $this->error = @ldap_error($this->cid);
655             return($r ? $r : 0);
656         }else{
657             $this->error = "Could not connect to LDAP server";
658             return("");
659         }
660     }
662     function makeReadableErrors($error,$attrs)
663     { 
664         global $config;
666         if($this->success()) return("");
668         $str = "";
669         if(preg_match("/^objectClass: value #([0-9]*) invalid per syntax$/", $this->get_additional_error())){
670             $oc = preg_replace("/^objectClass: value #([0-9]*) invalid per syntax$/","\\1", $this->get_additional_error());
671             if(isset($attrs['objectClass'][$oc])){
672                 $str.= " - <b>objectClass: ".$attrs['objectClass'][$oc]."</b>";
673             }
674         }
675         if($error == "Undefined attribute type"){
676             $str = " - <b>attribute: ".preg_replace("/:.*$/","",$this->get_additional_error())."</b>";
677         } 
679         @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,$attrs,"Erroneous data");
681         return($str);
682     }
684     function modify($attrs)
685     {
686         if(count($attrs) == 0){
687             return (0);
688         }
689         if($this->hascon){
690             $start = microtime(TRUE);
691             if ($this->reconnect) $this->connect();
692             $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
693             $this->error = @ldap_error($this->cid);
694             if(!$this->success()){
695                 $this->error.= $this->makeReadableErrors($this->error,$attrs);
696             }
698             // Create statistic table entry 
699             stats::log('ldap', $class = get_class($this), $category = array(),  $action = __FUNCTION__, 
700                     $amount = 1, $duration = (microtime(TRUE) - $start));
701             return($r ? $r : 0);
702         }else{
703             $this->error = "Could not connect to LDAP server";
704             return("");
705         }
706     }
708     function add($attrs)
709     {
710         if($this->hascon){
711             $start = microtime(TRUE);
712             if ($this->reconnect) $this->connect();
713             $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
714             $this->error = @ldap_error($this->cid);
715             if(!$this->success()){
716                 $this->error.= $this->makeReadableErrors($this->error,$attrs);
717             }
719             // Create statistic table entry 
720             stats::log('ldap', $class = get_class($this), $category = array(),  $action = __FUNCTION__, 
721                     $amount = 1, $duration = (microtime(TRUE) - $start));
723             return($r ? $r : 0);
724         }else{
725             $this->error = "Could not connect to LDAP server";
726             return("");
727         }
728     }
730     function create_missing_trees($srp, $target)
731     {
732         global $config;
734         $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
736         if ($target == $this->basedn){
737             $l= array("dummy");
738         } else {
739             $l= array_reverse(gosa_ldap_explode_dn($real_path));
740         }
741         unset($l['count']);
742         $cdn= $this->basedn;
743         $tag= "";
745         /* Load schema if available... */
746         $classes= $this->get_objectclasses();
748         foreach ($l as $part){
749             if ($part != "dummy"){
750                 $cdn= "$part,$cdn";
751             }
753             /* Ignore referrals */
754             $found= false;
755             foreach($this->referrals as $ref){
756                 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URI']);
757                 if ($base == $cdn){
758                     $found= true;
759                     break;
760                 }
761             }
762             if ($found){
763                 continue;
764             }
766             $this->cat ($srp, $cdn);
767             $attrs= $this->fetch($srp);
769             /* Create missing entry? */
770             if (count ($attrs)){
772                 /* Catch the tag - if present */
773                 if (isset($attrs['gosaUnitTag'][0])){
774                     $tag= $attrs['gosaUnitTag'][0];
775                 }
777             } else {
778                 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
779                 $param= LDAP::fix(preg_replace('/^[^=]+=([^,]+).*$/', '\\1', $cdn));
780                 $param=preg_replace(array('/\\\\,/','/\\\\"/'),array(',','"'),$param);
782                 $na= array();
784                 /* Automatic or traditional? */
785                 if(count($classes)){
787                     /* Get name of first matching objectClass */
788                     $ocname= "";
789                     foreach($classes as $class){
790                         if (isset($class['MUST']) && in_array($type, $class['MUST'])){
792                             /* Look for first classes that is structural... */
793                             if (isset($class['STRUCTURAL'])){
794                                 $ocname= $class['NAME'];
795                                 break;
796                             }
798                             /* Look for classes that are auxiliary... */
799                             if (isset($class['AUXILIARY'])){
800                                 $ocname= $class['NAME'];
801                             }
802                         }
803                     }
805                     /* Bail out, if we've nothing to do... */
806                     if ($ocname == ""){
807                         msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN %s: no object class found"), bold($type)), FATAL_ERROR_DIALOG);
808                         exit();
809                     }
811                     /* Assemble_entry */
812                     if ($tag != ""){
813                         $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
814                         $na["gosaUnitTag"]= $tag;
815                     } else {
816                         $na['objectClass']= array($ocname);
817                     }
818                     if (isset($classes[$ocname]['AUXILIARY'])){
819                         $na['objectClass'][]= $classes[$ocname]['SUP'];
820                     }
821                     if ($type == "dc"){
822                         /* This is bad actually, but - tell me a better way? */
823                         $na['objectClass'][]= 'locality';
824                     }
825                     $na[$type]= $param;
827                     // Fill in MUST values - but do not overwrite existing ones.
828                     if (is_array($classes[$ocname]['MUST'])){
829                         foreach($classes[$ocname]['MUST'] as $attr){
830                             if(isset($na[$attr]) && !empty($na[$attr])) continue;
831                             $na[$attr]= "filled";
832                         }
833                     }
835                 } else {
837                     /* Use alternative add... */
838                     switch ($type){
839                         case 'ou':
840                             if ($tag != ""){
841                                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
842                                 $na["gosaUnitTag"]= $tag;
843                             } else {
844                                 $na["objectClass"]= "organizationalUnit";
845                             }
846                             $na["ou"]= $param;
847                             break;
848                         case 'dc':
849                             if ($tag != ""){
850                                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
851                                 $na["gosaUnitTag"]= $tag;
852                             } else {
853                                 $na["objectClass"]= array("dcObject", "top", "locality");
854                             }
855                             $na["dc"]= $param;
856                             break;
857                         default:
858                             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN %s: not supported"), bold($type)), FATAL_ERROR_DIALOG);
859                             exit();
860                     }
862                 }
863                 $this->cd($cdn);
864                 $this->add($na);
866                 if (!$this->success()){
868                     print_a(array($cdn,$na));
870                     msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()));
871                     return FALSE;
872                 }
873             }
874         }
876         return TRUE;
877     }
880     function recursive_remove($srp)
881     {
882         $delarray= array();
884         /* Get sorted list of dn's to delete */
885         $this->search ($srp, "(objectClass=*)");
886         while ($this->fetch($srp)){
887             $deldn= $this->getDN($srp);
888             $delarray[$deldn]= strlen($deldn);
889         }
890         arsort ($delarray);
891         reset ($delarray);
893         /* Delete all dn's in subtree */
894         foreach ($delarray as $key => $value){
895             $this->rmdir($key);
896         }
897     }
900     function get_attribute($dn, $name,$r_array=0)
901     {
902         $data= "";
903         if ($this->reconnect) $this->connect();
904         $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
906         /* fill data from LDAP */
907         if ($sr) {
908             $ei= @ldap_first_entry($this->cid, $sr);
909             if ($ei) {
910                 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
911                     $data= $info[0];
912                 }
913             }
914         }
915         if($r_array==0) {
916             return ($data);
917         } else {
918             return ($info);
919         }
920     }
924     function get_additional_error()
925     {
926         $error= "";
927         @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
928         return ($error);
929     }
932     function success()
933     {
934         return (preg_match('/Success/i', $this->error));
935     }
938     function get_error()
939     {
940         if ($this->error == 'Success'){
941             return $this->error;
942         } else {
943             $adderror= $this->get_additional_error();
944             if ($adderror != ""){
945                 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on %s using LDAP server %s"), bold($this->basedn), bold($this->hostname)).")";
946             } else {
947                 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), bold($this->hostname)).")";
948             }
949             return $error;
950         }
951     }
953     function get_credentials($url, $referrals= NULL)
954     {
955         $ret= array();
956         $url= preg_replace('!\?\?.*$!', '', $url);
957         $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
959         if ($referrals === NULL){
960             $referrals= $this->referrals;
961         }
963         if (isset($referrals[$server])){
964             return ($referrals[$server]);
965         } else {
966             $ret['ADMINDN']= LDAP::fix($this->binddn);
967             $ret['ADMINPASSWORD']= $this->bindpw;
968         }
970         return ($ret);
971     }
974     /*! \brief  Generates an ldif for all entries matching the filter settings, scope and limit.
975      *  @param  $dn           The entry to export.
976      *  @param  $filter       Limit the exported object to those maching this filter.
977      *  @param  $scope        'base', 'sub' .. see manpage for 'ldapmodify' for details.
978      *  @param  $limit        Limits the result.
979      */
980     function generateLdif ($dn, $filter= "(objectClass=*)", $scope = 'sub', $limit=0)
981     {
982         // Ensure that limit is numeric if not skip here.
983         if(!empty($limit) && !is_numeric($limit)){
984             trigger_error(sprintf("Invalid parameter for limit '%s', a numeric value is required."), $limit);
985             return(NULL);
986         }
987         $limit = (!$limit)?'':' -z '.$limit;
989         // Check scope values
990         $scope = trim($scope);
991         if(!empty($scope) && !in_array($scope, array('base', 'one', 'sub', 'children'))){
992             trigger_error(sprintf("Invalid parameter for scope '%s', please use 'base', 'one', 'sub' or 'children'."), $scope);
993             return(NULL);
994         }
995         $scope = (!empty($scope))?' -s '.$scope: '';
997         // First check if we are able to call 'ldapsearch' on the command line.
998         $check = shell_exec('which ldapsearch');
999         if(empty($check)){
1000             $this->error = sprintf(_("Command line programm %s is missing!"), bold('ldapsearch'));
1001             return(NULL);
1002         }
1004         // Prepare parameters to be valid for shell execution
1005         $dn = escapeshellarg($dn);
1006         $pwd = $this->bindpw;
1007         $host = escapeshellarg($this->hostname);
1008         $admin = escapeshellarg($this->binddn);
1009         $filter = escapeshellarg($filter);
1010         $cmd = "ldapsearch -x -LLLL -D {$admin} {$filter} {$limit} {$scope} -H {$host} -b {$dn} -W ";
1012         // Create list of process pipes  
1013         $descriptorspec = array(
1014                 0 => array("pipe", "r"),  // stdin
1015                 1 => array("pipe", "w"),  // stdout
1016                 2 => array("pipe", "w")); // stderr
1018         // Try to open the process 
1019         $process = proc_open($cmd, $descriptorspec, $pipes);
1020         if (is_resource($process)) {
1022             // Write the password to stdin
1023             fwrite($pipes[0], $pwd);
1024             fclose($pipes[0]);
1026             // Get results from stdout and stderr
1027             $res = stream_get_contents($pipes[1]);
1028             $err = stream_get_contents($pipes[2]);
1029             fclose($pipes[1]);
1031             // Close the process and check its return value
1032             if(proc_close($process) != 0){
1033                 $this->error = $err;
1034                 return(NULL);
1035             }
1036         }
1037         return($res);
1038     }
1041     function gen_xls ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1042     {
1043         $display= array();
1045         $this->cd($dn);
1046         $this->search($srp, "$filter");
1048         $i=0;
1049         while ($attrs= $this->fetch($srp)){
1050             $j=0;
1052             foreach ($attributes as $at){
1053                 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1054                 $j++;
1055             }
1057             $i++;
1058         }
1060         return ($display);
1061     }
1064     function dn_exists($dn)
1065     {
1066         return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
1067     }
1071     /*  This funktion imports ldifs 
1073         If DeleteOldEntries is true, the destination entry will be deleted first. 
1074         If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
1075         if JustMofify id false the destination dn will be overwritten by the new ldif. 
1076      */
1078     function import_complete_ldif($srp, $str_attr,$error,$JustModify,$DeleteOldEntries)
1079     {
1080         if($this->reconnect) $this->connect();
1082         /* First we have to split the string into empty lines.
1083            An empty line indicates an new Entry */
1084         $entries = preg_split("/\n/",$str_attr);
1086         $data = "";
1087         $cnt = 0; 
1088         $current_line = 0;
1090         /* FIX ldif */
1091         $last = "";
1092         $tmp  = "";
1093         $i = 0;
1094         foreach($entries as $entry){
1095             if(preg_match("/^ /",$entry)){
1096                 $tmp[$i] .= trim($entry);
1097             }else{
1098                 $i ++;
1099                 $tmp[$i] = trim($entry);
1100             }
1101         }
1103         /* Every single line ... */
1104         foreach($tmp as $entry) {
1105             $current_line ++;
1107             /* Removing Spaces to .. 
1108                .. test if a new entry begins */
1109             $tmp  = str_replace(" ","",$data );
1111             /* .. prevent empty lines in an entry */
1112             $tmp2 = str_replace(" ","",$entry);
1114             /* If the Block ends (Empty Line) */
1115             if((empty($entry))&&(!empty($tmp))) {
1116                 /* Add collected lines as a complete block */
1117                 $all[$cnt] = $data;
1118                 $cnt ++;
1119                 $data ="";
1120             } else {
1122                 /* Append lines ... */
1123                 if(!empty($tmp2)) {
1124                     /* check if we need base64_decode for this line */
1125                     if(strstr($tmp2, "::") !== false)
1126                     {
1127                         $encoded = explode("::",$entry);
1128                         $attr  = trim($encoded[0]);
1129                         $value = base64_decode(trim($encoded[1]));
1130                         /* Add linenumber */
1131                         $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
1132                     }
1133                     else
1134                     {
1135                         /* Add Linenumber */ 
1136                         $data .= $current_line."#".base64_encode($entry)."\n";
1137                     }
1138                 }
1139             }
1140         }
1142         /* The Data we collected is not in the array all[];
1143            For example the Data is stored like this..
1145            all[0] = "1#dn : .... \n 
1146            2#ObjectType: person \n ...."
1148            Now we check every insertblock and try to insert */
1149         foreach ( $all as $single) {
1150             $lineone = preg_split("/\n/",$single);  
1151             $ndn = explode("#", $lineone[0]);
1152             $line = base64_decode($ndn[1]);
1154             $dnn = explode (":",$line,2);
1155             $current_line = $ndn[0];
1156             $dn    = $dnn[0];
1157             $value = $dnn[1];
1159             /* Every block must begin with a dn */
1160             if($dn != "dn") {
1161                 $error= sprintf(_("Invalid DN %s: block to be imported should start with 'dn: ...' in line %s"), bold($line), bold($current_line));
1162                 return -2;  
1163             }
1165             /* Should we use Modify instead of Add */
1166             $usemodify= false;
1168             /* Delete before insert */
1169             $usermdir= false;
1171             /* The dn address already exists, Don't delete destination entry, overwrite it */
1172             if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1174                 $usermdir = $usemodify = false;
1176                 /* Delete old entry first, then add new */
1177             } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1179                 /* Delete first, then add */
1180                 $usermdir = true;        
1182             } elseif(($this->dn_exists($value))&&($JustModify)) {
1184                 /* Modify instead of Add */
1185                 $usemodify = true;
1186             }
1188             /* If we can't Import, return with a file error */
1189             if(!$this->import_single_entry($srp, $single,$usemodify,$usermdir) ) {
1190                 $error= sprintf(_("Error while importing DN %s: please check LDIF from line %s on!"), bold($line),
1191                         $current_line);
1192                 return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1193         }
1195         return (INSERT_OK);
1196     }
1199     /* Imports a single entry 
1200        If $delete is true;  The old entry will be deleted if it exists.
1201        if $modify is true;  All variables that are not touched by the new ldif will be kept.
1202        if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1203      */
1204     function import_single_entry($srp, $str_attr,$modify,$delete)
1205     {
1206         global $config;
1208         if(!$config){
1209             trigger_error("Can't import ldif, can't read config object.");
1210         }
1213         if($this->reconnect) $this->connect();
1215         $ret = false;
1216         $rows= preg_split("/\n/",$str_attr);
1217         $data= false;
1219         foreach($rows as $row) {
1221             /* Check if we use Linenumbers (when import_complete_ldif is called we use
1222                Linenumbers) Linenumbers are use like this 123#attribute : value */
1223             if(!empty($row)) {
1224                 if(strpos($row,"#")!=FALSE) {
1226                     /* We are using line numbers 
1227                        Because there is a # before a : */
1228                     $tmp1= explode("#",$row);
1229                     $current_line= $tmp1[0];
1230                     $row= base64_decode($tmp1[1]);
1231                 }
1233                 /* Split the line into  attribute  and value */
1234                 $attr   = explode(":", $row,2);
1235                 $attr[0]= trim($attr[0]);  /* attribute */
1236                 $attr[1]= $attr[1];  /* value */
1238                 /* Check :: was used to indicate base64_encoded strings */
1239                 if($attr[1][0] == ":"){
1240                     $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1241                     $attr[1]=base64_decode($attr[1]);
1242                 }
1244                 $attr[1] = trim($attr[1]);
1246                 /* Check for attributes that are used more than once */
1247                 if(!isset($data[$attr[0]])) {
1248                     $data[$attr[0]]=$attr[1];
1249                 } else {
1250                     $tmp = $data[$attr[0]];
1252                     if(!is_array($tmp)) {
1253                         $new[0]=$tmp;
1254                         $new[1]=$attr[1];
1255                         $datas[$attr[0]]['count']=1;             
1256                         $data[$attr[0]]=$new;
1257                     } else {
1258                         $cnt = $datas[$attr[0]]['count'];           
1259                         $cnt ++;
1260                         $data[$attr[0]][$cnt]=$attr[1];
1261                         $datas[$attr[0]]['count'] = $cnt;
1262                     }
1263                 }
1264             }
1265         }
1267         /* If dn is an index of data, we should try to insert the data */
1268         if(isset($data['dn'])) {
1270             /* Fix dn */
1271             $tmp = gosa_ldap_explode_dn($data['dn']);
1272             unset($tmp['count']);
1273             $newdn ="";
1274             foreach($tmp as $tm){
1275                 $newdn.= trim($tm).",";
1276             }
1277             $newdn = preg_replace("/,$/","",$newdn);
1278             $data['dn'] = $newdn;
1280             /* Creating Entry */
1281             $this->cd($data['dn']);
1283             /* Delete existing entry */
1284             if($delete){
1285                 $this->rmdir_recursive($srp, $data['dn']);
1286             }
1288             /* Create missing trees */
1289             $this->cd ($this->basedn);
1290             $this->cd($config->current['BASE']);
1291             $this->create_missing_trees($srp, preg_replace("/^[^,]+,/","",$data['dn']));
1292             $this->cd($data['dn']);
1294             $dn = $data['dn'];
1295             unset($data['dn']);
1297             if(!$modify){
1299                 $this->cat($srp, $dn);
1300                 if($this->count($srp)){
1302                     /* The destination entry exists, overwrite it with the new entry */
1303                     $attrs = $this->fetch($srp);
1304                     foreach($attrs as $name => $value ){
1305                         if(!is_numeric($name)){
1306                             if(in_array($name,array("dn","count"))) continue;
1307                             if(!isset($data[$name])){
1308                                 $data[$name] = array();
1309                             }
1310                         }
1311                     }
1312                     $ret = $this->modify($data);
1314                 }else{
1316                     /* The destination entry doesn't exists, create it */
1317                     $ret = $this->add($data);
1318                 }
1320             } else {
1322                 /* Keep all vars that aren't touched by this ldif */
1323                 $ret = $this->modify($data);
1324             }
1325         }
1327         if (!$this->success()){
1328             msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $dn, "", get_class()));
1329         }
1331         return($ret);
1332     }
1335     function importcsv($str)
1336     {
1337         $lines = preg_split("/\n/",$str);
1338         foreach($lines as $line)
1339         {
1340             /* continue if theres a comment */
1341             if(substr(trim($line),0,1)=="#"){
1342                 continue;
1343             }
1345             $line= str_replace ("\t\t","\t",$line);
1346             $line= str_replace ("\t"  ,"," ,$line);
1347             echo $line;
1349             $cells = explode(",",$line )  ;
1350             $linet= str_replace ("\t\t",",",$line);
1351             $cells = preg_split("/\t/",$line);
1352             $count = count($cells);  
1353         }
1355     }
1357     function get_objectclasses( $force_reload = FALSE)
1358     {
1359         $objectclasses = array();
1360         global $config;
1362         /* Return the cached results. */
1363         if(class_available('session') && session::global_is_set("LDAP_CACHE::get_objectclasses") && !$force_reload){
1364             $objectclasses = session::global_get("LDAP_CACHE::get_objectclasses");
1365             return($objectclasses);
1366         }
1368 # Get base to look for schema 
1369         $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1370         $attr = @ldap_get_entries($this->cid,$sr);
1371         if (!isset($attr[0]['subschemasubentry'][0])){
1372             return array();
1373         }
1375         /* Get list of objectclasses and fill array */
1376         $nb= $attr[0]['subschemasubentry'][0];
1377         $objectclasses= array();
1378         $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1379         $attrs= ldap_get_entries($this->cid,$sr);
1380         if (!isset($attrs[0])){
1381             return array();
1382         }
1383         foreach ($attrs[0]['objectclasses'] as $val){
1384             if (preg_match('/^[0-9]+$/', $val)){
1385                 continue;
1386             }
1387             $name= "OID";
1388             $pattern= explode(' ', $val);
1389             $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1390             $objectclasses[$ocname]= array();
1392             foreach($pattern as $chunk){
1393                 switch($chunk){
1395                     case '(':
1396                         $value= "";
1397                         break;
1399                     case ')': if ($name != ""){
1400                                   $v = $this->value2container($value);
1401                                   if(in_array($name, array('MUST', 'MAY')) && !is_array($v)){
1402                                       $v = array($v);
1403                                   }
1404                                   $objectclasses[$ocname][$name]= $v;
1405                               }
1406                               $name= "";
1407                               $value= "";
1408                               break;
1410                     case 'NAME':
1411                     case 'DESC':
1412                     case 'SUP':
1413                     case 'STRUCTURAL':
1414                     case 'ABSTRACT':
1415                     case 'AUXILIARY':
1416                     case 'MUST':
1417                     case 'MAY':
1418                               if ($name != ""){
1419                                   $v = $this->value2container($value);
1420                                   if(in_array($name, array('MUST', 'MAY')) && !is_array($v)){
1421                                       $v = array($v);
1422                                   }
1423                                   $objectclasses[$ocname][$name]= $v;
1424                               }
1425                               $name= $chunk;
1426                               $value= "";
1427                               break;
1429                     default:  $value.= $chunk." ";
1430                 }
1431             }
1433         }
1434         if(class_available("session")){
1435             session::global_set("LDAP_CACHE::get_objectclasses",$objectclasses);
1436         }
1438         return $objectclasses;
1439     }
1442     function value2container($value)
1443     {
1444         /* Set emtpy values to "true" only */
1445         if (preg_match('/^\s*$/', $value)){
1446             return true;
1447         }
1449         /* Remove ' and " if needed */
1450         $value= preg_replace('/^[\'"]/', '', $value);
1451         $value= preg_replace('/[\'"] *$/', '', $value);
1453         /* Convert to array if $ is inside... */
1454         if (preg_match('/\$/', $value)){
1455             $container= preg_split('/\s*\$\s*/', $value);
1456         } else {
1457             $container= chop($value);
1458         }
1460         return ($container);
1461     }
1464     function log($string)
1465     {
1466         if (session::global_is_set('config')){
1467             $cfg = session::global_get('config');
1468             if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1469                 syslog (LOG_INFO, $string);
1470             }
1471         }
1472     }
1474     /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1475     function getCn($dn){
1476         $simple= explode(",", $dn);
1478         foreach($simple as $piece) {
1479             $partial= explode("=", $piece);
1481             if($partial[0] == "cn"){
1482                 return $partial[1];
1483             }
1484         }
1485     }
1488     function get_naming_contexts($server, $admin= "", $password= "")
1489     {
1490         /* Build LDAP connection */
1491         $ds= ldap_connect ($server);
1492         if (!$ds) {
1493             die ("Can't bind to LDAP. No check possible!");
1494         }
1495         ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1496         $r= ldap_bind ($ds, $admin, $password);
1498         /* Get base to look for naming contexts */
1499         $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1500         $attr= @ldap_get_entries($ds,$sr);
1502         return ($attr[0]['namingcontexts']);
1503     }
1506     function get_root_dse($server, $admin= "", $password= "")
1507     {
1508         /* Build LDAP connection */
1509         $ds= ldap_connect ($server);
1510         if (!$ds) {
1511             die ("Can't bind to LDAP. No check possible!");
1512         }
1513         ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1514         $r= ldap_bind ($ds, $admin, $password);
1516         /* Get base to look for naming contexts */
1517         $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1518         $attr= @ldap_get_entries($ds,$sr);
1520         /* Return empty array, if nothing was set */
1521         if (!isset($attr[0])){
1522             return array();
1523         }
1525         /* Rework array... */
1526         $result= array();
1527         for ($i= 0; $i<$attr[0]['count']; $i++){
1528             $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1529             unset($result[$attr[0][$i]]['count']);
1530         }
1532         return ($result);
1533     }
1536 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1537 ?>