Code

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