Code

Fixed parameter expansion
[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 to fix problematic characters in DN's that are used for search
98      requests. I.e. member=....                                               */
99   function prepare4filter($dn)
100   {
101         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', @LDAP::fix($dn)));
102   }
105   function connect()
106   {
107     $this->hascon=false;
108     $this->reconnect=false;
109     if ($this->cid= @ldap_connect($this->hostname)) {
110       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
111       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
112         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
113         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
114       }
115       if (function_exists("ldap_start_tls") && $this->tls){
116         @ldap_start_tls($this->cid);
117       }
119       $this->error = "No Error";
120       if ($bid = @ldap_bind($this->cid, $this->fix($this->binddn), $this->bindpw)) {
121         $this->error = "Success";
122         $this->hascon=true;
123       } else {
124         if ($this->reconnect){
125           if ($this->error != "Success"){
126             $this->error = "Could not rebind to " . $this->binddn;
127           }
128         } else {
129           $this->error = "Could not bind to " . $this->binddn;
130         }
131       }
132     } else {
133       $this->error = "Could not connect to LDAP server";
134     }
135   }
137   function rebind($ldap, $referral)
138   {
139     $credentials= $this->get_credentials($referral);
140     if (@ldap_bind($ldap, $this->fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
141       $this->error = "Success";
142       $this->hascon=true;
143       $this->reconnect= true;
144       return (0);
145     } else {
146       $this->error = "Could not bind to " . $credentials['ADMIN'];
147       return NULL;
148     }
149   }
151   function reconnect()
152   {
153     if ($this->reconnect){
154       @ldap_unbind($this->cid);
155       $this->cid = NULL;
156     }
157   }
159   function unbind()
160   {
161     @ldap_unbind($this->cid);
162     $this->cid = NULL;
163   }
165   function disconnect()
166   {
167     if($this->hascon){
168       @ldap_close($this->cid);
169       $this->hascon=false;
170     }
171   }
173   function cd($dir)
174   {
175     if ($dir == "..")
176       $this->basedn = $this->getParentDir();
177     else
178       $this->basedn = $this->convert($dir);
179   }
181   function getParentDir($basedn = "")
182   {
183     if ($basedn=="")
184       $basedn = $this->basedn;
185     else
186       $basedn = $this->convert($this->basedn);
187     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
188   }
190   function search($filter, $attrs= array())
191   {
192     if($this->hascon){
193       if ($this->reconnect) $this->connect();
195       $start = microtime();
196    
197       $this->clearResult();
198       $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
199       $this->error = @ldap_error($this->cid);
200       $this->resetResult();
201       $this->hasres=true;
202    
203       /* Check if query took longer as specified in max_ldap_query_time */
204       if($this->max_ldap_query_time){
205         $diff = get_MicroTimeDiff($start,microtime());
206         if($diff > $this->max_ldap_query_time){
207           print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
208         }
209       }
211       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
213       return($this->sr);
214     }else{
215       $this->error = "Could not connect to LDAP server";
216       return("");
217     }
218   }
220   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
221   {
222     if($this->hascon){
223       if ($this->reconnect) $this->connect();
224       $this->clearResult();
225       if ($basedn == "")
226         $basedn = $this->basedn;
227       else
228         $basedn= $this->convert($basedn);
229   
230       $start = microtime();
232       $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
233       $this->error = @ldap_error($this->cid);
234       $this->resetResult();
235       $this->hasres=true;
237        /* Check if query took longer as specified in max_ldap_query_time */
238       if($this->max_ldap_query_time){
239         $diff = get_MicroTimeDiff($start,microtime());
240         if($diff > $this->max_ldap_query_time){
241           print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
242         }
243       }
245       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
247       return($this->sr);
248     }else{
249       $this->error = "Could not connect to LDAP server";
250       return("");
251     }
252   }
254   function cat($dn,$attrs= array("*"))
255   {
256     if($this->hascon){
257       if ($this->reconnect) $this->connect();
258       $start = microtime();
259       $this->clearResult();
260       $filter = "(objectclass=*)";
261       $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
262       $this->error = @ldap_error($this->cid);
263       $this->resetResult();
264       $this->hasres=true;
265       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=cat('".$this->fix($dn)."')");
266       return($this->sr);
267     }else{
268       $this->error = "Could not connect to LDAP server";
269       return("");
270     }
271   }
273   function set_size_limit($size)
274   {
275     /* Ignore zero settings */
276     if ($size == 0){
277       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
278     }
279     if($this->hascon){
280       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
281     } else {
282       $this->error = "Could not connect to LDAP server";
283     }
284   }
286   function fetch()
287   {
288     $att= array();
289     if($this->hascon){
290       if($this->hasres){
291         if ($this->start == 0)
292         {
293           $this->start = 1;
294           $this->re= @ldap_first_entry($this->cid, $this->sr);
295         } else {
296           $this->re= @ldap_next_entry($this->cid, $this->re);
297         }
298         if ($this->re)
299         {
300           $att= @ldap_get_attributes($this->cid, $this->re);
301           $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
302         }
303         $this->error = @ldap_error($this->cid);
304         if (!isset($att)){
305           $att= array();
306         }
307         return($att);
308       }else{
309         $this->error = "Perform a Fetch with no Search";
310         return("");
311       }
312     }else{
313       $this->error = "Could not connect to LDAP server";
314       return("");
315     }
316   }
318   function resetResult()
319   {
320     $this->start = 0;
321   }
323   function clearResult()
324   {
325     if($this->hasres){
326       $this->hasres = false;
327       @ldap_free_result($this->sr);
328     }
329   }
331   function getDN()
332   {
333     if($this->hascon){
334       if($this->hasres){
336         if(!$this->re)
337           {
338           $this->error = "Perform a Fetch with no valid Result";
339           }
340           else
341           {
342           $rv = @ldap_get_dn($this->cid, $this->re);
343         
344           $this->error = @ldap_error($this->cid);
345           return(trim($this->convert($rv)));
346            }
347       }else{
348         $this->error = "Perform a Fetch with no Search";
349         return("");
350       }
351     }else{
352       $this->error = "Could not connect to LDAP server";
353       return("");
354     }
355   }
357   function count()
358   {
359     if($this->hascon){
360       if($this->hasres){
361         $rv = @ldap_count_entries($this->cid, $this->sr);
362         $this->error = @ldap_error($this->cid);
363         return($rv);
364       }else{
365         $this->error = "Perform a Fetch with no Search";
366         return("");
367       }
368     }else{
369       $this->error = "Could not connect to LDAP server";
370       return("");
371     }
372   }
374   function rm($attrs = "", $dn = "")
375   {
376     if($this->hascon){
377       if ($this->reconnect) $this->connect();
378       if ($dn == "")
379         $dn = $this->basedn;
381       $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
382       $this->error = @ldap_error($this->cid);
383       return($r);
384     }else{
385       $this->error = "Could not connect to LDAP server";
386       return("");
387     }
388   }
390   function rename($attrs, $dn = "")
391   {
392     if($this->hascon){
393       if ($this->reconnect) $this->connect();
394       if ($dn == "")
395         $dn = $this->basedn;
397       $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
398       $this->error = @ldap_error($this->cid);
399       return($r);
400     }else{
401       $this->error = "Could not connect to LDAP server";
402       return("");
403     }
404   }
406   function rmdir($deletedn)
407   {
408     if($this->hascon){
409       if ($this->reconnect) $this->connect();
410       $start= microtime();
411       $r = @ldap_delete($this->cid, $this->fix($deletedn));
412       $this->error = @ldap_error($this->cid);
413       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=delete('".$this->fix($deletedn)."')");
414       return($r ? $r : 0);
415     }else{
416       $this->error = "Could not connect to LDAP server";
417       return("");
418     }
419   }
421   /**
422   *  Function rmdir_recursive
423   *
424   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
425   *  Parameters:  The dn to delete
426   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
427   *
428   */
430   function rmdir_recursive($deletedn)
431   {
432     if($this->hascon){
433       if ($this->reconnect) $this->connect();
434       $delarray= array();
435         
436       /* Get sorted list of dn's to delete */
437       $this->ls ("(objectClass=*)",$deletedn);
438       while ($this->fetch()){
439         $deldn= $this->getDN();
440         $delarray[$deldn]= strlen($deldn);
441       }
442       arsort ($delarray);
443       reset ($delarray);
445       /* Really Delete ALL dn's in subtree */
446       foreach ($delarray as $key => $value){
447         $this->rmdir_recursive($key);
448       }
449       
450       /* Finally Delete own Node */
451       $r = @ldap_delete($this->cid, $this->fix($deletedn));
452       $this->error = @ldap_error($this->cid);
453       return($r ? $r : 0);
454     }else{
455       $this->error = "Could not connect to LDAP server";
456       return("");
457     }
458   }
460   /* Copy given attributes and sub-dns with attributes to destination dn 
461   */
462   function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
463   {
464     error_reporting(E_ALL);
465     
466     if($is_first){
467       echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
468     }else{
469       if(preg_match("/^ou=/",$sourcedn)){
470         echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
471       }else{
472         $tmp = split(",",$sourcedn);
473         
474         echo "&nbsp;<b>"._("Object").":</b> ";
476         $deststr = @LDAP::fix($destinationdn);
477         if(strlen($deststr) > 96){
478           $deststr = substr($deststr,0,96)."...";
479         }
481         echo $deststr."<br>";
482       }
483     }
485     flush();
486     
487     if($this->hascon){
488       if ($this->reconnect) $this->connect();
490       /* Save base dn */
491       $basedn= $this->basedn;
492       $delarray= array();
493      
494       /* Check if destination entry already exists */
495       $this->cat($destinationdn);
497       if($this->count()){
498         return;
499       }else{
500         
501         $this->clearResult();
503         /* Get source entry */
504         $this->cd($basedn);
505         $this->cat($sourcedn);
506         $attr = $this->fetch();
508         /* Error while fetching object / attribute abort*/
509         if((!$attr) || (count($attr)) ==0) {
510           echo _("Error while fetching source dn - aborted!");
511           return;
512         }
513   
514         /* check if this is a department */
515         if(in_array("organizationalUnit",$attr['objectClass'])){
516           $attr['dn'] = $this->convert($destinationdn);
517           $this->cd($basedn);
518           $this->create_missing_trees($destinationdn);
519           $this->cd($destinationdn);
521           /* If is first entry, append FAIbranch to department entry */
522           if($is_first){
523             $this->cat($destinationdn);
524             $attr= $this->fetch();
526             /* Filter unneeded informations */
527             foreach($attr as $key => $value){
528               if(is_numeric($key)) unset($attr[$key]);
529               if(isset($attr[$key]['count'])){
530                 if(is_array($attr[$key])){
531                   unset($attr[$key]['count']);
532                 }
533               }
534             }
535             
536             unset($attr['count']);
537             unset($attr['dn']);
539             /* Add marking attribute */
540             $attr['objectClass'][] = "FAIbranch";
541             
542             /* Add this entry */
543             $this->modify($attr);
544           }
545         }else{
547           /* If this is no department */
548           foreach($attr as $key => $value){
549             if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon"))){
550               $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
551               $ei= ldap_first_entry($this->cid, $sr);
552               if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
553                 $attr[$key] = $tmp;
554               }
555             }
557             if(is_numeric($key)) unset($attr[$key]);
558             if(isset($attr[$key]['count'])){
559               if(is_array($attr[$key])){
560                 unset($attr[$key]['count']);
561               }
562             }
563           }
564           unset($attr['count']);
565           unset($attr['dn']);
567           if(!in_array("gosaApplication" , $attr['objectClass'])){
568             if($type=="branch"){
569               $attr['FAIstate'] ="branch";
570             }elseif($type=="freeze"){
571               $attr['FAIstate'] ="freeze";
572             }else{
573               print_red(_("Unknown FAIstate %s"),$type);
574             }
575           }elseif(in_array("gosaApplication",$attr['objectClass'])){
576             if(!in_array("FAIobject",$attr['objectClass'])){
577               $attr['objectClass'][] = "FAIobject";
578             }
579             $attr['FAIstate'] = $type;
580           }
582           /* Replace FAIdebianRelease with new release name */
583           if(in_array("FAIpackageList" , $attr['objectClass'])){
584             $attr['FAIdebianRelease'] = $destinationName;
585             if($type=="branch"){
586               $attr['FAIstate'] ="branch";
587             }elseif($type=="freeze"){
588               $attr['FAIstate'] ="freeze";
589             }else{
590               print_red(_("Unknown FAIstate %s"),$type);
591             }
592           }
594           /* Add entry */
595           $this->cd($destinationdn);
596           $this->cat($destinationdn);
597           $a = $this->fetch();
598           if(!count($a)){
599             $this->add($attr);
600           }
602           if($this->error != "Success"){
603             /* Some error occurred */
604             print "---------------------------------------------";
605             print $this->get_error()."<br>";
606             print $sourcedn."<br>";
607             print $destinationdn."<br>";
608             print_a( $attr);
609             exit();
610           }          
611         }
612       }
614       echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
616       $this->ls ("(objectClass=*)",$sourcedn);
617       while ($this->fetch()){
618         $deldn= $this->getDN();
619         $delarray[$deldn]= strlen($deldn);
620       }
621       asort ($delarray);
622       reset ($delarray);
624        $depth ++;
625       foreach($delarray as $dn => $bla){
626         if($dn != $destinationdn){
627           $this->cd($basedn);
628           $item = $this->fetch($this->cat($dn));
629           if(!in_array("FAIbranch",$item['objectClass'])){
630             $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
631           } 
632         }
633       }
634     }
635     if($is_first){
636       echo "<p class='seperator'>&nbsp;</p>";
637     }
639   }
641   function modify($attrs)
642   {
643     if(count($attrs) == 0){
644       return (0);
645     }
646     if($this->hascon){
647       if ($this->reconnect) $this->connect();
648       $start= microtime();
649       $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
650       $this->error = @ldap_error($this->cid);
651       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=modify('$this->basedn')");
652       return($r ? $r : 0);
653     }else{
654       $this->error = "Could not connect to LDAP server";
655       return("");
656     }
657   }
659   function add($attrs)
660   {
661     if($this->hascon){
662       if ($this->reconnect) $this->connect();
663       $start= microtime();
664       $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
665       $this->error = @ldap_error($this->cid);
666       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=add('$this->basedn')");
667       return($r ? $r : 0);
668     }else{
669       $this->error = "Could not connect to LDAP server";
670       return("");
671     }
672   }
675   function create_missing_trees($target)
676   {
677     global $config;
679     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
681     if ($target == $this->basedn){
682       $l= array("dummy");
683     } else {
684       $l= array_reverse(gosa_ldap_explode_dn($real_path));
685     }
686     unset($l['count']);
687     $cdn= $this->basedn;
688     $tag= "";
690     /* Load schema if available... */
691     $classes= $this->get_objectclasses();
693     foreach ($l as $part){
694       if ($part != "dummy"){
695         $cdn= "$part,$cdn";
696       }
698       /* Ignore referrals */
699       $found= false;
700       foreach($this->referrals as $ref){
701         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
702         if ($base == $cdn){
703           $found= true;
704           break;
705         }
706       }
707       if ($found){
708         continue;
709       }
711       $this->cat ($cdn);
712       $attrs= $this->fetch();
714       /* Create missing entry? */
715       if (count ($attrs)){
716         /* Catch the tag - if present */
717         if (isset($attrs['gosaUnitTag'][0])){
718           $tag= $attrs['gosaUnitTag'][0];
719         }
721       } else {
722         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
723         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
725         $na= array();
727         /* Automatic or traditional? */
728         if(count($classes)){
730           /* Get name of first matching objectClass */
731           $ocname= "";
732           foreach($classes as $class){
733             if (isset($class['MUST']) && $class['MUST'] == "$type"){
735               /* Look for first classes that is structural... */
736               if (isset($class['STRUCTURAL'])){
737                 $ocname= $class['NAME'];
738                 break;
739               }
741               /* Look for classes that are auxiliary... */
742               if (isset($class['AUXILIARY'])){
743                 $ocname= $class['NAME'];
744               }
745             }
746           }
748           /* Bail out, if we've nothing to do... */
749           if ($ocname == ""){
750             print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
751             echo $_SESSION['errors'];
752             exit;
753           }
755           /* Assemble_entry */
756           if ($tag != ""){
757             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
758             $na["gosaUnitTag"]= $tag;
759           } else {
760             $na['objectClass']= array($ocname);
761           }
762           if (isset($classes[$ocname]['AUXILIARY'])){
763             $na['objectClass'][]= $classes[$ocname]['SUP'];
764           }
765           if ($type == "dc"){
766             /* This is bad actually, but - tell me a better way? */
767             $na['objectClass'][]= 'locality';
768           }
769           $na[$type]= $param;
770           if (is_array($classes[$ocname]['MUST'])){
771             foreach($classes[$ocname]['MUST'] as $attr){
772               $na[$attr]= "filled";
773             }
774           }
776         } else {
778           /* Use alternative add... */
779           switch ($type){
780             case 'ou':
781               if ($tag != ""){
782                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
783                 $na["gosaUnitTag"]= $tag;
784               } else {
785                 $na["objectClass"]= "organizationalUnit";
786               }
787               $na["ou"]= $param;
788               break;
789             case 'dc':
790               if ($tag != ""){
791                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
792                 $na["gosaUnitTag"]= $tag;
793               } else {
794                 $na["objectClass"]= array("dcObject", "top", "locality");
795               }
796               $na["dc"]= $param;
797               break;
798             default:
799               print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
800               echo $_SESSION['errors'];
801               exit;
802           }
804         }
805         $this->cd($cdn);
806         $this->add($na);
807         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
808         if (!preg_match('/success/i', $this->error)){
809           return FALSE;
810         }
811       }
812     }
814     return TRUE;
815   }
818   function create_missing_trees_old($target)
819   {
820     /* Ignore create_missing trees if the base equals target */
821     if ($target == $this->basedn){
822      return;
823     }
825     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
826     $tmp = ldap_explode_dn($real_path,0);
827     if(!$tmp){
828       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)));
829       return;
830     }
832     $l= array_reverse($tmp);
833     unset($l['count']);
834     $cdn= $this->basedn;
835     $tag= "";
837     foreach ($l as $part){
838       $cdn= "$part,$cdn";
840       /* Ignore referrals */
841       $found= false;
842       foreach($this->referrals as $ref){
843         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
844         if ($base == $cdn){
845           $found= true;
846           break;
847         }
848       }
849       if ($found){
850         continue;
851       }
853       $this->cat ($cdn);
854       $attrs= $this->fetch();
856       /* Create missing entry? */
857       if (count ($attrs)){
858       
859         /* Catch the tag - if present */
860         if (isset($attrs['gosaUnitTag'][0])){
861           $tag= $attrs['gosaUnitTag'][0];
862         }
864       } else {
865         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
866         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
868         $na= array();
869         switch ($type){
870           case 'ou':
871             if ($tag != ""){
872               $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
873               $na["gosaUnitTag"]= $tag;
874             } else {
875               $na["objectClass"]= "organizationalUnit";
876             }
877             $na["ou"]= $param;
878             break;
879           case 'dc':
880             if ($tag != ""){
881               $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
882               $na["gosaUnitTag"]= $tag;
883             } else {
884               $na["objectClass"]= array("dcObject", "top", "locality");
885             }
886             $na["dc"]= $param;
887             break;
888           default:
889             print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
890             echo $_SESSION['errors'];
891             exit;
892         }
893         $this->cd($cdn);
894         $this->add($na);
895       }
896     }
897   }
899   function recursive_remove()
900   {
901     $delarray= array();
903     /* Get sorted list of dn's to delete */
904     $this->search ("(objectClass=*)");
905     while ($this->fetch()){
906       $deldn= $this->getDN();
907       $delarray[$deldn]= strlen($deldn);
908     }
909     arsort ($delarray);
910     reset ($delarray);
912     /* Delete all dn's in subtree */
913     foreach ($delarray as $key => $value){
914       $this->rmdir($key);
915     }
916   }
918   function get_attribute($dn, $name,$r_array=0)
919   {
920     $data= "";
921     if ($this->reconnect) $this->connect();
922     $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
924     /* fill data from LDAP */
925     if ($sr) {
926       $ei= @ldap_first_entry($this->cid, $sr);
927       if ($ei) {
928         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
929           $data= $info[0];
930         }
932       }
933     }
934     if($r_array==0)
935     return ($data);
936     else
937     return ($info);
938   
939   
940   }
941  
944   function get_additional_error()
945   {
946     $error= "";
947     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
948     return ($error);
949   }
951   function get_error()
952   {
953     if ($this->error == 'Success'){
954       return $this->error;
955     } else {
956       $adderror= $this->get_additional_error();
957       if ($adderror != ""){
958         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
959       } else {
960         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
961       }
962       return $error;
963     }
964   }
966   function get_credentials($url, $referrals= NULL)
967   {
968     $ret= array();
969     $url= preg_replace('!\?\?.*$!', '', $url);
970     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
972     if ($referrals == NULL){
973       $referrals= $this->referrals;
974     }
976     if (isset($referrals[$server])){
977       return ($referrals[$server]);
978     } else {
979       $ret['ADMIN']= $this->fix($this->binddn);
980       $ret['PASSWORD']= $this->bindpw;
981     }
983     return ($ret);
984   }
987   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
988   {
989     $display= "";
991     if ($recursive){
992       $this->cd($dn);
993       $this->search("$filter", array('dn'));
994       while ($attrs= $this->fetch()){
995         $display.= $this->gen_one_entry($attrs['dn'], $filter, $attributes);
996         $display.= "\n";
997       }
998     } else {
999       $display.= $this->gen_one_entry($dn);
1000     }
1002     return ($display);
1003   }
1005 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1006   {
1007     $display= "";
1009       $this->cd($dn);
1010       $this->search("$filter");
1012       $i=0;
1013       while ($attrs= $this->fetch()){
1014         $j=0;
1016         foreach ($attributes as $at){
1017           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1018           $j++;
1019         }
1021         $i++;
1022       }
1024     return ($display);
1025   }
1028   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
1029   {
1030     $ret = "";
1031     $data = "";
1032     if($this->reconnect){
1033       $this->connect();
1034     }
1036     /* Searching Ldap Tree */
1037     $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
1039     /* Get the first entry */   
1040     $entry= @ldap_first_entry($this->cid, $sr);
1042     /* Get all attributes related to that Objekt */
1043     $atts = array();
1044     
1045     /* Assemble dn */
1046     $atts[0]['name']  = "dn";
1047     $atts[0]['value'] = array('count' => 1, 0 => $dn);
1049     /* Reset index */
1050     $i = 1 ; 
1051   $identifier = array();
1052     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
1053     while ($attribute) {
1054       $i++;
1055       $atts[$i]['name']  = $attribute;
1056       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
1058       /* Next one */
1059       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
1060     }
1062     foreach($atts as $at)
1063     {
1064       for ($i= 0; $i<$at['value']['count']; $i++){
1066         /* Check if we must encode the data */
1067         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
1068           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
1069         } else {
1070           $ret .= $at['name'].": ".$at['value'][$i]."\n";
1071         }
1072       }
1073     }
1075     return($ret);
1076   }
1079   function dn_exists($dn)
1080   {
1081     return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
1082   }
1083   
1086   function import_complete_ldif($str_attr,&$error,$overwrite,$cleanup)
1087   {
1088     if($this->reconnect) $this->connect();
1090     /* First we have to splitt the string ito detect empty lines
1091        An empty line indicates an new Entry */
1092     $entries = split("\n",$str_attr);
1094     $data = "";
1095     $cnt = 0; 
1096     $current_line = 0;
1098     /* Every single line ... */
1099     foreach($entries as $entry) {
1100       $current_line ++;
1102       /* Removing Spaces to .. 
1103          .. test if a new entry begins */
1104       $tmp  = str_replace(" ","",$data );
1106       /* .. prevent empty lines in an entry */
1107       $tmp2 = str_replace(" ","",$entry);
1109       /* If the Block ends (Empty Line) */
1110       if((empty($entry))&&(!empty($tmp))) {
1111         /* Add collected lines as a complete block */
1112         $all[$cnt] = $data;
1113         $cnt ++;
1114         $data ="";
1115       } else {
1117         /* Append lines ... */
1118         if(!empty($tmp2)) {
1119           /* check if we need base64_decode for this line */
1120           if(ereg("::",$tmp2))
1121           {
1122             $encoded = split("::",$entry);
1123             $attr  = $encoded[0];
1124             $value = base64_decode($encoded[1]);
1125             /* Add linenumber */
1126             $data .= $current_line."#".$attr.":".$value."\n";
1127           }
1128           else
1129           {
1130             /* Add Linenumber */ 
1131             $data .= $current_line."#".$entry."\n";
1132           }
1133         }
1134       }
1135     }
1137     /* The Data we collected is not in the array all[];
1138        For example the Data is stored like this..
1140        all[0] = "1#dn : .... \n 
1141        2#ObjectType: person \n ...."
1142        
1143        Now we check every insertblock and try to insert */
1144     foreach ( $all as $single) {
1145       $lineone = split("\n",$single);  
1146       $ndn = split("#", $lineone[0]);
1147       $line = $ndn[1];
1149       $dnn = split (":",$line,2);
1150       $current_line = $ndn[0];
1151       $dn    = $dnn[0];
1152       $value = $dnn[1];
1154       /* Every block must begin with a dn */
1155       if($dn != "dn") {
1156         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1157         return -2;  
1158       }
1160       /* Should we use Modify instead of Add */
1161       $usemodify= false;
1163       /* Delete before insert */
1164       $usermdir= false;
1165     
1166       /* The dn address already exists! */
1167       if (($this->dn_exists($value))&&((!$overwrite)&&(!$cleanup))) {
1169         $error= sprintf(_("The dn: '%s' (from line %s) already exists in the LDAP database."), $line, $current_line);
1170         return ALREADY_EXISTING_ENTRY;   
1172       } elseif(($this->dn_exists($value))&&($cleanup)){
1174         /* Delete first, then add */
1175         $usermdir = true;        
1177       } elseif(($this->dn_exists($value))&&($overwrite)) {
1178         
1179         /* Modify instead of Add */
1180         $usemodify = true;
1181       }
1182      
1183       /* If we can't Import, return with a file error */
1184       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1185         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1186                         $current_line);
1187         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1188     }
1190     return (INSERT_OK);
1191   }
1194   /* Imports a single entry */
1195   function import_single_entry($str_attr,$modify,$delete)
1196   {
1197     if($this->reconnect) $this->connect();
1199     $ret = false;
1200     $rows= split("\n",$str_attr);
1201     $data= false;
1203     foreach($rows as $row) {
1204       
1205       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1206          Linenumbers) Linenumbers are use like this 123#attribute : value */
1207       if(!empty($row)) {
1208         if((strpos($row,"#")!=FALSE)&&(strpos($row,"#")<strpos($row,":"))) {
1210           /* We are using line numbers 
1211              Because there is a # before a : */
1212           $tmp1= split("#",$row);
1213           $current_line= $tmp1[0];
1214           $row= $tmp1[1];
1215         }
1217         /* Split the line into  attribute  and value */
1218         $attr   = split(":", $row,2);
1219         $attr[0]= trim($attr[0]);  /* attribute */
1220         $attr[1]= trim($attr[1]);  /* value */
1222         /* Check for attributes that are used more than once */
1223         if(!isset($data[$attr[0]])) {
1224           $data[$attr[0]]=$attr[1];
1225         } else {
1226           $tmp = $data[$attr[0]];
1228           if(!is_array($tmp)) {
1229             $new[0]=$tmp;
1230             $new[1]=$attr[1];
1231             $datas[$attr[0]]['count']=1;             
1232             $data[$attr[0]]=$new;
1233           } else {
1234             $cnt = $datas[$attr[0]]['count'];           
1235             $cnt ++;
1236             $data[$attr[0]][$cnt]=$attr[1];
1237             $datas[$attr[0]]['count'] = $cnt;
1238           }
1239         }
1240       }
1241     } 
1242     
1243     /* If dn is an index of data, we should try to insert the data */
1244     if(isset($data['dn'])) {
1245       /* Creating Entry */
1246       $this->cd($data['dn']);
1248       /* Delete existing entry */
1249       if($delete){
1250         $this->rmdir($data['dn']);
1251       }
1252       
1253       /* Create missing trees */
1254       $this->create_missing_trees($data['dn']);
1255       unset($data['dn']);
1256       
1257       /* If entry exists use modify */
1258       if(!$modify){
1259         $ret = $this->add($data);
1260       } else {
1261         $ret = $this->modify($data);
1262       }
1263     }
1265     return($ret);
1266   }
1268   
1269   function importcsv($str)
1270   {
1271     $lines = split("\n",$str);
1272     foreach($lines as $line)
1273     {
1274       /* continue if theres a comment */
1275       if(substr(trim($line),0,1)=="#"){
1276         continue;
1277       }
1279       $line= str_replace ("\t\t","\t",$line);
1280       $line= str_replace ("\t"  ,"," ,$line);
1281       echo $line;
1283       $cells = split(",",$line )  ;
1284       $linet= str_replace ("\t\t",",",$line);
1285       $cells = split("\t",$line);
1286       $count = count($cells);  
1287     }
1289   }
1290   
1291   function get_objectclasses_old()
1292   {
1293     $objectclasses = array();
1295           # Get base to look for schema 
1296           $sr           = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1297           $attr         = @ldap_get_entries($this->cid,$sr);
1299           if (!isset($attr[0]['subschemasubentry'][0])){
1300       $this->error  = @ldap_error($this->cid);
1301       gosa_log($this->get_error());
1302       return array();
1303           }
1304         
1305           # Get list of objectclasses
1306           $nb= $attr[0]['subschemasubentry'][0];
1307           $objectclasses= array();
1308           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1309           $attrs= ldap_get_entries($this->cid,$sr);
1310           if (!isset($attrs[0])){
1311             return array();
1312           }
1313           foreach ($attrs[0]['objectclasses'] as $val){
1314             $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1315             if ($name != $val){
1316               $objectclasses[$name]= $val;
1317             }
1318           }
1319           
1320           return $objectclasses;
1321   }
1324   function get_objectclasses()
1325   {
1326     global $config;
1327     $objectclasses = array();
1329     if(isset($config) && preg_match("/config/i",get_class($config))){
1330       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1331         return($objectclasses);
1332       }
1333     }
1335 # Get base to look for schema
1336     $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1337     if(!$sr){
1338       $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1339     }
1341     $attr = @ldap_get_entries($this->cid,$sr);
1342     if (!isset($attr[0]['subschemasubentry'][0])){
1343       return array();
1344     }
1346     /* Get list of objectclasses and fill array */
1347     $nb= $attr[0]['subschemasubentry'][0];
1348     $objectclasses= array();
1349     $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1350     $attrs= ldap_get_entries($this->cid,$sr);
1351     if (!isset($attrs[0])){
1352       return array();
1353     }
1354     foreach ($attrs[0]['objectclasses'] as $val){
1355       if (preg_match('/^[0-9]+$/', $val)){
1356         continue;
1357       }
1358       $name= "OID";
1359       $pattern= split(' ', $val);
1360       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1361       $objectclasses[$ocname]= array();
1363       foreach($pattern as $chunk){
1364         switch($chunk){
1366           case '(':
1367             $value= "";
1368             break;
1370           case ')': if ($name != ""){
1371                       $objectclasses[$ocname][$name]= $this->value2container($value);
1372                     }
1373                     $name= "";
1374                     $value= "";
1375                     break;
1377           case 'NAME':
1378           case 'DESC':
1379           case 'SUP':
1380           case 'STRUCTURAL':
1381           case 'ABSTRACT':
1382           case 'AUXILIARY':
1383           case 'MUST':
1384           case 'MAY':
1385                     if ($name != ""){
1386                       $objectclasses[$ocname][$name]= $this->value2container($value);
1387                     }
1388                     $name= $chunk;
1389                     $value= "";
1390                     break;
1392           default:  $value.= $chunk." ";
1393         }
1394       }
1396     }
1398     return $objectclasses;
1399   }
1401   function value2container($value)
1402   {
1403     /* Set emtpy values to "true" only */
1404     if (preg_match('/^\s*$/', $value)){
1405       return true;
1406     }
1408     /* Remove ' and " if needed */
1409     $value= preg_replace('/^[\'"]/', '', $value);
1410     $value= preg_replace('/[\'"] *$/', '', $value);
1412     /* Convert to array if $ is inside... */
1413     if (preg_match('/\$/', $value)){
1414       $container= preg_split('/\s*\$\s*/', $value);
1415     } else {
1416       $container= chop($value);
1417     }
1419     return ($container);
1420   }
1422   function log($string)
1423   {
1424     if (isset($_SESSION['config'])){
1425       $cfg= $_SESSION['config'];
1426       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1427         syslog (LOG_INFO, $string);
1428       }
1429     }
1430   }
1431   
1432   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1433   function getCn($dn){
1434     $simple= split(",", $dn);
1436     foreach($simple as $piece) {
1437       $partial= split("=", $piece);
1439       if($partial[0] == "cn"){
1440         return $partial[1];
1441       }
1442     }
1444   }
1446   function get_naming_contexts($server, $admin= "", $password= "")
1447   {
1448     /* Build LDAP connection */
1449     $ds= ldap_connect ($server);
1450     if (!$ds) {
1451       die ("Can't bind to LDAP. No check possible!");
1452     }
1453     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1454     $r= ldap_bind ($ds, $admin, $password);
1456     /* Get base to look for naming contexts */
1457     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1458     $attr= @ldap_get_entries($ds,$sr);
1460     return ($attr[0]['namingcontexts']);
1461   }
1465 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1466 ?>