Code

f59c332cc425b66e5dffc8a5923d5952bd0dfc76
[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 $hasres   =false;
35   var $reconnect=false;
36   var $tls      = false;
37   var $basedn   ="";
38   var $cid;
39   var $error    = ""; // Any error messages to be returned can be put here
40   var $start    = 0; // 0 if we are fetching the first entry, otherwise 1
41   var $objectClasses = array(); // Information read from slapd.oc.conf
42   var $binddn   = "";
43   var $bindpw   = "";
44   var $hostname = "";
45   var $follow_referral = FALSE;
46   var $referrals= array();
47   var $max_ldap_query_time = 0;   // 0, empty or negative values will disable this check 
49   var $re = NULL;  
51   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
52   {
53     global $config;
54     $this->follow_referral= $follow_referral;
55     $this->tls=$tls;
56     $this->binddn=LDAP::convert($binddn);
58     $this->bindpw=$bindpw;
59     $this->hostname=$hostname;
61     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
62     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
63       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
64       $this->max_ldap_query_time = (float)($str);
65     }
67     $this->connect();
68   }
71   /* Function to replace all problematic characters inside a DN by \001XX, where
72      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
73      that this character is inside a DN.
74      
75      Currently used codes:
76       ,   => CO
77       \2C => CO
78       (   => OB
79       )   => CB
80       /   => SL                                                                  */
81   static function convert($dn)
82   {
83     if (SPECIALS_OVERRIDE == TRUE){
84       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
85                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
86                            $dn);
87       return (preg_replace('/,\s+/', ',', $tmp));
88     } else {
89       return ($dn);
90     }
91   }
94   /* Function to fix all problematic characters inside a DN by replacing \001XX
95      codes to their original values. See "convert" for mor information. 
96      ',' characters are always expanded to \, (not \2C), since all tested LDAP
97      servers seem to take it the correct way.                                  */
98   static function fix($dn)
99   {
100     if (SPECIALS_OVERRIDE == TRUE){
101       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
102                            array("\,", "(", ")", "/"),
103                            $dn));
104     } else {
105       return ($dn);
106     }
107   }
110   /* Function to fix problematic characters in DN's that are used for search
111      requests. I.e. member=....                                               */
112   static function prepare4filter($dn)
113   {
114         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
115   }
118   function connect()
119   {
120     $this->hascon=false;
121     $this->reconnect=false;
122     if ($this->cid= @ldap_connect($this->hostname)) {
123       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
124       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
125         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
126         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
127       }
128       if (function_exists("ldap_start_tls") && $this->tls){
129         @ldap_start_tls($this->cid);
130       }
132       $this->error = "No Error";
133       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
134         $this->error = "Success";
135         $this->hascon=true;
136       } else {
137         if ($this->reconnect){
138           if ($this->error != "Success"){
139             $this->error = "Could not rebind to " . $this->binddn;
140           }
141         } else {
142           $this->error = "Could not bind to " . $this->binddn;
143         }
144       }
145     } else {
146       $this->error = "Could not connect to LDAP server";
147     }
148   }
150   function rebind($ldap, $referral)
151   {
152     $credentials= $this->get_credentials($referral);
153     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
154       $this->error = "Success";
155       $this->hascon=true;
156       $this->reconnect= true;
157       return (0);
158     } else {
159       $this->error = "Could not bind to " . $credentials['ADMIN'];
160       return NULL;
161     }
162   }
164   function reconnect()
165   {
166     if ($this->reconnect){
167       @ldap_unbind($this->cid);
168       $this->cid = NULL;
169     }
170   }
172   function unbind()
173   {
174     @ldap_unbind($this->cid);
175     $this->cid = NULL;
176   }
178   function disconnect()
179   {
180     if($this->hascon){
181       @ldap_close($this->cid);
182       $this->hascon=false;
183     }
184   }
186   function cd($dir)
187   {
188     if ($dir == "..")
189       $this->basedn = $this->getParentDir();
190     else
191       $this->basedn = LDAP::convert($dir);
192   }
194   function getParentDir($basedn = "")
195   {
196     if ($basedn=="")
197       $basedn = $this->basedn;
198     else
199       $basedn = LDAP::convert($this->basedn);
200     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
201   }
203   
204   /* Checks if there is still unfetched data 
205    */
206   function checkResult()
207   {
208     /* Check if we have started a search before  */
209     if($this->start != 0 && $this->re){
210       
211       /* Check if there are still unfetched elements */
212       if(is_resource(@ldap_next_entry($this->cid, $this->re))){
213         trigger_error("A new search was initiated while an older search wasn't fetched completely.");
214       }
215     }
216   }
218   function search($filter, $attrs= array())
219   {
220     if($this->hascon){
221       if ($this->reconnect) $this->connect();
223       /* Check if there are still unfetched objects from last search 
224        */
225       $this->checkResult();
227       $start = microtime();
228       $this->clearResult();
229       $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
230       $this->error = @ldap_error($this->cid);
231       $this->resetResult();
232       $this->hasres=true;
233    
234       /* Check if query took longer as specified in max_ldap_query_time */
235       if($this->max_ldap_query_time){
236         $diff = get_MicroTimeDiff($start,microtime());
237         if($diff > $this->max_ldap_query_time){
238           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
239         }
240       }
242       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
243       return($this->sr);
244     }else{
245       $this->error = "Could not connect to LDAP server";
246       return("");
247     }
248   }
250   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
251   {
252     if($this->hascon){
253       if ($this->reconnect) $this->connect();
255       /* Check if there are still unfetched objects from last search 
256        */
257       $this->checkResult();
259       $this->clearResult();
260       if ($basedn == "")
261         $basedn = $this->basedn;
262       else
263         $basedn= LDAP::convert($basedn);
264   
265       $start = microtime();
266       $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
267       $this->error = @ldap_error($this->cid);
268       $this->resetResult();
269       $this->hasres=true;
271        /* Check if query took longer as specified in max_ldap_query_time */
272       if($this->max_ldap_query_time){
273         $diff = get_MicroTimeDiff($start,microtime());
274         if($diff > $this->max_ldap_query_time){
275           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
276         }
277       }
279       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
281       return($this->sr);
282     }else{
283       $this->error = "Could not connect to LDAP server";
284       return("");
285     }
286   }
288   function cat($dn,$attrs= array("*"))
289   {
290     if($this->hascon){
291       if ($this->reconnect) $this->connect();
293       /* Check if there are still unfetched objects from last search 
294        */
295       $this->checkResult();
297       $this->clearResult();
298       $filter = "(objectclass=*)";
299       $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
300       $this->error = @ldap_error($this->cid);
301       $this->resetResult();
302       $this->hasres=true;
303       return($this->sr);
304     }else{
305       $this->error = "Could not connect to LDAP server";
306       return("");
307     }
308   }
310   function set_size_limit($size)
311   {
312     /* Ignore zero settings */
313     if ($size == 0){
314       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
315     }
316     if($this->hascon){
317       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
318     } else {
319       $this->error = "Could not connect to LDAP server";
320     }
321   }
323   function fetch()
324   {
325     $att= array();
326     if($this->hascon){
327       if($this->hasres){
328         if ($this->start == 0)
329         {
330           if ($this->sr){
331             $this->start = 1;
332             $this->re= @ldap_first_entry($this->cid, $this->sr);
333           } else {
334             return array();
335           }
336         } else {
337           $this->re= @ldap_next_entry($this->cid, $this->re);
338         }
339         if ($this->re)
340         {
341           $att= @ldap_get_attributes($this->cid, $this->re);
342           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
343         }
344         $this->error = @ldap_error($this->cid);
345         if (!isset($att)){
346           $att= array();
347         }
348         return($att);
349       }else{
350         $this->error = "Perform a Fetch with no Search";
351         return("");
352       }
353     }else{
354       $this->error = "Could not connect to LDAP server";
355       return("");
356     }
357   }
359   function resetResult()
360   {
361     $this->start = 0;
362   }
364   function clearResult()
365   {
366     if($this->hasres){
367       $this->hasres = false;
368       @ldap_free_result($this->sr);
369     }
370   }
372   function getDN()
373   {
374     if($this->hascon){
375       if($this->hasres){
377         if(!$this->re)
378           {
379           $this->error = "Perform a Fetch with no valid Result";
380           }
381           else
382           {
383           $rv = @ldap_get_dn($this->cid, $this->re);
384         
385           $this->error = @ldap_error($this->cid);
386           return(trim(LDAP::convert($rv)));
387            }
388       }else{
389         $this->error = "Perform a Fetch with no Search";
390         return("");
391       }
392     }else{
393       $this->error = "Could not connect to LDAP server";
394       return("");
395     }
396   }
398   function count()
399   {
400     if($this->hascon){
401       if($this->hasres){
402         $rv = @ldap_count_entries($this->cid, $this->sr);
403         $this->error = @ldap_error($this->cid);
404         return($rv);
405       }else{
406         $this->error = "Perform a Fetch with no Search";
407         return("");
408       }
409     }else{
410       $this->error = "Could not connect to LDAP server";
411       return("");
412     }
413   }
415   function rm($attrs = "", $dn = "")
416   {
417     if($this->hascon){
418       if ($this->reconnect) $this->connect();
419       if ($dn == "")
420         $dn = $this->basedn;
422       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
423       $this->error = @ldap_error($this->cid);
424       return($r);
425     }else{
426       $this->error = "Could not connect to LDAP server";
427       return("");
428     }
429   }
431   function rename($attrs, $dn = "")
432   {
433     if($this->hascon){
434       if ($this->reconnect) $this->connect();
435       if ($dn == "")
436         $dn = $this->basedn;
438       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
439       $this->error = @ldap_error($this->cid);
440       return($r);
441     }else{
442       $this->error = "Could not connect to LDAP server";
443       return("");
444     }
445   }
447   function rmdir($deletedn)
448   {
449     if($this->hascon){
450       if ($this->reconnect) $this->connect();
451       $r = @ldap_delete($this->cid, LDAP::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   /**
461   *  Function rmdir_recursive
462   *
463   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
464   *  Parameters:  The dn to delete
465   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
466   *
467   */
469   function rmdir_recursive($deletedn)
470   {
471     if($this->hascon){
472       if ($this->reconnect) $this->connect();
473       $delarray= array();
474         
475       /* Get sorted list of dn's to delete */
476       $this->ls ("(objectClass=*)",$deletedn);
477       while ($this->fetch()){
478         $deldn= $this->getDN();
479         $delarray[$deldn]= strlen($deldn);
480       }
481       arsort ($delarray);
482       reset ($delarray);
484       /* Really Delete ALL dn's in subtree */
485       foreach ($delarray as $key => $value){
486         $this->rmdir_recursive($key);
487       }
488       
489       /* Finally Delete own Node */
490       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
491       $this->error = @ldap_error($this->cid);
492       return($r ? $r : 0);
493     }else{
494       $this->error = "Could not connect to LDAP server";
495       return("");
496     }
497   }
500   function modify($attrs)
501   {
502     if(count($attrs) == 0){
503       return (0);
504     }
505     if($this->hascon){
506       if ($this->reconnect) $this->connect();
507       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
508       $this->error = @ldap_error($this->cid);
509       return($r ? $r : 0);
510     }else{
511       $this->error = "Could not connect to LDAP server";
512       return("");
513     }
514   }
516   function add($attrs)
517   {
518     if($this->hascon){
519       if ($this->reconnect) $this->connect();
520       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
521       $this->error = @ldap_error($this->cid);
522       return($r ? $r : 0);
523     }else{
524       $this->error = "Could not connect to LDAP server";
525       return("");
526     }
527   }
529   function create_missing_trees($target)
530   {
531     global $config;
533     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
535     if ($target == $this->basedn){
536       $l= array("dummy");
537     } else {
538       $l= array_reverse(gosa_ldap_explode_dn($real_path));
539     }
540     unset($l['count']);
541     $cdn= $this->basedn;
542     $tag= "";
544     /* Load schema if available... */
545     $classes= $this->get_objectclasses();
547     foreach ($l as $part){
548       if ($part != "dummy"){
549         $cdn= "$part,$cdn";
550       }
552       /* Ignore referrals */
553       $found= false;
554       foreach($this->referrals as $ref){
555         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
556         if ($base == $cdn){
557           $found= true;
558           break;
559         }
560       }
561       if ($found){
562         continue;
563       }
565       $this->cat ($cdn);
566       $attrs= $this->fetch();
568       /* Create missing entry? */
569       if (count ($attrs)){
570       
571         /* Catch the tag - if present */
572         if (isset($attrs['gosaUnitTag'][0])){
573           $tag= $attrs['gosaUnitTag'][0];
574         }
576       } else {
577         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
578         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
580         $na= array();
582         /* Automatic or traditional? */
583         if(count($classes)){
585           /* Get name of first matching objectClass */
586           $ocname= "";
587           foreach($classes as $class){
588             if (isset($class['MUST']) && $class['MUST'] == "$type"){
590               /* Look for first classes that is structural... */
591               if (isset($class['STRUCTURAL'])){
592                 $ocname= $class['NAME'];
593                 break;
594               }
596               /* Look for classes that are auxiliary... */
597               if (isset($class['AUXILIARY'])){
598                 $ocname= $class['NAME'];
599               }
600             }
601           }
603           /* Bail out, if we've nothing to do... */
604           if ($ocname == ""){
605             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), FATAL_ERROR_DIALOG);
606             exit();
607           }
609           /* Assemble_entry */
610           if ($tag != ""){
611             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
612             $na["gosaUnitTag"]= $tag;
613           } else {
614             $na['objectClass']= array($ocname);
615           }
616           if (isset($classes[$ocname]['AUXILIARY'])){
617             $na['objectClass'][]= $classes[$ocname]['SUP'];
618           }
619           if ($type == "dc"){
620             /* This is bad actually, but - tell me a better way? */
621             $na['objectClass'][]= 'locality';
622           }
623           $na[$type]= $param;
624           if (is_array($classes[$ocname]['MUST'])){
625             foreach($classes[$ocname]['MUST'] as $attr){
626               $na[$attr]= "filled";
627             }
628           }
630         } else {
632           /* Use alternative add... */
633           switch ($type){
634             case 'ou':
635               if ($tag != ""){
636                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
637                 $na["gosaUnitTag"]= $tag;
638               } else {
639                 $na["objectClass"]= "organizationalUnit";
640               }
641               $na["ou"]= $param;
642               break;
643             case 'dc':
644               if ($tag != ""){
645                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
646                 $na["gosaUnitTag"]= $tag;
647               } else {
648                 $na["objectClass"]= array("dcObject", "top", "locality");
649               }
650               $na["dc"]= $param;
651               break;
652             default:
653               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
654               exit();
655           }
657         }
658         $this->cd($cdn);
659         $this->add($na);
660     
661         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
662         if (!preg_match('/success/i', $this->error)){
663           return FALSE;
664         }
665       }
666     }
668     return TRUE;
669   }
672   function recursive_remove()
673   {
674     $delarray= array();
676     /* Get sorted list of dn's to delete */
677     $this->search ("(objectClass=*)");
678     while ($this->fetch()){
679       $deldn= $this->getDN();
680       $delarray[$deldn]= strlen($deldn);
681     }
682     arsort ($delarray);
683     reset ($delarray);
685     /* Delete all dn's in subtree */
686     foreach ($delarray as $key => $value){
687       $this->rmdir($key);
688     }
689   }
691   function get_attribute($dn, $name,$r_array=0)
692   {
693     $data= "";
694     if ($this->reconnect) $this->connect();
695     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
697     /* fill data from LDAP */
698     if ($sr) {
699       $ei= @ldap_first_entry($this->cid, $sr);
700       if ($ei) {
701         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
702           $data= $info[0];
703         }
704       }
705     }
706     if($r_array==0)
707     return ($data);
708     else
709     return ($info);
710   
711   
712   }
713  
716   function get_additional_error()
717   {
718     $error= "";
719     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
720     return ($error);
721   }
723   function get_error()
724   {
725     if ($this->error == 'Success'){
726       return $this->error;
727     } else {
728       $adderror= $this->get_additional_error();
729       if ($adderror != ""){
730         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
731       } else {
732         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
733       }
734       return $error;
735     }
736   }
738   function get_credentials($url, $referrals= NULL)
739   {
740     $ret= array();
741     $url= preg_replace('!\?\?.*$!', '', $url);
742     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
744     if ($referrals === NULL){
745       $referrals= $this->referrals;
746     }
748     if (isset($referrals[$server])){
749       return ($referrals[$server]);
750     } else {
751       $ret['ADMIN']= LDAP::fix($this->binddn);
752       $ret['PASSWORD']= $this->bindpw;
753     }
755     return ($ret);
756   }
759   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
760   {
761     $display= "";
763     if ($recursive){
764       $this->cd($dn);
765       $this->ls($filter,$dn, array('dn','objectClass'));
766       $deps = array();
768       $display .= $this->gen_one_entry($dn)."\n";
770       while ($attrs= $this->fetch()){
771         $deps[] = $attrs['dn'];
772       }
773       foreach($deps as $dn){
774         $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
775       }
776     } else {
777       $display.= $this->gen_one_entry($dn);
778     }
779     return ($display);
780   }
783   function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
784   {
785     $display= array();
787       $this->cd($dn);
788       $this->search("$filter");
790       $i=0;
791       while ($attrs= $this->fetch()){
792         $j=0;
794         foreach ($attributes as $at){
795           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
796           $j++;
797         }
799         $i++;
800       }
802     return ($display);
803   }
806   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
807   {
808     $ret = "";
809     $data = "";
810     if($this->reconnect){
811       $this->connect();
812     }
814     /* Searching Ldap Tree */
815     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
817     /* Get the first entry */   
818     $entry= @ldap_first_entry($this->cid, $sr);
820     /* Get all attributes related to that Objekt */
821     $atts = array();
822     
823     /* Assemble dn */
824     $atts[0]['name']  = "dn";
825     $atts[0]['value'] = array('count' => 1, 0 => $dn);
827     /* Reset index */
828     $i = 1 ; 
829   $identifier = array();
830     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
831     while ($attribute) {
832       $i++;
833       $atts[$i]['name']  = $attribute;
834       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
836       /* Next one */
837       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
838     }
840     foreach($atts as $at)
841     {
842       for ($i= 0; $i<$at['value']['count']; $i++){
844         /* Check if we must encode the data */
845         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
846           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
847         } else {
848           $ret .= $at['name'].": ".$at['value'][$i]."\n";
849         }
850       }
851     }
853     return($ret);
854   }
857   function dn_exists($dn)
858   {
859     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
860   }
861   
864   /*  This funktion imports ldifs 
865         
866       If DeleteOldEntries is true, the destination entry will be deleted first. 
867       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
868       if JustMofify id false the destination dn will be overwritten by the new ldif. 
869     */
871   function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
872   {
873     if($this->reconnect) $this->connect();
875     /* First we have to splitt the string ito detect empty lines
876        An empty line indicates an new Entry */
877     $entries = split("\n",$str_attr);
879     $data = "";
880     $cnt = 0; 
881     $current_line = 0;
883     /* FIX ldif */
884     $last = "";
885     $tmp  = "";
886     $i = 0;
887     foreach($entries as $entry){
888       if(preg_match("/^ /",$entry)){
889         $tmp[$i] .= trim($entry);
890       }else{
891         $i ++;
892         $tmp[$i] = trim($entry);
893       }
894     }
896     /* Every single line ... */
897     foreach($tmp as $entry) {
898       $current_line ++;
900       /* Removing Spaces to .. 
901          .. test if a new entry begins */
902       $tmp  = str_replace(" ","",$data );
904       /* .. prevent empty lines in an entry */
905       $tmp2 = str_replace(" ","",$entry);
907       /* If the Block ends (Empty Line) */
908       if((empty($entry))&&(!empty($tmp))) {
909         /* Add collected lines as a complete block */
910         $all[$cnt] = $data;
911         $cnt ++;
912         $data ="";
913       } else {
915         /* Append lines ... */
916         if(!empty($tmp2)) {
917           /* check if we need base64_decode for this line */
918           if(ereg("::",$tmp2))
919           {
920             $encoded = split("::",$entry);
921             $attr  = trim($encoded[0]);
922             $value = base64_decode(trim($encoded[1]));
923             /* Add linenumber */
924             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
925           }
926           else
927           {
928             /* Add Linenumber */ 
929             $data .= $current_line."#".base64_encode($entry)."\n";
930           }
931         }
932       }
933     }
935     /* The Data we collected is not in the array all[];
936        For example the Data is stored like this..
938        all[0] = "1#dn : .... \n 
939        2#ObjectType: person \n ...."
940        
941        Now we check every insertblock and try to insert */
942     foreach ( $all as $single) {
943       $lineone = split("\n",$single);  
944       $ndn = split("#", $lineone[0]);
945       $line = base64_decode($ndn[1]);
947       $dnn = split (":",$line,2);
948       $current_line = $ndn[0];
949       $dn    = $dnn[0];
950       $value = $dnn[1];
952       /* Every block must begin with a dn */
953       if($dn != "dn") {
954         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
955         return -2;  
956       }
958       /* Should we use Modify instead of Add */
959       $usemodify= false;
961       /* Delete before insert */
962       $usermdir= false;
963     
964       /* The dn address already exists, Don't delete destination entry, overwrite it */
965       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
967         $usermdir = $usemodify = false;
969       /* Delete old entry first, then add new */
970       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
972         /* Delete first, then add */
973         $usermdir = true;        
975       } elseif(($this->dn_exists($value))&&($JustModify)) {
976         
977         /* Modify instead of Add */
978         $usemodify = true;
979       }
980      
981       /* If we can't Import, return with a file error */
982       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
983         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
984                         $current_line);
985         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
986     }
988     return (INSERT_OK);
989   }
992   /* Imports a single entry 
993       If $delete is true;  The old entry will be deleted if it exists.
994       if $modify is true;  All variables that are not touched by the new ldif will be kept.
995       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
996   */
997   function import_single_entry($str_attr,$modify,$delete)
998   {
999     global $config;
1001     if(!$config){
1002       trigger_error("Can't import ldif, can't read config object.");
1003     }
1004   
1006     if($this->reconnect) $this->connect();
1008     $ret = false;
1009     $rows= split("\n",$str_attr);
1010     $data= false;
1012     foreach($rows as $row) {
1013       
1014       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1015          Linenumbers) Linenumbers are use like this 123#attribute : value */
1016       if(!empty($row)) {
1017         if(strpos($row,"#")!=FALSE) {
1019           /* We are using line numbers 
1020              Because there is a # before a : */
1021           $tmp1= split("#",$row);
1022           $current_line= $tmp1[0];
1023           $row= base64_decode($tmp1[1]);
1024         }
1026         /* Split the line into  attribute  and value */
1027         $attr   = split(":", $row,2);
1028         $attr[0]= trim($attr[0]);  /* attribute */
1029         $attr[1]= $attr[1];  /* value */
1031         /* Check :: was used to indicate base64_encoded strings */
1032         if($attr[1][0] == ":"){
1033           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1034           $attr[1]=base64_decode($attr[1]);
1035         }
1037         $attr[1] = trim($attr[1]);
1039         /* Check for attributes that are used more than once */
1040         if(!isset($data[$attr[0]])) {
1041           $data[$attr[0]]=$attr[1];
1042         } else {
1043           $tmp = $data[$attr[0]];
1045           if(!is_array($tmp)) {
1046             $new[0]=$tmp;
1047             $new[1]=$attr[1];
1048             $datas[$attr[0]]['count']=1;             
1049             $data[$attr[0]]=$new;
1050           } else {
1051             $cnt = $datas[$attr[0]]['count'];           
1052             $cnt ++;
1053             $data[$attr[0]][$cnt]=$attr[1];
1054             $datas[$attr[0]]['count'] = $cnt;
1055           }
1056         }
1057       }
1058     }
1060     /* If dn is an index of data, we should try to insert the data */
1061     if(isset($data['dn'])) {
1063       /* Fix dn */
1064       $tmp = gosa_ldap_explode_dn($data['dn']);
1065       unset($tmp['count']);
1066       $newdn ="";
1067       foreach($tmp as $tm){
1068         $newdn.= trim($tm).",";
1069       }
1070       $newdn = preg_replace("/,$/","",$newdn);
1071       $data['dn'] = $newdn;
1072    
1073       /* Creating Entry */
1074       $this->cd($data['dn']);
1076       /* Delete existing entry */
1077       if($delete){
1078         $this->rmdir_recursive($data['dn']);
1079       }
1080      
1081       /* Create missing trees */
1082       $this->cd ($this->basedn);
1083       $this->cd($config->current['BASE']);
1084       $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1085       $this->cd($data['dn']);
1087       $dn = $data['dn'];
1088       unset($data['dn']);
1089       
1090       if(!$modify){
1092         $this->cat($dn);
1093         if($this->count()){
1094         
1095           /* The destination entry exists, overwrite it with the new entry */
1096           $attrs = $this->fetch();
1097           foreach($attrs as $name => $value ){
1098             if(!is_numeric($name)){
1099               if(in_array($name,array("dn","count"))) continue;
1100               if(!isset($data[$name])){
1101                 $data[$name] = array();
1102               }
1103             }
1104           }
1105           $ret = $this->modify($data);
1106     
1107         }else{
1108     
1109           /* The destination entry doesn't exists, create it */
1110           $ret = $this->add($data);
1111         }
1113       } else {
1114         
1115         /* Keep all vars that aren't touched by this ldif */
1116         $ret = $this->modify($data);
1117       }
1118     }
1119     show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1120     return($ret);
1121   }
1123   
1124   function importcsv($str)
1125   {
1126     $lines = split("\n",$str);
1127     foreach($lines as $line)
1128     {
1129       /* continue if theres a comment */
1130       if(substr(trim($line),0,1)=="#"){
1131         continue;
1132       }
1134       $line= str_replace ("\t\t","\t",$line);
1135       $line= str_replace ("\t"  ,"," ,$line);
1136       echo $line;
1138       $cells = split(",",$line )  ;
1139       $linet= str_replace ("\t\t",",",$line);
1140       $cells = split("\t",$line);
1141       $count = count($cells);  
1142     }
1144   }
1145   
1146   function get_objectclasses()
1147   {
1148     $objectclasses = array();
1149     global $config;
1151     /* Only read schema if it is allowed */
1152     if(isset($config) && preg_match("/config/i",get_class($config))){
1153       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1154         return($objectclasses);
1155       } 
1156     }
1158     /* Return the cached results. */
1159     if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1160       $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1161       return($objectclasses);
1162     }
1163         
1164           # Get base to look for schema 
1165           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1166     if(!$sr){
1167             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1168     }
1170           $attr = @ldap_get_entries($this->cid,$sr);
1171           if (!isset($attr[0]['subschemasubentry'][0])){
1172             return array();
1173           }
1174         
1175           /* Get list of objectclasses and fill array */
1176           $nb= $attr[0]['subschemasubentry'][0];
1177           $objectclasses= array();
1178           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1179           $attrs= ldap_get_entries($this->cid,$sr);
1180           if (!isset($attrs[0])){
1181             return array();
1182           }
1183           foreach ($attrs[0]['objectclasses'] as $val){
1184       if (preg_match('/^[0-9]+$/', $val)){
1185         continue;
1186       }
1187       $name= "OID";
1188       $pattern= split(' ', $val);
1189       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1190       $objectclasses[$ocname]= array();
1192       foreach($pattern as $chunk){
1193         switch($chunk){
1195           case '(':
1196                     $value= "";
1197                     break;
1199           case ')': if ($name != ""){
1200                       $objectclasses[$ocname][$name]= $this->value2container($value);
1201                     }
1202                     $name= "";
1203                     $value= "";
1204                     break;
1206           case 'NAME':
1207           case 'DESC':
1208           case 'SUP':
1209           case 'STRUCTURAL':
1210           case 'ABSTRACT':
1211           case 'AUXILIARY':
1212           case 'MUST':
1213           case 'MAY':
1214                     if ($name != ""){
1215                       $objectclasses[$ocname][$name]= $this->value2container($value);
1216                     }
1217                     $name= $chunk;
1218                     $value= "";
1219                     break;
1221           default:  $value.= $chunk." ";
1222         }
1223       }
1225           }
1226     if(class_available("session")){
1227       session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1228     }
1229           return $objectclasses;
1230   }
1233   function value2container($value)
1234   {
1235     /* Set emtpy values to "true" only */
1236     if (preg_match('/^\s*$/', $value)){
1237       return true;
1238     }
1240     /* Remove ' and " if needed */
1241     $value= preg_replace('/^[\'"]/', '', $value);
1242     $value= preg_replace('/[\'"] *$/', '', $value);
1244     /* Convert to array if $ is inside... */
1245     if (preg_match('/\$/', $value)){
1246       $container= preg_split('/\s*\$\s*/', $value);
1247     } else {
1248       $container= chop($value);
1249     }
1251     return ($container);
1252   }
1255   function log($string)
1256   {
1257     if (session::is_set('config')){
1258       $cfg = session::get('config');
1259       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1260         syslog (LOG_INFO, $string);
1261       }
1262     }
1263   }
1265   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1266   function getCn($dn){
1267     $simple= split(",", $dn);
1269     foreach($simple as $piece) {
1270       $partial= split("=", $piece);
1272       if($partial[0] == "cn"){
1273         return $partial[1];
1274       }
1275     }
1276   }
1279   function get_naming_contexts($server, $admin= "", $password= "")
1280   {
1281     /* Build LDAP connection */
1282     $ds= ldap_connect ($server);
1283     if (!$ds) {
1284       die ("Can't bind to LDAP. No check possible!");
1285     }
1286     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1287     $r= ldap_bind ($ds, $admin, $password);
1289     /* Get base to look for naming contexts */
1290     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1291     $attr= @ldap_get_entries($ds,$sr);
1293     return ($attr[0]['namingcontexts']);
1294   }
1297   function get_root_dse($server, $admin= "", $password= "")
1298   {
1299     /* Build LDAP connection */
1300     $ds= ldap_connect ($server);
1301     if (!$ds) {
1302       die ("Can't bind to LDAP. No check possible!");
1303     }
1304     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1305     $r= ldap_bind ($ds, $admin, $password);
1307     /* Get base to look for naming contexts */
1308     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1309     $attr= @ldap_get_entries($ds,$sr);
1310    
1311     /* Return empty array, if nothing was set */
1312     if (!isset($attr[0])){
1313       return array();
1314     }
1316     /* Rework array... */
1317     $result= array();
1318     for ($i= 0; $i<$attr[0]['count']; $i++){
1319       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1320       unset($result[$attr[0][$i]]['count']);
1321     }
1323     return ($result);
1324   }
1326 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1327 ?>