Code

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