Code

Added robustness to tabs::save()
[gosa.git] / gosa-core / include / class_ldap.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  * Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
6  * Copyright (C) 1998  Eric Kilfoil <eric@ipass.net>
7  *
8  * ID: $$Id$$
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
25 define("ALREADY_EXISTING_ENTRY",-10001);
26 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
27 define("NO_FILE_UPLOADED",10003);
28 define("INSERT_OK",10000);
29 define("SPECIALS_OVERRIDE", TRUE);
31 class LDAP{
33   var $hascon   =false;
34   var $reconnect=false;
35   var $tls      = false;
36   var $cid;
37   var $hasres   = array();
38   var $sr       = array();
39   var $re       = array();
40   var $basedn   ="";
41   var $start    = array(); // 0 if we are fetching the first entry, otherwise 1
42   var $error    = ""; // Any error messages to be returned can be put here
43   var $srp      = 0;
44   var $objectClasses = array(); // Information read from slapd.oc.conf
45   var $binddn   = "";
46   var $bindpw   = "";
47   var $hostname = "";
48   var $follow_referral = FALSE;
49   var $referrals= array();
50   var $max_ldap_query_time = 0;   // 0, empty or negative values will disable this check 
52   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
53   {
54     global $config;
55     $this->follow_referral= $follow_referral;
56     $this->tls=$tls;
57     $this->binddn=LDAP::convert($binddn);
59     $this->bindpw=$bindpw;
60     $this->hostname=$hostname;
62     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
63     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
64       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
65       $this->max_ldap_query_time = (float)($str);
66     }
68     $this->connect();
69   }
72   function getSearchResource()
73   {
74     $this->sr[$this->srp]= NULL;
75     $this->start[$this->srp]= 0;
76     $this->hasres[$this->srp]= false;
77     return $this->srp++;
78   }
81   /* Function to replace all problematic characters inside a DN by \001XX, where
82      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
83      that this character is inside a DN.
84      
85      Currently used codes:
86       ,   => CO
87       \2C => CO
88       (   => OB
89       )   => CB
90       /   => SL                                                                  */
91   static function convert($dn)
92   {
93     if (SPECIALS_OVERRIDE == TRUE){
94       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
95                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
96                            $dn);
97       return (preg_replace('/,\s+/', ',', $tmp));
98     } else {
99       return ($dn);
100     }
101   }
104   /* Function to fix all problematic characters inside a DN by replacing \001XX
105      codes to their original values. See "convert" for mor information. 
106      ',' characters are always expanded to \, (not \2C), since all tested LDAP
107      servers seem to take it the correct way.                                  */
108   static function fix($dn)
109   {
110     if (SPECIALS_OVERRIDE == TRUE){
111       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
112                            array("\,", "(", ")", "/"),
113                            $dn));
114     } else {
115       return ($dn);
116     }
117   }
120   /* Function to fix problematic characters in DN's that are used for search
121      requests. I.e. member=....                                               */
122   static function prepare4filter($dn)
123   {
124         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
125   }
128   function connect()
129   {
130     $this->hascon=false;
131     $this->reconnect=false;
132     if ($this->cid= @ldap_connect($this->hostname)) {
133       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
134       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
135         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
136         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
137       }
138       if (function_exists("ldap_start_tls") && $this->tls){
139         @ldap_start_tls($this->cid);
140       }
142       $this->error = "No Error";
143       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
144         $this->error = "Success";
145         $this->hascon=true;
146       } else {
147         if ($this->reconnect){
148           if ($this->error != "Success"){
149             $this->error = "Could not rebind to " . $this->binddn;
150           }
151         } else {
152           $this->error = "Could not bind to " . $this->binddn;
153         }
154       }
155     } else {
156       $this->error = "Could not connect to LDAP server";
157     }
158   }
160   function rebind($ldap, $referral)
161   {
162     $credentials= $this->get_credentials($referral);
163     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
164       $this->error = "Success";
165       $this->hascon=true;
166       $this->reconnect= true;
167       return (0);
168     } else {
169       $this->error = "Could not bind to " . $credentials['ADMIN'];
170       return NULL;
171     }
172   }
174   function reconnect()
175   {
176     if ($this->reconnect){
177       @ldap_unbind($this->cid);
178       $this->cid = NULL;
179     }
180   }
182   function unbind()
183   {
184     @ldap_unbind($this->cid);
185     $this->cid = NULL;
186   }
188   function disconnect()
189   {
190     if($this->hascon){
191       @ldap_close($this->cid);
192       $this->hascon=false;
193     }
194   }
196   function cd($dir)
197   {
198     if ($dir == ".."){
199       $this->basedn = $this->getParentDir();
200     } else {
201       $this->basedn = LDAP::convert($dir);
202     }
203   }
205   function getParentDir($basedn = "")
206   {
207     if ($basedn==""){
208       $basedn = $this->basedn;
209     } else {
210       $basedn = LDAP::convert($this->basedn);
211     }
212     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
213   }
215   
216   function search($srp, $filter, $attrs= array())
217   {
218     if($this->hascon){
219       if ($this->reconnect) $this->connect();
221       $start = microtime();
222       $this->clearResult($srp);
223       $this->sr[$srp] = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
224       $this->error = @ldap_error($this->cid);
225       $this->resetResult($srp);
226       $this->hasres[$srp]=true;
227    
228       /* Check if query took longer as specified in max_ldap_query_time */
229       if($this->max_ldap_query_time){
230         $diff = get_MicroTimeDiff($start,microtime());
231         if($diff > $this->max_ldap_query_time){
232           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
233         }
234       }
236       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
237       return($this->sr[$srp]);
238     }else{
239       $this->error = "Could not connect to LDAP server";
240       return("");
241     }
242   }
244   function ls($srp, $filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
245   {
246     if($this->hascon){
247       if ($this->reconnect) $this->connect();
249       $this->clearResult($srp);
250       if ($basedn == "")
251         $basedn = $this->basedn;
252       else
253         $basedn= LDAP::convert($basedn);
254   
255       $start = microtime();
256       $this->sr[$srp] = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
257       $this->error = @ldap_error($this->cid);
258       $this->resetResult($srp);
259       $this->hasres[$srp]=true;
261        /* Check if query took longer as specified in max_ldap_query_time */
262       if($this->max_ldap_query_time){
263         $diff = get_MicroTimeDiff($start,microtime());
264         if($diff > $this->max_ldap_query_time){
265           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
266         }
267       }
269       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
271       return($this->sr[$srp]);
272     }else{
273       $this->error = "Could not connect to LDAP server";
274       return("");
275     }
276   }
278   function cat($srp, $dn,$attrs= array("*"))
279   {
280     if($this->hascon){
281       if ($this->reconnect) $this->connect();
283       $this->clearResult($srp);
284       $filter = "(objectclass=*)";
285       $this->sr[$srp] = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
286       $this->error = @ldap_error($this->cid);
287       $this->resetResult($srp);
288       $this->hasres[$srp]=true;
289       return($this->sr[$srp]);
290     }else{
291       $this->error = "Could not connect to LDAP server";
292       return("");
293     }
294   }
296   function set_size_limit($size)
297   {
298     /* Ignore zero settings */
299     if ($size == 0){
300       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
301     }
302     if($this->hascon){
303       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
304     } else {
305       $this->error = "Could not connect to LDAP server";
306     }
307   }
309   function fetch($srp)
310   {
311     $att= array();
312     if($this->hascon){
313       if($this->hasres[$srp]){
314         if ($this->start[$srp] == 0)
315         {
316           if ($this->sr[$srp]){
317             $this->start[$srp] = 1;
318             $this->re[$srp]= @ldap_first_entry($this->cid, $this->sr[$srp]);
319           } else {
320             return array();
321           }
322         } else {
323           $this->re[$srp]= @ldap_next_entry($this->cid, $this->re[$srp]);
324         }
325         if ($this->re[$srp])
326         {
327           $att= @ldap_get_attributes($this->cid, $this->re[$srp]);
328           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re[$srp])));
329         }
330         $this->error = @ldap_error($this->cid);
331         if (!isset($att)){
332           $att= array();
333         }
334         return($att);
335       }else{
336         $this->error = "Perform a fetch with no search";
337         return("");
338       }
339     }else{
340       $this->error = "Could not connect to LDAP server";
341       return("");
342     }
343   }
345   function resetResult($srp)
346   {
347     $this->start[$srp] = 0;
348   }
350   function clearResult($srp)
351   {
352     if($this->hasres[$srp]){
353       $this->hasres[$srp] = false;
354       @ldap_free_result($this->sr[$srp]);
355     }
356   }
358   function getDN($srp)
359   {
360     if($this->hascon){
361       if($this->hasres[$srp]){
363         if(!$this->re[$srp])
364           {
365           $this->error = "Perform a Fetch with no valid Result";
366           }
367           else
368           {
369           $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
370         
371           $this->error = @ldap_error($this->cid);
372           return(trim(LDAP::convert($rv)));
373            }
374       }else{
375         $this->error = "Perform a Fetch with no Search";
376         return("");
377       }
378     }else{
379       $this->error = "Could not connect to LDAP server";
380       return("");
381     }
382   }
384   function count($srp)
385   {
386     if($this->hascon){
387       if($this->hasres[$srp]){
388         $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
389         $this->error = @ldap_error($this->cid);
390         return($rv);
391       }else{
392         $this->error = "Perform a Fetch with no Search";
393         return("");
394       }
395     }else{
396       $this->error = "Could not connect to LDAP server";
397       return("");
398     }
399   }
401   function rm($attrs = "", $dn = "")
402   {
403     if($this->hascon){
404       if ($this->reconnect) $this->connect();
405       if ($dn == "")
406         $dn = $this->basedn;
408       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
409       $this->error = @ldap_error($this->cid);
410       return($r);
411     }else{
412       $this->error = "Could not connect to LDAP server";
413       return("");
414     }
415   }
417   function rename($attrs, $dn = "")
418   {
419     if($this->hascon){
420       if ($this->reconnect) $this->connect();
421       if ($dn == "")
422         $dn = $this->basedn;
424       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
425       $this->error = @ldap_error($this->cid);
426       return($r);
427     }else{
428       $this->error = "Could not connect to LDAP server";
429       return("");
430     }
431   }
433   function rmdir($deletedn)
434   {
435     if($this->hascon){
436       if ($this->reconnect) $this->connect();
437       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
438       $this->error = @ldap_error($this->cid);
439       return($r ? $r : 0);
440     }else{
441       $this->error = "Could not connect to LDAP server";
442       return("");
443     }
444   }
447   /*! \brief Move the given Ldap entry from $source to $dest
448       @param  String  $source The source dn.
449       @param  String  $dest   The destination dn.
450       @return Boolean TRUE on success else FALSE.
451    */
452   function rename_dn($source,$dest)
453   {
454     /* Check if source and destination are the same entry */
455     if(strtolower($source) == strtolower($dest)){
456       trigger_error("Source and destination can't be the same entry.");
457       return(FALSE);
458     }
460     /* Check if destination entry exists */    
461     if($this->dn_exists($dest)){
462       trigger_error("Destination '$dest' already exists.");
463       return(FALSE);
464     }
466     /* Extract the name and the parent part out ouf source dn.
467         e.g.  cn=herbert,ou=department,dc=... 
468          parent   =>  ou=department,dc=...
469          dest_rdn =>  cn=herbert
470      */
471     $parent   = preg_replace("/^[^,]+,/","",$dest);
472     $dest_rdn = preg_replace("/,.*$/","",$dest);
473   
474     if($this->hascon){
475       if ($this->reconnect) $this->connect();
476       $r= @ldap_rename($this->cid,$source,$dest_rdn,$parent,TRUE); 
477       $this->error = @ldap_error($this->cid);
479       /* Check if destination dn exists, if not the 
480           server may not support this operation */
481       $r &= $this->dn_exists($dest);
482       return(!$r ? $r : TRUE);
483     }else{
484       $this->error = "Could not connect to LDAP server";
485       return(FALSE);
486     }
487   }
490   /**
491   *  Function rmdir_recursive
492   *
493   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
494   *  Parameters:  The dn to delete
495   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
496   *
497   */
498   function rmdir_recursive($srp, $deletedn)
499   {
500     if($this->hascon){
501       if ($this->reconnect) $this->connect();
502       $delarray= array();
503         
504       /* Get sorted list of dn's to delete */
505       $this->ls ($srp, "(objectClass=*)",$deletedn);
506       while ($this->fetch($srp)){
507         $deldn= $this->getDN($srp);
508         $delarray[$deldn]= strlen($deldn);
509       }
510       arsort ($delarray);
511       reset ($delarray);
513       /* Really Delete ALL dn's in subtree */
514       foreach ($delarray as $key => $value){
515         $this->rmdir_recursive($srp, $key);
516       }
517       
518       /* Finally Delete own Node */
519       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
520       $this->error = @ldap_error($this->cid);
521       return($r ? $r : 0);
522     }else{
523       $this->error = "Could not connect to LDAP server";
524       return("");
525     }
526   }
529   function modify($attrs)
530   {
531     if(count($attrs) == 0){
532       return (0);
533     }
534     if($this->hascon){
535       if ($this->reconnect) $this->connect();
536       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
537       $this->error = @ldap_error($this->cid);
538       return($r ? $r : 0);
539     }else{
540       $this->error = "Could not connect to LDAP server";
541       return("");
542     }
543   }
545   function add($attrs)
546   {
547     if($this->hascon){
548       if ($this->reconnect) $this->connect();
549       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
550       $this->error = @ldap_error($this->cid);
551       return($r ? $r : 0);
552     }else{
553       $this->error = "Could not connect to LDAP server";
554       return("");
555     }
556   }
558   function create_missing_trees($srp, $target)
559   {
560     global $config;
562     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
564     if ($target == $this->basedn){
565       $l= array("dummy");
566     } else {
567       $l= array_reverse(gosa_ldap_explode_dn($real_path));
568     }
569     unset($l['count']);
570     $cdn= $this->basedn;
571     $tag= "";
573     /* Load schema if available... */
574     $classes= $this->get_objectclasses();
576     foreach ($l as $part){
577       if ($part != "dummy"){
578         $cdn= "$part,$cdn";
579       }
581       /* Ignore referrals */
582       $found= false;
583       foreach($this->referrals as $ref){
584         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
585         if ($base == $cdn){
586           $found= true;
587           break;
588         }
589       }
590       if ($found){
591         continue;
592       }
594       $this->cat ($srp, $cdn);
595       $attrs= $this->fetch($srp);
597       /* Create missing entry? */
598       if (count ($attrs)){
599       
600         /* Catch the tag - if present */
601         if (isset($attrs['gosaUnitTag'][0])){
602           $tag= $attrs['gosaUnitTag'][0];
603         }
605       } else {
606         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
607         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
609         $na= array();
611         /* Automatic or traditional? */
612         if(count($classes)){
614           /* Get name of first matching objectClass */
615           $ocname= "";
616           foreach($classes as $class){
617             if (isset($class['MUST']) && $class['MUST'] == "$type"){
619               /* Look for first classes that is structural... */
620               if (isset($class['STRUCTURAL'])){
621                 $ocname= $class['NAME'];
622                 break;
623               }
625               /* Look for classes that are auxiliary... */
626               if (isset($class['AUXILIARY'])){
627                 $ocname= $class['NAME'];
628               }
629             }
630           }
632           /* Bail out, if we've nothing to do... */
633           if ($ocname == ""){
634             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found!"),$type), FATAL_ERROR_DIALOG);
635             exit();
636           }
638           /* Assemble_entry */
639           if ($tag != ""){
640             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
641             $na["gosaUnitTag"]= $tag;
642           } else {
643             $na['objectClass']= array($ocname);
644           }
645           if (isset($classes[$ocname]['AUXILIARY'])){
646             $na['objectClass'][]= $classes[$ocname]['SUP'];
647           }
648           if ($type == "dc"){
649             /* This is bad actually, but - tell me a better way? */
650             $na['objectClass'][]= 'locality';
651           }
652           $na[$type]= $param;
653           if (is_array($classes[$ocname]['MUST'])){
654             foreach($classes[$ocname]['MUST'] as $attr){
655               $na[$attr]= "filled";
656             }
657           }
659         } else {
661           /* Use alternative add... */
662           switch ($type){
663             case 'ou':
664               if ($tag != ""){
665                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
666                 $na["gosaUnitTag"]= $tag;
667               } else {
668                 $na["objectClass"]= "organizationalUnit";
669               }
670               $na["ou"]= $param;
671               break;
672             case 'dc':
673               if ($tag != ""){
674                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
675                 $na["gosaUnitTag"]= $tag;
676               } else {
677                 $na["objectClass"]= array("dcObject", "top", "locality");
678               }
679               $na["dc"]= $param;
680               break;
681             default:
682               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
683               exit();
684           }
686         }
687         $this->cd($cdn);
688         $this->add($na);
689     
690         if (!$this->success()){
691           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()));
692           return FALSE;
693         }
694       }
695     }
697     return TRUE;
698   }
701   function recursive_remove($srp)
702   {
703     $delarray= array();
705     /* Get sorted list of dn's to delete */
706     $this->search ($srp, "(objectClass=*)");
707     while ($this->fetch($srp)){
708       $deldn= $this->getDN($srp);
709       $delarray[$deldn]= strlen($deldn);
710     }
711     arsort ($delarray);
712     reset ($delarray);
714     /* Delete all dn's in subtree */
715     foreach ($delarray as $key => $value){
716       $this->rmdir($key);
717     }
718   }
720   function get_attribute($dn, $name,$r_array=0)
721   {
722     $data= "";
723     if ($this->reconnect) $this->connect();
724     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
726     /* fill data from LDAP */
727     if ($sr) {
728       $ei= @ldap_first_entry($this->cid, $sr);
729       if ($ei) {
730         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
731           $data= $info[0];
732         }
733       }
734     }
735     if($r_array==0)
736     return ($data);
737     else
738     return ($info);
739   
740   
741   }
742  
745   function get_additional_error()
746   {
747     $error= "";
748     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
749     return ($error);
750   }
753   function success()
754   {
755     return (preg_match('/Success/i', $this->error));
756   }
759   function get_error()
760   {
761     if ($this->error == 'Success'){
762       return $this->error;
763     } else {
764       $adderror= $this->get_additional_error();
765       if ($adderror != ""){
766         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
767       } else {
768         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
769       }
770       return $error;
771     }
772   }
774   function get_credentials($url, $referrals= NULL)
775   {
776     $ret= array();
777     $url= preg_replace('!\?\?.*$!', '', $url);
778     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
780     if ($referrals === NULL){
781       $referrals= $this->referrals;
782     }
784     if (isset($referrals[$server])){
785       return ($referrals[$server]);
786     } else {
787       $ret['ADMIN']= LDAP::fix($this->binddn);
788       $ret['PASSWORD']= $this->bindpw;
789     }
791     return ($ret);
792   }
795   function gen_ldif ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
796   {
797     $display= "";
799     if ($recursive){
800       $this->cd($dn);
801       $this->ls($srp, $filter,$dn, array('dn','objectClass'));
802       $deps = array();
804       $display .= $this->gen_one_entry($dn)."\n";
806       while ($attrs= $this->fetch($srp)){
807         $deps[] = $attrs['dn'];
808       }
809       foreach($deps as $dn){
810         $display .= $this->gen_ldif($srp, $dn, $filter,$attributes,$recursive);
811       }
812     } else {
813       $display.= $this->gen_one_entry($dn);
814     }
815     return ($display);
816   }
819   function gen_xls ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
820   {
821     $display= array();
823       $this->cd($dn);
824       $this->search($srp, "$filter");
826       $i=0;
827       while ($attrs= $this->fetch($srp)){
828         $j=0;
830         foreach ($attributes as $at){
831           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
832           $j++;
833         }
835         $i++;
836       }
838     return ($display);
839   }
842   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
843   {
844     $ret = "";
845     $data = "";
846     if($this->reconnect){
847       $this->connect();
848     }
850     /* Searching Ldap Tree */
851     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
853     /* Get the first entry */   
854     $entry= @ldap_first_entry($this->cid, $sr);
856     /* Get all attributes related to that Objekt */
857     $atts = array();
858     
859     /* Assemble dn */
860     $atts[0]['name']  = "dn";
861     $atts[0]['value'] = array('count' => 1, 0 => $dn);
863     /* Reset index */
864     $i = 1 ; 
865   $identifier = array();
866     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
867     while ($attribute) {
868       $i++;
869       $atts[$i]['name']  = $attribute;
870       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
872       /* Next one */
873       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
874     }
876     foreach($atts as $at)
877     {
878       for ($i= 0; $i<$at['value']['count']; $i++){
880         /* Check if we must encode the data */
881         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
882           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
883         } else {
884           $ret .= $at['name'].": ".$at['value'][$i]."\n";
885         }
886       }
887     }
889     return($ret);
890   }
893   function dn_exists($dn)
894   {
895     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
896   }
897   
900   /*  This funktion imports ldifs 
901         
902       If DeleteOldEntries is true, the destination entry will be deleted first. 
903       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
904       if JustMofify id false the destination dn will be overwritten by the new ldif. 
905     */
907   function import_complete_ldif($srp, $str_attr,$error,$JustModify,$DeleteOldEntries)
908   {
909     if($this->reconnect) $this->connect();
911     /* First we have to splitt the string ito detect empty lines
912        An empty line indicates an new Entry */
913     $entries = split("\n",$str_attr);
915     $data = "";
916     $cnt = 0; 
917     $current_line = 0;
919     /* FIX ldif */
920     $last = "";
921     $tmp  = "";
922     $i = 0;
923     foreach($entries as $entry){
924       if(preg_match("/^ /",$entry)){
925         $tmp[$i] .= trim($entry);
926       }else{
927         $i ++;
928         $tmp[$i] = trim($entry);
929       }
930     }
932     /* Every single line ... */
933     foreach($tmp as $entry) {
934       $current_line ++;
936       /* Removing Spaces to .. 
937          .. test if a new entry begins */
938       $tmp  = str_replace(" ","",$data );
940       /* .. prevent empty lines in an entry */
941       $tmp2 = str_replace(" ","",$entry);
943       /* If the Block ends (Empty Line) */
944       if((empty($entry))&&(!empty($tmp))) {
945         /* Add collected lines as a complete block */
946         $all[$cnt] = $data;
947         $cnt ++;
948         $data ="";
949       } else {
951         /* Append lines ... */
952         if(!empty($tmp2)) {
953           /* check if we need base64_decode for this line */
954           if(ereg("::",$tmp2))
955           {
956             $encoded = split("::",$entry);
957             $attr  = trim($encoded[0]);
958             $value = base64_decode(trim($encoded[1]));
959             /* Add linenumber */
960             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
961           }
962           else
963           {
964             /* Add Linenumber */ 
965             $data .= $current_line."#".base64_encode($entry)."\n";
966           }
967         }
968       }
969     }
971     /* The Data we collected is not in the array all[];
972        For example the Data is stored like this..
974        all[0] = "1#dn : .... \n 
975        2#ObjectType: person \n ...."
976        
977        Now we check every insertblock and try to insert */
978     foreach ( $all as $single) {
979       $lineone = split("\n",$single);  
980       $ndn = split("#", $lineone[0]);
981       $line = base64_decode($ndn[1]);
983       $dnn = split (":",$line,2);
984       $current_line = $ndn[0];
985       $dn    = $dnn[0];
986       $value = $dnn[1];
988       /* Every block must begin with a dn */
989       if($dn != "dn") {
990         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
991         return -2;  
992       }
994       /* Should we use Modify instead of Add */
995       $usemodify= false;
997       /* Delete before insert */
998       $usermdir= false;
999     
1000       /* The dn address already exists, Don't delete destination entry, overwrite it */
1001       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1003         $usermdir = $usemodify = false;
1005       /* Delete old entry first, then add new */
1006       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1008         /* Delete first, then add */
1009         $usermdir = true;        
1011       } elseif(($this->dn_exists($value))&&($JustModify)) {
1012         
1013         /* Modify instead of Add */
1014         $usemodify = true;
1015       }
1016      
1017       /* If we can't Import, return with a file error */
1018       if(!$this->import_single_entry($srp, $single,$usemodify,$usermdir) ) {
1019         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1020                         $current_line);
1021         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1022     }
1024     return (INSERT_OK);
1025   }
1028   /* Imports a single entry 
1029       If $delete is true;  The old entry will be deleted if it exists.
1030       if $modify is true;  All variables that are not touched by the new ldif will be kept.
1031       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1032   */
1033   function import_single_entry($srp, $str_attr,$modify,$delete)
1034   {
1035     global $config;
1037     if(!$config){
1038       trigger_error("Can't import ldif, can't read config object.");
1039     }
1040   
1042     if($this->reconnect) $this->connect();
1044     $ret = false;
1045     $rows= split("\n",$str_attr);
1046     $data= false;
1048     foreach($rows as $row) {
1049       
1050       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1051          Linenumbers) Linenumbers are use like this 123#attribute : value */
1052       if(!empty($row)) {
1053         if(strpos($row,"#")!=FALSE) {
1055           /* We are using line numbers 
1056              Because there is a # before a : */
1057           $tmp1= split("#",$row);
1058           $current_line= $tmp1[0];
1059           $row= base64_decode($tmp1[1]);
1060         }
1062         /* Split the line into  attribute  and value */
1063         $attr   = split(":", $row,2);
1064         $attr[0]= trim($attr[0]);  /* attribute */
1065         $attr[1]= $attr[1];  /* value */
1067         /* Check :: was used to indicate base64_encoded strings */
1068         if($attr[1][0] == ":"){
1069           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1070           $attr[1]=base64_decode($attr[1]);
1071         }
1073         $attr[1] = trim($attr[1]);
1075         /* Check for attributes that are used more than once */
1076         if(!isset($data[$attr[0]])) {
1077           $data[$attr[0]]=$attr[1];
1078         } else {
1079           $tmp = $data[$attr[0]];
1081           if(!is_array($tmp)) {
1082             $new[0]=$tmp;
1083             $new[1]=$attr[1];
1084             $datas[$attr[0]]['count']=1;             
1085             $data[$attr[0]]=$new;
1086           } else {
1087             $cnt = $datas[$attr[0]]['count'];           
1088             $cnt ++;
1089             $data[$attr[0]][$cnt]=$attr[1];
1090             $datas[$attr[0]]['count'] = $cnt;
1091           }
1092         }
1093       }
1094     }
1096     /* If dn is an index of data, we should try to insert the data */
1097     if(isset($data['dn'])) {
1099       /* Fix dn */
1100       $tmp = gosa_ldap_explode_dn($data['dn']);
1101       unset($tmp['count']);
1102       $newdn ="";
1103       foreach($tmp as $tm){
1104         $newdn.= trim($tm).",";
1105       }
1106       $newdn = preg_replace("/,$/","",$newdn);
1107       $data['dn'] = $newdn;
1108    
1109       /* Creating Entry */
1110       $this->cd($data['dn']);
1112       /* Delete existing entry */
1113       if($delete){
1114         $this->rmdir_recursive($srp, $data['dn']);
1115       }
1116      
1117       /* Create missing trees */
1118       $this->cd ($this->basedn);
1119       $this->cd($config->current['BASE']);
1120       $this->create_missing_trees($srp, preg_replace("/^[^,]+,/","",$data['dn']));
1121       $this->cd($data['dn']);
1123       $dn = $data['dn'];
1124       unset($data['dn']);
1125       
1126       if(!$modify){
1128         $this->cat($srp, $dn);
1129         if($this->count($srp)){
1130         
1131           /* The destination entry exists, overwrite it with the new entry */
1132           $attrs = $this->fetch($srp);
1133           foreach($attrs as $name => $value ){
1134             if(!is_numeric($name)){
1135               if(in_array($name,array("dn","count"))) continue;
1136               if(!isset($data[$name])){
1137                 $data[$name] = array();
1138               }
1139             }
1140           }
1141           $ret = $this->modify($data);
1142     
1143         }else{
1144     
1145           /* The destination entry doesn't exists, create it */
1146           $ret = $this->add($data);
1147         }
1149       } else {
1150         
1151         /* Keep all vars that aren't touched by this ldif */
1152         $ret = $this->modify($data);
1153       }
1154     }
1156     if (!$this->success()){
1157       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $dn, "", get_class()));
1158     }
1160     return($ret);
1161   }
1163   
1164   function importcsv($str)
1165   {
1166     $lines = split("\n",$str);
1167     foreach($lines as $line)
1168     {
1169       /* continue if theres a comment */
1170       if(substr(trim($line),0,1)=="#"){
1171         continue;
1172       }
1174       $line= str_replace ("\t\t","\t",$line);
1175       $line= str_replace ("\t"  ,"," ,$line);
1176       echo $line;
1178       $cells = split(",",$line )  ;
1179       $linet= str_replace ("\t\t",",",$line);
1180       $cells = split("\t",$line);
1181       $count = count($cells);  
1182     }
1184   }
1185   
1186   function get_objectclasses()
1187   {
1188     $objectclasses = array();
1189     global $config;
1191     /* Only read schema if it is allowed */
1192     if(isset($config) && preg_match("/config/i",get_class($config))){
1193       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1194         return($objectclasses);
1195       } 
1196     }
1198     /* Return the cached results. */
1199     if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1200       $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1201       return($objectclasses);
1202     }
1203         
1204           # Get base to look for schema 
1205           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1206     if(!$sr){
1207             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1208     }
1210           $attr = @ldap_get_entries($this->cid,$sr);
1211           if (!isset($attr[0]['subschemasubentry'][0])){
1212             return array();
1213           }
1214         
1215           /* Get list of objectclasses and fill array */
1216           $nb= $attr[0]['subschemasubentry'][0];
1217           $objectclasses= array();
1218           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1219           $attrs= ldap_get_entries($this->cid,$sr);
1220           if (!isset($attrs[0])){
1221             return array();
1222           }
1223           foreach ($attrs[0]['objectclasses'] as $val){
1224       if (preg_match('/^[0-9]+$/', $val)){
1225         continue;
1226       }
1227       $name= "OID";
1228       $pattern= split(' ', $val);
1229       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1230       $objectclasses[$ocname]= array();
1232       foreach($pattern as $chunk){
1233         switch($chunk){
1235           case '(':
1236                     $value= "";
1237                     break;
1239           case ')': if ($name != ""){
1240                       $objectclasses[$ocname][$name]= $this->value2container($value);
1241                     }
1242                     $name= "";
1243                     $value= "";
1244                     break;
1246           case 'NAME':
1247           case 'DESC':
1248           case 'SUP':
1249           case 'STRUCTURAL':
1250           case 'ABSTRACT':
1251           case 'AUXILIARY':
1252           case 'MUST':
1253           case 'MAY':
1254                     if ($name != ""){
1255                       $objectclasses[$ocname][$name]= $this->value2container($value);
1256                     }
1257                     $name= $chunk;
1258                     $value= "";
1259                     break;
1261           default:  $value.= $chunk." ";
1262         }
1263       }
1265           }
1266     if(class_available("session")){
1267       session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1268     }
1269           return $objectclasses;
1270   }
1273   function value2container($value)
1274   {
1275     /* Set emtpy values to "true" only */
1276     if (preg_match('/^\s*$/', $value)){
1277       return true;
1278     }
1280     /* Remove ' and " if needed */
1281     $value= preg_replace('/^[\'"]/', '', $value);
1282     $value= preg_replace('/[\'"] *$/', '', $value);
1284     /* Convert to array if $ is inside... */
1285     if (preg_match('/\$/', $value)){
1286       $container= preg_split('/\s*\$\s*/', $value);
1287     } else {
1288       $container= chop($value);
1289     }
1291     return ($container);
1292   }
1295   function log($string)
1296   {
1297     if (session::is_set('config')){
1298       $cfg = session::get('config');
1299       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1300         syslog (LOG_INFO, $string);
1301       }
1302     }
1303   }
1305   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1306   function getCn($dn){
1307     $simple= split(",", $dn);
1309     foreach($simple as $piece) {
1310       $partial= split("=", $piece);
1312       if($partial[0] == "cn"){
1313         return $partial[1];
1314       }
1315     }
1316   }
1319   function get_naming_contexts($server, $admin= "", $password= "")
1320   {
1321     /* Build LDAP connection */
1322     $ds= ldap_connect ($server);
1323     if (!$ds) {
1324       die ("Can't bind to LDAP. No check possible!");
1325     }
1326     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1327     $r= ldap_bind ($ds, $admin, $password);
1329     /* Get base to look for naming contexts */
1330     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1331     $attr= @ldap_get_entries($ds,$sr);
1333     return ($attr[0]['namingcontexts']);
1334   }
1337   function get_root_dse($server, $admin= "", $password= "")
1338   {
1339     /* Build LDAP connection */
1340     $ds= ldap_connect ($server);
1341     if (!$ds) {
1342       die ("Can't bind to LDAP. No check possible!");
1343     }
1344     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1345     $r= ldap_bind ($ds, $admin, $password);
1347     /* Get base to look for naming contexts */
1348     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1349     $attr= @ldap_get_entries($ds,$sr);
1350    
1351     /* Return empty array, if nothing was set */
1352     if (!isset($attr[0])){
1353       return array();
1354     }
1356     /* Rework array... */
1357     $result= array();
1358     for ($i= 0; $i<$attr[0]['count']; $i++){
1359       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1360       unset($result[$attr[0][$i]]['count']);
1361     }
1363     return ($result);
1364   }
1367 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1368 ?>