Code

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