Code

Return old attr, not new
[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.
85      Currently used codes:
86      ,   => CO
87      \2C => CO
88      (   => OB
89      )   => CB
90      /   => SL                                                                  */
91   static function convert($dn)
92   {
93     if (SPECIALS_OVERRIDE == TRUE){
94       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
95           array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
96           $dn);
97       return (preg_replace('/,\s+/', ',', $tmp));
98     } else {
99       return ($dn);
100     }
101   }
104   /* Function to fix all problematic characters inside a DN by replacing \001XX
105      codes to their original values. See "convert" for mor information. 
106      ',' characters are always expanded to \, (not \2C), since all tested LDAP
107      servers seem to take it the correct way.                                  */
108   static function fix($dn)
109   {
110     if (SPECIALS_OVERRIDE == TRUE){
111       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
112             array("\,", "(", ")", "/"),
113             $dn));
114     } else {
115       return ($dn);
116     }
117   }
119   /* Function to fix problematic characters in DN's that are used for search
120      requests. I.e. member=....                                               */
121   static function prepare4filter($dn)
122   {
123     $fixed= normalizeLdap(str_replace('\\\\', '\\\\\\', LDAP::fix($dn)));
124     return str_replace('\\,', '\\\\,', $fixed);
125   }
128   function connect()
129   {
130     $this->hascon=false;
131     $this->reconnect=false;
132     if ($this->cid= @ldap_connect($this->hostname)) {
133       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
134       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
135         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
136         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
137       }
138       if (function_exists("ldap_start_tls") && $this->tls){
139         @ldap_start_tls($this->cid);
140       }
142       $this->error = "No Error";
143       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
144         $this->error = "Success";
145         $this->hascon=true;
146       } else {
147         if ($this->reconnect){
148           if ($this->error != "Success"){
149             $this->error = "Could not rebind to " . $this->binddn;
150           }
151         } else {
152           $this->error = "Could not bind to " . $this->binddn;
153         }
154       }
155     } else {
156       $this->error = "Could not connect to LDAP server";
157     }
158   }
160   function rebind($ldap, $referral)
161   {
162     $credentials= $this->get_credentials($referral);
163     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMINDN']), $credentials['ADMINPASSWORD'])) {
164       $this->error = "Success";
165       $this->hascon=true;
166       $this->reconnect= true;
167       return (0);
168     } else {
169       $this->error = "Could not bind to " . $credentials['ADMINDN'];
170       return NULL;
171     }
172   }
174   function reconnect()
175   {
176     if ($this->reconnect){
177       @ldap_unbind($this->cid);
178       $this->cid = NULL;
179     }
180   }
182   function unbind()
183   {
184     @ldap_unbind($this->cid);
185     $this->cid = NULL;
186   }
188   function disconnect()
189   {
190     if($this->hascon){
191       @ldap_close($this->cid);
192       $this->hascon=false;
193     }
194   }
196   function cd($dir)
197   {
198     if ($dir == ".."){
199       $this->basedn = $this->getParentDir();
200     } else {
201       $this->basedn = LDAP::convert($dir);
202     }
203   }
205   function getParentDir($basedn = "")
206   {
207     if ($basedn==""){
208       $basedn = $this->basedn;
209     } else {
210       $basedn = LDAP::convert($this->basedn);
211     }
212     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
213   }
215   
216   function search($srp, $filter, $attrs= array())
217   {
218     if($this->hascon){
219       if ($this->reconnect) $this->connect();
221       $start = microtime();
222       $this->clearResult($srp);
223       $this->sr[$srp] = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
224       $this->error = @ldap_error($this->cid);
225       $this->resetResult($srp);
226       $this->hasres[$srp]=true;
227    
228       /* Check if query took longer as specified in max_ldap_query_time */
229       if($this->max_ldap_query_time){
230         $diff = get_MicroTimeDiff($start,microtime());
231         if($diff > $this->max_ldap_query_time){
232           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
233         }
234       }
236       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
237       return($this->sr[$srp]);
238     }else{
239       $this->error = "Could not connect to LDAP server";
240       return("");
241     }
242   }
244   function ls($srp, $filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
245   {
246     if($this->hascon){
247       if ($this->reconnect) $this->connect();
249       $this->clearResult($srp);
250       if ($basedn == "")
251         $basedn = $this->basedn;
252       else
253         $basedn= LDAP::convert($basedn);
254   
255       $start = microtime();
256       $this->sr[$srp] = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
257       $this->error = @ldap_error($this->cid);
258       $this->resetResult($srp);
259       $this->hasres[$srp]=true;
261        /* Check if query took longer as specified in max_ldap_query_time */
262       if($this->max_ldap_query_time){
263         $diff = get_MicroTimeDiff($start,microtime());
264         if($diff > $this->max_ldap_query_time){
265           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
266         }
267       }
269       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
271       return($this->sr[$srp]);
272     }else{
273       $this->error = "Could not connect to LDAP server";
274       return("");
275     }
276   }
278   function cat($srp, $dn,$attrs= array("*"))
279   {
280     if($this->hascon){
281       if ($this->reconnect) $this->connect();
283       $this->clearResult($srp);
284       $filter = "(objectclass=*)";
285       $this->sr[$srp] = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
286       $this->error = @ldap_error($this->cid);
287       $this->resetResult($srp);
288       $this->hasres[$srp]=true;
289       return($this->sr[$srp]);
290     }else{
291       $this->error = "Could not connect to LDAP server";
292       return("");
293     }
294   }
296   function object_match_filter($dn,$filter)
297   {
298     if($this->hascon){
299       if ($this->reconnect) $this->connect();
300       $res =  @ldap_read($this->cid, LDAP::fix($dn), $filter, array("objectClass"));
301       $rv =   @ldap_count_entries($this->cid, $res);
302       return($rv);
303     }else{
304       $this->error = "Could not connect to LDAP server";
305       return(FALSE);
306     }
307   }
309   function set_size_limit($size)
310   {
311     /* Ignore zero settings */
312     if ($size == 0){
313       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
314     }
315     if($this->hascon){
316       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
317     } else {
318       $this->error = "Could not connect to LDAP server";
319     }
320   }
322   function fetch($srp)
323   {
324     $att= array();
325     if($this->hascon){
326       if($this->hasres[$srp]){
327         if ($this->start[$srp] == 0)
328         {
329           if ($this->sr[$srp]){
330             $this->start[$srp] = 1;
331             $this->re[$srp]= @ldap_first_entry($this->cid, $this->sr[$srp]);
332           } else {
333             return array();
334           }
335         } else {
336           $this->re[$srp]= @ldap_next_entry($this->cid, $this->re[$srp]);
337         }
338         if ($this->re[$srp])
339         {
340           $att= @ldap_get_attributes($this->cid, $this->re[$srp]);
341           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re[$srp])));
342         }
343         $this->error = @ldap_error($this->cid);
344         if (!isset($att)){
345           $att= array();
346         }
347         return($att);
348       }else{
349         $this->error = "Perform a fetch with no search";
350         return("");
351       }
352     }else{
353       $this->error = "Could not connect to LDAP server";
354       return("");
355     }
356   }
358   function resetResult($srp)
359   {
360     $this->start[$srp] = 0;
361   }
363   function clearResult($srp)
364   {
365     if($this->hasres[$srp]){
366       $this->hasres[$srp] = false;
367       @ldap_free_result($this->sr[$srp]);
368     }
369   }
371   function getDN($srp)
372   {
373     if($this->hascon){
374       if($this->hasres[$srp]){
376         if(!$this->re[$srp])
377           {
378           $this->error = "Perform a Fetch with no valid Result";
379           }
380           else
381           {
382           $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
383         
384           $this->error = @ldap_error($this->cid);
385           return(trim(LDAP::convert($rv)));
386            }
387       }else{
388         $this->error = "Perform a Fetch with no Search";
389         return("");
390       }
391     }else{
392       $this->error = "Could not connect to LDAP server";
393       return("");
394     }
395   }
397   function count($srp)
398   {
399     if($this->hascon){
400       if($this->hasres[$srp]){
401         $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
402         $this->error = @ldap_error($this->cid);
403         return($rv);
404       }else{
405         $this->error = "Perform a Fetch with no Search";
406         return("");
407       }
408     }else{
409       $this->error = "Could not connect to LDAP server";
410       return("");
411     }
412   }
414   function rm($attrs = "", $dn = "")
415   {
416     if($this->hascon){
417       if ($this->reconnect) $this->connect();
418       if ($dn == "")
419         $dn = $this->basedn;
421       $r = ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
422       $this->error = @ldap_error($this->cid);
423       return($r);
424     }else{
425       $this->error = "Could not connect to LDAP server";
426       return("");
427     }
428   }
430   function mod_add($attrs = "", $dn = "")
431   {
432     if($this->hascon){
433       if ($this->reconnect) $this->connect();
434       if ($dn == "")
435         $dn = $this->basedn;
437       $r = @ldap_mod_add($this->cid, LDAP::fix($dn), $attrs);
438       $this->error = @ldap_error($this->cid);
439       return($r);
440     }else{
441       $this->error = "Could not connect to LDAP server";
442       return("");
443     }
444   }
446   function rename($attrs, $dn = "")
447   {
448     if($this->hascon){
449       if ($this->reconnect) $this->connect();
450       if ($dn == "")
451         $dn = $this->basedn;
453       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
454       $this->error = @ldap_error($this->cid);
455       return($r);
456     }else{
457       $this->error = "Could not connect to LDAP server";
458       return("");
459     }
460   }
462   function rmdir($deletedn)
463   {
464     if($this->hascon){
465       if ($this->reconnect) $this->connect();
466       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
467       $this->error = @ldap_error($this->cid);
468       return($r ? $r : 0);
469     }else{
470       $this->error = "Could not connect to LDAP server";
471       return("");
472     }
473   }
476   /*! \brief Move the given Ldap entry from $source to $dest
477       @param  String  $source The source dn.
478       @param  String  $dest   The destination dn.
479       @return Boolean TRUE on success else FALSE.
480    */
481   function rename_dn($source,$dest)
482   {
483     /* Check if source and destination are the same entry */
484     if(strtolower($source) == strtolower($dest)){
485       trigger_error("Source and destination can't be the same entry.");
486       $this->error = "Source and destination can't be the same entry.";
487       return(FALSE);
488     }
490     /* Check if destination entry exists */    
491     if($this->dn_exists($dest)){
492       trigger_error("Destination '$dest' already exists.");
493       $this->error = "Destination '$dest' already exists.";
494       return(FALSE);
495     }
497     /* Extract the name and the parent part out ouf source dn.
498         e.g.  cn=herbert,ou=department,dc=... 
499          parent   =>  ou=department,dc=...
500          dest_rdn =>  cn=herbert
501      */
502     $parent   = preg_replace("/^[^,]+,/","", $dest);
503     $dest_rdn = preg_replace("/,.*$/","",$dest);
505     if($this->hascon){
506       if ($this->reconnect) $this->connect();
507       $r= ldap_rename($this->cid,@LDAP::fix($source), @LDAP::fix($dest_rdn),@LDAP::fix($parent),TRUE); 
508       $this->error = ldap_error($this->cid);
510       /* Check if destination dn exists, if not the 
511           server may not support this operation */
512       $r &= is_resource($this->dn_exists($dest));
513       return($r);
514     }else{
515       $this->error = "Could not connect to LDAP server";
516       return(FALSE);
517     }
518   }
521   /**
522   *  Function rmdir_recursive
523   *
524   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
525   *  Parameters:  The dn to delete
526   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
527   *
528   */
529   function rmdir_recursive($srp, $deletedn)
530   {
531     if($this->hascon){
532       if ($this->reconnect) $this->connect();
533       $delarray= array();
534         
535       /* Get sorted list of dn's to delete */
536       $this->ls ($srp, "(objectClass=*)",$deletedn);
537       while ($this->fetch($srp)){
538         $deldn= $this->getDN($srp);
539         $delarray[$deldn]= strlen($deldn);
540       }
541       arsort ($delarray);
542       reset ($delarray);
544       /* Really Delete ALL dn's in subtree */
545       foreach ($delarray as $key => $value){
546         $this->rmdir_recursive($srp, $key);
547       }
548       
549       /* Finally Delete own Node */
550       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
551       $this->error = @ldap_error($this->cid);
552       return($r ? $r : 0);
553     }else{
554       $this->error = "Could not connect to LDAP server";
555       return("");
556     }
557   }
560   function modify($attrs)
561   {
562     if(count($attrs) == 0){
563       return (0);
564     }
565     if($this->hascon){
566       if ($this->reconnect) $this->connect();
567       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
568       $this->error = @ldap_error($this->cid);
569       return($r ? $r : 0);
570     }else{
571       $this->error = "Could not connect to LDAP server";
572       return("");
573     }
574   }
576   function add($attrs)
577   {
578     if($this->hascon){
579       if ($this->reconnect) $this->connect();
580       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
581       $this->error = @ldap_error($this->cid);
582       return($r ? $r : 0);
583     }else{
584       $this->error = "Could not connect to LDAP server";
585       return("");
586     }
587   }
589   function create_missing_trees($srp, $target)
590   {
591     global $config;
593     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
595     if ($target == $this->basedn){
596       $l= array("dummy");
597     } else {
598       $l= array_reverse(gosa_ldap_explode_dn($real_path));
599     }
600     unset($l['count']);
601     $cdn= $this->basedn;
602     $tag= "";
604     /* Load schema if available... */
605     $classes= $this->get_objectclasses();
607     foreach ($l as $part){
608       if ($part != "dummy"){
609         $cdn= "$part,$cdn";
610       }
612       /* Ignore referrals */
613       $found= false;
614       foreach($this->referrals as $ref){
615         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URI']);
616         if ($base == $cdn){
617           $found= true;
618           break;
619         }
620       }
621       if ($found){
622         continue;
623       }
625       $this->cat ($srp, $cdn);
626       $attrs= $this->fetch($srp);
628       /* Create missing entry? */
629       if (count ($attrs)){
630       
631         /* Catch the tag - if present */
632         if (isset($attrs['gosaUnitTag'][0])){
633           $tag= $attrs['gosaUnitTag'][0];
634         }
636       } else {
637         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
638         $param= preg_replace('/^[^=]+=([^,]+).*$/', '\\1', $cdn);
640         $na= array();
642         /* Automatic or traditional? */
643         if(count($classes)){
645           /* Get name of first matching objectClass */
646           $ocname= "";
647           foreach($classes as $class){
648             if (isset($class['MUST']) && $class['MUST'] == "$type"){
650               /* Look for first classes that is structural... */
651               if (isset($class['STRUCTURAL'])){
652                 $ocname= $class['NAME'];
653                 break;
654               }
656               /* Look for classes that are auxiliary... */
657               if (isset($class['AUXILIARY'])){
658                 $ocname= $class['NAME'];
659               }
660             }
661           }
663           /* Bail out, if we've nothing to do... */
664           if ($ocname == ""){
665             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found!"),$type), FATAL_ERROR_DIALOG);
666             exit();
667           }
669           /* Assemble_entry */
670           if ($tag != ""){
671             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
672             $na["gosaUnitTag"]= $tag;
673           } else {
674             $na['objectClass']= array($ocname);
675           }
676           if (isset($classes[$ocname]['AUXILIARY'])){
677             $na['objectClass'][]= $classes[$ocname]['SUP'];
678           }
679           if ($type == "dc"){
680             /* This is bad actually, but - tell me a better way? */
681             $na['objectClass'][]= 'locality';
682           }
683           $na[$type]= $param;
684           if (is_array($classes[$ocname]['MUST'])){
685             foreach($classes[$ocname]['MUST'] as $attr){
686               $na[$attr]= "filled";
687             }
688           }
690         } else {
692           /* Use alternative add... */
693           switch ($type){
694             case 'ou':
695               if ($tag != ""){
696                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
697                 $na["gosaUnitTag"]= $tag;
698               } else {
699                 $na["objectClass"]= "organizationalUnit";
700               }
701               $na["ou"]= $param;
702               break;
703             case 'dc':
704               if ($tag != ""){
705                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
706                 $na["gosaUnitTag"]= $tag;
707               } else {
708                 $na["objectClass"]= array("dcObject", "top", "locality");
709               }
710               $na["dc"]= $param;
711               break;
712             default:
713               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
714               exit();
715           }
717         }
718         $this->cd($cdn);
719         $this->add($na);
720     
721         if (!$this->success()){
723           print_a(array($cdn,$na));
725           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()));
726           return FALSE;
727         }
728       }
729     }
731     return TRUE;
732   }
735   function recursive_remove($srp)
736   {
737     $delarray= array();
739     /* Get sorted list of dn's to delete */
740     $this->search ($srp, "(objectClass=*)");
741     while ($this->fetch($srp)){
742       $deldn= $this->getDN($srp);
743       $delarray[$deldn]= strlen($deldn);
744     }
745     arsort ($delarray);
746     reset ($delarray);
748     /* Delete all dn's in subtree */
749     foreach ($delarray as $key => $value){
750       $this->rmdir($key);
751     }
752   }
755   function get_attribute($dn, $name,$r_array=0)
756   {
757     $data= "";
758     if ($this->reconnect) $this->connect();
759     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
761     /* fill data from LDAP */
762     if ($sr) {
763       $ei= @ldap_first_entry($this->cid, $sr);
764       if ($ei) {
765         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
766           $data= $info[0];
767         }
768       }
769     }
770     if($r_array==0) {
771       return ($data);
772     } else {
773       return ($info);
774     }
775   }
776  
779   function get_additional_error()
780   {
781     $error= "";
782     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
783     return ($error);
784   }
787   function success()
788   {
789     return (preg_match('/Success/i', $this->error));
790   }
793   function get_error()
794   {
795     if ($this->error == 'Success'){
796       return $this->error;
797     } else {
798       $adderror= $this->get_additional_error();
799       if ($adderror != ""){
800         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
801       } else {
802         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
803       }
804       return $error;
805     }
806   }
808   function get_credentials($url, $referrals= NULL)
809   {
810     $ret= array();
811     $url= preg_replace('!\?\?.*$!', '', $url);
812     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
814     if ($referrals === NULL){
815       $referrals= $this->referrals;
816     }
818     if (isset($referrals[$server])){
819       return ($referrals[$server]);
820     } else {
821       $ret['ADMINDN']= LDAP::fix($this->binddn);
822       $ret['ADMINPASSWORD']= $this->bindpw;
823     }
825     return ($ret);
826   }
829   function gen_ldif ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
830   {
831     $display= "";
833     if ($recursive){
834       $this->cd($dn);
835       $this->ls($srp, $filter,$dn, array('dn','objectClass'));
836       $deps = array();
838       $display .= $this->gen_one_entry($dn)."\n";
840       while ($attrs= $this->fetch($srp)){
841         $deps[] = $attrs['dn'];
842       }
843       foreach($deps as $dn){
844         $display .= $this->gen_ldif($srp, $dn, $filter,$attributes,$recursive);
845       }
846     } else {
847       $display.= $this->gen_one_entry($dn);
848     }
849     return ($display);
850   }
853   function gen_xls ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
854   {
855     $display= array();
857       $this->cd($dn);
858       $this->search($srp, "$filter");
860       $i=0;
861       while ($attrs= $this->fetch($srp)){
862         $j=0;
864         foreach ($attributes as $at){
865           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
866           $j++;
867         }
869         $i++;
870       }
872     return ($display);
873   }
876   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
877   {
878     $ret = "";
879     $data = "";
880     if($this->reconnect){
881       $this->connect();
882     }
884     /* Searching Ldap Tree */
885     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
887     /* Get the first entry */   
888     $entry= @ldap_first_entry($this->cid, $sr);
890     /* Get all attributes related to that Objekt */
891     $atts = array();
892     
893     /* Assemble dn */
894     $atts[0]['name']  = "dn";
895     $atts[0]['value'] = array('count' => 1, 0 => $dn);
897     /* Reset index */
898     $i = 1 ; 
899   $identifier = array();
900     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
901     while ($attribute) {
902       $i++;
903       $atts[$i]['name']  = $attribute;
904       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
906       /* Next one */
907       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
908     }
910     foreach($atts as $at)
911     {
912       for ($i= 0; $i<$at['value']['count']; $i++){
914         /* Check if we must encode the data */
915         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
916           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
917         } else {
918           $ret .= $at['name'].": ".$at['value'][$i]."\n";
919         }
920       }
921     }
923     return($ret);
924   }
927   function dn_exists($dn)
928   {
929     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
930   }
931   
934   /*  This funktion imports ldifs 
935         
936       If DeleteOldEntries is true, the destination entry will be deleted first. 
937       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
938       if JustMofify id false the destination dn will be overwritten by the new ldif. 
939     */
941   function import_complete_ldif($srp, $str_attr,$error,$JustModify,$DeleteOldEntries)
942   {
943     if($this->reconnect) $this->connect();
945     /* First we have to splitt the string ito detect empty lines
946        An empty line indicates an new Entry */
947     $entries = split("\n",$str_attr);
949     $data = "";
950     $cnt = 0; 
951     $current_line = 0;
953     /* FIX ldif */
954     $last = "";
955     $tmp  = "";
956     $i = 0;
957     foreach($entries as $entry){
958       if(preg_match("/^ /",$entry)){
959         $tmp[$i] .= trim($entry);
960       }else{
961         $i ++;
962         $tmp[$i] = trim($entry);
963       }
964     }
966     /* Every single line ... */
967     foreach($tmp as $entry) {
968       $current_line ++;
970       /* Removing Spaces to .. 
971          .. test if a new entry begins */
972       $tmp  = str_replace(" ","",$data );
974       /* .. prevent empty lines in an entry */
975       $tmp2 = str_replace(" ","",$entry);
977       /* If the Block ends (Empty Line) */
978       if((empty($entry))&&(!empty($tmp))) {
979         /* Add collected lines as a complete block */
980         $all[$cnt] = $data;
981         $cnt ++;
982         $data ="";
983       } else {
985         /* Append lines ... */
986         if(!empty($tmp2)) {
987           /* check if we need base64_decode for this line */
988           if(ereg("::",$tmp2))
989           {
990             $encoded = split("::",$entry);
991             $attr  = trim($encoded[0]);
992             $value = base64_decode(trim($encoded[1]));
993             /* Add linenumber */
994             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
995           }
996           else
997           {
998             /* Add Linenumber */ 
999             $data .= $current_line."#".base64_encode($entry)."\n";
1000           }
1001         }
1002       }
1003     }
1005     /* The Data we collected is not in the array all[];
1006        For example the Data is stored like this..
1008        all[0] = "1#dn : .... \n 
1009        2#ObjectType: person \n ...."
1010        
1011        Now we check every insertblock and try to insert */
1012     foreach ( $all as $single) {
1013       $lineone = split("\n",$single);  
1014       $ndn = split("#", $lineone[0]);
1015       $line = base64_decode($ndn[1]);
1017       $dnn = split (":",$line,2);
1018       $current_line = $ndn[0];
1019       $dn    = $dnn[0];
1020       $value = $dnn[1];
1022       /* Every block must begin with a dn */
1023       if($dn != "dn") {
1024         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1025         return -2;  
1026       }
1028       /* Should we use Modify instead of Add */
1029       $usemodify= false;
1031       /* Delete before insert */
1032       $usermdir= false;
1033     
1034       /* The dn address already exists, Don't delete destination entry, overwrite it */
1035       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1037         $usermdir = $usemodify = false;
1039       /* Delete old entry first, then add new */
1040       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1042         /* Delete first, then add */
1043         $usermdir = true;        
1045       } elseif(($this->dn_exists($value))&&($JustModify)) {
1046         
1047         /* Modify instead of Add */
1048         $usemodify = true;
1049       }
1050      
1051       /* If we can't Import, return with a file error */
1052       if(!$this->import_single_entry($srp, $single,$usemodify,$usermdir) ) {
1053         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1054                         $current_line);
1055         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
1056     }
1058     return (INSERT_OK);
1059   }
1062   /* Imports a single entry 
1063       If $delete is true;  The old entry will be deleted if it exists.
1064       if $modify is true;  All variables that are not touched by the new ldif will be kept.
1065       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1066   */
1067   function import_single_entry($srp, $str_attr,$modify,$delete)
1068   {
1069     global $config;
1071     if(!$config){
1072       trigger_error("Can't import ldif, can't read config object.");
1073     }
1074   
1076     if($this->reconnect) $this->connect();
1078     $ret = false;
1079     $rows= split("\n",$str_attr);
1080     $data= false;
1082     foreach($rows as $row) {
1083       
1084       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1085          Linenumbers) Linenumbers are use like this 123#attribute : value */
1086       if(!empty($row)) {
1087         if(strpos($row,"#")!=FALSE) {
1089           /* We are using line numbers 
1090              Because there is a # before a : */
1091           $tmp1= split("#",$row);
1092           $current_line= $tmp1[0];
1093           $row= base64_decode($tmp1[1]);
1094         }
1096         /* Split the line into  attribute  and value */
1097         $attr   = split(":", $row,2);
1098         $attr[0]= trim($attr[0]);  /* attribute */
1099         $attr[1]= $attr[1];  /* value */
1101         /* Check :: was used to indicate base64_encoded strings */
1102         if($attr[1][0] == ":"){
1103           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1104           $attr[1]=base64_decode($attr[1]);
1105         }
1107         $attr[1] = trim($attr[1]);
1109         /* Check for attributes that are used more than once */
1110         if(!isset($data[$attr[0]])) {
1111           $data[$attr[0]]=$attr[1];
1112         } else {
1113           $tmp = $data[$attr[0]];
1115           if(!is_array($tmp)) {
1116             $new[0]=$tmp;
1117             $new[1]=$attr[1];
1118             $datas[$attr[0]]['count']=1;             
1119             $data[$attr[0]]=$new;
1120           } else {
1121             $cnt = $datas[$attr[0]]['count'];           
1122             $cnt ++;
1123             $data[$attr[0]][$cnt]=$attr[1];
1124             $datas[$attr[0]]['count'] = $cnt;
1125           }
1126         }
1127       }
1128     }
1130     /* If dn is an index of data, we should try to insert the data */
1131     if(isset($data['dn'])) {
1133       /* Fix dn */
1134       $tmp = gosa_ldap_explode_dn($data['dn']);
1135       unset($tmp['count']);
1136       $newdn ="";
1137       foreach($tmp as $tm){
1138         $newdn.= trim($tm).",";
1139       }
1140       $newdn = preg_replace("/,$/","",$newdn);
1141       $data['dn'] = $newdn;
1142    
1143       /* Creating Entry */
1144       $this->cd($data['dn']);
1146       /* Delete existing entry */
1147       if($delete){
1148         $this->rmdir_recursive($srp, $data['dn']);
1149       }
1150      
1151       /* Create missing trees */
1152       $this->cd ($this->basedn);
1153       $this->cd($config->current['BASE']);
1154       $this->create_missing_trees($srp, preg_replace("/^[^,]+,/","",$data['dn']));
1155       $this->cd($data['dn']);
1157       $dn = $data['dn'];
1158       unset($data['dn']);
1159       
1160       if(!$modify){
1162         $this->cat($srp, $dn);
1163         if($this->count($srp)){
1164         
1165           /* The destination entry exists, overwrite it with the new entry */
1166           $attrs = $this->fetch($srp);
1167           foreach($attrs as $name => $value ){
1168             if(!is_numeric($name)){
1169               if(in_array($name,array("dn","count"))) continue;
1170               if(!isset($data[$name])){
1171                 $data[$name] = array();
1172               }
1173             }
1174           }
1175           $ret = $this->modify($data);
1176     
1177         }else{
1178     
1179           /* The destination entry doesn't exists, create it */
1180           $ret = $this->add($data);
1181         }
1183       } else {
1184         
1185         /* Keep all vars that aren't touched by this ldif */
1186         $ret = $this->modify($data);
1187       }
1188     }
1190     if (!$this->success()){
1191       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $dn, "", get_class()));
1192     }
1194     return($ret);
1195   }
1197   
1198   function importcsv($str)
1199   {
1200     $lines = split("\n",$str);
1201     foreach($lines as $line)
1202     {
1203       /* continue if theres a comment */
1204       if(substr(trim($line),0,1)=="#"){
1205         continue;
1206       }
1208       $line= str_replace ("\t\t","\t",$line);
1209       $line= str_replace ("\t"  ,"," ,$line);
1210       echo $line;
1212       $cells = split(",",$line )  ;
1213       $linet= str_replace ("\t\t",",",$line);
1214       $cells = split("\t",$line);
1215       $count = count($cells);  
1216     }
1218   }
1219   
1220   function get_objectclasses( $force_reload = FALSE)
1221   {
1222     $objectclasses = array();
1223     global $config;
1225     /* Only read schema if it is allowed */
1226     if(isset($config) && preg_match("/config/i",get_class($config))){
1227       if ($config->get_cfg_value("schemaCheck") != "true"){
1228         return($objectclasses);
1229       } 
1230     }
1232     /* Return the cached results. */
1233     if(class_available('session') && session::global_is_set("LDAP_CACHE::get_objectclasses") && !$force_reload){
1234       $objectclasses = session::global_get("LDAP_CACHE::get_objectclasses");
1235       return($objectclasses);
1236     }
1237         
1238           # Get base to look for schema 
1239           $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1240           $attr = @ldap_get_entries($this->cid,$sr);
1241           if (!isset($attr[0]['subschemasubentry'][0])){
1242             return array();
1243           }
1244         
1245           /* Get list of objectclasses and fill array */
1246           $nb= $attr[0]['subschemasubentry'][0];
1247           $objectclasses= array();
1248           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1249           $attrs= ldap_get_entries($this->cid,$sr);
1250           if (!isset($attrs[0])){
1251             return array();
1252           }
1253           foreach ($attrs[0]['objectclasses'] as $val){
1254       if (preg_match('/^[0-9]+$/', $val)){
1255         continue;
1256       }
1257       $name= "OID";
1258       $pattern= split(' ', $val);
1259       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1260       $objectclasses[$ocname]= array();
1262       foreach($pattern as $chunk){
1263         switch($chunk){
1265           case '(':
1266                     $value= "";
1267                     break;
1269           case ')': if ($name != ""){
1270                       $objectclasses[$ocname][$name]= $this->value2container($value);
1271                     }
1272                     $name= "";
1273                     $value= "";
1274                     break;
1276           case 'NAME':
1277           case 'DESC':
1278           case 'SUP':
1279           case 'STRUCTURAL':
1280           case 'ABSTRACT':
1281           case 'AUXILIARY':
1282           case 'MUST':
1283           case 'MAY':
1284                     if ($name != ""){
1285                       $objectclasses[$ocname][$name]= $this->value2container($value);
1286                     }
1287                     $name= $chunk;
1288                     $value= "";
1289                     break;
1291           default:  $value.= $chunk." ";
1292         }
1293       }
1295           }
1296     if(class_available("session")){
1297       session::global_set("LDAP_CACHE::get_objectclasses",$objectclasses);
1298     }
1300           return $objectclasses;
1301   }
1304   function value2container($value)
1305   {
1306     /* Set emtpy values to "true" only */
1307     if (preg_match('/^\s*$/', $value)){
1308       return true;
1309     }
1311     /* Remove ' and " if needed */
1312     $value= preg_replace('/^[\'"]/', '', $value);
1313     $value= preg_replace('/[\'"] *$/', '', $value);
1315     /* Convert to array if $ is inside... */
1316     if (preg_match('/\$/', $value)){
1317       $container= preg_split('/\s*\$\s*/', $value);
1318     } else {
1319       $container= chop($value);
1320     }
1322     return ($container);
1323   }
1326   function log($string)
1327   {
1328     if (session::global_is_set('config')){
1329       $cfg = session::global_get('config');
1330       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1331         syslog (LOG_INFO, $string);
1332       }
1333     }
1334   }
1336   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1337   function getCn($dn){
1338     $simple= split(",", $dn);
1340     foreach($simple as $piece) {
1341       $partial= split("=", $piece);
1343       if($partial[0] == "cn"){
1344         return $partial[1];
1345       }
1346     }
1347   }
1350   function get_naming_contexts($server, $admin= "", $password= "")
1351   {
1352     /* Build LDAP connection */
1353     $ds= ldap_connect ($server);
1354     if (!$ds) {
1355       die ("Can't bind to LDAP. No check possible!");
1356     }
1357     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1358     $r= ldap_bind ($ds, $admin, $password);
1360     /* Get base to look for naming contexts */
1361     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1362     $attr= @ldap_get_entries($ds,$sr);
1364     return ($attr[0]['namingcontexts']);
1365   }
1368   function get_root_dse($server, $admin= "", $password= "")
1369   {
1370     /* Build LDAP connection */
1371     $ds= ldap_connect ($server);
1372     if (!$ds) {
1373       die ("Can't bind to LDAP. No check possible!");
1374     }
1375     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1376     $r= ldap_bind ($ds, $admin, $password);
1378     /* Get base to look for naming contexts */
1379     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1380     $attr= @ldap_get_entries($ds,$sr);
1381    
1382     /* Return empty array, if nothing was set */
1383     if (!isset($attr[0])){
1384       return array();
1385     }
1387     /* Rework array... */
1388     $result= array();
1389     for ($i= 0; $i<$attr[0]['count']; $i++){
1390       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1391       unset($result[$attr[0][$i]]['count']);
1392     }
1394     return ($result);
1395   }
1398 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1399 ?>