Code

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