Code

4e0b82ac9720f30a3ec2ba65ca9f47290bcb9dbb
[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   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
50   {
51     global $config;
52     $this->follow_referral= $follow_referral;
53     $this->tls=$tls;
54     $this->binddn=LDAP::convert($binddn);
56     $this->bindpw=$bindpw;
57     $this->hostname=$hostname;
59     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
60     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
61       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
62       $this->max_ldap_query_time = (float)($str);
63     }
65     $this->connect();
66   }
69   /* Function to replace all problematic characters inside a DN by \001XX, where
70      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
71      that this character is inside a DN.
72      
73      Currently used codes:
74       ,   => CO
75       \2C => CO
76       (   => OB
77       )   => CB
78       /   => SL                                                                  */
79   static function convert($dn)
80   {
81     if (SPECIALS_OVERRIDE == TRUE){
82       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
83                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
84                            $dn);
85       return (preg_replace('/,\s+/', ',', $tmp));
86     } else {
87       return ($dn);
88     }
89   }
92   /* Function to fix all problematic characters inside a DN by replacing \001XX
93      codes to their original values. See "convert" for mor information. 
94      ',' characters are always expanded to \, (not \2C), since all tested LDAP
95      servers seem to take it the correct way.                                  */
96   static function fix($dn)
97   {
98     if (SPECIALS_OVERRIDE == TRUE){
99       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
100                            array("\,", "(", ")", "/"),
101                            $dn));
102     } else {
103       return ($dn);
104     }
105   }
108   /* Function to fix problematic characters in DN's that are used for search
109      requests. I.e. member=....                                               */
110   static function prepare4filter($dn)
111   {
112         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
113   }
116   function connect()
117   {
118     $this->hascon=false;
119     $this->reconnect=false;
120     if ($this->cid= @ldap_connect($this->hostname)) {
121       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
122       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
123         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
124         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
125       }
126       if (function_exists("ldap_start_tls") && $this->tls){
127         @ldap_start_tls($this->cid);
128       }
130       $this->error = "No Error";
131       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
132         $this->error = "Success";
133         $this->hascon=true;
134       } else {
135         if ($this->reconnect){
136           if ($this->error != "Success"){
137             $this->error = "Could not rebind to " . $this->binddn;
138           }
139         } else {
140           $this->error = "Could not bind to " . $this->binddn;
141         }
142       }
143     } else {
144       $this->error = "Could not connect to LDAP server";
145     }
146   }
148   function rebind($ldap, $referral)
149   {
150     $credentials= $this->get_credentials($referral);
151     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
152       $this->error = "Success";
153       $this->hascon=true;
154       $this->reconnect= true;
155       return (0);
156     } else {
157       $this->error = "Could not bind to " . $credentials['ADMIN'];
158       return NULL;
159     }
160   }
162   function reconnect()
163   {
164     if ($this->reconnect){
165       @ldap_unbind($this->cid);
166       $this->cid = NULL;
167     }
168   }
170   function unbind()
171   {
172     @ldap_unbind($this->cid);
173     $this->cid = NULL;
174   }
176   function disconnect()
177   {
178     if($this->hascon){
179       @ldap_close($this->cid);
180       $this->hascon=false;
181     }
182   }
184   function cd($dir)
185   {
186     if ($dir == "..")
187       $this->basedn = $this->getParentDir();
188     else
189       $this->basedn = LDAP::convert($dir);
190   }
192   function getParentDir($basedn = "")
193   {
194     if ($basedn=="")
195       $basedn = $this->basedn;
196     else
197       $basedn = LDAP::convert($this->basedn);
198     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
199   }
201   function search($filter, $attrs= array())
202   {
203     if($this->hascon){
204       if ($this->reconnect) $this->connect();
206       $start = microtime();
207    
208       $this->clearResult();
209       $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
210       $this->error = @ldap_error($this->cid);
211       $this->resetResult();
212       $this->hasres=true;
213    
214       /* Check if query took longer as specified in max_ldap_query_time */
215       if($this->max_ldap_query_time){
216         $diff = get_MicroTimeDiff($start,microtime());
217         if($diff > $this->max_ldap_query_time){
218           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
219         }
220       }
222       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
223       return($this->sr);
224     }else{
225       $this->error = "Could not connect to LDAP server";
226       return("");
227     }
228   }
230   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
231   {
232     if($this->hascon){
233       if ($this->reconnect) $this->connect();
234       $this->clearResult();
235       if ($basedn == "")
236         $basedn = $this->basedn;
237       else
238         $basedn= LDAP::convert($basedn);
239   
240       $start = microtime();
241       $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
242       $this->error = @ldap_error($this->cid);
243       $this->resetResult();
244       $this->hasres=true;
246        /* Check if query took longer as specified in max_ldap_query_time */
247       if($this->max_ldap_query_time){
248         $diff = get_MicroTimeDiff($start,microtime());
249         if($diff > $this->max_ldap_query_time){
250           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
251         }
252       }
254       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
256       return($this->sr);
257     }else{
258       $this->error = "Could not connect to LDAP server";
259       return("");
260     }
261   }
263   function cat($dn,$attrs= array("*"))
264   {
265     if($this->hascon){
266       if ($this->reconnect) $this->connect();
267       $this->clearResult();
268       $filter = "(objectclass=*)";
269       $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
270       $this->error = @ldap_error($this->cid);
271       $this->resetResult();
272       $this->hasres=true;
273       return($this->sr);
274     }else{
275       $this->error = "Could not connect to LDAP server";
276       return("");
277     }
278   }
280   function set_size_limit($size)
281   {
282     /* Ignore zero settings */
283     if ($size == 0){
284       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
285     }
286     if($this->hascon){
287       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
288     } else {
289       $this->error = "Could not connect to LDAP server";
290     }
291   }
293   function fetch()
294   {
295     $att= array();
296     if($this->hascon){
297       if($this->hasres){
298         if ($this->start == 0)
299         {
300           if ($this->sr){
301             $this->start = 1;
302             $this->re= @ldap_first_entry($this->cid, $this->sr);
303           } else {
304             return array();
305           }
306         } else {
307           $this->re= @ldap_next_entry($this->cid, $this->re);
308         }
309         if ($this->re)
310         {
311           $att= @ldap_get_attributes($this->cid, $this->re);
312           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
313         }
314         $this->error = @ldap_error($this->cid);
315         if (!isset($att)){
316           $att= array();
317         }
318         return($att);
319       }else{
320         $this->error = "Perform a Fetch with no Search";
321         return("");
322       }
323     }else{
324       $this->error = "Could not connect to LDAP server";
325       return("");
326     }
327   }
329   function resetResult()
330   {
331     $this->start = 0;
332   }
334   function clearResult()
335   {
336     if($this->hasres){
337       $this->hasres = false;
338       @ldap_free_result($this->sr);
339     }
340   }
342   function getDN()
343   {
344     if($this->hascon){
345       if($this->hasres){
347         if(!$this->re)
348           {
349           $this->error = "Perform a Fetch with no valid Result";
350           }
351           else
352           {
353           $rv = @ldap_get_dn($this->cid, $this->re);
354         
355           $this->error = @ldap_error($this->cid);
356           return(trim(LDAP::convert($rv)));
357            }
358       }else{
359         $this->error = "Perform a Fetch with no Search";
360         return("");
361       }
362     }else{
363       $this->error = "Could not connect to LDAP server";
364       return("");
365     }
366   }
368   function count()
369   {
370     if($this->hascon){
371       if($this->hasres){
372         $rv = @ldap_count_entries($this->cid, $this->sr);
373         $this->error = @ldap_error($this->cid);
374         return($rv);
375       }else{
376         $this->error = "Perform a Fetch with no Search";
377         return("");
378       }
379     }else{
380       $this->error = "Could not connect to LDAP server";
381       return("");
382     }
383   }
385   function rm($attrs = "", $dn = "")
386   {
387     if($this->hascon){
388       if ($this->reconnect) $this->connect();
389       if ($dn == "")
390         $dn = $this->basedn;
392       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
393       $this->error = @ldap_error($this->cid);
394       return($r);
395     }else{
396       $this->error = "Could not connect to LDAP server";
397       return("");
398     }
399   }
401   function rename($attrs, $dn = "")
402   {
403     if($this->hascon){
404       if ($this->reconnect) $this->connect();
405       if ($dn == "")
406         $dn = $this->basedn;
408       $r = @ldap_mod_replace($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 rmdir($deletedn)
418   {
419     if($this->hascon){
420       if ($this->reconnect) $this->connect();
421       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
422       $this->error = @ldap_error($this->cid);
423       return($r ? $r : 0);
424     }else{
425       $this->error = "Could not connect to LDAP server";
426       return("");
427     }
428   }
430   /**
431   *  Function rmdir_recursive
432   *
433   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
434   *  Parameters:  The dn to delete
435   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
436   *
437   */
439   function rmdir_recursive($deletedn)
440   {
441     if($this->hascon){
442       if ($this->reconnect) $this->connect();
443       $delarray= array();
444         
445       /* Get sorted list of dn's to delete */
446       $this->ls ("(objectClass=*)",$deletedn);
447       while ($this->fetch()){
448         $deldn= $this->getDN();
449         $delarray[$deldn]= strlen($deldn);
450       }
451       arsort ($delarray);
452       reset ($delarray);
454       /* Really Delete ALL dn's in subtree */
455       foreach ($delarray as $key => $value){
456         $this->rmdir_recursive($key);
457       }
458       
459       /* Finally Delete own Node */
460       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
461       $this->error = @ldap_error($this->cid);
462       return($r ? $r : 0);
463     }else{
464       $this->error = "Could not connect to LDAP server";
465       return("");
466     }
467   }
470   function modify($attrs)
471   {
472     if(count($attrs) == 0){
473       return (0);
474     }
475     if($this->hascon){
476       if ($this->reconnect) $this->connect();
477       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
478       $this->error = @ldap_error($this->cid);
479       return($r ? $r : 0);
480     }else{
481       $this->error = "Could not connect to LDAP server";
482       return("");
483     }
484   }
486   function add($attrs)
487   {
488     if($this->hascon){
489       if ($this->reconnect) $this->connect();
490       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
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   }
499   function create_missing_trees($target)
500   {
501     global $config;
503     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
505     if ($target == $this->basedn){
506       $l= array("dummy");
507     } else {
508       $l= array_reverse(gosa_ldap_explode_dn($real_path));
509     }
510     unset($l['count']);
511     $cdn= $this->basedn;
512     $tag= "";
514     /* Load schema if available... */
515     $classes= $this->get_objectclasses();
517     foreach ($l as $part){
518       if ($part != "dummy"){
519         $cdn= "$part,$cdn";
520       }
522       /* Ignore referrals */
523       $found= false;
524       foreach($this->referrals as $ref){
525         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
526         if ($base == $cdn){
527           $found= true;
528           break;
529         }
530       }
531       if ($found){
532         continue;
533       }
535       $this->cat ($cdn);
536       $attrs= $this->fetch();
538       /* Create missing entry? */
539       if (count ($attrs)){
540       
541         /* Catch the tag - if present */
542         if (isset($attrs['gosaUnitTag'][0])){
543           $tag= $attrs['gosaUnitTag'][0];
544         }
546       } else {
547         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
548         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
550         $na= array();
552         /* Automatic or traditional? */
553         if(count($classes)){
555           /* Get name of first matching objectClass */
556           $ocname= "";
557           foreach($classes as $class){
558             if (isset($class['MUST']) && $class['MUST'] == "$type"){
560               /* Look for first classes that is structural... */
561               if (isset($class['STRUCTURAL'])){
562                 $ocname= $class['NAME'];
563                 break;
564               }
566               /* Look for classes that are auxiliary... */
567               if (isset($class['AUXILIARY'])){
568                 $ocname= $class['NAME'];
569               }
570             }
571           }
573           /* Bail out, if we've nothing to do... */
574           if ($ocname == ""){
575             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), FATAL_ERROR_DIALOG);
576             exit();
577           }
579           /* Assemble_entry */
580           if ($tag != ""){
581             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
582             $na["gosaUnitTag"]= $tag;
583           } else {
584             $na['objectClass']= array($ocname);
585           }
586           if (isset($classes[$ocname]['AUXILIARY'])){
587             $na['objectClass'][]= $classes[$ocname]['SUP'];
588           }
589           if ($type == "dc"){
590             /* This is bad actually, but - tell me a better way? */
591             $na['objectClass'][]= 'locality';
592           }
593           $na[$type]= $param;
594           if (is_array($classes[$ocname]['MUST'])){
595             foreach($classes[$ocname]['MUST'] as $attr){
596               $na[$attr]= "filled";
597             }
598           }
600         } else {
602           /* Use alternative add... */
603           switch ($type){
604             case 'ou':
605               if ($tag != ""){
606                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
607                 $na["gosaUnitTag"]= $tag;
608               } else {
609                 $na["objectClass"]= "organizationalUnit";
610               }
611               $na["ou"]= $param;
612               break;
613             case 'dc':
614               if ($tag != ""){
615                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
616                 $na["gosaUnitTag"]= $tag;
617               } else {
618                 $na["objectClass"]= array("dcObject", "top", "locality");
619               }
620               $na["dc"]= $param;
621               break;
622             default:
623               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
624               exit();
625           }
627         }
628         $this->cd($cdn);
629         $this->add($na);
630     
631         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
632         if (!preg_match('/success/i', $this->error)){
633           return FALSE;
634         }
635       }
636     }
638     return TRUE;
639   }
642   function recursive_remove()
643   {
644     $delarray= array();
646     /* Get sorted list of dn's to delete */
647     $this->search ("(objectClass=*)");
648     while ($this->fetch()){
649       $deldn= $this->getDN();
650       $delarray[$deldn]= strlen($deldn);
651     }
652     arsort ($delarray);
653     reset ($delarray);
655     /* Delete all dn's in subtree */
656     foreach ($delarray as $key => $value){
657       $this->rmdir($key);
658     }
659   }
661   function get_attribute($dn, $name,$r_array=0)
662   {
663     $data= "";
664     if ($this->reconnect) $this->connect();
665     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
667     /* fill data from LDAP */
668     if ($sr) {
669       $ei= @ldap_first_entry($this->cid, $sr);
670       if ($ei) {
671         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
672           $data= $info[0];
673         }
674       }
675     }
676     if($r_array==0)
677     return ($data);
678     else
679     return ($info);
680   
681   
682   }
683  
686   function get_additional_error()
687   {
688     $error= "";
689     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
690     return ($error);
691   }
693   function get_error()
694   {
695     if ($this->error == 'Success'){
696       return $this->error;
697     } else {
698       $adderror= $this->get_additional_error();
699       if ($adderror != ""){
700         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
701       } else {
702         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
703       }
704       return $error;
705     }
706   }
708   function get_credentials($url, $referrals= NULL)
709   {
710     $ret= array();
711     $url= preg_replace('!\?\?.*$!', '', $url);
712     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
714     if ($referrals === NULL){
715       $referrals= $this->referrals;
716     }
718     if (isset($referrals[$server])){
719       return ($referrals[$server]);
720     } else {
721       $ret['ADMIN']= LDAP::fix($this->binddn);
722       $ret['PASSWORD']= $this->bindpw;
723     }
725     return ($ret);
726   }
729   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
730   {
731     $display= "";
733     if ($recursive){
734       $this->cd($dn);
735       $this->ls($filter,$dn, array('dn','objectClass'));
736       $deps = array();
738       $display .= $this->gen_one_entry($dn)."\n";
740       while ($attrs= $this->fetch()){
741         $deps[] = $attrs['dn'];
742       }
743       foreach($deps as $dn){
744         $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
745       }
746     } else {
747       $display.= $this->gen_one_entry($dn);
748     }
749     return ($display);
750   }
753   function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
754   {
755     $display= array();
757       $this->cd($dn);
758       $this->search("$filter");
760       $i=0;
761       while ($attrs= $this->fetch()){
762         $j=0;
764         foreach ($attributes as $at){
765           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
766           $j++;
767         }
769         $i++;
770       }
772     return ($display);
773   }
776   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
777   {
778     $ret = "";
779     $data = "";
780     if($this->reconnect){
781       $this->connect();
782     }
784     /* Searching Ldap Tree */
785     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
787     /* Get the first entry */   
788     $entry= @ldap_first_entry($this->cid, $sr);
790     /* Get all attributes related to that Objekt */
791     $atts = array();
792     
793     /* Assemble dn */
794     $atts[0]['name']  = "dn";
795     $atts[0]['value'] = array('count' => 1, 0 => $dn);
797     /* Reset index */
798     $i = 1 ; 
799   $identifier = array();
800     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
801     while ($attribute) {
802       $i++;
803       $atts[$i]['name']  = $attribute;
804       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
806       /* Next one */
807       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
808     }
810     foreach($atts as $at)
811     {
812       for ($i= 0; $i<$at['value']['count']; $i++){
814         /* Check if we must encode the data */
815         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
816           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
817         } else {
818           $ret .= $at['name'].": ".$at['value'][$i]."\n";
819         }
820       }
821     }
823     return($ret);
824   }
827   function dn_exists($dn)
828   {
829     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
830   }
831   
834   /*  This funktion imports ldifs 
835         
836       If DeleteOldEntries is true, the destination entry will be deleted first. 
837       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
838       if JustMofify id false the destination dn will be overwritten by the new ldif. 
839     */
841   function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
842   {
843     if($this->reconnect) $this->connect();
845     /* First we have to splitt the string ito detect empty lines
846        An empty line indicates an new Entry */
847     $entries = split("\n",$str_attr);
849     $data = "";
850     $cnt = 0; 
851     $current_line = 0;
853     /* FIX ldif */
854     $last = "";
855     $tmp  = "";
856     $i = 0;
857     foreach($entries as $entry){
858       if(preg_match("/^ /",$entry)){
859         $tmp[$i] .= trim($entry);
860       }else{
861         $i ++;
862         $tmp[$i] = trim($entry);
863       }
864     }
866     /* Every single line ... */
867     foreach($tmp as $entry) {
868       $current_line ++;
870       /* Removing Spaces to .. 
871          .. test if a new entry begins */
872       $tmp  = str_replace(" ","",$data );
874       /* .. prevent empty lines in an entry */
875       $tmp2 = str_replace(" ","",$entry);
877       /* If the Block ends (Empty Line) */
878       if((empty($entry))&&(!empty($tmp))) {
879         /* Add collected lines as a complete block */
880         $all[$cnt] = $data;
881         $cnt ++;
882         $data ="";
883       } else {
885         /* Append lines ... */
886         if(!empty($tmp2)) {
887           /* check if we need base64_decode for this line */
888           if(ereg("::",$tmp2))
889           {
890             $encoded = split("::",$entry);
891             $attr  = trim($encoded[0]);
892             $value = base64_decode(trim($encoded[1]));
893             /* Add linenumber */
894             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
895           }
896           else
897           {
898             /* Add Linenumber */ 
899             $data .= $current_line."#".base64_encode($entry)."\n";
900           }
901         }
902       }
903     }
905     /* The Data we collected is not in the array all[];
906        For example the Data is stored like this..
908        all[0] = "1#dn : .... \n 
909        2#ObjectType: person \n ...."
910        
911        Now we check every insertblock and try to insert */
912     foreach ( $all as $single) {
913       $lineone = split("\n",$single);  
914       $ndn = split("#", $lineone[0]);
915       $line = base64_decode($ndn[1]);
917       $dnn = split (":",$line,2);
918       $current_line = $ndn[0];
919       $dn    = $dnn[0];
920       $value = $dnn[1];
922       /* Every block must begin with a dn */
923       if($dn != "dn") {
924         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
925         return -2;  
926       }
928       /* Should we use Modify instead of Add */
929       $usemodify= false;
931       /* Delete before insert */
932       $usermdir= false;
933     
934       /* The dn address already exists, Don't delete destination entry, overwrite it */
935       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
937         $usermdir = $usemodify = false;
939       /* Delete old entry first, then add new */
940       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
942         /* Delete first, then add */
943         $usermdir = true;        
945       } elseif(($this->dn_exists($value))&&($JustModify)) {
946         
947         /* Modify instead of Add */
948         $usemodify = true;
949       }
950      
951       /* If we can't Import, return with a file error */
952       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
953         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
954                         $current_line);
955         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
956     }
958     return (INSERT_OK);
959   }
962   /* Imports a single entry 
963       If $delete is true;  The old entry will be deleted if it exists.
964       if $modify is true;  All variables that are not touched by the new ldif will be kept.
965       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
966   */
967   function import_single_entry($str_attr,$modify,$delete)
968   {
969     global $config;
971     if(!$config){
972       trigger_error("Can't import ldif, can't read config object.");
973     }
974   
976     if($this->reconnect) $this->connect();
978     $ret = false;
979     $rows= split("\n",$str_attr);
980     $data= false;
982     foreach($rows as $row) {
983       
984       /* Check if we use Linenumbers (when import_complete_ldif is called we use
985          Linenumbers) Linenumbers are use like this 123#attribute : value */
986       if(!empty($row)) {
987         if(strpos($row,"#")!=FALSE) {
989           /* We are using line numbers 
990              Because there is a # before a : */
991           $tmp1= split("#",$row);
992           $current_line= $tmp1[0];
993           $row= base64_decode($tmp1[1]);
994         }
996         /* Split the line into  attribute  and value */
997         $attr   = split(":", $row,2);
998         $attr[0]= trim($attr[0]);  /* attribute */
999         $attr[1]= $attr[1];  /* value */
1001         /* Check :: was used to indicate base64_encoded strings */
1002         if($attr[1][0] == ":"){
1003           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1004           $attr[1]=base64_decode($attr[1]);
1005         }
1007         $attr[1] = trim($attr[1]);
1009         /* Check for attributes that are used more than once */
1010         if(!isset($data[$attr[0]])) {
1011           $data[$attr[0]]=$attr[1];
1012         } else {
1013           $tmp = $data[$attr[0]];
1015           if(!is_array($tmp)) {
1016             $new[0]=$tmp;
1017             $new[1]=$attr[1];
1018             $datas[$attr[0]]['count']=1;             
1019             $data[$attr[0]]=$new;
1020           } else {
1021             $cnt = $datas[$attr[0]]['count'];           
1022             $cnt ++;
1023             $data[$attr[0]][$cnt]=$attr[1];
1024             $datas[$attr[0]]['count'] = $cnt;
1025           }
1026         }
1027       }
1028     }
1030     /* If dn is an index of data, we should try to insert the data */
1031     if(isset($data['dn'])) {
1033       /* Fix dn */
1034       $tmp = gosa_ldap_explode_dn($data['dn']);
1035       unset($tmp['count']);
1036       $newdn ="";
1037       foreach($tmp as $tm){
1038         $newdn.= trim($tm).",";
1039       }
1040       $newdn = preg_replace("/,$/","",$newdn);
1041       $data['dn'] = $newdn;
1042    
1043       /* Creating Entry */
1044       $this->cd($data['dn']);
1046       /* Delete existing entry */
1047       if($delete){
1048         $this->rmdir_recursive($data['dn']);
1049       }
1050      
1051       /* Create missing trees */
1052       $this->cd ($this->basedn);
1053       $this->cd($config->current['BASE']);
1054       $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1055       $this->cd($data['dn']);
1057       $dn = $data['dn'];
1058       unset($data['dn']);
1059       
1060       if(!$modify){
1062         $this->cat($dn);
1063         if($this->count()){
1064         
1065           /* The destination entry exists, overwrite it with the new entry */
1066           $attrs = $this->fetch();
1067           foreach($attrs as $name => $value ){
1068             if(!is_numeric($name)){
1069               if(in_array($name,array("dn","count"))) continue;
1070               if(!isset($data[$name])){
1071                 $data[$name] = array();
1072               }
1073             }
1074           }
1075           $ret = $this->modify($data);
1076     
1077         }else{
1078     
1079           /* The destination entry doesn't exists, create it */
1080           $ret = $this->add($data);
1081         }
1083       } else {
1084         
1085         /* Keep all vars that aren't touched by this ldif */
1086         $ret = $this->modify($data);
1087       }
1088     }
1089     show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1090     return($ret);
1091   }
1093   
1094   function importcsv($str)
1095   {
1096     $lines = split("\n",$str);
1097     foreach($lines as $line)
1098     {
1099       /* continue if theres a comment */
1100       if(substr(trim($line),0,1)=="#"){
1101         continue;
1102       }
1104       $line= str_replace ("\t\t","\t",$line);
1105       $line= str_replace ("\t"  ,"," ,$line);
1106       echo $line;
1108       $cells = split(",",$line )  ;
1109       $linet= str_replace ("\t\t",",",$line);
1110       $cells = split("\t",$line);
1111       $count = count($cells);  
1112     }
1114   }
1115   
1116   function get_objectclasses()
1117   {
1118     $objectclasses = array();
1119     global $config;
1121     /* Only read schema if it is allowed */
1122     if(isset($config) && preg_match("/config/i",get_class($config))){
1123       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1124         return($objectclasses);
1125       } 
1126     }
1128     /* Return the cached results. */
1129     if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1130       $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1131       return($objectclasses);
1132     }
1133         
1134           # Get base to look for schema 
1135           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1136     if(!$sr){
1137             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1138     }
1140           $attr = @ldap_get_entries($this->cid,$sr);
1141           if (!isset($attr[0]['subschemasubentry'][0])){
1142             return array();
1143           }
1144         
1145           /* Get list of objectclasses and fill array */
1146           $nb= $attr[0]['subschemasubentry'][0];
1147           $objectclasses= array();
1148           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1149           $attrs= ldap_get_entries($this->cid,$sr);
1150           if (!isset($attrs[0])){
1151             return array();
1152           }
1153           foreach ($attrs[0]['objectclasses'] as $val){
1154       if (preg_match('/^[0-9]+$/', $val)){
1155         continue;
1156       }
1157       $name= "OID";
1158       $pattern= split(' ', $val);
1159       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1160       $objectclasses[$ocname]= array();
1162       foreach($pattern as $chunk){
1163         switch($chunk){
1165           case '(':
1166                     $value= "";
1167                     break;
1169           case ')': if ($name != ""){
1170                       $objectclasses[$ocname][$name]= $this->value2container($value);
1171                     }
1172                     $name= "";
1173                     $value= "";
1174                     break;
1176           case 'NAME':
1177           case 'DESC':
1178           case 'SUP':
1179           case 'STRUCTURAL':
1180           case 'ABSTRACT':
1181           case 'AUXILIARY':
1182           case 'MUST':
1183           case 'MAY':
1184                     if ($name != ""){
1185                       $objectclasses[$ocname][$name]= $this->value2container($value);
1186                     }
1187                     $name= $chunk;
1188                     $value= "";
1189                     break;
1191           default:  $value.= $chunk." ";
1192         }
1193       }
1195           }
1196     if(class_available("session")){
1197       session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1198     }
1199           return $objectclasses;
1200   }
1203   function value2container($value)
1204   {
1205     /* Set emtpy values to "true" only */
1206     if (preg_match('/^\s*$/', $value)){
1207       return true;
1208     }
1210     /* Remove ' and " if needed */
1211     $value= preg_replace('/^[\'"]/', '', $value);
1212     $value= preg_replace('/[\'"] *$/', '', $value);
1214     /* Convert to array if $ is inside... */
1215     if (preg_match('/\$/', $value)){
1216       $container= preg_split('/\s*\$\s*/', $value);
1217     } else {
1218       $container= chop($value);
1219     }
1221     return ($container);
1222   }
1225   function log($string)
1226   {
1227     if (session::is_set('config')){
1228       $cfg = session::get('config');
1229       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1230         syslog (LOG_INFO, $string);
1231       }
1232     }
1233   }
1235   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1236   function getCn($dn){
1237     $simple= split(",", $dn);
1239     foreach($simple as $piece) {
1240       $partial= split("=", $piece);
1242       if($partial[0] == "cn"){
1243         return $partial[1];
1244       }
1245     }
1246   }
1249   function get_naming_contexts($server, $admin= "", $password= "")
1250   {
1251     /* Build LDAP connection */
1252     $ds= ldap_connect ($server);
1253     if (!$ds) {
1254       die ("Can't bind to LDAP. No check possible!");
1255     }
1256     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1257     $r= ldap_bind ($ds, $admin, $password);
1259     /* Get base to look for naming contexts */
1260     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1261     $attr= @ldap_get_entries($ds,$sr);
1263     return ($attr[0]['namingcontexts']);
1264   }
1267   function get_root_dse($server, $admin= "", $password= "")
1268   {
1269     /* Build LDAP connection */
1270     $ds= ldap_connect ($server);
1271     if (!$ds) {
1272       die ("Can't bind to LDAP. No check possible!");
1273     }
1274     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1275     $r= ldap_bind ($ds, $admin, $password);
1277     /* Get base to look for naming contexts */
1278     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1279     $attr= @ldap_get_entries($ds,$sr);
1280    
1281     /* Return empty array, if nothing was set */
1282     if (!isset($attr[0])){
1283       return array();
1284     }
1286     /* Rework array... */
1287     $result= array();
1288     for ($i= 0; $i<$attr[0]['count']; $i++){
1289       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1290       unset($result[$attr[0][$i]]['count']);
1291     }
1293     return ($result);
1294   }
1296 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1297 ?>