Code

Language cleanup
[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(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
64       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
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       $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   }
120   /* Function to fix problematic characters in DN's that are used for search
121      requests. I.e. member=....                                               */
122   static function prepare4filter($dn)
123   {
124         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
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['ADMIN']), $credentials['PASSWORD'])) {
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['ADMIN'];
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 set_size_limit($size)
297   {
298     /* Ignore zero settings */
299     if ($size == 0){
300       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
301     }
302     if($this->hascon){
303       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
304     } else {
305       $this->error = "Could not connect to LDAP server";
306     }
307   }
309   function fetch($srp)
310   {
311     $att= array();
312     if($this->hascon){
313       if($this->hasres[$srp]){
314         if ($this->start[$srp] == 0)
315         {
316           if ($this->sr[$srp]){
317             $this->start[$srp] = 1;
318             $this->re[$srp]= @ldap_first_entry($this->cid, $this->sr[$srp]);
319           } else {
320             return array();
321           }
322         } else {
323           $this->re[$srp]= @ldap_next_entry($this->cid, $this->re[$srp]);
324         }
325         if ($this->re[$srp])
326         {
327           $att= @ldap_get_attributes($this->cid, $this->re[$srp]);
328           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re[$srp])));
329         }
330         $this->error = @ldap_error($this->cid);
331         if (!isset($att)){
332           $att= array();
333         }
334         return($att);
335       }else{
336         $this->error = "Perform a fetch with no search";
337         return("");
338       }
339     }else{
340       $this->error = "Could not connect to LDAP server";
341       return("");
342     }
343   }
345   function resetResult($srp)
346   {
347     $this->start[$srp] = 0;
348   }
350   function clearResult($srp)
351   {
352     if($this->hasres[$srp]){
353       $this->hasres[$srp] = false;
354       @ldap_free_result($this->sr[$srp]);
355     }
356   }
358   function getDN($srp)
359   {
360     if($this->hascon){
361       if($this->hasres[$srp]){
363         if(!$this->re[$srp])
364           {
365           $this->error = "Perform a Fetch with no valid Result";
366           }
367           else
368           {
369           $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
370         
371           $this->error = @ldap_error($this->cid);
372           return(trim(LDAP::convert($rv)));
373            }
374       }else{
375         $this->error = "Perform a Fetch with no Search";
376         return("");
377       }
378     }else{
379       $this->error = "Could not connect to LDAP server";
380       return("");
381     }
382   }
384   function count($srp)
385   {
386     if($this->hascon){
387       if($this->hasres[$srp]){
388         $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
389         $this->error = @ldap_error($this->cid);
390         return($rv);
391       }else{
392         $this->error = "Perform a Fetch with no Search";
393         return("");
394       }
395     }else{
396       $this->error = "Could not connect to LDAP server";
397       return("");
398     }
399   }
401   function rm($attrs = "", $dn = "")
402   {
403     if($this->hascon){
404       if ($this->reconnect) $this->connect();
405       if ($dn == "")
406         $dn = $this->basedn;
408       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
409       $this->error = @ldap_error($this->cid);
410       return($r);
411     }else{
412       $this->error = "Could not connect to LDAP server";
413       return("");
414     }
415   }
417   function rename($attrs, $dn = "")
418   {
419     if($this->hascon){
420       if ($this->reconnect) $this->connect();
421       if ($dn == "")
422         $dn = $this->basedn;
424       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
425       $this->error = @ldap_error($this->cid);
426       return($r);
427     }else{
428       $this->error = "Could not connect to LDAP server";
429       return("");
430     }
431   }
433   function rmdir($deletedn)
434   {
435     if($this->hascon){
436       if ($this->reconnect) $this->connect();
437       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
438       $this->error = @ldap_error($this->cid);
439       return($r ? $r : 0);
440     }else{
441       $this->error = "Could not connect to LDAP server";
442       return("");
443     }
444   }
446   /**
447   *  Function rmdir_recursive
448   *
449   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
450   *  Parameters:  The dn to delete
451   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
452   *
453   */
455   function rmdir_recursive($srp, $deletedn)
456   {
457     if($this->hascon){
458       if ($this->reconnect) $this->connect();
459       $delarray= array();
460         
461       /* Get sorted list of dn's to delete */
462       $this->ls ($srp, "(objectClass=*)",$deletedn);
463       while ($this->fetch($srp)){
464         $deldn= $this->getDN($srp);
465         $delarray[$deldn]= strlen($deldn);
466       }
467       arsort ($delarray);
468       reset ($delarray);
470       /* Really Delete ALL dn's in subtree */
471       foreach ($delarray as $key => $value){
472         $this->rmdir_recursive($srp, $key);
473       }
474       
475       /* Finally Delete own Node */
476       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
477       $this->error = @ldap_error($this->cid);
478       return($r ? $r : 0);
479     }else{
480       $this->error = "Could not connect to LDAP server";
481       return("");
482     }
483   }
486   function modify($attrs)
487   {
488     if(count($attrs) == 0){
489       return (0);
490     }
491     if($this->hascon){
492       if ($this->reconnect) $this->connect();
493       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
494       $this->error = @ldap_error($this->cid);
495       return($r ? $r : 0);
496     }else{
497       $this->error = "Could not connect to LDAP server";
498       return("");
499     }
500   }
502   function add($attrs)
503   {
504     if($this->hascon){
505       if ($this->reconnect) $this->connect();
506       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
507       $this->error = @ldap_error($this->cid);
508       return($r ? $r : 0);
509     }else{
510       $this->error = "Could not connect to LDAP server";
511       return("");
512     }
513   }
515   function create_missing_trees($srp, $target)
516   {
517     global $config;
519     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
521     if ($target == $this->basedn){
522       $l= array("dummy");
523     } else {
524       $l= array_reverse(gosa_ldap_explode_dn($real_path));
525     }
526     unset($l['count']);
527     $cdn= $this->basedn;
528     $tag= "";
530     /* Load schema if available... */
531     $classes= $this->get_objectclasses();
533     foreach ($l as $part){
534       if ($part != "dummy"){
535         $cdn= "$part,$cdn";
536       }
538       /* Ignore referrals */
539       $found= false;
540       foreach($this->referrals as $ref){
541         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
542         if ($base == $cdn){
543           $found= true;
544           break;
545         }
546       }
547       if ($found){
548         continue;
549       }
551       $this->cat ($srp, $cdn);
552       $attrs= $this->fetch($srp);
554       /* Create missing entry? */
555       if (count ($attrs)){
556       
557         /* Catch the tag - if present */
558         if (isset($attrs['gosaUnitTag'][0])){
559           $tag= $attrs['gosaUnitTag'][0];
560         }
562       } else {
563         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
564         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
566         $na= array();
568         /* Automatic or traditional? */
569         if(count($classes)){
571           /* Get name of first matching objectClass */
572           $ocname= "";
573           foreach($classes as $class){
574             if (isset($class['MUST']) && $class['MUST'] == "$type"){
576               /* Look for first classes that is structural... */
577               if (isset($class['STRUCTURAL'])){
578                 $ocname= $class['NAME'];
579                 break;
580               }
582               /* Look for classes that are auxiliary... */
583               if (isset($class['AUXILIARY'])){
584                 $ocname= $class['NAME'];
585               }
586             }
587           }
589           /* Bail out, if we've nothing to do... */
590           if ($ocname == ""){
591             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found!"),$type), FATAL_ERROR_DIALOG);
592             exit();
593           }
595           /* Assemble_entry */
596           if ($tag != ""){
597             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
598             $na["gosaUnitTag"]= $tag;
599           } else {
600             $na['objectClass']= array($ocname);
601           }
602           if (isset($classes[$ocname]['AUXILIARY'])){
603             $na['objectClass'][]= $classes[$ocname]['SUP'];
604           }
605           if ($type == "dc"){
606             /* This is bad actually, but - tell me a better way? */
607             $na['objectClass'][]= 'locality';
608           }
609           $na[$type]= $param;
610           if (is_array($classes[$ocname]['MUST'])){
611             foreach($classes[$ocname]['MUST'] as $attr){
612               $na[$attr]= "filled";
613             }
614           }
616         } else {
618           /* Use alternative add... */
619           switch ($type){
620             case 'ou':
621               if ($tag != ""){
622                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
623                 $na["gosaUnitTag"]= $tag;
624               } else {
625                 $na["objectClass"]= "organizationalUnit";
626               }
627               $na["ou"]= $param;
628               break;
629             case 'dc':
630               if ($tag != ""){
631                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
632                 $na["gosaUnitTag"]= $tag;
633               } else {
634                 $na["objectClass"]= array("dcObject", "top", "locality");
635               }
636               $na["dc"]= $param;
637               break;
638             default:
639               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
640               exit();
641           }
643         }
644         $this->cd($cdn);
645         $this->add($na);
646     
647         if (!$this->success()){
648           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()));
649           return FALSE;
650         }
651       }
652     }
654     return TRUE;
655   }
658   function recursive_remove($srp)
659   {
660     $delarray= array();
662     /* Get sorted list of dn's to delete */
663     $this->search ($srp, "(objectClass=*)");
664     while ($this->fetch($srp)){
665       $deldn= $this->getDN($srp);
666       $delarray[$deldn]= strlen($deldn);
667     }
668     arsort ($delarray);
669     reset ($delarray);
671     /* Delete all dn's in subtree */
672     foreach ($delarray as $key => $value){
673       $this->rmdir($key);
674     }
675   }
677   function get_attribute($dn, $name,$r_array=0)
678   {
679     $data= "";
680     if ($this->reconnect) $this->connect();
681     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
683     /* fill data from LDAP */
684     if ($sr) {
685       $ei= @ldap_first_entry($this->cid, $sr);
686       if ($ei) {
687         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
688           $data= $info[0];
689         }
690       }
691     }
692     if($r_array==0)
693     return ($data);
694     else
695     return ($info);
696   
697   
698   }
699  
702   function get_additional_error()
703   {
704     $error= "";
705     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
706     return ($error);
707   }
710   function success()
711   {
712     return (preg_match('/Success/i', $this->error));
713   }
716   function get_error()
717   {
718     if ($this->error == 'Success'){
719       return $this->error;
720     } else {
721       $adderror= $this->get_additional_error();
722       if ($adderror != ""){
723         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
724       } else {
725         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
726       }
727       return $error;
728     }
729   }
731   function get_credentials($url, $referrals= NULL)
732   {
733     $ret= array();
734     $url= preg_replace('!\?\?.*$!', '', $url);
735     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
737     if ($referrals === NULL){
738       $referrals= $this->referrals;
739     }
741     if (isset($referrals[$server])){
742       return ($referrals[$server]);
743     } else {
744       $ret['ADMIN']= LDAP::fix($this->binddn);
745       $ret['PASSWORD']= $this->bindpw;
746     }
748     return ($ret);
749   }
752   function gen_ldif ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
753   {
754     $display= "";
756     if ($recursive){
757       $this->cd($dn);
758       $this->ls($srp, $filter,$dn, array('dn','objectClass'));
759       $deps = array();
761       $display .= $this->gen_one_entry($dn)."\n";
763       while ($attrs= $this->fetch($srp)){
764         $deps[] = $attrs['dn'];
765       }
766       foreach($deps as $dn){
767         $display .= $this->gen_ldif($srp, $dn, $filter,$attributes,$recursive);
768       }
769     } else {
770       $display.= $this->gen_one_entry($dn);
771     }
772     return ($display);
773   }
776   function gen_xls ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
777   {
778     $display= array();
780       $this->cd($dn);
781       $this->search($srp, "$filter");
783       $i=0;
784       while ($attrs= $this->fetch($srp)){
785         $j=0;
787         foreach ($attributes as $at){
788           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
789           $j++;
790         }
792         $i++;
793       }
795     return ($display);
796   }
799   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
800   {
801     $ret = "";
802     $data = "";
803     if($this->reconnect){
804       $this->connect();
805     }
807     /* Searching Ldap Tree */
808     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
810     /* Get the first entry */   
811     $entry= @ldap_first_entry($this->cid, $sr);
813     /* Get all attributes related to that Objekt */
814     $atts = array();
815     
816     /* Assemble dn */
817     $atts[0]['name']  = "dn";
818     $atts[0]['value'] = array('count' => 1, 0 => $dn);
820     /* Reset index */
821     $i = 1 ; 
822   $identifier = array();
823     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
824     while ($attribute) {
825       $i++;
826       $atts[$i]['name']  = $attribute;
827       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
829       /* Next one */
830       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
831     }
833     foreach($atts as $at)
834     {
835       for ($i= 0; $i<$at['value']['count']; $i++){
837         /* Check if we must encode the data */
838         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
839           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
840         } else {
841           $ret .= $at['name'].": ".$at['value'][$i]."\n";
842         }
843       }
844     }
846     return($ret);
847   }
850   function dn_exists($dn)
851   {
852     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
853   }
854   
857   /*  This funktion imports ldifs 
858         
859       If DeleteOldEntries is true, the destination entry will be deleted first. 
860       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
861       if JustMofify id false the destination dn will be overwritten by the new ldif. 
862     */
864   function import_complete_ldif($srp, $str_attr,&$error,$JustModify,$DeleteOldEntries)
865   {
866     if($this->reconnect) $this->connect();
868     /* First we have to splitt the string ito detect empty lines
869        An empty line indicates an new Entry */
870     $entries = split("\n",$str_attr);
872     $data = "";
873     $cnt = 0; 
874     $current_line = 0;
876     /* FIX ldif */
877     $last = "";
878     $tmp  = "";
879     $i = 0;
880     foreach($entries as $entry){
881       if(preg_match("/^ /",$entry)){
882         $tmp[$i] .= trim($entry);
883       }else{
884         $i ++;
885         $tmp[$i] = trim($entry);
886       }
887     }
889     /* Every single line ... */
890     foreach($tmp as $entry) {
891       $current_line ++;
893       /* Removing Spaces to .. 
894          .. test if a new entry begins */
895       $tmp  = str_replace(" ","",$data );
897       /* .. prevent empty lines in an entry */
898       $tmp2 = str_replace(" ","",$entry);
900       /* If the Block ends (Empty Line) */
901       if((empty($entry))&&(!empty($tmp))) {
902         /* Add collected lines as a complete block */
903         $all[$cnt] = $data;
904         $cnt ++;
905         $data ="";
906       } else {
908         /* Append lines ... */
909         if(!empty($tmp2)) {
910           /* check if we need base64_decode for this line */
911           if(ereg("::",$tmp2))
912           {
913             $encoded = split("::",$entry);
914             $attr  = trim($encoded[0]);
915             $value = base64_decode(trim($encoded[1]));
916             /* Add linenumber */
917             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
918           }
919           else
920           {
921             /* Add Linenumber */ 
922             $data .= $current_line."#".base64_encode($entry)."\n";
923           }
924         }
925       }
926     }
928     /* The Data we collected is not in the array all[];
929        For example the Data is stored like this..
931        all[0] = "1#dn : .... \n 
932        2#ObjectType: person \n ...."
933        
934        Now we check every insertblock and try to insert */
935     foreach ( $all as $single) {
936       $lineone = split("\n",$single);  
937       $ndn = split("#", $lineone[0]);
938       $line = base64_decode($ndn[1]);
940       $dnn = split (":",$line,2);
941       $current_line = $ndn[0];
942       $dn    = $dnn[0];
943       $value = $dnn[1];
945       /* Every block must begin with a dn */
946       if($dn != "dn") {
947         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
948         return -2;  
949       }
951       /* Should we use Modify instead of Add */
952       $usemodify= false;
954       /* Delete before insert */
955       $usermdir= false;
956     
957       /* The dn address already exists, Don't delete destination entry, overwrite it */
958       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
960         $usermdir = $usemodify = false;
962       /* Delete old entry first, then add new */
963       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
965         /* Delete first, then add */
966         $usermdir = true;        
968       } elseif(($this->dn_exists($value))&&($JustModify)) {
969         
970         /* Modify instead of Add */
971         $usemodify = true;
972       }
973      
974       /* If we can't Import, return with a file error */
975       if(!$this->import_single_entry($srp, $single,$usemodify,$usermdir) ) {
976         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
977                         $current_line);
978         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
979     }
981     return (INSERT_OK);
982   }
985   /* Imports a single entry 
986       If $delete is true;  The old entry will be deleted if it exists.
987       if $modify is true;  All variables that are not touched by the new ldif will be kept.
988       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
989   */
990   function import_single_entry($srp, $str_attr,$modify,$delete)
991   {
992     global $config;
994     if(!$config){
995       trigger_error("Can't import ldif, can't read config object.");
996     }
997   
999     if($this->reconnect) $this->connect();
1001     $ret = false;
1002     $rows= split("\n",$str_attr);
1003     $data= false;
1005     foreach($rows as $row) {
1006       
1007       /* Check if we use Linenumbers (when import_complete_ldif is called we use
1008          Linenumbers) Linenumbers are use like this 123#attribute : value */
1009       if(!empty($row)) {
1010         if(strpos($row,"#")!=FALSE) {
1012           /* We are using line numbers 
1013              Because there is a # before a : */
1014           $tmp1= split("#",$row);
1015           $current_line= $tmp1[0];
1016           $row= base64_decode($tmp1[1]);
1017         }
1019         /* Split the line into  attribute  and value */
1020         $attr   = split(":", $row,2);
1021         $attr[0]= trim($attr[0]);  /* attribute */
1022         $attr[1]= $attr[1];  /* value */
1024         /* Check :: was used to indicate base64_encoded strings */
1025         if($attr[1][0] == ":"){
1026           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1027           $attr[1]=base64_decode($attr[1]);
1028         }
1030         $attr[1] = trim($attr[1]);
1032         /* Check for attributes that are used more than once */
1033         if(!isset($data[$attr[0]])) {
1034           $data[$attr[0]]=$attr[1];
1035         } else {
1036           $tmp = $data[$attr[0]];
1038           if(!is_array($tmp)) {
1039             $new[0]=$tmp;
1040             $new[1]=$attr[1];
1041             $datas[$attr[0]]['count']=1;             
1042             $data[$attr[0]]=$new;
1043           } else {
1044             $cnt = $datas[$attr[0]]['count'];           
1045             $cnt ++;
1046             $data[$attr[0]][$cnt]=$attr[1];
1047             $datas[$attr[0]]['count'] = $cnt;
1048           }
1049         }
1050       }
1051     }
1053     /* If dn is an index of data, we should try to insert the data */
1054     if(isset($data['dn'])) {
1056       /* Fix dn */
1057       $tmp = gosa_ldap_explode_dn($data['dn']);
1058       unset($tmp['count']);
1059       $newdn ="";
1060       foreach($tmp as $tm){
1061         $newdn.= trim($tm).",";
1062       }
1063       $newdn = preg_replace("/,$/","",$newdn);
1064       $data['dn'] = $newdn;
1065    
1066       /* Creating Entry */
1067       $this->cd($data['dn']);
1069       /* Delete existing entry */
1070       if($delete){
1071         $this->rmdir_recursive($srp, $data['dn']);
1072       }
1073      
1074       /* Create missing trees */
1075       $this->cd ($this->basedn);
1076       $this->cd($config->current['BASE']);
1077       $this->create_missing_trees($srp, preg_replace("/^[^,]+,/","",$data['dn']));
1078       $this->cd($data['dn']);
1080       $dn = $data['dn'];
1081       unset($data['dn']);
1082       
1083       if(!$modify){
1085         $this->cat($srp, $dn);
1086         if($this->count($srp)){
1087         
1088           /* The destination entry exists, overwrite it with the new entry */
1089           $attrs = $this->fetch($srp);
1090           foreach($attrs as $name => $value ){
1091             if(!is_numeric($name)){
1092               if(in_array($name,array("dn","count"))) continue;
1093               if(!isset($data[$name])){
1094                 $data[$name] = array();
1095               }
1096             }
1097           }
1098           $ret = $this->modify($data);
1099     
1100         }else{
1101     
1102           /* The destination entry doesn't exists, create it */
1103           $ret = $this->add($data);
1104         }
1106       } else {
1107         
1108         /* Keep all vars that aren't touched by this ldif */
1109         $ret = $this->modify($data);
1110       }
1111     }
1113     if (!$this->success()){
1114       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $dn, "", get_class()));
1115     }
1117     return($ret);
1118   }
1120   
1121   function importcsv($str)
1122   {
1123     $lines = split("\n",$str);
1124     foreach($lines as $line)
1125     {
1126       /* continue if theres a comment */
1127       if(substr(trim($line),0,1)=="#"){
1128         continue;
1129       }
1131       $line= str_replace ("\t\t","\t",$line);
1132       $line= str_replace ("\t"  ,"," ,$line);
1133       echo $line;
1135       $cells = split(",",$line )  ;
1136       $linet= str_replace ("\t\t",",",$line);
1137       $cells = split("\t",$line);
1138       $count = count($cells);  
1139     }
1141   }
1142   
1143   function get_objectclasses()
1144   {
1145     $objectclasses = array();
1146     global $config;
1148     /* Only read schema if it is allowed */
1149     if(isset($config) && preg_match("/config/i",get_class($config))){
1150       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1151         return($objectclasses);
1152       } 
1153     }
1155     /* Return the cached results. */
1156     if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1157       $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1158       return($objectclasses);
1159     }
1160         
1161           # Get base to look for schema 
1162           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1163     if(!$sr){
1164             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1165     }
1167           $attr = @ldap_get_entries($this->cid,$sr);
1168           if (!isset($attr[0]['subschemasubentry'][0])){
1169             return array();
1170           }
1171         
1172           /* Get list of objectclasses and fill array */
1173           $nb= $attr[0]['subschemasubentry'][0];
1174           $objectclasses= array();
1175           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1176           $attrs= ldap_get_entries($this->cid,$sr);
1177           if (!isset($attrs[0])){
1178             return array();
1179           }
1180           foreach ($attrs[0]['objectclasses'] as $val){
1181       if (preg_match('/^[0-9]+$/', $val)){
1182         continue;
1183       }
1184       $name= "OID";
1185       $pattern= split(' ', $val);
1186       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1187       $objectclasses[$ocname]= array();
1189       foreach($pattern as $chunk){
1190         switch($chunk){
1192           case '(':
1193                     $value= "";
1194                     break;
1196           case ')': if ($name != ""){
1197                       $objectclasses[$ocname][$name]= $this->value2container($value);
1198                     }
1199                     $name= "";
1200                     $value= "";
1201                     break;
1203           case 'NAME':
1204           case 'DESC':
1205           case 'SUP':
1206           case 'STRUCTURAL':
1207           case 'ABSTRACT':
1208           case 'AUXILIARY':
1209           case 'MUST':
1210           case 'MAY':
1211                     if ($name != ""){
1212                       $objectclasses[$ocname][$name]= $this->value2container($value);
1213                     }
1214                     $name= $chunk;
1215                     $value= "";
1216                     break;
1218           default:  $value.= $chunk." ";
1219         }
1220       }
1222           }
1223     if(class_available("session")){
1224       session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1225     }
1226           return $objectclasses;
1227   }
1230   function value2container($value)
1231   {
1232     /* Set emtpy values to "true" only */
1233     if (preg_match('/^\s*$/', $value)){
1234       return true;
1235     }
1237     /* Remove ' and " if needed */
1238     $value= preg_replace('/^[\'"]/', '', $value);
1239     $value= preg_replace('/[\'"] *$/', '', $value);
1241     /* Convert to array if $ is inside... */
1242     if (preg_match('/\$/', $value)){
1243       $container= preg_split('/\s*\$\s*/', $value);
1244     } else {
1245       $container= chop($value);
1246     }
1248     return ($container);
1249   }
1252   function log($string)
1253   {
1254     if (session::is_set('config')){
1255       $cfg = session::get('config');
1256       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1257         syslog (LOG_INFO, $string);
1258       }
1259     }
1260   }
1262   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1263   function getCn($dn){
1264     $simple= split(",", $dn);
1266     foreach($simple as $piece) {
1267       $partial= split("=", $piece);
1269       if($partial[0] == "cn"){
1270         return $partial[1];
1271       }
1272     }
1273   }
1276   function get_naming_contexts($server, $admin= "", $password= "")
1277   {
1278     /* Build LDAP connection */
1279     $ds= ldap_connect ($server);
1280     if (!$ds) {
1281       die ("Can't bind to LDAP. No check possible!");
1282     }
1283     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1284     $r= ldap_bind ($ds, $admin, $password);
1286     /* Get base to look for naming contexts */
1287     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1288     $attr= @ldap_get_entries($ds,$sr);
1290     return ($attr[0]['namingcontexts']);
1291   }
1294   function get_root_dse($server, $admin= "", $password= "")
1295   {
1296     /* Build LDAP connection */
1297     $ds= ldap_connect ($server);
1298     if (!$ds) {
1299       die ("Can't bind to LDAP. No check possible!");
1300     }
1301     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1302     $r= ldap_bind ($ds, $admin, $password);
1304     /* Get base to look for naming contexts */
1305     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1306     $attr= @ldap_get_entries($ds,$sr);
1307    
1308     /* Return empty array, if nothing was set */
1309     if (!isset($attr[0])){
1310       return array();
1311     }
1313     /* Rework array... */
1314     $result= array();
1315     for ($i= 0; $i<$attr[0]['count']; $i++){
1316       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1317       unset($result[$attr[0][$i]]['count']);
1318     }
1320     return ($result);
1321   }
1324 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1325 ?>