Code

Updated check
[gosa.git] / include / class_ldap.inc
1 <?php
2 /*****************************************************************************
3   newldap.inc - version 1.0
4   Copyright (C) 2003 Alejandro Escanero Blanco <alex@ofmin.com>
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   var $re = NULL;  
37   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
38   {
39     global $config;
40     $this->follow_referral= $follow_referral;
41     $this->tls=$tls;
42     $this->binddn=$this->convert($binddn);
44     $this->bindpw=$bindpw;
45     $this->hostname=$hostname;
47     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
48     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
49       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
50       $this->max_ldap_query_time = (float)($str);
51     }
53     $this->connect();
54   }
57   /* Function to replace all problematic characters inside a DN by \001XX, where
58      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
59      that this character is inside a DN.
60      
61      Currently used codes:
62       ,   => CO
63       \2C => CO
64       \22 => TO
65       \"  => TO
66       \+  => PL
67       (   => OB
68       )   => CB
69       /   => SL                                                                  */
70   function convert($dn)
71   {
72     if (SPECIALS_OVERRIDE == TRUE){
73       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//", "/\\\\22/", '/\\\\"/', "/\\\\3D/", "/\\\\2B/", '/\\\\+/'),
74                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL", "\001TO", "\001TO", "\001GL", "\001PL", "\001PL"),
75                            $dn);
76       return (preg_replace('/,\s+/', ',', $tmp));
77     } else {
78       return ($dn);
79     }
80   }
83   /* Function to fix all problematic characters inside a DN by replacing \001XX
84      codes to their original values. See "convert" for mor information. 
85      ',' characters are always expanded to \, (not \2C), since all tested LDAP
86      servers seem to take it the correct way.                                  */
87   function fix($dn)
88   {
89     if (SPECIALS_OVERRIDE == TRUE){
90       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/", "/\001TO/", "/\001PL/", "/\001GL/"),
91                            array("\,", "(", ")", "/", '\"', "\+", "="),
92                            $dn));
93     } else {
94       return ($dn);
95     }
96   }
99   /* Function to fix problematic characters in DN's that are used for search
100      requests. I.e. member=....                                               */
101   function prepare4filter($dn)
102   {
103         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', @LDAP::fix($dn)));
104   }
107   function connect()
108   {
109     $this->hascon=false;
110     $this->reconnect=false;
111     if ($this->cid= @ldap_connect($this->hostname)) {
112       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
113       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
114         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
115         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
116       }
117       if (function_exists("ldap_start_tls") && $this->tls){
118         @ldap_start_tls($this->cid);
119       }
121       $this->error = "No Error";
122       if ($bid = @ldap_bind($this->cid, $this->fix($this->binddn), $this->bindpw)) {
123         $this->error = "Success";
124         $this->hascon=true;
125       } else {
126         if ($this->reconnect){
127           if ($this->error != "Success"){
128             $this->error = "Could not rebind to " . $this->binddn;
129           }
130         } else {
131           $this->error = "Could not bind to " . $this->binddn;
132         }
133       }
134     } else {
135       $this->error = "Could not connect to LDAP server";
136     }
137   }
139   function rebind($ldap, $referral)
140   {
141     $credentials= $this->get_credentials($referral);
142     if (@ldap_bind($ldap, $this->fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
143       $this->error = "Success";
144       $this->hascon=true;
145       $this->reconnect= true;
146       return (0);
147     } else {
148       $this->error = "Could not bind to " . $credentials['ADMIN'];
149       return NULL;
150     }
151   }
153   function reconnect()
154   {
155     if ($this->reconnect){
156       @ldap_unbind($this->cid);
157       $this->cid = NULL;
158     }
159   }
161   function unbind()
162   {
163     @ldap_unbind($this->cid);
164     $this->cid = NULL;
165   }
167   function disconnect()
168   {
169     if($this->hascon){
170       @ldap_close($this->cid);
171       $this->hascon=false;
172     }
173   }
175   function cd($dir)
176   {
177     if ($dir == "..")
178       $this->basedn = $this->getParentDir();
179     else
180       $this->basedn = $this->convert($dir);
181   }
183   function getParentDir($basedn = "")
184   {
185     if ($basedn=="")
186       $basedn = $this->basedn;
187     else
188       $basedn = $this->convert($this->basedn);
189     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
190   }
192   
193   /* Checks if there is still unfetched data 
194    */
195   function checkResult()
196   {
197     /* Check if we have started a search before  */
198     if($this->start != 0 && $this->re){
199       
200       /* Check if there are still unfetched elements */
201       if(is_resource(@ldap_next_entry($this->cid, $this->re))){
202         trigger_error("A new search was initiated while an older search wasn't fetched completely.");
203       }
204     }
205   }
207   function search($filter, $attrs= array())
208   {
209     if($this->hascon){
210       if ($this->reconnect) $this->connect();
212       /* Check if there are still unfetched objects from last search 
213        */
214       $this->checkResult();
216       $start = microtime();
217       $this->clearResult();
218       $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
219       $this->error = @ldap_error($this->cid);
220       $this->resetResult();
221       $this->hasres=true;
222    
223       /* Check if query took longer as specified in max_ldap_query_time */
224       if($this->max_ldap_query_time){
225         $diff = get_MicroTimeDiff($start,microtime());
226         if($diff > $this->max_ldap_query_time){
227           print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
228         }
229       }
231       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
233       return($this->sr);
234     }else{
235       $this->error = "Could not connect to LDAP server";
236       return("");
237     }
238   }
240   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
241   {
242     if($this->hascon){
243       if ($this->reconnect) $this->connect();
245       /* Check if there are still unfetched objects from last search 
246        */
247       $this->checkResult();
249       $this->clearResult();
250       if ($basedn == "")
251         $basedn = $this->basedn;
252       else
253         $basedn= $this->convert($basedn);
254   
255       $start = microtime();
257       $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
258       $this->error = @ldap_error($this->cid);
259       $this->resetResult();
260       $this->hasres=true;
262        /* Check if query took longer as specified in max_ldap_query_time */
263       if($this->max_ldap_query_time){
264         $diff = get_MicroTimeDiff($start,microtime());
265         if($diff > $this->max_ldap_query_time){
266           print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
267         }
268       }
270       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
272       return($this->sr);
273     }else{
274       $this->error = "Could not connect to LDAP server";
275       return("");
276     }
277   }
279   function cat($dn,$attrs= array("*"))
280   {
281     if($this->hascon){
282       if ($this->reconnect) $this->connect();
284       /* Check if there are still unfetched objects from last search
285        */
286       $this->checkResult();
288       $start = microtime();
289       $this->clearResult();
290       $filter = "(objectclass=*)";
291       $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
292       $this->error = @ldap_error($this->cid);
293       $this->resetResult();
294       $this->hasres=true;
295       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=cat('".$this->fix($dn)."')");
296       return($this->sr);
297     }else{
298       $this->error = "Could not connect to LDAP server";
299       return("");
300     }
301   }
303   function set_size_limit($size)
304   {
305     /* Ignore zero settings */
306     if ($size == 0){
307       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
308     }
309     if($this->hascon){
310       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
311     } else {
312       $this->error = "Could not connect to LDAP server";
313     }
314   }
316   function fetch()
317   {
318     $att= array();
319     if($this->hascon){
320       if($this->hasres){
321         if ($this->start == 0)
322         {
323           $this->start = 1;
324           $this->re= @ldap_first_entry($this->cid, $this->sr);
325         } else {
326           $this->re= @ldap_next_entry($this->cid, $this->re);
327         }
328         if ($this->re)
329         {
330           $att= @ldap_get_attributes($this->cid, $this->re);
331           $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
332         }
333         $this->error = @ldap_error($this->cid);
334         if (!isset($att)){
335           $att= array();
336         }
337         return($att);
338       }else{
339         $this->error = "Perform a Fetch with no Search";
340         return("");
341       }
342     }else{
343       $this->error = "Could not connect to LDAP server";
344       return("");
345     }
346   }
348   function resetResult()
349   {
350     $this->start = 0;
351   }
353   function clearResult()
354   {
355     if($this->hasres){
356       $this->hasres = false;
357       @ldap_free_result($this->sr);
358     }
359   }
361   function getDN()
362   {
363     if($this->hascon){
364       if($this->hasres){
366         if(!$this->re)
367           {
368           $this->error = "Perform a Fetch with no valid Result";
369           }
370           else
371           {
372           $rv = @ldap_get_dn($this->cid, $this->re);
373         
374           $this->error = @ldap_error($this->cid);
375           return(trim($this->convert($rv)));
376            }
377       }else{
378         $this->error = "Perform a Fetch with no Search";
379         return("");
380       }
381     }else{
382       $this->error = "Could not connect to LDAP server";
383       return("");
384     }
385   }
387   function count()
388   {
389     if($this->hascon){
390       if($this->hasres){
391         $rv = @ldap_count_entries($this->cid, $this->sr);
392         $this->error = @ldap_error($this->cid);
393         return($rv);
394       }else{
395         $this->error = "Perform a Fetch with no Search";
396         return("");
397       }
398     }else{
399       $this->error = "Could not connect to LDAP server";
400       return("");
401     }
402   }
404   function rm($attrs = "", $dn = "")
405   {
406     if($this->hascon){
407       if ($this->reconnect) $this->connect();
408       if ($dn == "")
409         $dn = $this->basedn;
411       $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
412       $this->error = @ldap_error($this->cid);
413       return($r);
414     }else{
415       $this->error = "Could not connect to LDAP server";
416       return("");
417     }
418   }
420   function rename($attrs, $dn = "")
421   {
422     if($this->hascon){
423       if ($this->reconnect) $this->connect();
424       if ($dn == "")
425         $dn = $this->basedn;
427       $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
428       $this->error = @ldap_error($this->cid);
429       return($r);
430     }else{
431       $this->error = "Could not connect to LDAP server";
432       return("");
433     }
434   }
436   function rmdir($deletedn)
437   {
438     if($this->hascon){
439       if ($this->reconnect) $this->connect();
440       $start= microtime();
441       $r = @ldap_delete($this->cid, $this->fix($deletedn));
442       $this->error = @ldap_error($this->cid);
443       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=delete('".$this->fix($deletedn)."')");
444       return($r ? $r : 0);
445     }else{
446       $this->error = "Could not connect to LDAP server";
447       return("");
448     }
449   }
451   /**
452   *  Function rmdir_recursive
453   *
454   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
455   *  Parameters:  The dn to delete
456   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
457   *
458   */
460   function rmdir_recursive($deletedn)
461   {
462     if($this->hascon){
463       if ($this->reconnect) $this->connect();
464       $delarray= array();
465         
466       /* Get sorted list of dn's to delete */
467       $this->ls ("(objectClass=*)",$deletedn);
468       while ($this->fetch()){
469         $deldn= $this->getDN();
470         $delarray[$deldn]= strlen($deldn);
471       }
472       arsort ($delarray);
473       reset ($delarray);
475       /* Really Delete ALL dn's in subtree */
476       foreach ($delarray as $key => $value){
477         $this->rmdir_recursive($key);
478       }
479       
480       /* Finally Delete own Node */
481       $r = @ldap_delete($this->cid, $this->fix($deletedn));
482       $this->error = @ldap_error($this->cid);
483       return($r ? $r : 0);
484     }else{
485       $this->error = "Could not connect to LDAP server";
486       return("");
487     }
488   }
490   /* Copy given attributes and sub-dns with attributes to destination dn 
491   */
492   function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
493   {
494     error_reporting(E_ALL);
495     
496     if($is_first){
497       echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
498     }else{
499       if(preg_match("/^ou=/",$sourcedn)){
500         echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
501       }else{
502         $tmp = split(",",$sourcedn);
503         
504         echo "&nbsp;<b>"._("Object").":</b> ";
506         $deststr = @LDAP::fix($destinationdn);
507         if(strlen($deststr) > 96){
508           $deststr = substr($deststr,0,96)."...";
509         }
511         echo $deststr."<br>";
512       }
513     }
515     flush();
516     
517     if($this->hascon){
518       if ($this->reconnect) $this->connect();
520       /* Save base dn */
521       $basedn= $this->basedn;
522       $delarray= array();
523      
524       /* Check if destination entry already exists */
525       $this->cat($destinationdn);
527       if($this->count()){
528         return;
529       }else{
530         
531         $this->clearResult();
533         /* Get source entry */
534         $this->cd($basedn);
535         $this->cat($sourcedn);
536         $attr = $this->fetch();
538         /* Error while fetching object / attribute abort*/
539         if((!$attr) || (count($attr)) ==0) {
540           echo _("Error while fetching source dn - aborted!");
541           return;
542         }
543   
544         /* check if this is a department */
545         if(in_array("organizationalUnit",$attr['objectClass'])){
546           $attr['dn'] = $this->convert($destinationdn);
547           $this->cd($basedn);
548           $this->create_missing_trees($destinationdn);
549           $this->cd($destinationdn);
551           /* If is first entry, append FAIbranch to department entry */
552           if($is_first){
553             $this->cat($destinationdn);
554             $attr= $this->fetch();
556             /* Filter unneeded informations */
557             foreach($attr as $key => $value){
558               if(is_numeric($key)) unset($attr[$key]);
559               if(isset($attr[$key]['count'])){
560                 if(is_array($attr[$key])){
561                   unset($attr[$key]['count']);
562                 }
563               }
564             }
565             
566             unset($attr['count']);
567             unset($attr['dn']);
569             /* Add marking attribute */
570             $attr['objectClass'][] = "FAIbranch";
571             
572             /* Add this entry */
573             $this->modify($attr);
574           }
575         }else{
577           /* If this is no department */
578           foreach($attr as $key => $value){
579             if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon"))){
580               $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
581               $ei= ldap_first_entry($this->cid, $sr);
582               if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
583                 $attr[$key] = $tmp;
584               }
585             }
587             if(is_numeric($key)) unset($attr[$key]);
588             if(isset($attr[$key]['count'])){
589               if(is_array($attr[$key])){
590                 unset($attr[$key]['count']);
591               }
592             }
593           }
594           unset($attr['count']);
595           unset($attr['dn']);
597           if(!in_array("gosaApplication" , $attr['objectClass'])){
598             if($type=="branch"){
599               $attr['FAIstate'] ="branch";
600             }elseif($type=="freeze"){
601               $attr['FAIstate'] ="freeze";
602             }else{
603               print_red(_("Unknown FAIstate %s"),$type);
604             }
605           }elseif(in_array("gosaApplication",$attr['objectClass'])){
606             if(!in_array("FAIobject",$attr['objectClass'])){
607               $attr['objectClass'][] = "FAIobject";
608             }
609             $attr['FAIstate'] = $type;
610           }
612           /* Replace FAIdebianRelease with new release name */
613           if(in_array("FAIpackageList" , $attr['objectClass'])){
614             $attr['FAIdebianRelease'] = $destinationName;
615             if($type=="branch"){
616               $attr['FAIstate'] ="branch";
617             }elseif($type=="freeze"){
618               $attr['FAIstate'] ="freeze";
619             }else{
620               print_red(_("Unknown FAIstate %s"),$type);
621             }
622           }
624           /* Add entry */
625           $this->cd($destinationdn);
626           $this->cat($destinationdn);
627           $a = $this->fetch();
628           if(!count($a)){
629             $this->add($attr);
630           }
632           if($this->error != "Success"){
633             /* Some error occurred */
634             print "---------------------------------------------";
635             print $this->get_error()."<br>";
636             print $sourcedn."<br>";
637             print $destinationdn."<br>";
638             print_a( $attr);
639             exit();
640           }          
641         }
642       }
644       echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
646       $this->ls ("(objectClass=*)",$sourcedn);
647       while ($this->fetch()){
648         $deldn= $this->getDN();
649         $delarray[$deldn]= strlen($deldn);
650       }
651       asort ($delarray);
652       reset ($delarray);
654        $depth ++;
655       foreach($delarray as $dn => $bla){
656         if($dn != $destinationdn){
657           $this->cd($basedn);
658           $item = $this->fetch($this->cat($dn));
659           if(!in_array("FAIbranch",$item['objectClass'])){
660             $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
661           } 
662         }
663       }
664     }
665     if($is_first){
666       echo "<p class='seperator'>&nbsp;</p>";
667     }
669   }
671   function modify($attrs)
672   {
673     if(count($attrs) == 0){
674       return (0);
675     }
676     if($this->hascon){
677       if ($this->reconnect) $this->connect();
678       $start= microtime();
679       $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
680       $this->error = @ldap_error($this->cid);
681       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=modify('$this->basedn')");
682       return($r ? $r : 0);
683     }else{
684       $this->error = "Could not connect to LDAP server";
685       return("");
686     }
687   }
689   function add($attrs)
690   {
691     if($this->hascon){
692       if ($this->reconnect) $this->connect();
693       $start= microtime();
694       $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
695       $this->error = @ldap_error($this->cid);
696       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=add('$this->basedn')");
697       return($r ? $r : 0);
698     }else{
699       $this->error = "Could not connect to LDAP server";
700       return("");
701     }
702   }
705   function create_missing_trees($target)
706   {
707     global $config;
709     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
711     if ($target == $this->basedn){
712       $l= array("dummy");
713     } else {
714       $l= array_reverse(gosa_ldap_explode_dn($real_path));
715     }
716     unset($l['count']);
717     $cdn= $this->basedn;
718     $tag= "";
720     /* Load schema if available... */
721     $classes= $this->get_objectclasses();
723     foreach ($l as $part){
724       if ($part != "dummy"){
725         $cdn= "$part,$cdn";
726       }
728       /* Ignore referrals */
729       $found= false;
730       foreach($this->referrals as $ref){
731         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
732         if ($base == $cdn){
733           $found= true;
734           break;
735         }
736       }
737       if ($found){
738         continue;
739       }
741       $this->cat ($cdn);
742       $attrs= $this->fetch();
744       /* Create missing entry? */
745       if (count ($attrs)){
746         /* Catch the tag - if present */
747         if (isset($attrs['gosaUnitTag'][0])){
748           $tag= $attrs['gosaUnitTag'][0];
749         }
751       } else {
752         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
753         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
755         $na= array();
757         /* Automatic or traditional? */
758         if(count($classes)){
760           /* Get name of first matching objectClass */
761           $ocname= "";
762           foreach($classes as $class){
763             if (isset($class['MUST']) && $class['MUST'] == "$type"){
765               /* Look for first classes that is structural... */
766               if (isset($class['STRUCTURAL'])){
767                 $ocname= $class['NAME'];
768                 break;
769               }
771               /* Look for classes that are auxiliary... */
772               if (isset($class['AUXILIARY'])){
773                 $ocname= $class['NAME'];
774               }
775             }
776           }
778           /* Bail out, if we've nothing to do... */
779           if ($ocname == ""){
780             print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
781             echo $_SESSION['errors'];
782             exit;
783           }
785           /* Assemble_entry */
786           if ($tag != ""){
787             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
788             $na["gosaUnitTag"]= $tag;
789           } else {
790             $na['objectClass']= array($ocname);
791           }
792           if (isset($classes[$ocname]['AUXILIARY'])){
793             $na['objectClass'][]= $classes[$ocname]['SUP'];
794           }
795           if ($type == "dc"){
796             /* This is bad actually, but - tell me a better way? */
797             $na['objectClass'][]= 'locality';
798           }
799           $na[$type]= $param;
800           if (is_array($classes[$ocname]['MUST'])){
801             foreach($classes[$ocname]['MUST'] as $attr){
802               $na[$attr]= "filled";
803             }
804           }
806         } else {
808           /* Use alternative add... */
809           switch ($type){
810             case 'ou':
811               if ($tag != ""){
812                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
813                 $na["gosaUnitTag"]= $tag;
814               } else {
815                 $na["objectClass"]= "organizationalUnit";
816               }
817               $na["ou"]= $param;
818               break;
819             case 'dc':
820               if ($tag != ""){
821                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
822                 $na["gosaUnitTag"]= $tag;
823               } else {
824                 $na["objectClass"]= array("dcObject", "top", "locality");
825               }
826               $na["dc"]= $param;
827               break;
828             default:
829               print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
830               echo $_SESSION['errors'];
831               exit;
832           }
834         }
835         $this->cd($cdn);
836         $this->add($na);
837         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
838         if (!preg_match('/success/i', $this->error)){
839           return FALSE;
840         }
841       }
842     }
844     return TRUE;
845   }
848   function create_missing_trees_old($target)
849   {
850     /* Ignore create_missing trees if the base equals target */
851     if ($target == $this->basedn){
852      return;
853     }
855     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
856     $tmp = ldap_explode_dn($real_path,0);
857     if(!$tmp){
858       print_red(sprintf(_("The referral url '%s' is missing the ldap base. It should look like this 'ldap://server:port/base'."),$this->fix($this->basedn)));
859       return;
860     }
862     $l= array_reverse($tmp);
863     unset($l['count']);
864     $cdn= $this->basedn;
865     $tag= "";
867     foreach ($l as $part){
868       $cdn= "$part,$cdn";
870       /* Ignore referrals */
871       $found= false;
872       foreach($this->referrals as $ref){
873         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
874         if ($base == $cdn){
875           $found= true;
876           break;
877         }
878       }
879       if ($found){
880         continue;
881       }
883       $this->cat ($cdn);
884       $attrs= $this->fetch();
886       /* Create missing entry? */
887       if (count ($attrs)){
888       
889         /* Catch the tag - if present */
890         if (isset($attrs['gosaUnitTag'][0])){
891           $tag= $attrs['gosaUnitTag'][0];
892         }
894       } else {
895         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
896         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
898         $na= array();
899         switch ($type){
900           case 'ou':
901             if ($tag != ""){
902               $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
903               $na["gosaUnitTag"]= $tag;
904             } else {
905               $na["objectClass"]= "organizationalUnit";
906             }
907             $na["ou"]= $param;
908             break;
909           case 'dc':
910             if ($tag != ""){
911               $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
912               $na["gosaUnitTag"]= $tag;
913             } else {
914               $na["objectClass"]= array("dcObject", "top", "locality");
915             }
916             $na["dc"]= $param;
917             break;
918           default:
919             print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
920             echo $_SESSION['errors'];
921             exit;
922         }
923         $this->cd($cdn);
924         $this->add($na);
925       }
926     }
927   }
929   function recursive_remove()
930   {
931     $delarray= array();
933     /* Get sorted list of dn's to delete */
934     $this->search ("(objectClass=*)");
935     while ($this->fetch()){
936       $deldn= $this->getDN();
937       $delarray[$deldn]= strlen($deldn);
938     }
939     arsort ($delarray);
940     reset ($delarray);
942     /* Delete all dn's in subtree */
943     foreach ($delarray as $key => $value){
944       $this->rmdir($key);
945     }
946   }
948   function get_attribute($dn, $name,$r_array=0)
949   {
950     $data= "";
951     if ($this->reconnect) $this->connect();
952     $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
954     /* fill data from LDAP */
955     if ($sr) {
956       $ei= @ldap_first_entry($this->cid, $sr);
957       if ($ei) {
958         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
959           $data= $info[0];
960         }
962       }
963     }
964     if($r_array==0)
965     return ($data);
966     else
967     return ($info);
968   
969   
970   }
971  
974   function get_additional_error()
975   {
976     $error= "";
977     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
978     return ($error);
979   }
981   function get_error()
982   {
983     if ($this->error == 'Success'){
984       return $this->error;
985     } else {
986       $adderror= $this->get_additional_error();
987       if ($adderror != ""){
988         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
989       } else {
990         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
991       }
992       return $error;
993     }
994   }
996   function get_credentials($url, $referrals= NULL)
997   {
998     $ret= array();
999     $url= preg_replace('!\?\?.*$!', '', $url);
1000     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
1002     if ($referrals == NULL){
1003       $referrals= $this->referrals;
1004     }
1006     if (isset($referrals[$server])){
1007       return ($referrals[$server]);
1008     } else {
1009       $ret['ADMIN']= $this->fix($this->binddn);
1010       $ret['PASSWORD']= $this->bindpw;
1011     }
1013     return ($ret);
1014   }
1017   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
1018   {
1019     $display= "";
1021     if ($recursive){
1022       $this->cd($dn);
1023       $this->search("$filter", array('dn'));
1024       while ($attrs= $this->fetch()){
1025         $display.= $this->gen_one_entry($attrs['dn'], $filter, $attributes);
1026         $display.= "\n";
1027       }
1028     } else {
1029       $display.= $this->gen_one_entry($dn);
1030     }
1032     return ($display);
1033   }
1035 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1036   {
1037     $display= "";
1039       $this->cd($dn);
1040       $this->search("$filter");
1042       $i=0;
1043       while ($attrs= $this->fetch()){
1044         $j=0;
1046         foreach ($attributes as $at){
1047           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1048           $j++;
1049         }
1051         $i++;
1052       }
1054     return ($display);
1055   }
1058   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
1059   {
1060     $ret = "";
1061     $data = "";
1062     if($this->reconnect){
1063       $this->connect();
1064     }
1066     /* Searching Ldap Tree */
1067     $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
1069     /* Get the first entry */   
1070     $entry= @ldap_first_entry($this->cid, $sr);
1072     /* Get all attributes related to that Objekt */
1073     $atts = array();
1074     
1075     /* Assemble dn */
1076     $atts[0]['name']  = "dn";
1077     $atts[0]['value'] = array('count' => 1, 0 => $dn);
1079     /* Reset index */
1080     $i = 1 ; 
1081   $identifier = array();
1082     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
1083     while ($attribute) {
1084       $i++;
1085       $atts[$i]['name']  = $attribute;
1086       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
1088       /* Next one */
1089       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
1090     }
1092     foreach($atts as $at)
1093     {
1094       for ($i= 0; $i<$at['value']['count']; $i++){
1096         /* Check if we must encode the data */
1097         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
1098           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
1099         } else {
1100           $ret .= $at['name'].": ".$at['value'][$i]."\n";
1101         }
1102       }
1103     }
1105     return($ret);
1106   }
1109   function dn_exists($dn)
1110   {
1111     return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
1112   }
1113   
1116   function import_complete_ldif($str_attr,&$error,$overwrite,$cleanup)
1117   {
1118     if($this->reconnect) $this->connect();
1120     /* First we have to splitt the string ito detect empty lines
1121        An empty line indicates an new Entry */
1122     $entries = split("\n",$str_attr);
1124     $data = "";
1125     $cnt = 0; 
1126     $current_line = 0;
1128     /* Every single line ... */
1129     foreach($entries as $entry) {
1130       $current_line ++;
1132       /* Removing Spaces to .. 
1133          .. test if a new entry begins */
1134       $tmp  = str_replace(" ","",$data );
1136       /* .. prevent empty lines in an entry */
1137       $tmp2 = str_replace(" ","",$entry);
1139       /* If the Block ends (Empty Line) */
1140       if((empty($entry))&&(!empty($tmp))) {
1141         /* Add collected lines as a complete block */
1142         $all[$cnt] = $data;
1143         $cnt ++;
1144         $data ="";
1145       } else {
1147         /* Append lines ... */
1148         if(!empty($tmp2)) {
1149           /* check if we need base64_decode for this line */
1150           if(ereg("::",$tmp2))
1151           {
1152             $encoded = split("::",$entry);
1153             $attr  = $encoded[0];
1154             $value = base64_decode($encoded[1]);
1155             /* Add linenumber */
1156             $data .= $current_line."#".$attr.":".$value."\n";
1157           }
1158           else
1159           {
1160             /* Add Linenumber */ 
1161             $data .= $current_line."#".$entry."\n";
1162           }
1163         }
1164       }
1165     }
1167     /* The Data we collected is not in the array all[];
1168        For example the Data is stored like this..
1170        all[0] = "1#dn : .... \n 
1171        2#ObjectType: person \n ...."
1172        
1173        Now we check every insertblock and try to insert */
1174     foreach ( $all as $single) {
1175       $lineone = split("\n",$single);  
1176       $ndn = split("#", $lineone[0]);
1177       $line = $ndn[1];
1179       $dnn = split (":",$line,2);
1180       $current_line = $ndn[0];
1181       $dn    = $dnn[0];
1182       $value = $dnn[1];
1184       /* Every block must begin with a dn */
1185       if($dn != "dn") {
1186         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1187         return -2;  
1188       }
1190       /* Should we use Modify instead of Add */
1191       $usemodify= false;
1193       /* Delete before insert */
1194       $usermdir= false;
1195     
1196       /* The dn address already exists! */
1197       if (($this->dn_exists($value))&&((!$overwrite)&&(!$cleanup))) {
1199         $error= sprintf(_("The dn: '%s' (from line %s) already exists in the LDAP database."), $line, $current_line);
1200         return ALREADY_EXISTING_ENTRY;   
1202       } elseif(($this->dn_exists($value))&&($cleanup)){
1204         /* Delete first, then add */
1205         $usermdir = true;        
1207       } elseif(($this->dn_exists($value))&&($overwrite)) {
1208         
1209         /* Modify instead of Add */
1210         $usemodify = true;
1211       }
1212      
1213       /* If we can't Import, return with a file error */
1214       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1215         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1216                         $current_line);
1217         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1218     }
1220     return (INSERT_OK);
1221   }
1224   /* Imports a single entry */
1225   function import_single_entry($str_attr,$modify,$delete)
1226   {
1227     if($this->reconnect) $this->connect();
1229     $ret = false;
1230     $rows= split("\n",$str_attr);
1231     $data= false;
1233     foreach($rows as $row) {
1234       
1235       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1236          Linenumbers) Linenumbers are use like this 123#attribute : value */
1237       if(!empty($row)) {
1238         if((strpos($row,"#")!=FALSE)&&(strpos($row,"#")<strpos($row,":"))) {
1240           /* We are using line numbers 
1241              Because there is a # before a : */
1242           $tmp1= split("#",$row);
1243           $current_line= $tmp1[0];
1244           $row= $tmp1[1];
1245         }
1247         /* Split the line into  attribute  and value */
1248         $attr   = split(":", $row,2);
1249         $attr[0]= trim($attr[0]);  /* attribute */
1250         $attr[1]= trim($attr[1]);  /* value */
1252         /* Check for attributes that are used more than once */
1253         if(!isset($data[$attr[0]])) {
1254           $data[$attr[0]]=$attr[1];
1255         } else {
1256           $tmp = $data[$attr[0]];
1258           if(!is_array($tmp)) {
1259             $new[0]=$tmp;
1260             $new[1]=$attr[1];
1261             $datas[$attr[0]]['count']=1;             
1262             $data[$attr[0]]=$new;
1263           } else {
1264             $cnt = $datas[$attr[0]]['count'];           
1265             $cnt ++;
1266             $data[$attr[0]][$cnt]=$attr[1];
1267             $datas[$attr[0]]['count'] = $cnt;
1268           }
1269         }
1270       }
1271     } 
1272     
1273     /* If dn is an index of data, we should try to insert the data */
1274     if(isset($data['dn'])) {
1275       /* Creating Entry */
1276       $this->cd($data['dn']);
1278       /* Delete existing entry */
1279       if($delete){
1280         $this->rmdir($data['dn']);
1281       }
1282       
1283       /* Create missing trees */
1284       $this->create_missing_trees($data['dn']);
1285       unset($data['dn']);
1286       
1287       /* If entry exists use modify */
1288       if(!$modify){
1289         $ret = $this->add($data);
1290       } else {
1291         $ret = $this->modify($data);
1292       }
1293     }
1295     return($ret);
1296   }
1298   
1299   function importcsv($str)
1300   {
1301     $lines = split("\n",$str);
1302     foreach($lines as $line)
1303     {
1304       /* continue if theres a comment */
1305       if(substr(trim($line),0,1)=="#"){
1306         continue;
1307       }
1309       $line= str_replace ("\t\t","\t",$line);
1310       $line= str_replace ("\t"  ,"," ,$line);
1311       echo $line;
1313       $cells = split(",",$line )  ;
1314       $linet= str_replace ("\t\t",",",$line);
1315       $cells = split("\t",$line);
1316       $count = count($cells);  
1317     }
1319   }
1320   
1321   function get_objectclasses_old()
1322   {
1323     $objectclasses = array();
1325           # Get base to look for schema 
1326           $sr           = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1327           $attr         = @ldap_get_entries($this->cid,$sr);
1329           if (!isset($attr[0]['subschemasubentry'][0])){
1330       $this->error  = @ldap_error($this->cid);
1331       gosa_log($this->get_error());
1332       return array();
1333           }
1334         
1335           # Get list of objectclasses
1336           $nb= $attr[0]['subschemasubentry'][0];
1337           $objectclasses= array();
1338           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1339           $attrs= ldap_get_entries($this->cid,$sr);
1340           if (!isset($attrs[0])){
1341             return array();
1342           }
1343           foreach ($attrs[0]['objectclasses'] as $val){
1344             $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1345             if ($name != $val){
1346               $objectclasses[$name]= $val;
1347             }
1348           }
1349           
1350           return $objectclasses;
1351   }
1354   function get_objectclasses()
1355   {
1356     global $config;
1357     $objectclasses = array();
1359     if(isset($config) && preg_match("/config/i",get_class($config))){
1360       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1361         return($objectclasses);
1362       }
1363     }
1365 # Get base to look for schema
1366     $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1367     if(!$sr){
1368       $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1369     }
1371     $attr = @ldap_get_entries($this->cid,$sr);
1372     if (!isset($attr[0]['subschemasubentry'][0])){
1373       return array();
1374     }
1376     /* Get list of objectclasses and fill array */
1377     $nb= $attr[0]['subschemasubentry'][0];
1378     $objectclasses= array();
1379     $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1380     $attrs= ldap_get_entries($this->cid,$sr);
1381     if (!isset($attrs[0])){
1382       return array();
1383     }
1384     foreach ($attrs[0]['objectclasses'] as $val){
1385       if (preg_match('/^[0-9]+$/', $val)){
1386         continue;
1387       }
1388       $name= "OID";
1389       $pattern= split(' ', $val);
1390       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1391       $objectclasses[$ocname]= array();
1393       foreach($pattern as $chunk){
1394         switch($chunk){
1396           case '(':
1397             $value= "";
1398             break;
1400           case ')': if ($name != ""){
1401                       $objectclasses[$ocname][$name]= $this->value2container($value);
1402                     }
1403                     $name= "";
1404                     $value= "";
1405                     break;
1407           case 'NAME':
1408           case 'DESC':
1409           case 'SUP':
1410           case 'STRUCTURAL':
1411           case 'ABSTRACT':
1412           case 'AUXILIARY':
1413           case 'MUST':
1414           case 'MAY':
1415                     if ($name != ""){
1416                       $objectclasses[$ocname][$name]= $this->value2container($value);
1417                     }
1418                     $name= $chunk;
1419                     $value= "";
1420                     break;
1422           default:  $value.= $chunk." ";
1423         }
1424       }
1426     }
1428     return $objectclasses;
1429   }
1431   function value2container($value)
1432   {
1433     /* Set emtpy values to "true" only */
1434     if (preg_match('/^\s*$/', $value)){
1435       return true;
1436     }
1438     /* Remove ' and " if needed */
1439     $value= preg_replace('/^[\'"]/', '', $value);
1440     $value= preg_replace('/[\'"] *$/', '', $value);
1442     /* Convert to array if $ is inside... */
1443     if (preg_match('/\$/', $value)){
1444       $container= preg_split('/\s*\$\s*/', $value);
1445     } else {
1446       $container= chop($value);
1447     }
1449     return ($container);
1450   }
1452   function log($string)
1453   {
1454     if (isset($_SESSION['config'])){
1455       $cfg= $_SESSION['config'];
1456       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1457         syslog (LOG_INFO, $string);
1458       }
1459     }
1460   }
1461   
1462   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1463   function getCn($dn){
1464     $simple= split(",", $dn);
1466     foreach($simple as $piece) {
1467       $partial= split("=", $piece);
1469       if($partial[0] == "cn"){
1470         return $partial[1];
1471       }
1472     }
1474   }
1476   function get_naming_contexts($server, $admin= "", $password= "")
1477   {
1478     /* Build LDAP connection */
1479     $ds= ldap_connect ($server);
1480     if (!$ds) {
1481       die ("Can't bind to LDAP. No check possible!");
1482     }
1483     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1484     $r= ldap_bind ($ds, $admin, $password);
1486     /* Get base to look for naming contexts */
1487     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1488     $attr= @ldap_get_entries($ds,$sr);
1490     return ($attr[0]['namingcontexts']);
1491   }
1495 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1496 ?>