Code

Closes #310 Moved copy_FAI_resource_recursive to faiManagement. It is only used there.
[gosa.git] / gosa-core / include / class_ldap.inc
1 <?php
2 /*****************************************************************************
3   newldap.inc - version 1.0
4   Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
5   Copyright (C) 2004-2006 Cajus Pollmeier <pollmeier@gonicus.de>
7   Based in code of ldap.inc of
8   Copyright (C) 1998  Eric Kilfoil <eric@ipass.net>
9  *****************************************************************************/
11 define("ALREADY_EXISTING_ENTRY",-10001);
12 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
13 define("NO_FILE_UPLOADED",10003);
14 define("INSERT_OK",10000);
15 define("SPECIALS_OVERRIDE", TRUE);
17 class LDAP{
19   var $hascon   =false;
20   var $hasres   =false;
21   var $reconnect=false;
22   var $tls      = false;
23   var $basedn   ="";
24   var $cid;
25   var $error    = ""; // Any error messages to be returned can be put here
26   var $start    = 0; // 0 if we are fetching the first entry, otherwise 1
27   var $objectClasses = array(); // Information read from slapd.oc.conf
28   var $binddn   = "";
29   var $bindpw   = "";
30   var $hostname = "";
31   var $follow_referral = FALSE;
32   var $referrals= array();
33   var $max_ldap_query_time = 0;   // 0, empty or negative values will disable this check 
35   function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
36   {
37     global $config;
38     $this->follow_referral= $follow_referral;
39     $this->tls=$tls;
40     $this->binddn=LDAP::convert($binddn);
42     $this->bindpw=$bindpw;
43     $this->hostname=$hostname;
45     /* Check if MAX_LDAP_QUERY_TIME is defined */ 
46     if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
47       $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
48       $this->max_ldap_query_time = (float)($str);
49     }
51     $this->connect();
52   }
55   /* Function to replace all problematic characters inside a DN by \001XX, where
56      \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
57      that this character is inside a DN.
58      
59      Currently used codes:
60       ,   => CO
61       \2C => CO
62       (   => OB
63       )   => CB
64       /   => SL                                                                  */
65   static function convert($dn)
66   {
67     if (SPECIALS_OVERRIDE == TRUE){
68       $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
69                            array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
70                            $dn);
71       return (preg_replace('/,\s+/', ',', $tmp));
72     } else {
73       return ($dn);
74     }
75   }
78   /* Function to fix all problematic characters inside a DN by replacing \001XX
79      codes to their original values. See "convert" for mor information. 
80      ',' characters are always expanded to \, (not \2C), since all tested LDAP
81      servers seem to take it the correct way.                                  */
82   static function fix($dn)
83   {
84     if (SPECIALS_OVERRIDE == TRUE){
85       return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
86                            array("\,", "(", ")", "/"),
87                            $dn));
88     } else {
89       return ($dn);
90     }
91   }
94   function connect()
95   {
96     $this->hascon=false;
97     $this->reconnect=false;
98     if ($this->cid= @ldap_connect($this->hostname)) {
99       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
100       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
101         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
102         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
103       }
104       if (function_exists("ldap_start_tls") && $this->tls){
105         @ldap_start_tls($this->cid);
106       }
108       $this->error = "No Error";
109       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
110         $this->error = "Success";
111         $this->hascon=true;
112       } else {
113         if ($this->reconnect){
114           if ($this->error != "Success"){
115             $this->error = "Could not rebind to " . $this->binddn;
116           }
117         } else {
118           $this->error = "Could not bind to " . $this->binddn;
119         }
120       }
121     } else {
122       $this->error = "Could not connect to LDAP server";
123     }
124   }
126   function rebind($ldap, $referral)
127   {
128     $credentials= $this->get_credentials($referral);
129     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
130       $this->error = "Success";
131       $this->hascon=true;
132       $this->reconnect= true;
133       return (0);
134     } else {
135       $this->error = "Could not bind to " . $credentials['ADMIN'];
136       return NULL;
137     }
138   }
140   function reconnect()
141   {
142     if ($this->reconnect){
143       @ldap_unbind($this->cid);
144       $this->cid = NULL;
145     }
146   }
148   function unbind()
149   {
150     @ldap_unbind($this->cid);
151     $this->cid = NULL;
152   }
154   function disconnect()
155   {
156     if($this->hascon){
157       @ldap_close($this->cid);
158       $this->hascon=false;
159     }
160   }
162   function cd($dir)
163   {
164     if ($dir == "..")
165       $this->basedn = $this->getParentDir();
166     else
167       $this->basedn = LDAP::convert($dir);
168   }
170   function getParentDir($basedn = "")
171   {
172     if ($basedn=="")
173       $basedn = $this->basedn;
174     else
175       $basedn = LDAP::convert($this->basedn);
176     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
177   }
179   function search($filter, $attrs= array())
180   {
181     if($this->hascon){
182       if ($this->reconnect) $this->connect();
184       $start = microtime();
185    
186       $this->clearResult();
187       $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
188       $this->error = @ldap_error($this->cid);
189       $this->resetResult();
190       $this->hasres=true;
191    
192       /* Check if query took longer as specified in max_ldap_query_time */
193       if($this->max_ldap_query_time){
194         $diff = get_MicroTimeDiff($start,microtime());
195         if($diff > $this->max_ldap_query_time){
196           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
197         }
198       }
200       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
201       return($this->sr);
202     }else{
203       $this->error = "Could not connect to LDAP server";
204       return("");
205     }
206   }
208   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
209   {
210     if($this->hascon){
211       if ($this->reconnect) $this->connect();
212       $this->clearResult();
213       if ($basedn == "")
214         $basedn = $this->basedn;
215       else
216         $basedn= LDAP::convert($basedn);
217   
218       $start = microtime();
219       $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
220       $this->error = @ldap_error($this->cid);
221       $this->resetResult();
222       $this->hasres=true;
224        /* Check if query took longer as specified in max_ldap_query_time */
225       if($this->max_ldap_query_time){
226         $diff = get_MicroTimeDiff($start,microtime());
227         if($diff > $this->max_ldap_query_time){
228           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
229         }
230       }
232       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
234       return($this->sr);
235     }else{
236       $this->error = "Could not connect to LDAP server";
237       return("");
238     }
239   }
241   function cat($dn,$attrs= array("*"))
242   {
243     if($this->hascon){
244       if ($this->reconnect) $this->connect();
245       $this->clearResult();
246       $filter = "(objectclass=*)";
247       $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
248       $this->error = @ldap_error($this->cid);
249       $this->resetResult();
250       $this->hasres=true;
251       return($this->sr);
252     }else{
253       $this->error = "Could not connect to LDAP server";
254       return("");
255     }
256   }
258   function set_size_limit($size)
259   {
260     /* Ignore zero settings */
261     if ($size == 0){
262       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
263     }
264     if($this->hascon){
265       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
266     } else {
267       $this->error = "Could not connect to LDAP server";
268     }
269   }
271   function fetch()
272   {
273     $att= array();
274     if($this->hascon){
275       if($this->hasres){
276         if ($this->start == 0)
277         {
278           if ($this->sr){
279             $this->start = 1;
280             $this->re= @ldap_first_entry($this->cid, $this->sr);
281           } else {
282             return array();
283           }
284         } else {
285           $this->re= @ldap_next_entry($this->cid, $this->re);
286         }
287         if ($this->re)
288         {
289           $att= @ldap_get_attributes($this->cid, $this->re);
290           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
291         }
292         $this->error = @ldap_error($this->cid);
293         if (!isset($att)){
294           $att= array();
295         }
296         return($att);
297       }else{
298         $this->error = "Perform a Fetch with no Search";
299         return("");
300       }
301     }else{
302       $this->error = "Could not connect to LDAP server";
303       return("");
304     }
305   }
307   function resetResult()
308   {
309     $this->start = 0;
310   }
312   function clearResult()
313   {
314     if($this->hasres){
315       $this->hasres = false;
316       @ldap_free_result($this->sr);
317     }
318   }
320   function getDN()
321   {
322     if($this->hascon){
323       if($this->hasres){
325         if(!$this->re)
326           {
327           $this->error = "Perform a Fetch with no valid Result";
328           }
329           else
330           {
331           $rv = @ldap_get_dn($this->cid, $this->re);
332         
333           $this->error = @ldap_error($this->cid);
334           return(trim(LDAP::convert($rv)));
335            }
336       }else{
337         $this->error = "Perform a Fetch with no Search";
338         return("");
339       }
340     }else{
341       $this->error = "Could not connect to LDAP server";
342       return("");
343     }
344   }
346   function count()
347   {
348     if($this->hascon){
349       if($this->hasres){
350         $rv = @ldap_count_entries($this->cid, $this->sr);
351         $this->error = @ldap_error($this->cid);
352         return($rv);
353       }else{
354         $this->error = "Perform a Fetch with no Search";
355         return("");
356       }
357     }else{
358       $this->error = "Could not connect to LDAP server";
359       return("");
360     }
361   }
363   function rm($attrs = "", $dn = "")
364   {
365     if($this->hascon){
366       if ($this->reconnect) $this->connect();
367       if ($dn == "")
368         $dn = $this->basedn;
370       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
371       $this->error = @ldap_error($this->cid);
372       return($r);
373     }else{
374       $this->error = "Could not connect to LDAP server";
375       return("");
376     }
377   }
379   function rename($attrs, $dn = "")
380   {
381     if($this->hascon){
382       if ($this->reconnect) $this->connect();
383       if ($dn == "")
384         $dn = $this->basedn;
386       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
387       $this->error = @ldap_error($this->cid);
388       return($r);
389     }else{
390       $this->error = "Could not connect to LDAP server";
391       return("");
392     }
393   }
395   function rmdir($deletedn)
396   {
397     if($this->hascon){
398       if ($this->reconnect) $this->connect();
399       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
400       $this->error = @ldap_error($this->cid);
401       return($r ? $r : 0);
402     }else{
403       $this->error = "Could not connect to LDAP server";
404       return("");
405     }
406   }
408   /**
409   *  Function rmdir_recursive
410   *
411   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
412   *  Parameters:  The dn to delete
413   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
414   *
415   */
417   function rmdir_recursive($deletedn)
418   {
419     if($this->hascon){
420       if ($this->reconnect) $this->connect();
421       $delarray= array();
422         
423       /* Get sorted list of dn's to delete */
424       $this->ls ("(objectClass=*)",$deletedn);
425       while ($this->fetch()){
426         $deldn= $this->getDN();
427         $delarray[$deldn]= strlen($deldn);
428       }
429       arsort ($delarray);
430       reset ($delarray);
432       /* Really Delete ALL dn's in subtree */
433       foreach ($delarray as $key => $value){
434         $this->rmdir_recursive($key);
435       }
436       
437       /* Finally Delete own Node */
438       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
439       $this->error = @ldap_error($this->cid);
440       return($r ? $r : 0);
441     }else{
442       $this->error = "Could not connect to LDAP server";
443       return("");
444     }
445   }
448   function modify($attrs)
449   {
450     if(count($attrs) == 0){
451       return (0);
452     }
453     if($this->hascon){
454       if ($this->reconnect) $this->connect();
455       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
456       $this->error = @ldap_error($this->cid);
457       return($r ? $r : 0);
458     }else{
459       $this->error = "Could not connect to LDAP server";
460       return("");
461     }
462   }
464   function add($attrs)
465   {
466     if($this->hascon){
467       if ($this->reconnect) $this->connect();
468       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
469       $this->error = @ldap_error($this->cid);
470       return($r ? $r : 0);
471     }else{
472       $this->error = "Could not connect to LDAP server";
473       return("");
474     }
475   }
477   function create_missing_trees($target)
478   {
479     global $config;
481     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
483     if ($target == $this->basedn){
484       $l= array("dummy");
485     } else {
486       $l= array_reverse(gosa_ldap_explode_dn($real_path));
487     }
488     unset($l['count']);
489     $cdn= $this->basedn;
490     $tag= "";
492     /* Load schema if available... */
493     $classes= $this->get_objectclasses();
495     foreach ($l as $part){
496       if ($part != "dummy"){
497         $cdn= "$part,$cdn";
498       }
500       /* Ignore referrals */
501       $found= false;
502       foreach($this->referrals as $ref){
503         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
504         if ($base == $cdn){
505           $found= true;
506           break;
507         }
508       }
509       if ($found){
510         continue;
511       }
513       $this->cat ($cdn);
514       $attrs= $this->fetch();
516       /* Create missing entry? */
517       if (count ($attrs)){
518       
519         /* Catch the tag - if present */
520         if (isset($attrs['gosaUnitTag'][0])){
521           $tag= $attrs['gosaUnitTag'][0];
522         }
524       } else {
525         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
526         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
528         $na= array();
530         /* Automatic or traditional? */
531         if(count($classes)){
533           /* Get name of first matching objectClass */
534           $ocname= "";
535           foreach($classes as $class){
536             if (isset($class['MUST']) && $class['MUST'] == "$type"){
538               /* Look for first classes that is structural... */
539               if (isset($class['STRUCTURAL'])){
540                 $ocname= $class['NAME'];
541                 break;
542               }
544               /* Look for classes that are auxiliary... */
545               if (isset($class['AUXILIARY'])){
546                 $ocname= $class['NAME'];
547               }
548             }
549           }
551           /* Bail out, if we've nothing to do... */
552           if ($ocname == ""){
553             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), ERROR_DIALOG);
554             display_error_page();
555           }
557           /* Assemble_entry */
558           if ($tag != ""){
559             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
560             $na["gosaUnitTag"]= $tag;
561           } else {
562             $na['objectClass']= array($ocname);
563           }
564           if (isset($classes[$ocname]['AUXILIARY'])){
565             $na['objectClass'][]= $classes[$ocname]['SUP'];
566           }
567           if ($type == "dc"){
568             /* This is bad actually, but - tell me a better way? */
569             $na['objectClass'][]= 'locality';
570           }
571           $na[$type]= $param;
572           if (is_array($classes[$ocname]['MUST'])){
573             foreach($classes[$ocname]['MUST'] as $attr){
574               $na[$attr]= "filled";
575             }
576           }
578         } else {
580           /* Use alternative add... */
581           switch ($type){
582             case 'ou':
583               if ($tag != ""){
584                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
585                 $na["gosaUnitTag"]= $tag;
586               } else {
587                 $na["objectClass"]= "organizationalUnit";
588               }
589               $na["ou"]= $param;
590               break;
591             case 'dc':
592               if ($tag != ""){
593                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
594                 $na["gosaUnitTag"]= $tag;
595               } else {
596                 $na["objectClass"]= array("dcObject", "top", "locality");
597               }
598               $na["dc"]= $param;
599               break;
600             default:
601               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), ERROR_DIALOG);
602               display_error_page();
603           }
605         }
606         $this->cd($cdn);
607         $this->add($na);
608     
609         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
610         if (!preg_match('/success/i', $this->error)){
611           return FALSE;
612         }
613       }
614     }
616     return TRUE;
617   }
620   function recursive_remove()
621   {
622     $delarray= array();
624     /* Get sorted list of dn's to delete */
625     $this->search ("(objectClass=*)");
626     while ($this->fetch()){
627       $deldn= $this->getDN();
628       $delarray[$deldn]= strlen($deldn);
629     }
630     arsort ($delarray);
631     reset ($delarray);
633     /* Delete all dn's in subtree */
634     foreach ($delarray as $key => $value){
635       $this->rmdir($key);
636     }
637   }
639   function get_attribute($dn, $name,$r_array=0)
640   {
641     $data= "";
642     if ($this->reconnect) $this->connect();
643     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
645     /* fill data from LDAP */
646     if ($sr) {
647       $ei= @ldap_first_entry($this->cid, $sr);
648       if ($ei) {
649         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
650           $data= $info[0];
651         }
652       }
653     }
654     if($r_array==0)
655     return ($data);
656     else
657     return ($info);
658   
659   
660   }
661  
664   function get_additional_error()
665   {
666     $error= "";
667     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
668     return ($error);
669   }
671   function get_error()
672   {
673     if ($this->error == 'Success'){
674       return $this->error;
675     } else {
676       $adderror= $this->get_additional_error();
677       if ($adderror != ""){
678         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
679       } else {
680         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
681       }
682       return $error;
683     }
684   }
686   function get_credentials($url, $referrals= NULL)
687   {
688     $ret= array();
689     $url= preg_replace('!\?\?.*$!', '', $url);
690     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
692     if ($referrals === NULL){
693       $referrals= $this->referrals;
694     }
696     if (isset($referrals[$server])){
697       return ($referrals[$server]);
698     } else {
699       $ret['ADMIN']= LDAP::fix($this->binddn);
700       $ret['PASSWORD']= $this->bindpw;
701     }
703     return ($ret);
704   }
707   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
708   {
709     $display= "";
711     if ($recursive){
712       $this->cd($dn);
713       $this->ls($filter,$dn, array('dn','objectClass'));
714       $deps = array();
716       $display .= $this->gen_one_entry($dn)."\n";
718       while ($attrs= $this->fetch()){
719         $deps[] = $attrs['dn'];
720       }
721       foreach($deps as $dn){
722         $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
723       }
724     } else {
725       $display.= $this->gen_one_entry($dn);
726     }
727     return ($display);
728   }
731   function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
732   {
733     $display= array();
735       $this->cd($dn);
736       $this->search("$filter");
738       $i=0;
739       while ($attrs= $this->fetch()){
740         $j=0;
742         foreach ($attributes as $at){
743           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
744           $j++;
745         }
747         $i++;
748       }
750     return ($display);
751   }
754   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
755   {
756     $ret = "";
757     $data = "";
758     if($this->reconnect){
759       $this->connect();
760     }
762     /* Searching Ldap Tree */
763     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
765     /* Get the first entry */   
766     $entry= @ldap_first_entry($this->cid, $sr);
768     /* Get all attributes related to that Objekt */
769     $atts = array();
770     
771     /* Assemble dn */
772     $atts[0]['name']  = "dn";
773     $atts[0]['value'] = array('count' => 1, 0 => $dn);
775     /* Reset index */
776     $i = 1 ; 
777   $identifier = array();
778     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
779     while ($attribute) {
780       $i++;
781       $atts[$i]['name']  = $attribute;
782       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
784       /* Next one */
785       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
786     }
788     foreach($atts as $at)
789     {
790       for ($i= 0; $i<$at['value']['count']; $i++){
792         /* Check if we must encode the data */
793         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
794           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
795         } else {
796           $ret .= $at['name'].": ".$at['value'][$i]."\n";
797         }
798       }
799     }
801     return($ret);
802   }
805   function dn_exists($dn)
806   {
807     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
808   }
809   
812   /*  This funktion imports ldifs 
813         
814       If DeleteOldEntries is true, the destination entry will be deleted first. 
815       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
816       if JustMofify id false the destination dn will be overwritten by the new ldif. 
817     */
819   function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
820   {
821     if($this->reconnect) $this->connect();
823     /* First we have to splitt the string ito detect empty lines
824        An empty line indicates an new Entry */
825     $entries = split("\n",$str_attr);
827     $data = "";
828     $cnt = 0; 
829     $current_line = 0;
831     /* FIX ldif */
832     $last = "";
833     $tmp  = "";
834     $i = 0;
835     foreach($entries as $entry){
836       if(preg_match("/^ /",$entry)){
837         $tmp[$i] .= trim($entry);
838       }else{
839         $i ++;
840         $tmp[$i] = trim($entry);
841       }
842     }
844     /* Every single line ... */
845     foreach($tmp as $entry) {
846       $current_line ++;
848       /* Removing Spaces to .. 
849          .. test if a new entry begins */
850       $tmp  = str_replace(" ","",$data );
852       /* .. prevent empty lines in an entry */
853       $tmp2 = str_replace(" ","",$entry);
855       /* If the Block ends (Empty Line) */
856       if((empty($entry))&&(!empty($tmp))) {
857         /* Add collected lines as a complete block */
858         $all[$cnt] = $data;
859         $cnt ++;
860         $data ="";
861       } else {
863         /* Append lines ... */
864         if(!empty($tmp2)) {
865           /* check if we need base64_decode for this line */
866           if(ereg("::",$tmp2))
867           {
868             $encoded = split("::",$entry);
869             $attr  = trim($encoded[0]);
870             $value = base64_decode(trim($encoded[1]));
871             /* Add linenumber */
872             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
873           }
874           else
875           {
876             /* Add Linenumber */ 
877             $data .= $current_line."#".base64_encode($entry)."\n";
878           }
879         }
880       }
881     }
883     /* The Data we collected is not in the array all[];
884        For example the Data is stored like this..
886        all[0] = "1#dn : .... \n 
887        2#ObjectType: person \n ...."
888        
889        Now we check every insertblock and try to insert */
890     foreach ( $all as $single) {
891       $lineone = split("\n",$single);  
892       $ndn = split("#", $lineone[0]);
893       $line = base64_decode($ndn[1]);
895       $dnn = split (":",$line,2);
896       $current_line = $ndn[0];
897       $dn    = $dnn[0];
898       $value = $dnn[1];
900       /* Every block must begin with a dn */
901       if($dn != "dn") {
902         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
903         return -2;  
904       }
906       /* Should we use Modify instead of Add */
907       $usemodify= false;
909       /* Delete before insert */
910       $usermdir= false;
911     
912       /* The dn address already exists, Don't delete destination entry, overwrite it */
913       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
915         $usermdir = $usemodify = false;
917       /* Delete old entry first, then add new */
918       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
920         /* Delete first, then add */
921         $usermdir = true;        
923       } elseif(($this->dn_exists($value))&&($JustModify)) {
924         
925         /* Modify instead of Add */
926         $usemodify = true;
927       }
928      
929       /* If we can't Import, return with a file error */
930       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
931         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
932                         $current_line);
933         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
934     }
936     return (INSERT_OK);
937   }
940   /* Imports a single entry 
941       If $delete is true;  The old entry will be deleted if it exists.
942       if $modify is true;  All variables that are not touched by the new ldif will be kept.
943       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
944   */
945   function import_single_entry($str_attr,$modify,$delete)
946   {
947     global $config;
949     if(!$config){
950       trigger_error("Can't import ldif, can't read config object.");
951     }
952   
954     if($this->reconnect) $this->connect();
956     $ret = false;
957     $rows= split("\n",$str_attr);
958     $data= false;
960     foreach($rows as $row) {
961       
962       /* Check if we use Linenumbers (when import_complete_ldif is called we use
963          Linenumbers) Linenumbers are use like this 123#attribute : value */
964       if(!empty($row)) {
965         if(strpos($row,"#")!=FALSE) {
967           /* We are using line numbers 
968              Because there is a # before a : */
969           $tmp1= split("#",$row);
970           $current_line= $tmp1[0];
971           $row= base64_decode($tmp1[1]);
972         }
974         /* Split the line into  attribute  and value */
975         $attr   = split(":", $row,2);
976         $attr[0]= trim($attr[0]);  /* attribute */
977         $attr[1]= $attr[1];  /* value */
979         /* Check :: was used to indicate base64_encoded strings */
980         if($attr[1][0] == ":"){
981           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
982           $attr[1]=base64_decode($attr[1]);
983         }
985         $attr[1] = trim($attr[1]);
987         /* Check for attributes that are used more than once */
988         if(!isset($data[$attr[0]])) {
989           $data[$attr[0]]=$attr[1];
990         } else {
991           $tmp = $data[$attr[0]];
993           if(!is_array($tmp)) {
994             $new[0]=$tmp;
995             $new[1]=$attr[1];
996             $datas[$attr[0]]['count']=1;             
997             $data[$attr[0]]=$new;
998           } else {
999             $cnt = $datas[$attr[0]]['count'];           
1000             $cnt ++;
1001             $data[$attr[0]][$cnt]=$attr[1];
1002             $datas[$attr[0]]['count'] = $cnt;
1003           }
1004         }
1005       }
1006     }
1008     /* If dn is an index of data, we should try to insert the data */
1009     if(isset($data['dn'])) {
1011       /* Fix dn */
1012       $tmp = gosa_ldap_explode_dn($data['dn']);
1013       unset($tmp['count']);
1014       $newdn ="";
1015       foreach($tmp as $tm){
1016         $newdn.= trim($tm).",";
1017       }
1018       $newdn = preg_replace("/,$/","",$newdn);
1019       $data['dn'] = $newdn;
1020    
1021       /* Creating Entry */
1022       $this->cd($data['dn']);
1024       /* Delete existing entry */
1025       if($delete){
1026         $this->rmdir_recursive($data['dn']);
1027       }
1028      
1029       /* Create missing trees */
1030       $this->cd ($this->basedn);
1031       $this->cd($config->current['BASE']);
1032       $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1033       $this->cd($data['dn']);
1035       $dn = $data['dn'];
1036       unset($data['dn']);
1037       
1038       if(!$modify){
1040         $this->cat($dn);
1041         if($this->count()){
1042         
1043           /* The destination entry exists, overwrite it with the new entry */
1044           $attrs = $this->fetch();
1045           foreach($attrs as $name => $value ){
1046             if(!is_numeric($name)){
1047               if(in_array($name,array("dn","count"))) continue;
1048               if(!isset($data[$name])){
1049                 $data[$name] = array();
1050               }
1051             }
1052           }
1053           $ret = $this->modify($data);
1054     
1055         }else{
1056     
1057           /* The destination entry doesn't exists, create it */
1058           $ret = $this->add($data);
1059         }
1061       } else {
1062         
1063         /* Keep all vars that aren't touched by this ldif */
1064         $ret = $this->modify($data);
1065       }
1066     }
1067     show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1068     return($ret);
1069   }
1071   
1072   function importcsv($str)
1073   {
1074     $lines = split("\n",$str);
1075     foreach($lines as $line)
1076     {
1077       /* continue if theres a comment */
1078       if(substr(trim($line),0,1)=="#"){
1079         continue;
1080       }
1082       $line= str_replace ("\t\t","\t",$line);
1083       $line= str_replace ("\t"  ,"," ,$line);
1084       echo $line;
1086       $cells = split(",",$line )  ;
1087       $linet= str_replace ("\t\t",",",$line);
1088       $cells = split("\t",$line);
1089       $count = count($cells);  
1090     }
1092   }
1093   
1094   function get_objectclasses()
1095   {
1096     $objectclasses = array();
1097     global $config;
1099     /* Only read schema if it is allowed */
1100     if(isset($config) && preg_match("/config/i",get_class($config))){
1101       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1102         return($objectclasses);
1103       } 
1104     }
1105         
1106           # Get base to look for schema 
1107           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1108     if(!$sr){
1109             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1110     }
1112           $attr = @ldap_get_entries($this->cid,$sr);
1113           if (!isset($attr[0]['subschemasubentry'][0])){
1114             return array();
1115           }
1116         
1117           /* Get list of objectclasses and fill array */
1118           $nb= $attr[0]['subschemasubentry'][0];
1119           $objectclasses= array();
1120           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1121           $attrs= ldap_get_entries($this->cid,$sr);
1122           if (!isset($attrs[0])){
1123             return array();
1124           }
1125           foreach ($attrs[0]['objectclasses'] as $val){
1126       if (preg_match('/^[0-9]+$/', $val)){
1127         continue;
1128       }
1129       $name= "OID";
1130       $pattern= split(' ', $val);
1131       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1132       $objectclasses[$ocname]= array();
1134       foreach($pattern as $chunk){
1135         switch($chunk){
1137           case '(':
1138                     $value= "";
1139                     break;
1141           case ')': if ($name != ""){
1142                       $objectclasses[$ocname][$name]= $this->value2container($value);
1143                     }
1144                     $name= "";
1145                     $value= "";
1146                     break;
1148           case 'NAME':
1149           case 'DESC':
1150           case 'SUP':
1151           case 'STRUCTURAL':
1152           case 'ABSTRACT':
1153           case 'AUXILIARY':
1154           case 'MUST':
1155           case 'MAY':
1156                     if ($name != ""){
1157                       $objectclasses[$ocname][$name]= $this->value2container($value);
1158                     }
1159                     $name= $chunk;
1160                     $value= "";
1161                     break;
1163           default:  $value.= $chunk." ";
1164         }
1165       }
1167           }
1168           return $objectclasses;
1169   }
1172   function value2container($value)
1173   {
1174     /* Set emtpy values to "true" only */
1175     if (preg_match('/^\s*$/', $value)){
1176       return true;
1177     }
1179     /* Remove ' and " if needed */
1180     $value= preg_replace('/^[\'"]/', '', $value);
1181     $value= preg_replace('/[\'"] *$/', '', $value);
1183     /* Convert to array if $ is inside... */
1184     if (preg_match('/\$/', $value)){
1185       $container= preg_split('/\s*\$\s*/', $value);
1186     } else {
1187       $container= chop($value);
1188     }
1190     return ($container);
1191   }
1194   function log($string)
1195   {
1196     if (session::is_set('config')){
1197       $cfg = session::get('config');
1198       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1199         syslog (LOG_INFO, $string);
1200       }
1201     }
1202   }
1204   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1205   function getCn($dn){
1206     $simple= split(",", $dn);
1208     foreach($simple as $piece) {
1209       $partial= split("=", $piece);
1211       if($partial[0] == "cn"){
1212         return $partial[1];
1213       }
1214     }
1215   }
1218   function get_naming_contexts($server, $admin= "", $password= "")
1219   {
1220     /* Build LDAP connection */
1221     $ds= ldap_connect ($server);
1222     if (!$ds) {
1223       die ("Can't bind to LDAP. No check possible!");
1224     }
1225     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1226     $r= ldap_bind ($ds, $admin, $password);
1228     /* Get base to look for naming contexts */
1229     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1230     $attr= @ldap_get_entries($ds,$sr);
1232     return ($attr[0]['namingcontexts']);
1233   }
1236   function get_root_dse($server, $admin= "", $password= "")
1237   {
1238     /* Build LDAP connection */
1239     $ds= ldap_connect ($server);
1240     if (!$ds) {
1241       die ("Can't bind to LDAP. No check possible!");
1242     }
1243     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1244     $r= ldap_bind ($ds, $admin, $password);
1246     /* Get base to look for naming contexts */
1247     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1248     $attr= @ldap_get_entries($ds,$sr);
1249    
1250     /* Return empty array, if nothing was set */
1251     if (!isset($attr[0])){
1252       return array();
1253     }
1255     /* Rework array... */
1256     $result= array();
1257     for ($i= 0; $i<$attr[0]['count']; $i++){
1258       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1259       unset($result[$attr[0][$i]]['count']);
1260     }
1262     return ($result);
1263   }
1265 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1266 ?>