Code

Added queue entry start / stop
[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 to fix problematic characters in DN's that are used for search
95      requests. I.e. member=....                                               */
96   static function prepare4filter($dn)
97   {
98         return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
99   }
102   function connect()
103   {
104     $this->hascon=false;
105     $this->reconnect=false;
106     if ($this->cid= @ldap_connect($this->hostname)) {
107       @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
108       if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
109         @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
110         @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
111       }
112       if (function_exists("ldap_start_tls") && $this->tls){
113         @ldap_start_tls($this->cid);
114       }
116       $this->error = "No Error";
117       if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
118         $this->error = "Success";
119         $this->hascon=true;
120       } else {
121         if ($this->reconnect){
122           if ($this->error != "Success"){
123             $this->error = "Could not rebind to " . $this->binddn;
124           }
125         } else {
126           $this->error = "Could not bind to " . $this->binddn;
127         }
128       }
129     } else {
130       $this->error = "Could not connect to LDAP server";
131     }
132   }
134   function rebind($ldap, $referral)
135   {
136     $credentials= $this->get_credentials($referral);
137     if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
138       $this->error = "Success";
139       $this->hascon=true;
140       $this->reconnect= true;
141       return (0);
142     } else {
143       $this->error = "Could not bind to " . $credentials['ADMIN'];
144       return NULL;
145     }
146   }
148   function reconnect()
149   {
150     if ($this->reconnect){
151       @ldap_unbind($this->cid);
152       $this->cid = NULL;
153     }
154   }
156   function unbind()
157   {
158     @ldap_unbind($this->cid);
159     $this->cid = NULL;
160   }
162   function disconnect()
163   {
164     if($this->hascon){
165       @ldap_close($this->cid);
166       $this->hascon=false;
167     }
168   }
170   function cd($dir)
171   {
172     if ($dir == "..")
173       $this->basedn = $this->getParentDir();
174     else
175       $this->basedn = LDAP::convert($dir);
176   }
178   function getParentDir($basedn = "")
179   {
180     if ($basedn=="")
181       $basedn = $this->basedn;
182     else
183       $basedn = LDAP::convert($this->basedn);
184     return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
185   }
187   function search($filter, $attrs= array())
188   {
189     if($this->hascon){
190       if ($this->reconnect) $this->connect();
192       $start = microtime();
193    
194       $this->clearResult();
195       $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
196       $this->error = @ldap_error($this->cid);
197       $this->resetResult();
198       $this->hasres=true;
199    
200       /* Check if query took longer as specified in max_ldap_query_time */
201       if($this->max_ldap_query_time){
202         $diff = get_MicroTimeDiff($start,microtime());
203         if($diff > $this->max_ldap_query_time){
204           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
205         }
206       }
208       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
209       return($this->sr);
210     }else{
211       $this->error = "Could not connect to LDAP server";
212       return("");
213     }
214   }
216   function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
217   {
218     if($this->hascon){
219       if ($this->reconnect) $this->connect();
220       $this->clearResult();
221       if ($basedn == "")
222         $basedn = $this->basedn;
223       else
224         $basedn= LDAP::convert($basedn);
225   
226       $start = microtime();
227       $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
228       $this->error = @ldap_error($this->cid);
229       $this->resetResult();
230       $this->hasres=true;
232        /* Check if query took longer as specified in max_ldap_query_time */
233       if($this->max_ldap_query_time){
234         $diff = get_MicroTimeDiff($start,microtime());
235         if($diff > $this->max_ldap_query_time){
236           msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
237         }
238       }
240       $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
242       return($this->sr);
243     }else{
244       $this->error = "Could not connect to LDAP server";
245       return("");
246     }
247   }
249   function cat($dn,$attrs= array("*"))
250   {
251     if($this->hascon){
252       if ($this->reconnect) $this->connect();
253       $this->clearResult();
254       $filter = "(objectclass=*)";
255       $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
256       $this->error = @ldap_error($this->cid);
257       $this->resetResult();
258       $this->hasres=true;
259       return($this->sr);
260     }else{
261       $this->error = "Could not connect to LDAP server";
262       return("");
263     }
264   }
266   function set_size_limit($size)
267   {
268     /* Ignore zero settings */
269     if ($size == 0){
270       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
271     }
272     if($this->hascon){
273       @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
274     } else {
275       $this->error = "Could not connect to LDAP server";
276     }
277   }
279   function fetch()
280   {
281     $att= array();
282     if($this->hascon){
283       if($this->hasres){
284         if ($this->start == 0)
285         {
286           if ($this->sr){
287             $this->start = 1;
288             $this->re= @ldap_first_entry($this->cid, $this->sr);
289           } else {
290             return array();
291           }
292         } else {
293           $this->re= @ldap_next_entry($this->cid, $this->re);
294         }
295         if ($this->re)
296         {
297           $att= @ldap_get_attributes($this->cid, $this->re);
298           $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
299         }
300         $this->error = @ldap_error($this->cid);
301         if (!isset($att)){
302           $att= array();
303         }
304         return($att);
305       }else{
306         $this->error = "Perform a Fetch with no Search";
307         return("");
308       }
309     }else{
310       $this->error = "Could not connect to LDAP server";
311       return("");
312     }
313   }
315   function resetResult()
316   {
317     $this->start = 0;
318   }
320   function clearResult()
321   {
322     if($this->hasres){
323       $this->hasres = false;
324       @ldap_free_result($this->sr);
325     }
326   }
328   function getDN()
329   {
330     if($this->hascon){
331       if($this->hasres){
333         if(!$this->re)
334           {
335           $this->error = "Perform a Fetch with no valid Result";
336           }
337           else
338           {
339           $rv = @ldap_get_dn($this->cid, $this->re);
340         
341           $this->error = @ldap_error($this->cid);
342           return(trim(LDAP::convert($rv)));
343            }
344       }else{
345         $this->error = "Perform a Fetch with no Search";
346         return("");
347       }
348     }else{
349       $this->error = "Could not connect to LDAP server";
350       return("");
351     }
352   }
354   function count()
355   {
356     if($this->hascon){
357       if($this->hasres){
358         $rv = @ldap_count_entries($this->cid, $this->sr);
359         $this->error = @ldap_error($this->cid);
360         return($rv);
361       }else{
362         $this->error = "Perform a Fetch with no Search";
363         return("");
364       }
365     }else{
366       $this->error = "Could not connect to LDAP server";
367       return("");
368     }
369   }
371   function rm($attrs = "", $dn = "")
372   {
373     if($this->hascon){
374       if ($this->reconnect) $this->connect();
375       if ($dn == "")
376         $dn = $this->basedn;
378       $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
379       $this->error = @ldap_error($this->cid);
380       return($r);
381     }else{
382       $this->error = "Could not connect to LDAP server";
383       return("");
384     }
385   }
387   function rename($attrs, $dn = "")
388   {
389     if($this->hascon){
390       if ($this->reconnect) $this->connect();
391       if ($dn == "")
392         $dn = $this->basedn;
394       $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
395       $this->error = @ldap_error($this->cid);
396       return($r);
397     }else{
398       $this->error = "Could not connect to LDAP server";
399       return("");
400     }
401   }
403   function rmdir($deletedn)
404   {
405     if($this->hascon){
406       if ($this->reconnect) $this->connect();
407       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
408       $this->error = @ldap_error($this->cid);
409       return($r ? $r : 0);
410     }else{
411       $this->error = "Could not connect to LDAP server";
412       return("");
413     }
414   }
416   /**
417   *  Function rmdir_recursive
418   *
419   *  Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
420   *  Parameters:  The dn to delete
421   *  GiveBack:    True on sucessfull , 0 in error, and "" when we don't get a ldap conection
422   *
423   */
425   function rmdir_recursive($deletedn)
426   {
427     if($this->hascon){
428       if ($this->reconnect) $this->connect();
429       $delarray= array();
430         
431       /* Get sorted list of dn's to delete */
432       $this->ls ("(objectClass=*)",$deletedn);
433       while ($this->fetch()){
434         $deldn= $this->getDN();
435         $delarray[$deldn]= strlen($deldn);
436       }
437       arsort ($delarray);
438       reset ($delarray);
440       /* Really Delete ALL dn's in subtree */
441       foreach ($delarray as $key => $value){
442         $this->rmdir_recursive($key);
443       }
444       
445       /* Finally Delete own Node */
446       $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
447       $this->error = @ldap_error($this->cid);
448       return($r ? $r : 0);
449     }else{
450       $this->error = "Could not connect to LDAP server";
451       return("");
452     }
453   }
456   function modify($attrs)
457   {
458     if(count($attrs) == 0){
459       return (0);
460     }
461     if($this->hascon){
462       if ($this->reconnect) $this->connect();
463       $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
464       $this->error = @ldap_error($this->cid);
465       return($r ? $r : 0);
466     }else{
467       $this->error = "Could not connect to LDAP server";
468       return("");
469     }
470   }
472   function add($attrs)
473   {
474     if($this->hascon){
475       if ($this->reconnect) $this->connect();
476       $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
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   }
485   function create_missing_trees($target)
486   {
487     global $config;
489     $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
491     if ($target == $this->basedn){
492       $l= array("dummy");
493     } else {
494       $l= array_reverse(gosa_ldap_explode_dn($real_path));
495     }
496     unset($l['count']);
497     $cdn= $this->basedn;
498     $tag= "";
500     /* Load schema if available... */
501     $classes= $this->get_objectclasses();
503     foreach ($l as $part){
504       if ($part != "dummy"){
505         $cdn= "$part,$cdn";
506       }
508       /* Ignore referrals */
509       $found= false;
510       foreach($this->referrals as $ref){
511         $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
512         if ($base == $cdn){
513           $found= true;
514           break;
515         }
516       }
517       if ($found){
518         continue;
519       }
521       $this->cat ($cdn);
522       $attrs= $this->fetch();
524       /* Create missing entry? */
525       if (count ($attrs)){
526       
527         /* Catch the tag - if present */
528         if (isset($attrs['gosaUnitTag'][0])){
529           $tag= $attrs['gosaUnitTag'][0];
530         }
532       } else {
533         $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
534         $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
536         $na= array();
538         /* Automatic or traditional? */
539         if(count($classes)){
541           /* Get name of first matching objectClass */
542           $ocname= "";
543           foreach($classes as $class){
544             if (isset($class['MUST']) && $class['MUST'] == "$type"){
546               /* Look for first classes that is structural... */
547               if (isset($class['STRUCTURAL'])){
548                 $ocname= $class['NAME'];
549                 break;
550               }
552               /* Look for classes that are auxiliary... */
553               if (isset($class['AUXILIARY'])){
554                 $ocname= $class['NAME'];
555               }
556             }
557           }
559           /* Bail out, if we've nothing to do... */
560           if ($ocname == ""){
561             msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), ERROR_DIALOG);
562             display_error_page();
563           }
565           /* Assemble_entry */
566           if ($tag != ""){
567             $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
568             $na["gosaUnitTag"]= $tag;
569           } else {
570             $na['objectClass']= array($ocname);
571           }
572           if (isset($classes[$ocname]['AUXILIARY'])){
573             $na['objectClass'][]= $classes[$ocname]['SUP'];
574           }
575           if ($type == "dc"){
576             /* This is bad actually, but - tell me a better way? */
577             $na['objectClass'][]= 'locality';
578           }
579           $na[$type]= $param;
580           if (is_array($classes[$ocname]['MUST'])){
581             foreach($classes[$ocname]['MUST'] as $attr){
582               $na[$attr]= "filled";
583             }
584           }
586         } else {
588           /* Use alternative add... */
589           switch ($type){
590             case 'ou':
591               if ($tag != ""){
592                 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
593                 $na["gosaUnitTag"]= $tag;
594               } else {
595                 $na["objectClass"]= "organizationalUnit";
596               }
597               $na["ou"]= $param;
598               break;
599             case 'dc':
600               if ($tag != ""){
601                 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
602                 $na["gosaUnitTag"]= $tag;
603               } else {
604                 $na["objectClass"]= array("dcObject", "top", "locality");
605               }
606               $na["dc"]= $param;
607               break;
608             default:
609               msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), ERROR_DIALOG);
610               display_error_page();
611           }
613         }
614         $this->cd($cdn);
615         $this->add($na);
616     
617         show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
618         if (!preg_match('/success/i', $this->error)){
619           return FALSE;
620         }
621       }
622     }
624     return TRUE;
625   }
628   function recursive_remove()
629   {
630     $delarray= array();
632     /* Get sorted list of dn's to delete */
633     $this->search ("(objectClass=*)");
634     while ($this->fetch()){
635       $deldn= $this->getDN();
636       $delarray[$deldn]= strlen($deldn);
637     }
638     arsort ($delarray);
639     reset ($delarray);
641     /* Delete all dn's in subtree */
642     foreach ($delarray as $key => $value){
643       $this->rmdir($key);
644     }
645   }
647   function get_attribute($dn, $name,$r_array=0)
648   {
649     $data= "";
650     if ($this->reconnect) $this->connect();
651     $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
653     /* fill data from LDAP */
654     if ($sr) {
655       $ei= @ldap_first_entry($this->cid, $sr);
656       if ($ei) {
657         if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
658           $data= $info[0];
659         }
660       }
661     }
662     if($r_array==0)
663     return ($data);
664     else
665     return ($info);
666   
667   
668   }
669  
672   function get_additional_error()
673   {
674     $error= "";
675     @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
676     return ($error);
677   }
679   function get_error()
680   {
681     if ($this->error == 'Success'){
682       return $this->error;
683     } else {
684       $adderror= $this->get_additional_error();
685       if ($adderror != ""){
686         $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
687       } else {
688         $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
689       }
690       return $error;
691     }
692   }
694   function get_credentials($url, $referrals= NULL)
695   {
696     $ret= array();
697     $url= preg_replace('!\?\?.*$!', '', $url);
698     $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
700     if ($referrals === NULL){
701       $referrals= $this->referrals;
702     }
704     if (isset($referrals[$server])){
705       return ($referrals[$server]);
706     } else {
707       $ret['ADMIN']= LDAP::fix($this->binddn);
708       $ret['PASSWORD']= $this->bindpw;
709     }
711     return ($ret);
712   }
715   function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
716   {
717     $display= "";
719     if ($recursive){
720       $this->cd($dn);
721       $this->ls($filter,$dn, array('dn','objectClass'));
722       $deps = array();
724       $display .= $this->gen_one_entry($dn)."\n";
726       while ($attrs= $this->fetch()){
727         $deps[] = $attrs['dn'];
728       }
729       foreach($deps as $dn){
730         $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
731       }
732     } else {
733       $display.= $this->gen_one_entry($dn);
734     }
735     return ($display);
736   }
739   function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
740   {
741     $display= array();
743       $this->cd($dn);
744       $this->search("$filter");
746       $i=0;
747       while ($attrs= $this->fetch()){
748         $j=0;
750         foreach ($attributes as $at){
751           $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
752           $j++;
753         }
755         $i++;
756       }
758     return ($display);
759   }
762   function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
763   {
764     $ret = "";
765     $data = "";
766     if($this->reconnect){
767       $this->connect();
768     }
770     /* Searching Ldap Tree */
771     $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
773     /* Get the first entry */   
774     $entry= @ldap_first_entry($this->cid, $sr);
776     /* Get all attributes related to that Objekt */
777     $atts = array();
778     
779     /* Assemble dn */
780     $atts[0]['name']  = "dn";
781     $atts[0]['value'] = array('count' => 1, 0 => $dn);
783     /* Reset index */
784     $i = 1 ; 
785   $identifier = array();
786     $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
787     while ($attribute) {
788       $i++;
789       $atts[$i]['name']  = $attribute;
790       $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
792       /* Next one */
793       $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
794     }
796     foreach($atts as $at)
797     {
798       for ($i= 0; $i<$at['value']['count']; $i++){
800         /* Check if we must encode the data */
801         if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
802           $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
803         } else {
804           $ret .= $at['name'].": ".$at['value'][$i]."\n";
805         }
806       }
807     }
809     return($ret);
810   }
813   function dn_exists($dn)
814   {
815     return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
816   }
817   
820   /*  This funktion imports ldifs 
821         
822       If DeleteOldEntries is true, the destination entry will be deleted first. 
823       If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
824       if JustMofify id false the destination dn will be overwritten by the new ldif. 
825     */
827   function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
828   {
829     if($this->reconnect) $this->connect();
831     /* First we have to splitt the string ito detect empty lines
832        An empty line indicates an new Entry */
833     $entries = split("\n",$str_attr);
835     $data = "";
836     $cnt = 0; 
837     $current_line = 0;
839     /* FIX ldif */
840     $last = "";
841     $tmp  = "";
842     $i = 0;
843     foreach($entries as $entry){
844       if(preg_match("/^ /",$entry)){
845         $tmp[$i] .= trim($entry);
846       }else{
847         $i ++;
848         $tmp[$i] = trim($entry);
849       }
850     }
852     /* Every single line ... */
853     foreach($tmp as $entry) {
854       $current_line ++;
856       /* Removing Spaces to .. 
857          .. test if a new entry begins */
858       $tmp  = str_replace(" ","",$data );
860       /* .. prevent empty lines in an entry */
861       $tmp2 = str_replace(" ","",$entry);
863       /* If the Block ends (Empty Line) */
864       if((empty($entry))&&(!empty($tmp))) {
865         /* Add collected lines as a complete block */
866         $all[$cnt] = $data;
867         $cnt ++;
868         $data ="";
869       } else {
871         /* Append lines ... */
872         if(!empty($tmp2)) {
873           /* check if we need base64_decode for this line */
874           if(ereg("::",$tmp2))
875           {
876             $encoded = split("::",$entry);
877             $attr  = trim($encoded[0]);
878             $value = base64_decode(trim($encoded[1]));
879             /* Add linenumber */
880             $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
881           }
882           else
883           {
884             /* Add Linenumber */ 
885             $data .= $current_line."#".base64_encode($entry)."\n";
886           }
887         }
888       }
889     }
891     /* The Data we collected is not in the array all[];
892        For example the Data is stored like this..
894        all[0] = "1#dn : .... \n 
895        2#ObjectType: person \n ...."
896        
897        Now we check every insertblock and try to insert */
898     foreach ( $all as $single) {
899       $lineone = split("\n",$single);  
900       $ndn = split("#", $lineone[0]);
901       $line = base64_decode($ndn[1]);
903       $dnn = split (":",$line,2);
904       $current_line = $ndn[0];
905       $dn    = $dnn[0];
906       $value = $dnn[1];
908       /* Every block must begin with a dn */
909       if($dn != "dn") {
910         $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
911         return -2;  
912       }
914       /* Should we use Modify instead of Add */
915       $usemodify= false;
917       /* Delete before insert */
918       $usermdir= false;
919     
920       /* The dn address already exists, Don't delete destination entry, overwrite it */
921       if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
923         $usermdir = $usemodify = false;
925       /* Delete old entry first, then add new */
926       } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
928         /* Delete first, then add */
929         $usermdir = true;        
931       } elseif(($this->dn_exists($value))&&($JustModify)) {
932         
933         /* Modify instead of Add */
934         $usemodify = true;
935       }
936      
937       /* If we can't Import, return with a file error */
938       if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
939         $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
940                         $current_line);
941         return UNKNOWN_TOKEN_IN_LDIF_FILE;      }
942     }
944     return (INSERT_OK);
945   }
948   /* Imports a single entry 
949       If $delete is true;  The old entry will be deleted if it exists.
950       if $modify is true;  All variables that are not touched by the new ldif will be kept.
951       if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
952   */
953   function import_single_entry($str_attr,$modify,$delete)
954   {
955     global $config;
957     if(!$config){
958       trigger_error("Can't import ldif, can't read config object.");
959     }
960   
962     if($this->reconnect) $this->connect();
964     $ret = false;
965     $rows= split("\n",$str_attr);
966     $data= false;
968     foreach($rows as $row) {
969       
970       /* Check if we use Linenumbers (when import_complete_ldif is called we use
971          Linenumbers) Linenumbers are use like this 123#attribute : value */
972       if(!empty($row)) {
973         if(strpos($row,"#")!=FALSE) {
975           /* We are using line numbers 
976              Because there is a # before a : */
977           $tmp1= split("#",$row);
978           $current_line= $tmp1[0];
979           $row= base64_decode($tmp1[1]);
980         }
982         /* Split the line into  attribute  and value */
983         $attr   = split(":", $row,2);
984         $attr[0]= trim($attr[0]);  /* attribute */
985         $attr[1]= $attr[1];  /* value */
987         /* Check :: was used to indicate base64_encoded strings */
988         if($attr[1][0] == ":"){
989           $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
990           $attr[1]=base64_decode($attr[1]);
991         }
993         $attr[1] = trim($attr[1]);
995         /* Check for attributes that are used more than once */
996         if(!isset($data[$attr[0]])) {
997           $data[$attr[0]]=$attr[1];
998         } else {
999           $tmp = $data[$attr[0]];
1001           if(!is_array($tmp)) {
1002             $new[0]=$tmp;
1003             $new[1]=$attr[1];
1004             $datas[$attr[0]]['count']=1;             
1005             $data[$attr[0]]=$new;
1006           } else {
1007             $cnt = $datas[$attr[0]]['count'];           
1008             $cnt ++;
1009             $data[$attr[0]][$cnt]=$attr[1];
1010             $datas[$attr[0]]['count'] = $cnt;
1011           }
1012         }
1013       }
1014     }
1016     /* If dn is an index of data, we should try to insert the data */
1017     if(isset($data['dn'])) {
1019       /* Fix dn */
1020       $tmp = gosa_ldap_explode_dn($data['dn']);
1021       unset($tmp['count']);
1022       $newdn ="";
1023       foreach($tmp as $tm){
1024         $newdn.= trim($tm).",";
1025       }
1026       $newdn = preg_replace("/,$/","",$newdn);
1027       $data['dn'] = $newdn;
1028    
1029       /* Creating Entry */
1030       $this->cd($data['dn']);
1032       /* Delete existing entry */
1033       if($delete){
1034         $this->rmdir_recursive($data['dn']);
1035       }
1036      
1037       /* Create missing trees */
1038       $this->cd ($this->basedn);
1039       $this->cd($config->current['BASE']);
1040       $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1041       $this->cd($data['dn']);
1043       $dn = $data['dn'];
1044       unset($data['dn']);
1045       
1046       if(!$modify){
1048         $this->cat($dn);
1049         if($this->count()){
1050         
1051           /* The destination entry exists, overwrite it with the new entry */
1052           $attrs = $this->fetch();
1053           foreach($attrs as $name => $value ){
1054             if(!is_numeric($name)){
1055               if(in_array($name,array("dn","count"))) continue;
1056               if(!isset($data[$name])){
1057                 $data[$name] = array();
1058               }
1059             }
1060           }
1061           $ret = $this->modify($data);
1062     
1063         }else{
1064     
1065           /* The destination entry doesn't exists, create it */
1066           $ret = $this->add($data);
1067         }
1069       } else {
1070         
1071         /* Keep all vars that aren't touched by this ldif */
1072         $ret = $this->modify($data);
1073       }
1074     }
1075     show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1076     return($ret);
1077   }
1079   
1080   function importcsv($str)
1081   {
1082     $lines = split("\n",$str);
1083     foreach($lines as $line)
1084     {
1085       /* continue if theres a comment */
1086       if(substr(trim($line),0,1)=="#"){
1087         continue;
1088       }
1090       $line= str_replace ("\t\t","\t",$line);
1091       $line= str_replace ("\t"  ,"," ,$line);
1092       echo $line;
1094       $cells = split(",",$line )  ;
1095       $linet= str_replace ("\t\t",",",$line);
1096       $cells = split("\t",$line);
1097       $count = count($cells);  
1098     }
1100   }
1101   
1102   function get_objectclasses()
1103   {
1104     $objectclasses = array();
1105     global $config;
1107     /* Only read schema if it is allowed */
1108     if(isset($config) && preg_match("/config/i",get_class($config))){
1109       if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1110         return($objectclasses);
1111       } 
1112     }
1113         
1114           # Get base to look for schema 
1115           $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1116     if(!$sr){
1117             $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1118     }
1120           $attr = @ldap_get_entries($this->cid,$sr);
1121           if (!isset($attr[0]['subschemasubentry'][0])){
1122             return array();
1123           }
1124         
1125           /* Get list of objectclasses and fill array */
1126           $nb= $attr[0]['subschemasubentry'][0];
1127           $objectclasses= array();
1128           $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1129           $attrs= ldap_get_entries($this->cid,$sr);
1130           if (!isset($attrs[0])){
1131             return array();
1132           }
1133           foreach ($attrs[0]['objectclasses'] as $val){
1134       if (preg_match('/^[0-9]+$/', $val)){
1135         continue;
1136       }
1137       $name= "OID";
1138       $pattern= split(' ', $val);
1139       $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1140       $objectclasses[$ocname]= array();
1142       foreach($pattern as $chunk){
1143         switch($chunk){
1145           case '(':
1146                     $value= "";
1147                     break;
1149           case ')': if ($name != ""){
1150                       $objectclasses[$ocname][$name]= $this->value2container($value);
1151                     }
1152                     $name= "";
1153                     $value= "";
1154                     break;
1156           case 'NAME':
1157           case 'DESC':
1158           case 'SUP':
1159           case 'STRUCTURAL':
1160           case 'ABSTRACT':
1161           case 'AUXILIARY':
1162           case 'MUST':
1163           case 'MAY':
1164                     if ($name != ""){
1165                       $objectclasses[$ocname][$name]= $this->value2container($value);
1166                     }
1167                     $name= $chunk;
1168                     $value= "";
1169                     break;
1171           default:  $value.= $chunk." ";
1172         }
1173       }
1175           }
1176           return $objectclasses;
1177   }
1180   function value2container($value)
1181   {
1182     /* Set emtpy values to "true" only */
1183     if (preg_match('/^\s*$/', $value)){
1184       return true;
1185     }
1187     /* Remove ' and " if needed */
1188     $value= preg_replace('/^[\'"]/', '', $value);
1189     $value= preg_replace('/[\'"] *$/', '', $value);
1191     /* Convert to array if $ is inside... */
1192     if (preg_match('/\$/', $value)){
1193       $container= preg_split('/\s*\$\s*/', $value);
1194     } else {
1195       $container= chop($value);
1196     }
1198     return ($container);
1199   }
1202   function log($string)
1203   {
1204     if (session::is_set('config')){
1205       $cfg = session::get('config');
1206       if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1207         syslog (LOG_INFO, $string);
1208       }
1209     }
1210   }
1212   /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1213   function getCn($dn){
1214     $simple= split(",", $dn);
1216     foreach($simple as $piece) {
1217       $partial= split("=", $piece);
1219       if($partial[0] == "cn"){
1220         return $partial[1];
1221       }
1222     }
1223   }
1226   function get_naming_contexts($server, $admin= "", $password= "")
1227   {
1228     /* Build LDAP connection */
1229     $ds= ldap_connect ($server);
1230     if (!$ds) {
1231       die ("Can't bind to LDAP. No check possible!");
1232     }
1233     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1234     $r= ldap_bind ($ds, $admin, $password);
1236     /* Get base to look for naming contexts */
1237     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1238     $attr= @ldap_get_entries($ds,$sr);
1240     return ($attr[0]['namingcontexts']);
1241   }
1244   function get_root_dse($server, $admin= "", $password= "")
1245   {
1246     /* Build LDAP connection */
1247     $ds= ldap_connect ($server);
1248     if (!$ds) {
1249       die ("Can't bind to LDAP. No check possible!");
1250     }
1251     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1252     $r= ldap_bind ($ds, $admin, $password);
1254     /* Get base to look for naming contexts */
1255     $sr  = @ldap_read ($ds, "", "objectClass=*", array("+"));
1256     $attr= @ldap_get_entries($ds,$sr);
1257    
1258     /* Return empty array, if nothing was set */
1259     if (!isset($attr[0])){
1260       return array();
1261     }
1263     /* Rework array... */
1264     $result= array();
1265     for ($i= 0; $i<$attr[0]['count']; $i++){
1266       $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1267       unset($result[$attr[0][$i]]['count']);
1268     }
1270     return ($result);
1271   }
1273 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1274 ?>