1 <?php
2 /*****************************************************************************
3 newldap.inc - version 1.0
4 Copyright (C) 2003 Alejandro Escanero Blanco <alex@ofmin.com>
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 var $re = NULL;
37 function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
38 {
39 global $config;
40 $this->follow_referral= $follow_referral;
41 $this->tls=$tls;
42 $this->binddn=$this->convert($binddn);
44 $this->bindpw=$bindpw;
45 $this->hostname=$hostname;
47 /* Check if MAX_LDAP_QUERY_TIME is defined */
48 if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
49 $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
50 $this->max_ldap_query_time = (float)($str);
51 }
53 $this->connect();
54 }
57 /* Function to replace all problematic characters inside a DN by \001XX, where
58 \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
59 that this character is inside a DN.
61 Currently used codes:
62 , => CO
63 \2C => CO
64 \22 => TO
65 \" => TO
66 \+ => PL
67 ( => OB
68 ) => CB
69 / => SL */
70 function convert($dn)
71 {
72 if (SPECIALS_OVERRIDE == TRUE){
73 $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//", "/\\\\22/", '/\\\\"/', "/\\\\3D/", "/\\\\2B/", '/\\\\+/'),
74 array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL", "\001TO", "\001TO", "\001GL", "\001PL", "\001PL"),
75 $dn);
76 return (preg_replace('/,\s+/', ',', $tmp));
77 } else {
78 return ($dn);
79 }
80 }
83 /* Function to fix all problematic characters inside a DN by replacing \001XX
84 codes to their original values. See "convert" for mor information.
85 ',' characters are always expanded to \, (not \2C), since all tested LDAP
86 servers seem to take it the correct way. */
87 function fix($dn)
88 {
89 if (SPECIALS_OVERRIDE == TRUE){
90 return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/", "/\001TO/", "/\001PL/", "/\001GL/"),
91 array("\,", "(", ")", "/", '\"', "\+", "="),
92 $dn));
93 } else {
94 return ($dn);
95 }
96 }
99 /* Function to fix problematic characters in DN's that are used for search
100 requests. I.e. member=.... */
101 function prepare4filter($dn)
102 {
103 return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', @LDAP::fix($dn)));
104 }
107 function connect()
108 {
109 $this->hascon=false;
110 $this->reconnect=false;
111 if ($this->cid= @ldap_connect($this->hostname)) {
112 @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
113 if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
114 @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
115 @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
116 }
117 if (function_exists("ldap_start_tls") && $this->tls){
118 @ldap_start_tls($this->cid);
119 }
121 $this->error = "No Error";
122 if ($bid = @ldap_bind($this->cid, $this->fix($this->binddn), $this->bindpw)) {
123 $this->error = "Success";
124 $this->hascon=true;
125 } else {
126 if ($this->reconnect){
127 if ($this->error != "Success"){
128 $this->error = "Could not rebind to " . $this->binddn;
129 }
130 } else {
131 $this->error = "Could not bind to " . $this->binddn;
132 }
133 }
134 } else {
135 $this->error = "Could not connect to LDAP server";
136 }
137 }
139 function rebind($ldap, $referral)
140 {
141 $credentials= $this->get_credentials($referral);
142 if (@ldap_bind($ldap, $this->fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
143 $this->error = "Success";
144 $this->hascon=true;
145 $this->reconnect= true;
146 return (0);
147 } else {
148 $this->error = "Could not bind to " . $credentials['ADMIN'];
149 return NULL;
150 }
151 }
153 function reconnect()
154 {
155 if ($this->reconnect){
156 @ldap_unbind($this->cid);
157 $this->cid = NULL;
158 }
159 }
161 function unbind()
162 {
163 @ldap_unbind($this->cid);
164 $this->cid = NULL;
165 }
167 function disconnect()
168 {
169 if($this->hascon){
170 @ldap_close($this->cid);
171 $this->hascon=false;
172 }
173 }
175 function cd($dir)
176 {
177 if ($dir == "..")
178 $this->basedn = $this->getParentDir();
179 else
180 $this->basedn = $this->convert($dir);
181 }
183 function getParentDir($basedn = "")
184 {
185 if ($basedn=="")
186 $basedn = $this->basedn;
187 else
188 $basedn = $this->convert($this->basedn);
189 return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
190 }
192 function search($filter, $attrs= array())
193 {
194 if($this->hascon){
195 if ($this->reconnect) $this->connect();
197 $start = microtime();
198 $this->clearResult();
199 $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
200 $this->error = @ldap_error($this->cid);
201 $this->resetResult();
202 $this->hasres=true;
204 /* Check if query took longer as specified in max_ldap_query_time */
205 if($this->max_ldap_query_time){
206 $diff = get_MicroTimeDiff($start,microtime());
207 if($diff > $this->max_ldap_query_time){
208 print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
209 }
210 }
212 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
214 return($this->sr);
215 }else{
216 $this->error = "Could not connect to LDAP server";
217 return("");
218 }
219 }
221 function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
222 {
223 if($this->hascon){
224 if ($this->reconnect) $this->connect();
226 $this->clearResult();
227 if ($basedn == "")
228 $basedn = $this->basedn;
229 else
230 $basedn= $this->convert($basedn);
232 $start = microtime();
234 $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
235 $this->error = @ldap_error($this->cid);
236 $this->resetResult();
237 $this->hasres=true;
239 /* Check if query took longer as specified in max_ldap_query_time */
240 if($this->max_ldap_query_time){
241 $diff = get_MicroTimeDiff($start,microtime());
242 if($diff > $this->max_ldap_query_time){
243 print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
244 }
245 }
247 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
249 return($this->sr);
250 }else{
251 $this->error = "Could not connect to LDAP server";
252 return("");
253 }
254 }
256 function cat($dn,$attrs= array("*"))
257 {
258 if($this->hascon){
259 if ($this->reconnect) $this->connect();
261 $start = microtime();
262 $this->clearResult();
263 $filter = "(objectclass=*)";
264 $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
265 $this->error = @ldap_error($this->cid);
266 $this->resetResult();
267 $this->hasres=true;
268 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=cat('".$this->fix($dn)."')");
269 return($this->sr);
270 }else{
271 $this->error = "Could not connect to LDAP server";
272 return("");
273 }
274 }
276 function set_size_limit($size)
277 {
278 /* Ignore zero settings */
279 if ($size == 0){
280 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
281 }
282 if($this->hascon){
283 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
284 } else {
285 $this->error = "Could not connect to LDAP server";
286 }
287 }
289 function fetch()
290 {
291 $att= array();
292 if($this->hascon){
293 if($this->hasres){
294 if ($this->start == 0)
295 {
296 $this->start = 1;
297 $this->re= @ldap_first_entry($this->cid, $this->sr);
298 } else {
299 $this->re= @ldap_next_entry($this->cid, $this->re);
300 }
301 if ($this->re)
302 {
303 $att= @ldap_get_attributes($this->cid, $this->re);
304 $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
305 }
306 $this->error = @ldap_error($this->cid);
307 if (!isset($att)){
308 $att= array();
309 }
310 return($att);
311 }else{
312 $this->error = "Perform a Fetch with no Search";
313 return("");
314 }
315 }else{
316 $this->error = "Could not connect to LDAP server";
317 return("");
318 }
319 }
321 function resetResult()
322 {
323 $this->start = 0;
324 }
326 function clearResult()
327 {
328 if($this->hasres){
329 $this->hasres = false;
330 @ldap_free_result($this->sr);
331 }
332 }
334 function getDN()
335 {
336 if($this->hascon){
337 if($this->hasres){
339 if(!$this->re)
340 {
341 $this->error = "Perform a Fetch with no valid Result";
342 }
343 else
344 {
345 $rv = @ldap_get_dn($this->cid, $this->re);
347 $this->error = @ldap_error($this->cid);
348 return(trim($this->convert($rv)));
349 }
350 }else{
351 $this->error = "Perform a Fetch with no Search";
352 return("");
353 }
354 }else{
355 $this->error = "Could not connect to LDAP server";
356 return("");
357 }
358 }
360 function count()
361 {
362 if($this->hascon){
363 if($this->hasres){
364 $rv = @ldap_count_entries($this->cid, $this->sr);
365 $this->error = @ldap_error($this->cid);
366 return($rv);
367 }else{
368 $this->error = "Perform a Fetch with no Search";
369 return("");
370 }
371 }else{
372 $this->error = "Could not connect to LDAP server";
373 return("");
374 }
375 }
377 function rm($attrs = "", $dn = "")
378 {
379 if($this->hascon){
380 if ($this->reconnect) $this->connect();
381 if ($dn == "")
382 $dn = $this->basedn;
384 $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
385 $this->error = @ldap_error($this->cid);
386 return($r);
387 }else{
388 $this->error = "Could not connect to LDAP server";
389 return("");
390 }
391 }
393 function rename($attrs, $dn = "")
394 {
395 if($this->hascon){
396 if ($this->reconnect) $this->connect();
397 if ($dn == "")
398 $dn = $this->basedn;
400 $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
401 $this->error = @ldap_error($this->cid);
402 return($r);
403 }else{
404 $this->error = "Could not connect to LDAP server";
405 return("");
406 }
407 }
409 function rmdir($deletedn)
410 {
411 if($this->hascon){
412 if ($this->reconnect) $this->connect();
413 $start= microtime();
414 $r = @ldap_delete($this->cid, $this->fix($deletedn));
415 $this->error = @ldap_error($this->cid);
416 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=delete('".$this->fix($deletedn)."')");
417 return($r ? $r : 0);
418 }else{
419 $this->error = "Could not connect to LDAP server";
420 return("");
421 }
422 }
424 /**
425 * Function rmdir_recursive
426 *
427 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
428 * Parameters: The dn to delete
429 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
430 *
431 */
433 function rmdir_recursive($deletedn)
434 {
435 if($this->hascon){
436 if ($this->reconnect) $this->connect();
437 $delarray= array();
439 /* Get sorted list of dn's to delete */
440 $this->ls ("(objectClass=*)",$deletedn);
441 while ($this->fetch()){
442 $deldn= $this->getDN();
443 $delarray[$deldn]= strlen($deldn);
444 }
445 arsort ($delarray);
446 reset ($delarray);
448 /* Really Delete ALL dn's in subtree */
449 foreach ($delarray as $key => $value){
450 $this->rmdir_recursive($key);
451 }
453 /* Finally Delete own Node */
454 $r = @ldap_delete($this->cid, $this->fix($deletedn));
455 $this->error = @ldap_error($this->cid);
456 return($r ? $r : 0);
457 }else{
458 $this->error = "Could not connect to LDAP server";
459 return("");
460 }
461 }
463 /* Copy given attributes and sub-dns with attributes to destination dn
464 */
465 function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
466 {
467 error_reporting(E_ALL);
469 if($is_first){
470 echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
471 }else{
472 if(preg_match("/^ou=/",$sourcedn)){
473 echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
474 }else{
475 $tmp = split(",",$sourcedn);
477 echo " <b>"._("Object").":</b> ";
479 $deststr = @LDAP::fix($destinationdn);
480 if(strlen($deststr) > 96){
481 $deststr = substr($deststr,0,96)."...";
482 }
484 echo $deststr."<br>";
485 }
486 }
488 flush();
490 if($this->hascon){
491 if ($this->reconnect) $this->connect();
493 /* Save base dn */
494 $basedn= $this->basedn;
495 $delarray= array();
497 /* Check if destination entry already exists */
498 $this->cat($destinationdn);
500 if($this->count()){
501 return;
502 }else{
504 $this->clearResult();
506 /* Get source entry */
507 $this->cd($basedn);
508 $this->cat($sourcedn);
509 $attr = $this->fetch();
511 /* Error while fetching object / attribute abort*/
512 if((!$attr) || (count($attr)) ==0) {
513 echo _("Error while fetching source dn - aborted!");
514 return;
515 }
517 /* check if this is a department */
518 if(in_array("organizationalUnit",$attr['objectClass'])){
519 $attr['dn'] = $this->convert($destinationdn);
520 $this->cd($basedn);
521 $this->create_missing_trees($destinationdn);
522 $this->cd($destinationdn);
524 /* If is first entry, append FAIbranch to department entry */
525 if($is_first){
526 $this->cat($destinationdn);
527 $attr= $this->fetch();
529 /* Filter unneeded informations */
530 foreach($attr as $key => $value){
531 if(is_numeric($key)) unset($attr[$key]);
532 if(isset($attr[$key]['count'])){
533 if(is_array($attr[$key])){
534 unset($attr[$key]['count']);
535 }
536 }
537 }
539 unset($attr['count']);
540 unset($attr['dn']);
542 /* Add marking attribute */
543 $attr['objectClass'][] = "FAIbranch";
545 /* Add this entry */
546 $this->modify($attr);
547 }
548 }else{
550 /* If this is no department */
551 foreach($attr as $key => $value){
552 if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon"))){
553 $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
554 $ei= ldap_first_entry($this->cid, $sr);
555 if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
556 $attr[$key] = $tmp;
557 }
558 }
560 if(is_numeric($key)) unset($attr[$key]);
561 if(isset($attr[$key]['count'])){
562 if(is_array($attr[$key])){
563 unset($attr[$key]['count']);
564 }
565 }
566 }
567 unset($attr['count']);
568 unset($attr['dn']);
570 if(!in_array("gosaApplication" , $attr['objectClass'])){
571 if($type=="branch"){
572 $attr['FAIstate'] ="branch";
573 }elseif($type=="freeze"){
574 $attr['FAIstate'] ="freeze";
575 }else{
576 print_red(_("Unknown FAIstate %s"),$type);
577 }
578 }elseif(in_array("gosaApplication",$attr['objectClass'])){
579 if(!in_array("FAIobject",$attr['objectClass'])){
580 $attr['objectClass'][] = "FAIobject";
581 }
582 $attr['FAIstate'] = $type;
583 }
585 /* Replace FAIdebianRelease with new release name */
586 if(in_array("FAIpackageList" , $attr['objectClass'])){
587 $attr['FAIdebianRelease'] = $destinationName;
588 if($type=="branch"){
589 $attr['FAIstate'] ="branch";
590 }elseif($type=="freeze"){
591 $attr['FAIstate'] ="freeze";
592 }else{
593 print_red(_("Unknown FAIstate %s"),$type);
594 }
595 }
597 /* Add entry */
598 $this->cd($destinationdn);
599 $this->cat($destinationdn);
600 $a = $this->fetch();
601 if(!count($a)){
602 $this->add($attr);
603 }
605 if($this->error != "Success"){
606 /* Some error occurred */
607 print "---------------------------------------------";
608 print $this->get_error()."<br>";
609 print $sourcedn."<br>";
610 print $destinationdn."<br>";
611 print_a( $attr);
612 exit();
613 }
614 }
615 }
617 echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
619 $this->ls ("(objectClass=*)",$sourcedn);
620 while ($this->fetch()){
621 $deldn= $this->getDN();
622 $delarray[$deldn]= strlen($deldn);
623 }
624 asort ($delarray);
625 reset ($delarray);
627 $depth ++;
628 foreach($delarray as $dn => $bla){
629 if($dn != $destinationdn){
630 $this->cd($basedn);
631 $item = $this->fetch($this->cat($dn));
632 if(!in_array("FAIbranch",$item['objectClass'])){
633 $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
634 }
635 }
636 }
637 }
638 if($is_first){
639 echo "<p class='seperator'> </p>";
640 }
642 }
644 function modify($attrs)
645 {
646 if(count($attrs) == 0){
647 return (0);
648 }
649 if($this->hascon){
650 if ($this->reconnect) $this->connect();
651 $start= microtime();
652 $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
653 $this->error = @ldap_error($this->cid);
654 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=modify('$this->basedn')");
655 return($r ? $r : 0);
656 }else{
657 $this->error = "Could not connect to LDAP server";
658 return("");
659 }
660 }
662 function add($attrs)
663 {
664 if($this->hascon){
665 if ($this->reconnect) $this->connect();
666 $start= microtime();
667 $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
668 $this->error = @ldap_error($this->cid);
669 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=add('$this->basedn')");
670 return($r ? $r : 0);
671 }else{
672 $this->error = "Could not connect to LDAP server";
673 return("");
674 }
675 }
678 function create_missing_trees($target)
679 {
680 global $config;
682 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
684 if ($target == $this->basedn){
685 $l= array("dummy");
686 } else {
687 $l= array_reverse(gosa_ldap_explode_dn($real_path));
688 }
689 unset($l['count']);
690 $cdn= $this->basedn;
691 $tag= "";
693 /* Load schema if available... */
694 $classes= $this->get_objectclasses();
696 foreach ($l as $part){
697 if ($part != "dummy"){
698 $cdn= "$part,$cdn";
699 }
701 /* Ignore referrals */
702 $found= false;
703 foreach($this->referrals as $ref){
704 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
705 if ($base == $cdn){
706 $found= true;
707 break;
708 }
709 }
710 if ($found){
711 continue;
712 }
714 $this->cat ($cdn);
715 $attrs= $this->fetch();
717 /* Create missing entry? */
718 if (count ($attrs)){
719 /* Catch the tag - if present */
720 if (isset($attrs['gosaUnitTag'][0])){
721 $tag= $attrs['gosaUnitTag'][0];
722 }
724 } else {
725 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
726 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
728 $na= array();
730 /* Automatic or traditional? */
731 if(count($classes)){
733 /* Get name of first matching objectClass */
734 $ocname= "";
735 foreach($classes as $class){
736 if (isset($class['MUST']) && $class['MUST'] == "$type"){
738 /* Look for first classes that is structural... */
739 if (isset($class['STRUCTURAL'])){
740 $ocname= $class['NAME'];
741 break;
742 }
744 /* Look for classes that are auxiliary... */
745 if (isset($class['AUXILIARY'])){
746 $ocname= $class['NAME'];
747 }
748 }
749 }
751 /* Bail out, if we've nothing to do... */
752 if ($ocname == ""){
753 print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
754 echo $_SESSION['errors'];
755 exit;
756 }
758 /* Assemble_entry */
759 if ($tag != ""){
760 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
761 $na["gosaUnitTag"]= $tag;
762 } else {
763 $na['objectClass']= array($ocname);
764 }
765 if (isset($classes[$ocname]['AUXILIARY'])){
766 $na['objectClass'][]= $classes[$ocname]['SUP'];
767 }
768 if ($type == "dc"){
769 /* This is bad actually, but - tell me a better way? */
770 $na['objectClass'][]= 'locality';
771 }
772 $na[$type]= $param;
773 if (is_array($classes[$ocname]['MUST'])){
774 foreach($classes[$ocname]['MUST'] as $attr){
775 $na[$attr]= "filled";
776 }
777 }
779 } else {
781 /* Use alternative add... */
782 switch ($type){
783 case 'ou':
784 if ($tag != ""){
785 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
786 $na["gosaUnitTag"]= $tag;
787 } else {
788 $na["objectClass"]= "organizationalUnit";
789 }
790 $na["ou"]= $param;
791 break;
792 case 'dc':
793 if ($tag != ""){
794 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
795 $na["gosaUnitTag"]= $tag;
796 } else {
797 $na["objectClass"]= array("dcObject", "top", "locality");
798 }
799 $na["dc"]= $param;
800 break;
801 default:
802 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
803 echo $_SESSION['errors'];
804 exit;
805 }
807 }
808 $this->cd($cdn);
809 $this->add($na);
810 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
811 if (!preg_match('/success/i', $this->error)){
812 return FALSE;
813 }
814 }
815 }
817 return TRUE;
818 }
821 function create_missing_trees_old($target)
822 {
823 /* Ignore create_missing trees if the base equals target */
824 if ($target == $this->basedn){
825 return;
826 }
828 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
829 $tmp = ldap_explode_dn($real_path,0);
830 if(!$tmp){
831 print_red(sprintf(_("The referral url '%s' is missing the ldap base. It should look like this 'ldap://server:port/base'."),$this->fix($this->basedn)));
832 return;
833 }
835 $l= array_reverse($tmp);
836 unset($l['count']);
837 $cdn= $this->basedn;
838 $tag= "";
840 foreach ($l as $part){
841 $cdn= "$part,$cdn";
843 /* Ignore referrals */
844 $found= false;
845 foreach($this->referrals as $ref){
846 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
847 if ($base == $cdn){
848 $found= true;
849 break;
850 }
851 }
852 if ($found){
853 continue;
854 }
856 $this->cat ($cdn);
857 $attrs= $this->fetch();
859 /* Create missing entry? */
860 if (count ($attrs)){
862 /* Catch the tag - if present */
863 if (isset($attrs['gosaUnitTag'][0])){
864 $tag= $attrs['gosaUnitTag'][0];
865 }
867 } else {
868 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
869 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
871 $na= array();
872 switch ($type){
873 case 'ou':
874 if ($tag != ""){
875 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
876 $na["gosaUnitTag"]= $tag;
877 } else {
878 $na["objectClass"]= "organizationalUnit";
879 }
880 $na["ou"]= $param;
881 break;
882 case 'dc':
883 if ($tag != ""){
884 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
885 $na["gosaUnitTag"]= $tag;
886 } else {
887 $na["objectClass"]= array("dcObject", "top", "locality");
888 }
889 $na["dc"]= $param;
890 break;
891 default:
892 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
893 echo $_SESSION['errors'];
894 exit;
895 }
896 $this->cd($cdn);
897 $this->add($na);
898 }
899 }
900 }
902 function recursive_remove()
903 {
904 $delarray= array();
906 /* Get sorted list of dn's to delete */
907 $this->search ("(objectClass=*)");
908 while ($this->fetch()){
909 $deldn= $this->getDN();
910 $delarray[$deldn]= strlen($deldn);
911 }
912 arsort ($delarray);
913 reset ($delarray);
915 /* Delete all dn's in subtree */
916 foreach ($delarray as $key => $value){
917 $this->rmdir($key);
918 }
919 }
921 function get_attribute($dn, $name,$r_array=0)
922 {
923 $data= "";
924 if ($this->reconnect) $this->connect();
925 $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
927 /* fill data from LDAP */
928 if ($sr) {
929 $ei= @ldap_first_entry($this->cid, $sr);
930 if ($ei) {
931 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
932 $data= $info[0];
933 }
935 }
936 }
937 if($r_array==0)
938 return ($data);
939 else
940 return ($info);
943 }
947 function get_additional_error()
948 {
949 $error= "";
950 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
951 return ($error);
952 }
954 function get_error()
955 {
956 if ($this->error == 'Success'){
957 return $this->error;
958 } else {
959 $adderror= $this->get_additional_error();
960 if ($adderror != ""){
961 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
962 } else {
963 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
964 }
965 return $error;
966 }
967 }
969 function get_credentials($url, $referrals= NULL)
970 {
971 $ret= array();
972 $url= preg_replace('!\?\?.*$!', '', $url);
973 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
975 if ($referrals == NULL){
976 $referrals= $this->referrals;
977 }
979 if (isset($referrals[$server])){
980 return ($referrals[$server]);
981 } else {
982 $ret['ADMIN']= $this->fix($this->binddn);
983 $ret['PASSWORD']= $this->bindpw;
984 }
986 return ($ret);
987 }
990 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
991 {
992 $display= "";
994 if ($recursive){
995 $this->cd($dn);
996 $this->search("$filter", array('dn'));
997 while ($attrs= $this->fetch()){
998 $display.= $this->gen_one_entry($attrs['dn'], $filter, $attributes);
999 $display.= "\n";
1000 }
1001 } else {
1002 $display.= $this->gen_one_entry($dn);
1003 }
1005 return ($display);
1006 }
1008 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1009 {
1010 $display= "";
1012 $this->cd($dn);
1013 $this->search("$filter");
1015 $i=0;
1016 while ($attrs= $this->fetch()){
1017 $j=0;
1019 foreach ($attributes as $at){
1020 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1021 $j++;
1022 }
1024 $i++;
1025 }
1027 return ($display);
1028 }
1031 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
1032 {
1033 $ret = "";
1034 $data = "";
1035 if($this->reconnect){
1036 $this->connect();
1037 }
1039 /* Searching Ldap Tree */
1040 $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
1042 /* Get the first entry */
1043 $entry= @ldap_first_entry($this->cid, $sr);
1045 /* Get all attributes related to that Objekt */
1046 $atts = array();
1048 /* Assemble dn */
1049 $atts[0]['name'] = "dn";
1050 $atts[0]['value'] = array('count' => 1, 0 => $dn);
1052 /* Reset index */
1053 $i = 1 ;
1054 $identifier = array();
1055 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
1056 while ($attribute) {
1057 $i++;
1058 $atts[$i]['name'] = $attribute;
1059 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
1061 /* Next one */
1062 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
1063 }
1065 foreach($atts as $at)
1066 {
1067 for ($i= 0; $i<$at['value']['count']; $i++){
1069 /* Check if we must encode the data */
1070 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
1071 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
1072 } else {
1073 $ret .= $at['name'].": ".$at['value'][$i]."\n";
1074 }
1075 }
1076 }
1078 return($ret);
1079 }
1082 function dn_exists($dn)
1083 {
1084 return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
1085 }
1089 function import_complete_ldif($str_attr,&$error,$overwrite,$cleanup)
1090 {
1091 if($this->reconnect) $this->connect();
1093 /* First we have to splitt the string ito detect empty lines
1094 An empty line indicates an new Entry */
1095 $entries = split("\n",$str_attr);
1097 $data = "";
1098 $cnt = 0;
1099 $current_line = 0;
1101 /* Every single line ... */
1102 foreach($entries as $entry) {
1103 $current_line ++;
1105 /* Removing Spaces to ..
1106 .. test if a new entry begins */
1107 $tmp = str_replace(" ","",$data );
1109 /* .. prevent empty lines in an entry */
1110 $tmp2 = str_replace(" ","",$entry);
1112 /* If the Block ends (Empty Line) */
1113 if((empty($entry))&&(!empty($tmp))) {
1114 /* Add collected lines as a complete block */
1115 $all[$cnt] = $data;
1116 $cnt ++;
1117 $data ="";
1118 } else {
1120 /* Append lines ... */
1121 if(!empty($tmp2)) {
1122 /* check if we need base64_decode for this line */
1123 if(ereg("::",$tmp2))
1124 {
1125 $encoded = split("::",$entry);
1126 $attr = $encoded[0];
1127 $value = base64_decode($encoded[1]);
1128 /* Add linenumber */
1129 $data .= $current_line."#".$attr.":".$value."\n";
1130 }
1131 else
1132 {
1133 /* Add Linenumber */
1134 $data .= $current_line."#".$entry."\n";
1135 }
1136 }
1137 }
1138 }
1140 /* The Data we collected is not in the array all[];
1141 For example the Data is stored like this..
1143 all[0] = "1#dn : .... \n
1144 2#ObjectType: person \n ...."
1146 Now we check every insertblock and try to insert */
1147 foreach ( $all as $single) {
1148 $lineone = split("\n",$single);
1149 $ndn = split("#", $lineone[0]);
1150 $line = $ndn[1];
1152 $dnn = split (":",$line,2);
1153 $current_line = $ndn[0];
1154 $dn = $dnn[0];
1155 $value = $dnn[1];
1157 /* Every block must begin with a dn */
1158 if($dn != "dn") {
1159 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1160 return -2;
1161 }
1163 /* Should we use Modify instead of Add */
1164 $usemodify= false;
1166 /* Delete before insert */
1167 $usermdir= false;
1169 /* The dn address already exists! */
1170 if (($this->dn_exists($value))&&((!$overwrite)&&(!$cleanup))) {
1172 $error= sprintf(_("The dn: '%s' (from line %s) already exists in the LDAP database."), $line, $current_line);
1173 return ALREADY_EXISTING_ENTRY;
1175 } elseif(($this->dn_exists($value))&&($cleanup)){
1177 /* Delete first, then add */
1178 $usermdir = true;
1180 } elseif(($this->dn_exists($value))&&($overwrite)) {
1182 /* Modify instead of Add */
1183 $usemodify = true;
1184 }
1186 /* If we can't Import, return with a file error */
1187 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1188 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1189 $current_line);
1190 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
1191 }
1193 return (INSERT_OK);
1194 }
1197 /* Imports a single entry */
1198 function import_single_entry($str_attr,$modify,$delete)
1199 {
1200 if($this->reconnect) $this->connect();
1202 $ret = false;
1203 $rows= split("\n",$str_attr);
1204 $data= false;
1206 foreach($rows as $row) {
1208 /* Check if we use Linenumbers (when import_complete_ldif is called we use
1209 Linenumbers) Linenumbers are use like this 123#attribute : value */
1210 if(!empty($row)) {
1211 if((strpos($row,"#")!=FALSE)&&(strpos($row,"#")<strpos($row,":"))) {
1213 /* We are using line numbers
1214 Because there is a # before a : */
1215 $tmp1= split("#",$row);
1216 $current_line= $tmp1[0];
1217 $row= $tmp1[1];
1218 }
1220 /* Split the line into attribute and value */
1221 $attr = split(":", $row,2);
1222 $attr[0]= trim($attr[0]); /* attribute */
1223 $attr[1]= trim($attr[1]); /* value */
1225 /* Check for attributes that are used more than once */
1226 if(!isset($data[$attr[0]])) {
1227 $data[$attr[0]]=$attr[1];
1228 } else {
1229 $tmp = $data[$attr[0]];
1231 if(!is_array($tmp)) {
1232 $new[0]=$tmp;
1233 $new[1]=$attr[1];
1234 $datas[$attr[0]]['count']=1;
1235 $data[$attr[0]]=$new;
1236 } else {
1237 $cnt = $datas[$attr[0]]['count'];
1238 $cnt ++;
1239 $data[$attr[0]][$cnt]=$attr[1];
1240 $datas[$attr[0]]['count'] = $cnt;
1241 }
1242 }
1243 }
1244 }
1246 /* If dn is an index of data, we should try to insert the data */
1247 if(isset($data['dn'])) {
1248 /* Creating Entry */
1249 $this->cd($data['dn']);
1251 /* Delete existing entry */
1252 if($delete){
1253 $this->rmdir($data['dn']);
1254 }
1256 /* Create missing trees */
1257 $this->create_missing_trees($data['dn']);
1258 unset($data['dn']);
1260 /* If entry exists use modify */
1261 if(!$modify){
1262 $ret = $this->add($data);
1263 } else {
1264 $ret = $this->modify($data);
1265 }
1266 }
1268 return($ret);
1269 }
1272 function importcsv($str)
1273 {
1274 $lines = split("\n",$str);
1275 foreach($lines as $line)
1276 {
1277 /* continue if theres a comment */
1278 if(substr(trim($line),0,1)=="#"){
1279 continue;
1280 }
1282 $line= str_replace ("\t\t","\t",$line);
1283 $line= str_replace ("\t" ,"," ,$line);
1284 echo $line;
1286 $cells = split(",",$line ) ;
1287 $linet= str_replace ("\t\t",",",$line);
1288 $cells = split("\t",$line);
1289 $count = count($cells);
1290 }
1292 }
1294 function get_objectclasses_old()
1295 {
1296 $objectclasses = array();
1298 # Get base to look for schema
1299 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1300 $attr = @ldap_get_entries($this->cid,$sr);
1302 if (!isset($attr[0]['subschemasubentry'][0])){
1303 $this->error = @ldap_error($this->cid);
1304 gosa_log($this->get_error());
1305 return array();
1306 }
1308 # Get list of objectclasses
1309 $nb= $attr[0]['subschemasubentry'][0];
1310 $objectclasses= array();
1311 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1312 $attrs= ldap_get_entries($this->cid,$sr);
1313 if (!isset($attrs[0])){
1314 return array();
1315 }
1316 foreach ($attrs[0]['objectclasses'] as $val){
1317 $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1318 if ($name != $val){
1319 $objectclasses[$name]= $val;
1320 }
1321 }
1323 return $objectclasses;
1324 }
1327 function get_objectclasses()
1328 {
1329 global $config;
1330 $objectclasses = array();
1332 if(isset($config) && preg_match("/config/i",get_class($config))){
1333 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1334 return($objectclasses);
1335 }
1336 }
1338 # Get base to look for schema
1339 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1340 if(!$sr){
1341 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1342 }
1344 $attr = @ldap_get_entries($this->cid,$sr);
1345 if (!isset($attr[0]['subschemasubentry'][0])){
1346 return array();
1347 }
1349 /* Get list of objectclasses and fill array */
1350 $nb= $attr[0]['subschemasubentry'][0];
1351 $objectclasses= array();
1352 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1353 $attrs= ldap_get_entries($this->cid,$sr);
1354 if (!isset($attrs[0])){
1355 return array();
1356 }
1357 foreach ($attrs[0]['objectclasses'] as $val){
1358 if (preg_match('/^[0-9]+$/', $val)){
1359 continue;
1360 }
1361 $name= "OID";
1362 $pattern= split(' ', $val);
1363 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1364 $objectclasses[$ocname]= array();
1366 foreach($pattern as $chunk){
1367 switch($chunk){
1369 case '(':
1370 $value= "";
1371 break;
1373 case ')': if ($name != ""){
1374 $objectclasses[$ocname][$name]= $this->value2container($value);
1375 }
1376 $name= "";
1377 $value= "";
1378 break;
1380 case 'NAME':
1381 case 'DESC':
1382 case 'SUP':
1383 case 'STRUCTURAL':
1384 case 'ABSTRACT':
1385 case 'AUXILIARY':
1386 case 'MUST':
1387 case 'MAY':
1388 if ($name != ""){
1389 $objectclasses[$ocname][$name]= $this->value2container($value);
1390 }
1391 $name= $chunk;
1392 $value= "";
1393 break;
1395 default: $value.= $chunk." ";
1396 }
1397 }
1399 }
1401 return $objectclasses;
1402 }
1404 function value2container($value)
1405 {
1406 /* Set emtpy values to "true" only */
1407 if (preg_match('/^\s*$/', $value)){
1408 return true;
1409 }
1411 /* Remove ' and " if needed */
1412 $value= preg_replace('/^[\'"]/', '', $value);
1413 $value= preg_replace('/[\'"] *$/', '', $value);
1415 /* Convert to array if $ is inside... */
1416 if (preg_match('/\$/', $value)){
1417 $container= preg_split('/\s*\$\s*/', $value);
1418 } else {
1419 $container= chop($value);
1420 }
1422 return ($container);
1423 }
1425 function log($string)
1426 {
1427 if (isset($_SESSION['config'])){
1428 $cfg= $_SESSION['config'];
1429 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1430 syslog (LOG_INFO, $string);
1431 }
1432 }
1433 }
1435 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1436 function getCn($dn){
1437 $simple= split(",", $dn);
1439 foreach($simple as $piece) {
1440 $partial= split("=", $piece);
1442 if($partial[0] == "cn"){
1443 return $partial[1];
1444 }
1445 }
1447 }
1449 function get_naming_contexts($server, $admin= "", $password= "")
1450 {
1451 /* Build LDAP connection */
1452 $ds= ldap_connect ($server);
1453 if (!$ds) {
1454 die ("Can't bind to LDAP. No check possible!");
1455 }
1456 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1457 $r= ldap_bind ($ds, $admin, $password);
1459 /* Get base to look for naming contexts */
1460 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1461 $attr= @ldap_get_entries($ds,$sr);
1463 return ($attr[0]['namingcontexts']);
1464 }
1466 }
1468 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1469 ?>