Code

Updated client dns handling
[gosa.git] / include / class_ldap.inc
1 <?php
2 /*****************************************************************************
3   newldap.inc - version 1.0
4   Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
5   Copyright (C) 2004-2006 Cajus Pollmeier <pollmeier@gonicus.de>
7   Based in code of ldap.inc of
8   Copyright (C) 1998  Eric Kilfoil <eric@ipass.net>
9  *****************************************************************************/
11 define("ALREADY_EXISTING_ENTRY",-10001);
12 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
13 define("NO_FILE_UPLOADED",10003);
14 define("INSERT_OK",10000);
15 define("SPECIALS_OVERRIDE", TRUE);
17 class LDAP{
19   var $hascon   =false;
20   var $hasres   =false;
21   var $reconnect=false;
22   var $tls      = false;
23   var $basedn   ="";
24   var $cid;
25   var $error    = ""; // Any error messages to be returned can be put here
26   var $start    = 0; // 0 if we are fetching the first entry, otherwise 1
27   var $objectClasses = array(); // Information read from slapd.oc.conf
28   var $binddn   = "";
29   var $bindpw   = "";
30   var $hostname = "";
31   var $follow_referral = FALSE;
32   var $referrals= array();
33   var $max_ldap_query_time = 0;   // 0, empty or negative values will disable this check 
35   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
36   {
37     global $config;
38     $this->follow_referral= $follow_referral;
39     $this->tls=$tls;
40     $this->binddn=$this->convert($binddn);
42     $this->bindpw=$bindpw;
43     $this->hostname=$hostname;
45     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
46     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
47       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
48       $this->max_ldap_query_time = (float)($str);
49     }
51     $this->connect();
52   }
55   /* Function to replace all problematic characters inside a DN by \001XX, where
56      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
57      that this character is inside a DN.
58      
59      Currently used codes:
60       ,   => CO
61       \2C => CO
62       (   => OB
63       )   => CB
64       /   => SL                                                                  */
65   function convert($dn)
66   {
67     if (SPECIALS_OVERRIDE == TRUE){
68       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
69                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
70                            $dn);
71       return (preg_replace('/,\s+/', ',', $tmp));
72     } else {
73       return ($dn);
74     }
75   }
78   /* Function to fix all problematic characters inside a DN by replacing \001XX
79      codes to their original values. See "convert" for mor information. 
80      ',' characters are always expanded to \, (not \2C), since all tested LDAP
81      servers seem to take it the correct way.                                  */
82   function fix($dn)
83   {
84     if (SPECIALS_OVERRIDE == TRUE){
85       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
86                            array("\,", "(", ")", "/"),
87                            $dn));
88     } else {
89       return ($dn);
90     }
91   }
94   function connect()
95   {
96     $this->hascon=false;
97     $this->reconnect=false;
98     if ($this->cid= @ldap_connect($this->hostname)) {
99       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
100       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
101         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
102         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
103       }
104       if (function_exists("ldap_start_tls") && $this->tls){
105         @ldap_start_tls($this->cid);
106       }
108       $this->error = "No Error";
109       if ($bid = @ldap_bind($this->cid, $this->fix($this->binddn), $this->bindpw)) {
110         $this->error = "Success";
111         $this->hascon=true;
112       } else {
113         if ($this->reconnect){
114           if ($this->error != "Success"){
115             $this->error = "Could not rebind to " . $this->binddn;
116           }
117         } else {
118           $this->error = "Could not bind to " . $this->binddn;
119         }
120       }
121     } else {
122       $this->error = "Could not connect to LDAP server";
123     }
124   }
126   function rebind($ldap, $referral)
127   {
128     $credentials= $this->get_credentials($referral);
129     if (@ldap_bind($ldap, $this->fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
130       $this->error = "Success";
131       $this->hascon=true;
132       $this->reconnect= true;
133       return (0);
134     } else {
135       $this->error = "Could not bind to " . $credentials['ADMIN'];
136       return NULL;
137     }
138   }
140   function reconnect()
141   {
142     if ($this->reconnect){
143       @ldap_unbind($this->cid);
144       $this->cid = NULL;
145     }
146   }
148   function unbind()
149   {
150     @ldap_unbind($this->cid);
151     $this->cid = NULL;
152   }
154   function disconnect()
155   {
156     if($this->hascon){
157       @ldap_close($this->cid);
158       $this->hascon=false;
159     }
160   }
162   function cd($dir)
163   {
164     if ($dir == "..")
165       $this->basedn = $this->getParentDir();
166     else
167       $this->basedn = $this->convert($dir);
168   }
170   function getParentDir($basedn = "")
171   {
172     if ($basedn=="")
173       $basedn = $this->basedn;
174     else
175       $basedn = $this->convert($this->basedn);
176     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
177   }
179   function search($filter, $attrs= array())
180   {
181     if($this->hascon){
182       if ($this->reconnect) $this->connect();
184       $start = microtime();
185    
186       $this->clearResult();
187       $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
188       $this->error = @ldap_error($this->cid);
189       $this->resetResult();
190       $this->hasres=true;
191    
192       /* Check if query took longer as specified in max_ldap_query_time */
193       if($this->max_ldap_query_time){
194         $diff = get_MicroTimeDiff($start,microtime());
195         if($diff > $this->max_ldap_query_time){
196           print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
197         }
198       }
200       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
201       return($this->sr);
202     }else{
203       $this->error = "Could not connect to LDAP server";
204       return("");
205     }
206   }
208   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
209   {
210     if($this->hascon){
211       if ($this->reconnect) $this->connect();
212       $this->clearResult();
213       if ($basedn == "")
214         $basedn = $this->basedn;
215       else
216         $basedn= $this->convert($basedn);
217   
218       $start = microtime();
220       $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
221       $this->error = @ldap_error($this->cid);
222       $this->resetResult();
223       $this->hasres=true;
225        /* Check if query took longer as specified in max_ldap_query_time */
226       if($this->max_ldap_query_time){
227         $diff = get_MicroTimeDiff($start,microtime());
228         if($diff > $this->max_ldap_query_time){
229           print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
230         }
231       }
233       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
235       return($this->sr);
236     }else{
237       $this->error = "Could not connect to LDAP server";
238       return("");
239     }
240   }
242   function cat($dn,$attrs= array("*"))
243   {
244     if($this->hascon){
245       if ($this->reconnect) $this->connect();
246       $this->clearResult();
247       $filter = "(objectclass=*)";
248       $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
249       $this->error = @ldap_error($this->cid);
250       $this->resetResult();
251       $this->hasres=true;
252       return($this->sr);
253     }else{
254       $this->error = "Could not connect to LDAP server";
255       return("");
256     }
257   }
259   function set_size_limit($size)
260   {
261     /* Ignore zero settings */
262     if ($size == 0){
263       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
264     }
265     if($this->hascon){
266       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
267     } else {
268       $this->error = "Could not connect to LDAP server";
269     }
270   }
272   function fetch()
273   {
274     $att= array();
275     if($this->hascon){
276       if($this->hasres){
277         if ($this->start == 0)
278         {
279           $this->start = 1;
280           $this->re= @ldap_first_entry($this->cid, $this->sr);
281         } else {
282           $this->re= @ldap_next_entry($this->cid, $this->re);
283         }
284         if ($this->re)
285         {
286           $att= @ldap_get_attributes($this->cid, $this->re);
287           $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
288         }
289         $this->error = @ldap_error($this->cid);
290         if (!isset($att)){
291           $att= array();
292         }
293         return($att);
294       }else{
295         $this->error = "Perform a Fetch with no Search";
296         return("");
297       }
298     }else{
299       $this->error = "Could not connect to LDAP server";
300       return("");
301     }
302   }
304   function resetResult()
305   {
306     $this->start = 0;
307   }
309   function clearResult()
310   {
311     if($this->hasres){
312       $this->hasres = false;
313       @ldap_free_result($this->sr);
314     }
315   }
317   function getDN()
318   {
319     if($this->hascon){
320       if($this->hasres){
322         if(!$this->re)
323           {
324           $this->error = "Perform a Fetch with no valid Result";
325           }
326           else
327           {
328           $rv = @ldap_get_dn($this->cid, $this->re);
329         
330           $this->error = @ldap_error($this->cid);
331           return(trim($this->convert($rv)));
332            }
333       }else{
334         $this->error = "Perform a Fetch with no Search";
335         return("");
336       }
337     }else{
338       $this->error = "Could not connect to LDAP server";
339       return("");
340     }
341   }
343   function count()
344   {
345     if($this->hascon){
346       if($this->hasres){
347         $rv = @ldap_count_entries($this->cid, $this->sr);
348         $this->error = @ldap_error($this->cid);
349         return($rv);
350       }else{
351         $this->error = "Perform a Fetch with no Search";
352         return("");
353       }
354     }else{
355       $this->error = "Could not connect to LDAP server";
356       return("");
357     }
358   }
360   function rm($attrs = "", $dn = "")
361   {
362     if($this->hascon){
363       if ($this->reconnect) $this->connect();
364       if ($dn == "")
365         $dn = $this->basedn;
367       $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
368       $this->error = @ldap_error($this->cid);
369       return($r);
370     }else{
371       $this->error = "Could not connect to LDAP server";
372       return("");
373     }
374   }
376   function rename($attrs, $dn = "")
377   {
378     if($this->hascon){
379       if ($this->reconnect) $this->connect();
380       if ($dn == "")
381         $dn = $this->basedn;
383       $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
384       $this->error = @ldap_error($this->cid);
385       return($r);
386     }else{
387       $this->error = "Could not connect to LDAP server";
388       return("");
389     }
390   }
392   function rmdir($deletedn)
393   {
394     if($this->hascon){
395       if ($this->reconnect) $this->connect();
396       $r = @ldap_delete($this->cid, $this->fix($deletedn));
397       $this->error = @ldap_error($this->cid);
398       return($r ? $r : 0);
399     }else{
400       $this->error = "Could not connect to LDAP server";
401       return("");
402     }
403   }
405   /**
406   *  Function rmdir_recursive
407   *
408   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
409   *  Parameters:  The dn to delete
410   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
411   *
412   */
414   function rmdir_recursive($deletedn)
415   {
416     if($this->hascon){
417       if ($this->reconnect) $this->connect();
418       $delarray= array();
419         
420       /* Get sorted list of dn's to delete */
421       $this->ls ("(objectClass=*)",$deletedn);
422       while ($this->fetch()){
423         $deldn= $this->getDN();
424         $delarray[$deldn]= strlen($deldn);
425       }
426       arsort ($delarray);
427       reset ($delarray);
429       /* Really Delete ALL dn's in subtree */
430       foreach ($delarray as $key => $value){
431         $this->rmdir_recursive($key);
432       }
433       
434       /* Finally Delete own Node */
435       $r = @ldap_delete($this->cid, $this->fix($deletedn));
436       $this->error = @ldap_error($this->cid);
437       return($r ? $r : 0);
438     }else{
439       $this->error = "Could not connect to LDAP server";
440       return("");
441     }
442   }
444   /* Copy given attributes and sub-dns with attributes to destination dn 
445   */
446   function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
447   {
448     error_reporting(E_ALL);
449     
450     if($is_first){
451       echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
452     }else{
453       if(preg_match("/^ou=/",$sourcedn)){
454         echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
455       }else{
456         $tmp = split(",",$sourcedn);
457         
458         echo "&nbsp;<b>"._("Object").":</b> ";
460         $deststr = @LDAP::fix($destinationdn);
461         if(strlen($deststr) > 96){
462           $deststr = substr($deststr,0,96)."...";
463         }
465         echo $deststr."<br>";
466       }
467     }
469     flush();
470     
471     if($this->hascon){
472       if ($this->reconnect) $this->connect();
474       /* Save base dn */
475       $basedn= $this->basedn;
476       $delarray= array();
477      
478       /* Check if destination entry already exists */
479       $this->cat($destinationdn);
481       if($this->count()){
482         return;
483       }else{
484         
485         $this->clearResult();
487         /* Get source entry */
488         $this->cd($basedn);
489         $this->cat($sourcedn);
490         $attr = $this->fetch();
492         /* Error while fetching object / attribute abort*/
493         if((!$attr) || (count($attr)) ==0) {
494           echo _("Error while fetching source dn - aborted!");
495           return;
496         }
497   
498         /* check if this is a department */
499         if(in_array("organizationalUnit",$attr['objectClass'])){
500           $attr['dn'] = $this->convert($destinationdn);
501           $this->cd($basedn);
502           $this->create_missing_trees($destinationdn);
503           $this->cd($destinationdn);
505           /* If is first entry, append FAIbranch to department entry */
506           if($is_first){
507             $this->cat($destinationdn);
508             $attr= $this->fetch();
510             /* Filter unneeded informations */
511             foreach($attr as $key => $value){
512               if(is_numeric($key)) unset($attr[$key]);
513               if(isset($attr[$key]['count'])){
514                 if(is_array($attr[$key])){
515                   unset($attr[$key]['count']);
516                 }
517               }
518             }
519             
520             unset($attr['count']);
521             unset($attr['dn']);
523             /* Add marking attribute */
524             $attr['objectClass'][] = "FAIbranch";
525             
526             /* Add this entry */
527             $this->modify($attr);
528           }
529         }else{
531           /* If this is no department */
532           foreach($attr as $key => $value){
533             if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon","gotoMimeIcon"))){
534               $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
535               $ei= ldap_first_entry($this->cid, $sr);
536               if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
537                 $attr[$key] = $tmp;
538               }
539             }
541             if(is_numeric($key)) unset($attr[$key]);
542             if(isset($attr[$key]['count'])){
543               if(is_array($attr[$key])){
544                 unset($attr[$key]['count']);
545               }
546             }
547           }
548           unset($attr['count']);
549           unset($attr['dn']);
551           if((!in_array("gosaApplication" , $attr['objectClass'])) && (!in_array("gotoMimeType", $attr['objectClass']))){
552             $attr['FAIdebianRelease'] = $destinationName;
553             if($type=="branch"){
554               $attr['FAIstate'] ="branch";
555             }elseif($type=="freeze"){
556               $attr['FAIstate'] ="freeze";
557             }else{
558               print_red(_("Unknown FAIstate %s"),$type);
559             }
560           }
562           /* Replace FAIdebianRelease with new release name */
563           if(in_array("FAIpackageList" , $attr['objectClass'])){
564             $attr['FAIdebianRelease'] = $destinationName; 
565           }
567           /* Add entry */
568           $this->cd($destinationdn);
569           $this->cat($destinationdn);
570           $a = $this->fetch();
571           if(!count($a)){
572             $this->add($attr);
573           }
575           if($this->error != "Success"){
576             /* Some error occurred */
577             print "---------------------------------------------";
578             print $this->get_error()."<br>";
579             print $sourcedn."<br>";
580             print $destinationdn."<br>";
581             print_a( $attr);
582             exit();
583           }          
584         }
585       }
587       echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
589       $this->ls ("(objectClass=*)",$sourcedn);
590       while ($this->fetch()){
591         $deldn= $this->getDN();
592         $delarray[$deldn]= strlen($deldn);
593       }
594       asort ($delarray);
595       reset ($delarray);
597        $depth ++;
598       foreach($delarray as $dn => $bla){
599         if($dn != $destinationdn){
600           $this->cd($basedn);
601           $item = $this->fetch($this->cat($dn));
602           if(!in_array("FAIbranch",$item['objectClass'])){
603             $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
604           } 
605         }
606       }
607     }
608     if($is_first){
609       echo "<p class='seperator'>&nbsp;</p>";
610     }
612   }
614   function modify($attrs)
615   {
616     if(count($attrs) == 0){
617       return (0);
618     }
619     if($this->hascon){
620       if ($this->reconnect) $this->connect();
621       $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
622       $this->error = @ldap_error($this->cid);
623       return($r ? $r : 0);
624     }else{
625       $this->error = "Could not connect to LDAP server";
626       return("");
627     }
628   }
630   function add($attrs)
631   {
632     if($this->hascon){
633       if ($this->reconnect) $this->connect();
634       $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
635       $this->error = @ldap_error($this->cid);
636       return($r ? $r : 0);
637     }else{
638       $this->error = "Could not connect to LDAP server";
639       return("");
640     }
641   }
643   function create_missing_trees($target)
644   {
645     global $config;
647     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
649     if ($target == $this->basedn){
650       $l= array("dummy");
651     } else {
652       $l= array_reverse(gosa_ldap_explode_dn($real_path));
653     }
654     unset($l['count']);
655     $cdn= $this->basedn;
656     $tag= "";
658     /* Load schema if available... */
659     $classes= $this->get_objectclasses();
661     foreach ($l as $part){
662       if ($part != "dummy"){
663         $cdn= "$part,$cdn";
664       }
666       /* Ignore referrals */
667       $found= false;
668       foreach($this->referrals as $ref){
669         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
670         if ($base == $cdn){
671           $found= true;
672           break;
673         }
674       }
675       if ($found){
676         continue;
677       }
679       $this->cat ($cdn);
680       $attrs= $this->fetch();
682       /* Create missing entry? */
683       if (count ($attrs)){
684       
685         /* Catch the tag - if present */
686         if (isset($attrs['gosaUnitTag'][0])){
687           $tag= $attrs['gosaUnitTag'][0];
688         }
690       } else {
691         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
692         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
694         $na= array();
696         /* Automatic or traditional? */
697         if(count($classes)){
699           /* Get name of first matching objectClass */
700           $ocname= "";
701           foreach($classes as $class){
702             if (isset($class['MUST']) && $class['MUST'] == "$type"){
704               /* Look for first classes that is structural... */
705               if (isset($class['STRUCTURAL'])){
706                 $ocname= $class['NAME'];
707                 break;
708               }
710               /* Look for classes that are auxiliary... */
711               if (isset($class['AUXILIARY'])){
712                 $ocname= $class['NAME'];
713               }
714             }
715           }
717           /* Bail out, if we've nothing to do... */
718           if ($ocname == ""){
719             print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
720             echo $_SESSION['errors'];
721             exit;
722           }
724           /* Assemble_entry */
725           if ($tag != ""){
726             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
727           } else {
728             $na['objectClass']= array($ocname);
729           }
730           if (isset($classes[$ocname]['AUXILIARY'])){
731             $na['objectClass'][]= $classes[$ocname]['SUP'];
732           }
733           if ($type == "dc"){
734             /* This is bad actually, but - tell me a better way? */
735             $na['objectClass'][]= 'locality';
736           }
737           $na[$type]= $param;
738           if (is_array($classes[$ocname]['MUST'])){
739             foreach($classes[$ocname]['MUST'] as $attr){
740               $na[$attr]= "filled";
741             }
742           }
744         } else {
746           /* Use alternative add... */
747           switch ($type){
748             case 'ou':
749               if ($tag != ""){
750                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
751                 $na["gosaUnitTag"]= $tag;
752               } else {
753                 $na["objectClass"]= "organizationalUnit";
754               }
755               $na["ou"]= $param;
756               break;
757             case 'dc':
758               if ($tag != ""){
759                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
760                 $na["gosaUnitTag"]= $tag;
761               } else {
762                 $na["objectClass"]= array("dcObject", "top", "locality");
763               }
764               $na["dc"]= $param;
765               break;
766             default:
767               print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
768               echo $_SESSION['errors'];
769               exit;
770           }
772         }
773         $this->cd($cdn);
774         $this->add($na);
775     
776         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
777         if (!preg_match('/success/i', $this->error)){
778           return FALSE;
779         }
780       }
781     }
783     return TRUE;
784   }
787   function recursive_remove()
788   {
789     $delarray= array();
791     /* Get sorted list of dn's to delete */
792     $this->search ("(objectClass=*)");
793     while ($this->fetch()){
794       $deldn= $this->getDN();
795       $delarray[$deldn]= strlen($deldn);
796     }
797     arsort ($delarray);
798     reset ($delarray);
800     /* Delete all dn's in subtree */
801     foreach ($delarray as $key => $value){
802       $this->rmdir($key);
803     }
804   }
806   function get_attribute($dn, $name,$r_array=0)
807   {
808     $data= "";
809     if ($this->reconnect) $this->connect();
810     $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
812     /* fill data from LDAP */
813     if ($sr) {
814       $ei= @ldap_first_entry($this->cid, $sr);
815       if ($ei) {
816         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
817           $data= $info[0];
818         }
820       }
821     }
822     if($r_array==0)
823     return ($data);
824     else
825     return ($info);
826   
827   
828   }
829  
832   function get_additional_error()
833   {
834     $error= "";
835     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
836     return ($error);
837   }
839   function get_error()
840   {
841     if ($this->error == 'Success'){
842       return $this->error;
843     } else {
844       $adderror= $this->get_additional_error();
845       if ($adderror != ""){
846         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
847       } else {
848         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
849       }
850       return $error;
851     }
852   }
854   function get_credentials($url, $referrals= NULL)
855   {
856     $ret= array();
857     $url= preg_replace('!\?\?.*$!', '', $url);
858     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
860     if ($referrals == NULL){
861       $referrals= $this->referrals;
862     }
864     if (isset($referrals[$server])){
865       return ($referrals[$server]);
866     } else {
867       $ret['ADMIN']= $this->fix($this->binddn);
868       $ret['PASSWORD']= $this->bindpw;
869     }
871     return ($ret);
872   }
875   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
876   {
877     $display= "";
879     if ($recursive){
880       $this->cd($dn);
881       $this->ls($filter,$dn, array('dn','objectClass'));
882       $deps = array();
884       $display .= $this->gen_one_entry($dn)."\n";
886       while ($attrs= $this->fetch()){
887         $deps[] = $attrs['dn'];
888       }
889       foreach($deps as $dn){
890         $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
891       }
892     } else {
893       $display.= $this->gen_one_entry($dn);
894     }
895     return ($display);
896   }
899   function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
900   {
901     $display= array();
903       $this->cd($dn);
904       $this->search("$filter");
906       $i=0;
907       while ($attrs= $this->fetch()){
908         $j=0;
910         foreach ($attributes as $at){
911           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
912           $j++;
913         }
915         $i++;
916       }
918     return ($display);
919   }
922   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
923   {
924     $ret = "";
925     $data = "";
926     if($this->reconnect){
927       $this->connect();
928     }
930     /* Searching Ldap Tree */
931     $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
933     /* Get the first entry */   
934     $entry= @ldap_first_entry($this->cid, $sr);
936     /* Get all attributes related to that Objekt */
937     $atts = array();
938     
939     /* Assemble dn */
940     $atts[0]['name']  = "dn";
941     $atts[0]['value'] = array('count' => 1, 0 => $dn);
943     /* Reset index */
944     $i = 1 ; 
945   $identifier = array();
946     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
947     while ($attribute) {
948       $i++;
949       $atts[$i]['name']  = $attribute;
950       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
952       /* Next one */
953       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
954     }
956     foreach($atts as $at)
957     {
958       for ($i= 0; $i<$at['value']['count']; $i++){
960         /* Check if we must encode the data */
961         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
962           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
963         } else {
964           $ret .= $at['name'].": ".$at['value'][$i]."\n";
965         }
966       }
967     }
969     return($ret);
970   }
973   function dn_exists($dn)
974   {
975     return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
976   }
977   
980   /*  This funktion imports ldifs 
981         
982       If DeleteOldEntries is true, the destination entry will be deleted first. 
983       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
984       if JustMofify id false the destination dn will be overwritten by the new ldif. 
985     */
987   function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
988   {
989     if($this->reconnect) $this->connect();
991     /* First we have to splitt the string ito detect empty lines
992        An empty line indicates an new Entry */
993     $entries = split("\n",$str_attr);
995     $data = "";
996     $cnt = 0; 
997     $current_line = 0;
999     /* FIX ldif */
1000     $last = "";
1001     $tmp  = "";
1002     $i = 0;
1003     foreach($entries as $entry){
1004       if(preg_match("/^ /",$entry)){
1005         $tmp[$i] .= trim($entry);
1006       }else{
1007         $i ++;
1008         $tmp[$i] = trim($entry);
1009       }
1010     }
1012     /* Every single line ... */
1013     foreach($tmp as $entry) {
1014       $current_line ++;
1016       /* Removing Spaces to .. 
1017          .. test if a new entry begins */
1018       $tmp  = str_replace(" ","",$data );
1020       /* .. prevent empty lines in an entry */
1021       $tmp2 = str_replace(" ","",$entry);
1023       /* If the Block ends (Empty Line) */
1024       if((empty($entry))&&(!empty($tmp))) {
1025         /* Add collected lines as a complete block */
1026         $all[$cnt] = $data;
1027         $cnt ++;
1028         $data ="";
1029       } else {
1031         /* Append lines ... */
1032         if(!empty($tmp2)) {
1033           /* check if we need base64_decode for this line */
1034           if(ereg("::",$tmp2))
1035           {
1036             $encoded = split("::",$entry);
1037             $attr  = trim($encoded[0]);
1038             $value = base64_decode(trim($encoded[1]));
1039             /* Add linenumber */
1040             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
1041           }
1042           else
1043           {
1044             /* Add Linenumber */ 
1045             $data .= $current_line."#".base64_encode($entry)."\n";
1046           }
1047         }
1048       }
1049     }
1051     /* The Data we collected is not in the array all[];
1052        For example the Data is stored like this..
1054        all[0] = "1#dn : .... \n 
1055        2#ObjectType: person \n ...."
1056        
1057        Now we check every insertblock and try to insert */
1058     foreach ( $all as $single) {
1059       $lineone = split("\n",$single);  
1060       $ndn = split("#", $lineone[0]);
1061       $line = base64_decode($ndn[1]);
1063       $dnn = split (":",$line,2);
1064       $current_line = $ndn[0];
1065       $dn    = $dnn[0];
1066       $value = $dnn[1];
1068       /* Every block must begin with a dn */
1069       if($dn != "dn") {
1070         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1071         return -2;  
1072       }
1074       /* Should we use Modify instead of Add */
1075       $usemodify= false;
1077       /* Delete before insert */
1078       $usermdir= false;
1079     
1080       /* The dn address already exists, Don't delete destination entry, overwrite it */
1081       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1083         $usermdir = $usemodify = false;
1085       /* Delete old entry first, then add new */
1086       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1088         /* Delete first, then add */
1089         $usermdir = true;        
1091       } elseif(($this->dn_exists($value))&&($JustModify)) {
1092         
1093         /* Modify instead of Add */
1094         $usemodify = true;
1095       }
1096      
1097       /* If we can't Import, return with a file error */
1098       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1099         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1100                         $current_line);
1101         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1102     }
1104     return (INSERT_OK);
1105   }
1108   /* Imports a single entry 
1109       If $delete is true;  The old entry will be deleted if it exists.
1110       if $modify is true;  All variables that are not touched by the new ldif will be kept.
1111       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1112   */
1113   function import_single_entry($str_attr,$modify,$delete)
1114   {
1115     global $config;
1117     if(!$config){
1118       trigger_error("Can't import ldif, can't read config object.");
1119     }
1120   
1122     if($this->reconnect) $this->connect();
1124     $ret = false;
1125     $rows= split("\n",$str_attr);
1126     $data= false;
1128     foreach($rows as $row) {
1129       
1130       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1131          Linenumbers) Linenumbers are use like this 123#attribute : value */
1132       if(!empty($row)) {
1133         if(strpos($row,"#")!=FALSE) {
1135           /* We are using line numbers 
1136              Because there is a # before a : */
1137           $tmp1= split("#",$row);
1138           $current_line= $tmp1[0];
1139           $row= base64_decode($tmp1[1]);
1140         }
1142         /* Split the line into  attribute  and value */
1143         $attr   = split(":", $row,2);
1144         $attr[0]= trim($attr[0]);  /* attribute */
1145         $attr[1]= $attr[1];  /* value */
1147         /* Check :: was used to indicate base64_encoded strings */
1148         if($attr[1][0] == ":"){
1149           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1150           $attr[1]=base64_decode($attr[1]);
1151         }
1153         $attr[1] = trim($attr[1]);
1155         /* Check for attributes that are used more than once */
1156         if(!isset($data[$attr[0]])) {
1157           $data[$attr[0]]=$attr[1];
1158         } else {
1159           $tmp = $data[$attr[0]];
1161           if(!is_array($tmp)) {
1162             $new[0]=$tmp;
1163             $new[1]=$attr[1];
1164             $datas[$attr[0]]['count']=1;             
1165             $data[$attr[0]]=$new;
1166           } else {
1167             $cnt = $datas[$attr[0]]['count'];           
1168             $cnt ++;
1169             $data[$attr[0]][$cnt]=$attr[1];
1170             $datas[$attr[0]]['count'] = $cnt;
1171           }
1172         }
1173       }
1174     }
1176     /* If dn is an index of data, we should try to insert the data */
1177     if(isset($data['dn'])) {
1179       /* Fix dn */
1180       $tmp = gosa_ldap_explode_dn($data['dn']);
1181       unset($tmp['count']);
1182       $newdn ="";
1183       foreach($tmp as $tm){
1184         $newdn.= trim($tm).",";
1185       }
1186       $newdn = preg_replace("/,$/","",$newdn);
1187       $data['dn'] = $newdn;
1188    
1189       /* Creating Entry */
1190       $this->cd($data['dn']);
1192       /* Delete existing entry */
1193       if($delete){
1194         $this->rmdir_recursive($data['dn']);
1195       }
1196      
1197       /* Create missing trees */
1198       $this->cd ($this->basedn);
1199       $this->cd($config->current['BASE']);
1200       $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1201       $this->cd($data['dn']);
1203       $dn = $data['dn'];
1204       unset($data['dn']);
1205       
1206       if(!$modify){
1208         $this->cat($dn);
1209         if($this->count()){
1210         
1211           /* The destination entry exists, overwrite it with the new entry */
1212           $attrs = $this->fetch();
1213           foreach($attrs as $name => $value ){
1214             if(!is_numeric($name)){
1215               if(in_array($name,array("dn","count"))) continue;
1216               if(!isset($data[$name])){
1217                 $data[$name] = array();
1218               }
1219             }
1220           }
1221           $ret = $this->modify($data);
1222     
1223         }else{
1224     
1225           /* The destination entry doesn't exists, create it */
1226           $ret = $this->add($data);
1227         }
1229       } else {
1230         
1231         /* Keep all vars that aren't touched by this ldif */
1232         $ret = $this->modify($data);
1233       }
1234     }
1235     show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1236     return($ret);
1237   }
1239   
1240   function importcsv($str)
1241   {
1242     $lines = split("\n",$str);
1243     foreach($lines as $line)
1244     {
1245       /* continue if theres a comment */
1246       if(substr(trim($line),0,1)=="#"){
1247         continue;
1248       }
1250       $line= str_replace ("\t\t","\t",$line);
1251       $line= str_replace ("\t"  ,"," ,$line);
1252       echo $line;
1254       $cells = split(",",$line )  ;
1255       $linet= str_replace ("\t\t",",",$line);
1256       $cells = split("\t",$line);
1257       $count = count($cells);  
1258     }
1260   }
1261   
1262   function get_objectclasses()
1263   {
1264     $objectclasses = array();
1265     global $config;
1267     /* Only read schema if it is allowed */
1268     if(isset($config) && preg_match("/config/i",get_class($config))){
1269       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1270         return($objectclasses);
1271       } 
1272     }
1273         
1274           # Get base to look for schema 
1275           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1276     if(!$sr){
1277             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1278     }
1280           $attr = @ldap_get_entries($this->cid,$sr);
1281           if (!isset($attr[0]['subschemasubentry'][0])){
1282             return array();
1283           }
1284         
1285           /* Get list of objectclasses and fill array */
1286           $nb= $attr[0]['subschemasubentry'][0];
1287           $objectclasses= array();
1288           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1289           $attrs= ldap_get_entries($this->cid,$sr);
1290           if (!isset($attrs[0])){
1291             return array();
1292           }
1293           foreach ($attrs[0]['objectclasses'] as $val){
1294       if (preg_match('/^[0-9]+$/', $val)){
1295         continue;
1296       }
1297       $name= "OID";
1298       $pattern= split(' ', $val);
1299       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1300       $objectclasses[$ocname]= array();
1302       foreach($pattern as $chunk){
1303         switch($chunk){
1305           case '(':
1306                     $value= "";
1307                     break;
1309           case ')': if ($name != ""){
1310                       $objectclasses[$ocname][$name]= $this->value2container($value);
1311                     }
1312                     $name= "";
1313                     $value= "";
1314                     break;
1316           case 'NAME':
1317           case 'DESC':
1318           case 'SUP':
1319           case 'STRUCTURAL':
1320           case 'ABSTRACT':
1321           case 'AUXILIARY':
1322           case 'MUST':
1323           case 'MAY':
1324                     if ($name != ""){
1325                       $objectclasses[$ocname][$name]= $this->value2container($value);
1326                     }
1327                     $name= $chunk;
1328                     $value= "";
1329                     break;
1331           default:  $value.= $chunk." ";
1332         }
1333       }
1335           }
1336           return $objectclasses;
1337   }
1340   function value2container($value)
1341   {
1342     /* Set emtpy values to "true" only */
1343     if (preg_match('/^\s*$/', $value)){
1344       return true;
1345     }
1347     /* Remove ' and " if needed */
1348     $value= preg_replace('/^[\'"]/', '', $value);
1349     $value= preg_replace('/[\'"] *$/', '', $value);
1351     /* Convert to array if $ is inside... */
1352     if (preg_match('/\$/', $value)){
1353       $container= preg_split('/\s*\$\s*/', $value);
1354     } else {
1355       $container= chop($value);
1356     }
1358     return ($container);
1359   }
1362   function log($string)
1363   {
1364     if (isset($_SESSION['config'])){
1365       $cfg= $_SESSION['config'];
1366       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1367         syslog (LOG_INFO, $string);
1368       }
1369     }
1370   }
1372   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1373   function getCn($dn){
1374     $simple= split(",", $dn);
1376     foreach($simple as $piece) {
1377       $partial= split("=", $piece);
1379       if($partial[0] == "cn"){
1380         return $partial[1];
1381       }
1382     }
1383   }
1386   function get_naming_contexts($server, $admin= "", $password= "")
1387   {
1388     /* Build LDAP connection */
1389     $ds= ldap_connect ($server);
1390     if (!$ds) {
1391       die ("Can't bind to LDAP. No check possible!");
1392     }
1393     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1394     $r= ldap_bind ($ds, $admin, $password);
1396     /* Get base to look for naming contexts */
1397     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1398     $attr= @ldap_get_entries($ds,$sr);
1400     return ($attr[0]['namingcontexts']);
1401   }
1404   function get_root_dse($server, $admin= "", $password= "")
1405   {
1406     /* Build LDAP connection */
1407     $ds= ldap_connect ($server);
1408     if (!$ds) {
1409       die ("Can't bind to LDAP. No check possible!");
1410     }
1411     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1412     $r= ldap_bind ($ds, $admin, $password);
1414     /* Get base to look for naming contexts */
1415     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1416     $attr= @ldap_get_entries($ds,$sr);
1417    
1418     /* Return empty array, if nothing was set */
1419     if (!isset($attr[0])){
1420       return array();
1421     }
1423     /* Rework array... */
1424     $result= array();
1425     for ($i= 0; $i<$attr[0]['count']; $i++){
1426       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1427       unset($result[$attr[0][$i]]['count']);
1428     }
1430     return ($result);
1431   }
1436 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1437 ?>