Code

- Corrected attr in place of attrs
[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   function search($filter, $attrs= array())
193   {
194     if($this->hascon){
195       if ($this->reconnect) $this->connect();
197       $start = microtime();
198       $this->clearResult();
199       $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
200       $this->error = @ldap_error($this->cid);
201       $this->resetResult();
202       $this->hasres=true;
203    
204       /* Check if query took longer as specified in max_ldap_query_time */
205       if($this->max_ldap_query_time){
206         $diff = get_MicroTimeDiff($start,microtime());
207         if($diff > $this->max_ldap_query_time){
208           print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
209         }
210       }
212       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
214       return($this->sr);
215     }else{
216       $this->error = "Could not connect to LDAP server";
217       return("");
218     }
219   }
221   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
222   {
223     if($this->hascon){
224       if ($this->reconnect) $this->connect();
226       $this->clearResult();
227       if ($basedn == "")
228         $basedn = $this->basedn;
229       else
230         $basedn= $this->convert($basedn);
231   
232       $start = microtime();
234       $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
235       $this->error = @ldap_error($this->cid);
236       $this->resetResult();
237       $this->hasres=true;
239        /* Check if query took longer as specified in max_ldap_query_time */
240       if($this->max_ldap_query_time){
241         $diff = get_MicroTimeDiff($start,microtime());
242         if($diff > $this->max_ldap_query_time){
243           print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
244         }
245       }
247       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
249       return($this->sr);
250     }else{
251       $this->error = "Could not connect to LDAP server";
252       return("");
253     }
254   }
256   function cat($dn,$attrs= array("*"))
257   {
258     if($this->hascon){
259       if ($this->reconnect) $this->connect();
261       $start = microtime();
262       $this->clearResult();
263       $filter = "(objectclass=*)";
264       $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
265       $this->error = @ldap_error($this->cid);
266       $this->resetResult();
267       $this->hasres=true;
268       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=cat('".$this->fix($dn)."')");
269       return($this->sr);
270     }else{
271       $this->error = "Could not connect to LDAP server";
272       return("");
273     }
274   }
276   function set_size_limit($size)
277   {
278     /* Ignore zero settings */
279     if ($size == 0){
280       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
281     }
282     if($this->hascon){
283       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
284     } else {
285       $this->error = "Could not connect to LDAP server";
286     }
287   }
289   function fetch()
290   {
291     $att= array();
292     if($this->hascon){
293       if($this->hasres){
294         if ($this->start == 0)
295         {
296           $this->start = 1;
297           $this->re= @ldap_first_entry($this->cid, $this->sr);
298         } else {
299           $this->re= @ldap_next_entry($this->cid, $this->re);
300         }
301         if ($this->re)
302         {
303           $att= @ldap_get_attributes($this->cid, $this->re);
304           $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
305         }
306         $this->error = @ldap_error($this->cid);
307         if (!isset($att)){
308           $att= array();
309         }
310         return($att);
311       }else{
312         $this->error = "Perform a Fetch with no Search";
313         return("");
314       }
315     }else{
316       $this->error = "Could not connect to LDAP server";
317       return("");
318     }
319   }
321   function resetResult()
322   {
323     $this->start = 0;
324   }
326   function clearResult()
327   {
328     if($this->hasres){
329       $this->hasres = false;
330       @ldap_free_result($this->sr);
331     }
332   }
334   function getDN()
335   {
336     if($this->hascon){
337       if($this->hasres){
339         if(!$this->re)
340           {
341           $this->error = "Perform a Fetch with no valid Result";
342           }
343           else
344           {
345           $rv = @ldap_get_dn($this->cid, $this->re);
346         
347           $this->error = @ldap_error($this->cid);
348           return(trim($this->convert($rv)));
349            }
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 count()
361   {
362     if($this->hascon){
363       if($this->hasres){
364         $rv = @ldap_count_entries($this->cid, $this->sr);
365         $this->error = @ldap_error($this->cid);
366         return($rv);
367       }else{
368         $this->error = "Perform a Fetch with no Search";
369         return("");
370       }
371     }else{
372       $this->error = "Could not connect to LDAP server";
373       return("");
374     }
375   }
377   function rm($attrs = "", $dn = "")
378   {
379     if($this->hascon){
380       if ($this->reconnect) $this->connect();
381       if ($dn == "")
382         $dn = $this->basedn;
384       $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
385       $this->error = @ldap_error($this->cid);
386       return($r);
387     }else{
388       $this->error = "Could not connect to LDAP server";
389       return("");
390     }
391   }
393   function rename($attrs, $dn = "")
394   {
395     if($this->hascon){
396       if ($this->reconnect) $this->connect();
397       if ($dn == "")
398         $dn = $this->basedn;
400       $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
401       $this->error = @ldap_error($this->cid);
402       return($r);
403     }else{
404       $this->error = "Could not connect to LDAP server";
405       return("");
406     }
407   }
409   function rmdir($deletedn)
410   {
411     if($this->hascon){
412       if ($this->reconnect) $this->connect();
413       $start= microtime();
414       $r = @ldap_delete($this->cid, $this->fix($deletedn));
415       $this->error = @ldap_error($this->cid);
416       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=delete('".$this->fix($deletedn)."')");
417       return($r ? $r : 0);
418     }else{
419       $this->error = "Could not connect to LDAP server";
420       return("");
421     }
422   }
424   /**
425   *  Function rmdir_recursive
426   *
427   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
428   *  Parameters:  The dn to delete
429   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
430   *
431   */
433   function rmdir_recursive($deletedn)
434   {
435     if($this->hascon){
436       if ($this->reconnect) $this->connect();
437       $delarray= array();
438         
439       /* Get sorted list of dn's to delete */
440       $this->ls ("(objectClass=*)",$deletedn);
441       while ($this->fetch()){
442         $deldn= $this->getDN();
443         $delarray[$deldn]= strlen($deldn);
444       }
445       arsort ($delarray);
446       reset ($delarray);
448       /* Really Delete ALL dn's in subtree */
449       foreach ($delarray as $key => $value){
450         $this->rmdir_recursive($key);
451       }
452       
453       /* Finally Delete own Node */
454       $r = @ldap_delete($this->cid, $this->fix($deletedn));
455       $this->error = @ldap_error($this->cid);
456       return($r ? $r : 0);
457     }else{
458       $this->error = "Could not connect to LDAP server";
459       return("");
460     }
461   }
463   /* Copy given attributes and sub-dns with attributes to destination dn 
464   */
465   function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
466   {
467     error_reporting(E_ALL);
468     
469     if($is_first){
470       echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
471     }else{
472       if(preg_match("/^ou=/",$sourcedn)){
473         echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
474       }else{
475         $tmp = split(",",$sourcedn);
476         
477         echo "&nbsp;<b>"._("Object").":</b> ";
479         $deststr = @LDAP::fix($destinationdn);
480         if(strlen($deststr) > 96){
481           $deststr = substr($deststr,0,96)."...";
482         }
484         echo $deststr."<br>";
485       }
486     }
488     flush();
489     
490     if($this->hascon){
491       if ($this->reconnect) $this->connect();
493       /* Save base dn */
494       $basedn= $this->basedn;
495       $delarray= array();
496      
497       /* Check if destination entry already exists */
498       $this->cat($destinationdn);
500       if($this->count()){
501         return;
502       }else{
503         
504         $this->clearResult();
506         /* Get source entry */
507         $this->cd($basedn);
508         $this->cat($sourcedn);
509         $attr = $this->fetch();
511         /* Error while fetching object / attribute abort*/
512         if((!$attr) || (count($attr)) ==0) {
513           echo _("Error while fetching source dn - aborted!");
514           return;
515         }
516   
517         /* check if this is a department */
518         if(in_array("organizationalUnit",$attr['objectClass'])){
519           $attr['dn'] = $this->convert($destinationdn);
520           $this->cd($basedn);
521           $this->create_missing_trees($destinationdn);
522           $this->cd($destinationdn);
524           /* If is first entry, append FAIbranch to department entry */
525           if($is_first){
526             $this->cat($destinationdn);
527             $attr= $this->fetch();
529             /* Filter unneeded informations */
530             foreach($attr as $key => $value){
531               if(is_numeric($key)) unset($attr[$key]);
532               if(isset($attr[$key]['count'])){
533                 if(is_array($attr[$key])){
534                   unset($attr[$key]['count']);
535                 }
536               }
537             }
538             
539             unset($attr['count']);
540             unset($attr['dn']);
542             /* Add marking attribute */
543             $attr['objectClass'][] = "FAIbranch";
544             
545             /* Add this entry */
546             $this->modify($attr);
547           }
548         }else{
550           /* If this is no department */
551           foreach($attr as $key => $value){
552             if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon"))){
553               $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
554               $ei= ldap_first_entry($this->cid, $sr);
555               if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
556                 $attr[$key] = $tmp;
557               }
558             }
560             if(is_numeric($key)) unset($attr[$key]);
561             if(isset($attr[$key]['count'])){
562               if(is_array($attr[$key])){
563                 unset($attr[$key]['count']);
564               }
565             }
566           }
567           unset($attr['count']);
568           unset($attr['dn']);
570           if(!in_array("gosaApplication" , $attr['objectClass'])){
571             if($type=="branch"){
572               $attr['FAIstate'] ="branch";
573             }elseif($type=="freeze"){
574               $attr['FAIstate'] ="freeze";
575             }else{
576               print_red(_("Unknown FAIstate %s"),$type);
577             }
578           }elseif(in_array("gosaApplication",$attr['objectClass'])){
579             if(!in_array("FAIobject",$attr['objectClass'])){
580               $attr['objectClass'][] = "FAIobject";
581             }
582             $attr['FAIstate'] = $type;
583           }
585           /* Replace FAIdebianRelease with new release name */
586           if(in_array("FAIpackageList" , $attr['objectClass'])){
587             $attr['FAIdebianRelease'] = $destinationName;
588             if($type=="branch"){
589               $attr['FAIstate'] ="branch";
590             }elseif($type=="freeze"){
591               $attr['FAIstate'] ="freeze";
592             }else{
593               print_red(_("Unknown FAIstate %s"),$type);
594             }
595           }
597           /* Add entry */
598           $this->cd($destinationdn);
599           $this->cat($destinationdn);
600           $a = $this->fetch();
601           if(!count($a)){
602             $this->add($attr);
603           }
605           if($this->error != "Success"){
606             /* Some error occurred */
607             print "---------------------------------------------";
608             print $this->get_error()."<br>";
609             print $sourcedn."<br>";
610             print $destinationdn."<br>";
611             print_a( $attr);
612             exit();
613           }          
614         }
615       }
617       echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
619       $this->ls ("(objectClass=*)",$sourcedn);
620       while ($this->fetch()){
621         $deldn= $this->getDN();
622         $delarray[$deldn]= strlen($deldn);
623       }
624       asort ($delarray);
625       reset ($delarray);
627        $depth ++;
628       foreach($delarray as $dn => $bla){
629         if($dn != $destinationdn){
630           $this->cd($basedn);
631           $item = $this->fetch($this->cat($dn));
632           if(!in_array("FAIbranch",$item['objectClass'])){
633             $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
634           } 
635         }
636       }
637     }
638     if($is_first){
639       echo "<p class='seperator'>&nbsp;</p>";
640     }
642   }
644   function modify($attrs)
645   {
646     if(count($attrs) == 0){
647       return (0);
648     }
649     if($this->hascon){
650       if ($this->reconnect) $this->connect();
651       $start= microtime();
652       $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
653       $this->error = @ldap_error($this->cid);
654       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=modify('$this->basedn')");
655       return($r ? $r : 0);
656     }else{
657       $this->error = "Could not connect to LDAP server";
658       return("");
659     }
660   }
662   function add($attrs)
663   {
664     if($this->hascon){
665       if ($this->reconnect) $this->connect();
666       $start= microtime();
667       $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
668       $this->error = @ldap_error($this->cid);
669       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=add('$this->basedn')");
670       return($r ? $r : 0);
671     }else{
672       $this->error = "Could not connect to LDAP server";
673       return("");
674     }
675   }
678   function create_missing_trees($target)
679   {
680     global $config;
682     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
684     if ($target == $this->basedn){
685       $l= array("dummy");
686     } else {
687       $l= array_reverse(gosa_ldap_explode_dn($real_path));
688     }
689     unset($l['count']);
690     $cdn= $this->basedn;
691     $tag= "";
693     /* Load schema if available... */
694     $classes= $this->get_objectclasses();
696     foreach ($l as $part){
697       if ($part != "dummy"){
698         $cdn= "$part,$cdn";
699       }
701       /* Ignore referrals */
702       $found= false;
703       foreach($this->referrals as $ref){
704         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
705         if ($base == $cdn){
706           $found= true;
707           break;
708         }
709       }
710       if ($found){
711         continue;
712       }
714       $this->cat ($cdn);
715       $attrs= $this->fetch();
717       /* Create missing entry? */
718       if (count ($attrs)){
719         /* Catch the tag - if present */
720         if (isset($attrs['gosaUnitTag'][0])){
721           $tag= $attrs['gosaUnitTag'][0];
722         }
724       } else {
725         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
726         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
728         $na= array();
730         /* Automatic or traditional? */
731         if(count($classes)){
733           /* Get name of first matching objectClass */
734           $ocname= "";
735           foreach($classes as $class){
736             if (isset($class['MUST']) && $class['MUST'] == "$type"){
738               /* Look for first classes that is structural... */
739               if (isset($class['STRUCTURAL'])){
740                 $ocname= $class['NAME'];
741                 break;
742               }
744               /* Look for classes that are auxiliary... */
745               if (isset($class['AUXILIARY'])){
746                 $ocname= $class['NAME'];
747               }
748             }
749           }
751           /* Bail out, if we've nothing to do... */
752           if ($ocname == ""){
753             print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
754             echo $_SESSION['errors'];
755             exit;
756           }
758           /* Assemble_entry */
759           if ($tag != ""){
760             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
761             $na["gosaUnitTag"]= $tag;
762           } else {
763             $na['objectClass']= array($ocname);
764           }
765           if (isset($classes[$ocname]['AUXILIARY'])){
766             $na['objectClass'][]= $classes[$ocname]['SUP'];
767           }
768           if ($type == "dc"){
769             /* This is bad actually, but - tell me a better way? */
770             $na['objectClass'][]= 'locality';
771           }
772           $na[$type]= $param;
773           if (is_array($classes[$ocname]['MUST'])){
774             foreach($classes[$ocname]['MUST'] as $attr){
775               $na[$attr]= "filled";
776             }
777           }
779         } else {
781           /* Use alternative add... */
782           switch ($type){
783             case 'ou':
784               if ($tag != ""){
785                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
786                 $na["gosaUnitTag"]= $tag;
787               } else {
788                 $na["objectClass"]= "organizationalUnit";
789               }
790               $na["ou"]= $param;
791               break;
792             case 'dc':
793               if ($tag != ""){
794                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
795                 $na["gosaUnitTag"]= $tag;
796               } else {
797                 $na["objectClass"]= array("dcObject", "top", "locality");
798               }
799               $na["dc"]= $param;
800               break;
801             default:
802               print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
803               echo $_SESSION['errors'];
804               exit;
805           }
807         }
808         $this->cd($cdn);
809         $this->add($na);
810         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
811         if (!preg_match('/success/i', $this->error)){
812           return FALSE;
813         }
814       }
815     }
817     return TRUE;
818   }
821   function create_missing_trees_old($target)
822   {
823     /* Ignore create_missing trees if the base equals target */
824     if ($target == $this->basedn){
825      return;
826     }
828     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
829     $tmp = ldap_explode_dn($real_path,0);
830     if(!$tmp){
831       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)));
832       return;
833     }
835     $l= array_reverse($tmp);
836     unset($l['count']);
837     $cdn= $this->basedn;
838     $tag= "";
840     foreach ($l as $part){
841       $cdn= "$part,$cdn";
843       /* Ignore referrals */
844       $found= false;
845       foreach($this->referrals as $ref){
846         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
847         if ($base == $cdn){
848           $found= true;
849           break;
850         }
851       }
852       if ($found){
853         continue;
854       }
856       $this->cat ($cdn);
857       $attrs= $this->fetch();
859       /* Create missing entry? */
860       if (count ($attrs)){
861       
862         /* Catch the tag - if present */
863         if (isset($attrs['gosaUnitTag'][0])){
864           $tag= $attrs['gosaUnitTag'][0];
865         }
867       } else {
868         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
869         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
871         $na= array();
872         switch ($type){
873           case 'ou':
874             if ($tag != ""){
875               $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
876               $na["gosaUnitTag"]= $tag;
877             } else {
878               $na["objectClass"]= "organizationalUnit";
879             }
880             $na["ou"]= $param;
881             break;
882           case 'dc':
883             if ($tag != ""){
884               $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
885               $na["gosaUnitTag"]= $tag;
886             } else {
887               $na["objectClass"]= array("dcObject", "top", "locality");
888             }
889             $na["dc"]= $param;
890             break;
891           default:
892             print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
893             echo $_SESSION['errors'];
894             exit;
895         }
896         $this->cd($cdn);
897         $this->add($na);
898       }
899     }
900   }
902   function recursive_remove()
903   {
904     $delarray= array();
906     /* Get sorted list of dn's to delete */
907     $this->search ("(objectClass=*)");
908     while ($this->fetch()){
909       $deldn= $this->getDN();
910       $delarray[$deldn]= strlen($deldn);
911     }
912     arsort ($delarray);
913     reset ($delarray);
915     /* Delete all dn's in subtree */
916     foreach ($delarray as $key => $value){
917       $this->rmdir($key);
918     }
919   }
921   function get_attribute($dn, $name,$r_array=0)
922   {
923     $data= "";
924     if ($this->reconnect) $this->connect();
925     $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
927     /* fill data from LDAP */
928     if ($sr) {
929       $ei= @ldap_first_entry($this->cid, $sr);
930       if ($ei) {
931         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
932           $data= $info[0];
933         }
935       }
936     }
937     if($r_array==0)
938     return ($data);
939     else
940     return ($info);
941   
942   
943   }
944  
947   function get_additional_error()
948   {
949     $error= "";
950     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
951     return ($error);
952   }
954   function get_error()
955   {
956     if ($this->error == 'Success'){
957       return $this->error;
958     } else {
959       $adderror= $this->get_additional_error();
960       if ($adderror != ""){
961         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
962       } else {
963         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
964       }
965       return $error;
966     }
967   }
969   function get_credentials($url, $referrals= NULL)
970   {
971     $ret= array();
972     $url= preg_replace('!\?\?.*$!', '', $url);
973     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
975     if ($referrals == NULL){
976       $referrals= $this->referrals;
977     }
979     if (isset($referrals[$server])){
980       return ($referrals[$server]);
981     } else {
982       $ret['ADMIN']= $this->fix($this->binddn);
983       $ret['PASSWORD']= $this->bindpw;
984     }
986     return ($ret);
987   }
990   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
991   {
992     $display= "";
994     if ($recursive){
995       $this->cd($dn);
996       $this->search("$filter", array('dn'));
997       while ($attrs= $this->fetch()){
998         $display.= $this->gen_one_entry($attrs['dn'], $filter, $attributes);
999         $display.= "\n";
1000       }
1001     } else {
1002       $display.= $this->gen_one_entry($dn);
1003     }
1005     return ($display);
1006   }
1008 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1009   {
1010     $display= "";
1012       $this->cd($dn);
1013       $this->search("$filter");
1015       $i=0;
1016       while ($attrs= $this->fetch()){
1017         $j=0;
1019         foreach ($attributes as $at){
1020           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1021           $j++;
1022         }
1024         $i++;
1025       }
1027     return ($display);
1028   }
1031   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
1032   {
1033     $ret = "";
1034     $data = "";
1035     if($this->reconnect){
1036       $this->connect();
1037     }
1039     /* Searching Ldap Tree */
1040     $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
1042     /* Get the first entry */   
1043     $entry= @ldap_first_entry($this->cid, $sr);
1045     /* Get all attributes related to that Objekt */
1046     $atts = array();
1047     
1048     /* Assemble dn */
1049     $atts[0]['name']  = "dn";
1050     $atts[0]['value'] = array('count' => 1, 0 => $dn);
1052     /* Reset index */
1053     $i = 1 ; 
1054   $identifier = array();
1055     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
1056     while ($attribute) {
1057       $i++;
1058       $atts[$i]['name']  = $attribute;
1059       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
1061       /* Next one */
1062       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
1063     }
1065     foreach($atts as $at)
1066     {
1067       for ($i= 0; $i<$at['value']['count']; $i++){
1069         /* Check if we must encode the data */
1070         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
1071           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
1072         } else {
1073           $ret .= $at['name'].": ".$at['value'][$i]."\n";
1074         }
1075       }
1076     }
1078     return($ret);
1079   }
1082   function dn_exists($dn)
1083   {
1084     return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
1085   }
1086   
1089   function import_complete_ldif($str_attr,&$error,$overwrite,$cleanup)
1090   {
1091     if($this->reconnect) $this->connect();
1093     /* First we have to splitt the string ito detect empty lines
1094        An empty line indicates an new Entry */
1095     $entries = split("\n",$str_attr);
1097     $data = "";
1098     $cnt = 0; 
1099     $current_line = 0;
1101     /* Every single line ... */
1102     foreach($entries as $entry) {
1103       $current_line ++;
1105       /* Removing Spaces to .. 
1106          .. test if a new entry begins */
1107       $tmp  = str_replace(" ","",$data );
1109       /* .. prevent empty lines in an entry */
1110       $tmp2 = str_replace(" ","",$entry);
1112       /* If the Block ends (Empty Line) */
1113       if((empty($entry))&&(!empty($tmp))) {
1114         /* Add collected lines as a complete block */
1115         $all[$cnt] = $data;
1116         $cnt ++;
1117         $data ="";
1118       } else {
1120         /* Append lines ... */
1121         if(!empty($tmp2)) {
1122           /* check if we need base64_decode for this line */
1123           if(ereg("::",$tmp2))
1124           {
1125             $encoded = split("::",$entry);
1126             $attr  = $encoded[0];
1127             $value = base64_decode($encoded[1]);
1128             /* Add linenumber */
1129             $data .= $current_line."#".$attr.":".$value."\n";
1130           }
1131           else
1132           {
1133             /* Add Linenumber */ 
1134             $data .= $current_line."#".$entry."\n";
1135           }
1136         }
1137       }
1138     }
1140     /* The Data we collected is not in the array all[];
1141        For example the Data is stored like this..
1143        all[0] = "1#dn : .... \n 
1144        2#ObjectType: person \n ...."
1145        
1146        Now we check every insertblock and try to insert */
1147     foreach ( $all as $single) {
1148       $lineone = split("\n",$single);  
1149       $ndn = split("#", $lineone[0]);
1150       $line = $ndn[1];
1152       $dnn = split (":",$line,2);
1153       $current_line = $ndn[0];
1154       $dn    = $dnn[0];
1155       $value = $dnn[1];
1157       /* Every block must begin with a dn */
1158       if($dn != "dn") {
1159         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1160         return -2;  
1161       }
1163       /* Should we use Modify instead of Add */
1164       $usemodify= false;
1166       /* Delete before insert */
1167       $usermdir= false;
1168     
1169       /* The dn address already exists! */
1170       if (($this->dn_exists($value))&&((!$overwrite)&&(!$cleanup))) {
1172         $error= sprintf(_("The dn: '%s' (from line %s) already exists in the LDAP database."), $line, $current_line);
1173         return ALREADY_EXISTING_ENTRY;   
1175       } elseif(($this->dn_exists($value))&&($cleanup)){
1177         /* Delete first, then add */
1178         $usermdir = true;        
1180       } elseif(($this->dn_exists($value))&&($overwrite)) {
1181         
1182         /* Modify instead of Add */
1183         $usemodify = true;
1184       }
1185      
1186       /* If we can't Import, return with a file error */
1187       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1188         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1189                         $current_line);
1190         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1191     }
1193     return (INSERT_OK);
1194   }
1197   /* Imports a single entry */
1198   function import_single_entry($str_attr,$modify,$delete)
1199   {
1200     if($this->reconnect) $this->connect();
1202     $ret = false;
1203     $rows= split("\n",$str_attr);
1204     $data= false;
1206     foreach($rows as $row) {
1207       
1208       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1209          Linenumbers) Linenumbers are use like this 123#attribute : value */
1210       if(!empty($row)) {
1211         if((strpos($row,"#")!=FALSE)&&(strpos($row,"#")<strpos($row,":"))) {
1213           /* We are using line numbers 
1214              Because there is a # before a : */
1215           $tmp1= split("#",$row);
1216           $current_line= $tmp1[0];
1217           $row= $tmp1[1];
1218         }
1220         /* Split the line into  attribute  and value */
1221         $attr   = split(":", $row,2);
1222         $attr[0]= trim($attr[0]);  /* attribute */
1223         $attr[1]= trim($attr[1]);  /* value */
1225         /* Check for attributes that are used more than once */
1226         if(!isset($data[$attr[0]])) {
1227           $data[$attr[0]]=$attr[1];
1228         } else {
1229           $tmp = $data[$attr[0]];
1231           if(!is_array($tmp)) {
1232             $new[0]=$tmp;
1233             $new[1]=$attr[1];
1234             $datas[$attr[0]]['count']=1;             
1235             $data[$attr[0]]=$new;
1236           } else {
1237             $cnt = $datas[$attr[0]]['count'];           
1238             $cnt ++;
1239             $data[$attr[0]][$cnt]=$attr[1];
1240             $datas[$attr[0]]['count'] = $cnt;
1241           }
1242         }
1243       }
1244     } 
1245     
1246     /* If dn is an index of data, we should try to insert the data */
1247     if(isset($data['dn'])) {
1248       /* Creating Entry */
1249       $this->cd($data['dn']);
1251       /* Delete existing entry */
1252       if($delete){
1253         $this->rmdir($data['dn']);
1254       }
1255       
1256       /* Create missing trees */
1257       $this->create_missing_trees($data['dn']);
1258       unset($data['dn']);
1259       
1260       /* If entry exists use modify */
1261       if(!$modify){
1262         $ret = $this->add($data);
1263       } else {
1264         $ret = $this->modify($data);
1265       }
1266     }
1268     return($ret);
1269   }
1271   
1272   function importcsv($str)
1273   {
1274     $lines = split("\n",$str);
1275     foreach($lines as $line)
1276     {
1277       /* continue if theres a comment */
1278       if(substr(trim($line),0,1)=="#"){
1279         continue;
1280       }
1282       $line= str_replace ("\t\t","\t",$line);
1283       $line= str_replace ("\t"  ,"," ,$line);
1284       echo $line;
1286       $cells = split(",",$line )  ;
1287       $linet= str_replace ("\t\t",",",$line);
1288       $cells = split("\t",$line);
1289       $count = count($cells);  
1290     }
1292   }
1293   
1294   function get_objectclasses_old()
1295   {
1296     $objectclasses = array();
1298           # Get base to look for schema 
1299           $sr           = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1300           $attr         = @ldap_get_entries($this->cid,$sr);
1302           if (!isset($attr[0]['subschemasubentry'][0])){
1303       $this->error  = @ldap_error($this->cid);
1304       gosa_log($this->get_error());
1305       return array();
1306           }
1307         
1308           # Get list of objectclasses
1309           $nb= $attr[0]['subschemasubentry'][0];
1310           $objectclasses= array();
1311           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1312           $attrs= ldap_get_entries($this->cid,$sr);
1313           if (!isset($attrs[0])){
1314             return array();
1315           }
1316           foreach ($attrs[0]['objectclasses'] as $val){
1317             $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1318             if ($name != $val){
1319               $objectclasses[$name]= $val;
1320             }
1321           }
1322           
1323           return $objectclasses;
1324   }
1327   function get_objectclasses()
1328   {
1329     global $config;
1330     $objectclasses = array();
1332     if(isset($config) && preg_match("/config/i",get_class($config))){
1333       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1334         return($objectclasses);
1335       }
1336     }
1338 # Get base to look for schema
1339     $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1340     if(!$sr){
1341       $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1342     }
1344     $attr = @ldap_get_entries($this->cid,$sr);
1345     if (!isset($attr[0]['subschemasubentry'][0])){
1346       return array();
1347     }
1349     /* Get list of objectclasses and fill array */
1350     $nb= $attr[0]['subschemasubentry'][0];
1351     $objectclasses= array();
1352     $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1353     $attrs= ldap_get_entries($this->cid,$sr);
1354     if (!isset($attrs[0])){
1355       return array();
1356     }
1357     foreach ($attrs[0]['objectclasses'] as $val){
1358       if (preg_match('/^[0-9]+$/', $val)){
1359         continue;
1360       }
1361       $name= "OID";
1362       $pattern= split(' ', $val);
1363       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1364       $objectclasses[$ocname]= array();
1366       foreach($pattern as $chunk){
1367         switch($chunk){
1369           case '(':
1370             $value= "";
1371             break;
1373           case ')': if ($name != ""){
1374                       $objectclasses[$ocname][$name]= $this->value2container($value);
1375                     }
1376                     $name= "";
1377                     $value= "";
1378                     break;
1380           case 'NAME':
1381           case 'DESC':
1382           case 'SUP':
1383           case 'STRUCTURAL':
1384           case 'ABSTRACT':
1385           case 'AUXILIARY':
1386           case 'MUST':
1387           case 'MAY':
1388                     if ($name != ""){
1389                       $objectclasses[$ocname][$name]= $this->value2container($value);
1390                     }
1391                     $name= $chunk;
1392                     $value= "";
1393                     break;
1395           default:  $value.= $chunk." ";
1396         }
1397       }
1399     }
1401     return $objectclasses;
1402   }
1404   function value2container($value)
1405   {
1406     /* Set emtpy values to "true" only */
1407     if (preg_match('/^\s*$/', $value)){
1408       return true;
1409     }
1411     /* Remove ' and " if needed */
1412     $value= preg_replace('/^[\'"]/', '', $value);
1413     $value= preg_replace('/[\'"] *$/', '', $value);
1415     /* Convert to array if $ is inside... */
1416     if (preg_match('/\$/', $value)){
1417       $container= preg_split('/\s*\$\s*/', $value);
1418     } else {
1419       $container= chop($value);
1420     }
1422     return ($container);
1423   }
1425   function log($string)
1426   {
1427     if (isset($_SESSION['config'])){
1428       $cfg= $_SESSION['config'];
1429       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1430         syslog (LOG_INFO, $string);
1431       }
1432     }
1433   }
1434   
1435   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1436   function getCn($dn){
1437     $simple= split(",", $dn);
1439     foreach($simple as $piece) {
1440       $partial= split("=", $piece);
1442       if($partial[0] == "cn"){
1443         return $partial[1];
1444       }
1445     }
1447   }
1449   function get_naming_contexts($server, $admin= "", $password= "")
1450   {
1451     /* Build LDAP connection */
1452     $ds= ldap_connect ($server);
1453     if (!$ds) {
1454       die ("Can't bind to LDAP. No check possible!");
1455     }
1456     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1457     $r= ldap_bind ($ds, $admin, $password);
1459     /* Get base to look for naming contexts */
1460     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1461     $attr= @ldap_get_entries($ds,$sr);
1463     return ($attr[0]['namingcontexts']);
1464   }
1468 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1469 ?>