11e3fc88448ef0ce0e96febafa81c01d286ed073
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.
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();
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;
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 print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
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);
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 print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
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);
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();
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 }
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 }
447 /* Copy given attributes and sub-dns with attributes to destination dn
448 */
449 function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
450 {
451 error_reporting(E_ALL | E_STRICT);
453 if($is_first){
454 echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".LDAP::fix($sourcedn)."</i>")."</h2>";
455 }else{
456 if(preg_match("/^ou=/",$sourcedn)){
457 echo "<h3>"._("Processing")." <i>".LDAP::fix($destinationdn)."</i></h3>";
458 }else{
459 $tmp = split(",",$sourcedn);
461 echo " <b>"._("Object").":</b> ";
463 $deststr = LDAP::fix($destinationdn);
464 if(strlen($deststr) > 96){
465 $deststr = substr($deststr,0,96)."...";
466 }
468 echo $deststr."<br>";
469 }
470 }
472 flush();
474 if($this->hascon){
475 if ($this->reconnect) $this->connect();
477 /* Save base dn */
478 $basedn= $this->basedn;
479 $delarray= array();
481 /* Check if destination entry already exists */
482 $this->cat($destinationdn);
484 if($this->count()){
485 return;
486 }else{
488 $this->clearResult();
490 /* Get source entry */
491 $this->cd($basedn);
492 $this->cat($sourcedn);
493 $attr = $this->fetch();
495 /* Error while fetching object / attribute abort*/
496 if((!$attr) || (count($attr)) ==0) {
497 echo _("Error while fetching source dn - aborted!");
498 return;
499 }
501 /* check if this is a department */
502 if(in_array("organizationalUnit",$attr['objectClass'])){
503 $attr['dn'] = LDAP::convert($destinationdn);
504 $this->cd($basedn);
505 $this->create_missing_trees($destinationdn);
506 $this->cd($destinationdn);
508 /* If is first entry, append FAIbranch to department entry */
509 if($is_first){
510 $this->cat($destinationdn);
511 $attr= $this->fetch();
513 /* Filter unneeded informations */
514 foreach($attr as $key => $value){
515 if(is_numeric($key)) unset($attr[$key]);
516 if(isset($attr[$key]['count'])){
517 if(is_array($attr[$key])){
518 unset($attr[$key]['count']);
519 }
520 }
521 }
523 unset($attr['count']);
524 unset($attr['dn']);
526 /* Add marking attribute */
527 $attr['objectClass'][] = "FAIbranch";
529 /* Add this entry */
530 $this->modify($attr);
531 }
532 }else{
534 /* If this is no department */
535 foreach($attr as $key => $value){
536 if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon","gotoMimeIcon"))){
537 $sr= ldap_read($this->cid, LDAP::fix($sourcedn), "$key=*", array($key));
538 $ei= ldap_first_entry($this->cid, $sr);
539 if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
540 $attr[$key] = $tmp;
541 }
542 }
544 if(is_numeric($key)) unset($attr[$key]);
545 if(isset($attr[$key]['count'])){
546 if(is_array($attr[$key])){
547 unset($attr[$key]['count']);
548 }
549 }
550 }
551 unset($attr['count']);
552 unset($attr['dn']);
554 if((!in_array("gosaApplication" , $attr['objectClass'])) && (!in_array("gotoMimeType", $attr['objectClass']))){
555 $attr['FAIdebianRelease'] = $destinationName;
556 if($type=="branch"){
557 $attr['FAIstate'] ="branch";
558 }elseif($type=="freeze"){
559 $attr['FAIstate'] ="freeze";
560 }else{
561 print_red(_("Unknown FAIstate %s"),$type);
562 }
563 }
565 /* Replace FAIdebianRelease with new release name */
566 if(in_array("FAIpackageList" , $attr['objectClass'])){
567 $attr['FAIdebianRelease'] = $destinationName;
568 }
570 /* Add entry */
571 $this->cd($destinationdn);
572 $this->cat($destinationdn);
573 $a = $this->fetch();
574 if(!count($a)){
575 $this->add($attr);
576 }
578 if($this->error != "Success"){
579 /* Some error occurred */
580 print "---------------------------------------------";
581 print $this->get_error()."<br>";
582 print $sourcedn."<br>";
583 print $destinationdn."<br>";
584 print_a( $attr);
585 exit();
586 }
587 }
588 }
590 echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
592 $this->ls ("(objectClass=*)",$sourcedn);
593 while ($this->fetch()){
594 $deldn= $this->getDN();
595 $delarray[$deldn]= strlen($deldn);
596 }
597 asort ($delarray);
598 reset ($delarray);
600 $depth ++;
601 foreach($delarray as $dn => $bla){
602 if($dn != $destinationdn){
603 $this->cd($basedn);
604 $item = $this->fetch($this->cat($dn));
605 if(!in_array("FAIbranch",$item['objectClass'])){
606 $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
607 }
608 }
609 }
610 }
611 if($is_first){
612 echo "<p class='seperator'> </p>";
613 }
615 }
617 function modify($attrs)
618 {
619 if(count($attrs) == 0){
620 return (0);
621 }
622 if($this->hascon){
623 if ($this->reconnect) $this->connect();
624 $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
625 $this->error = @ldap_error($this->cid);
626 return($r ? $r : 0);
627 }else{
628 $this->error = "Could not connect to LDAP server";
629 return("");
630 }
631 }
633 function add($attrs)
634 {
635 if($this->hascon){
636 if ($this->reconnect) $this->connect();
637 $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
638 $this->error = @ldap_error($this->cid);
639 return($r ? $r : 0);
640 }else{
641 $this->error = "Could not connect to LDAP server";
642 return("");
643 }
644 }
646 function create_missing_trees($target)
647 {
648 global $config;
650 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
652 if ($target == $this->basedn){
653 $l= array("dummy");
654 } else {
655 $l= array_reverse(gosa_ldap_explode_dn($real_path));
656 }
657 unset($l['count']);
658 $cdn= $this->basedn;
659 $tag= "";
661 /* Load schema if available... */
662 $classes= $this->get_objectclasses();
664 foreach ($l as $part){
665 if ($part != "dummy"){
666 $cdn= "$part,$cdn";
667 }
669 /* Ignore referrals */
670 $found= false;
671 foreach($this->referrals as $ref){
672 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
673 if ($base == $cdn){
674 $found= true;
675 break;
676 }
677 }
678 if ($found){
679 continue;
680 }
682 $this->cat ($cdn);
683 $attrs= $this->fetch();
685 /* Create missing entry? */
686 if (count ($attrs)){
688 /* Catch the tag - if present */
689 if (isset($attrs['gosaUnitTag'][0])){
690 $tag= $attrs['gosaUnitTag'][0];
691 }
693 } else {
694 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
695 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
697 $na= array();
699 /* Automatic or traditional? */
700 if(count($classes)){
702 /* Get name of first matching objectClass */
703 $ocname= "";
704 foreach($classes as $class){
705 if (isset($class['MUST']) && $class['MUST'] == "$type"){
707 /* Look for first classes that is structural... */
708 if (isset($class['STRUCTURAL'])){
709 $ocname= $class['NAME'];
710 break;
711 }
713 /* Look for classes that are auxiliary... */
714 if (isset($class['AUXILIARY'])){
715 $ocname= $class['NAME'];
716 }
717 }
718 }
720 /* Bail out, if we've nothing to do... */
721 if ($ocname == ""){
722 print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
723 display_error_page();
724 }
726 /* Assemble_entry */
727 if ($tag != ""){
728 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
729 $na["gosaUnitTag"]= $tag;
730 } else {
731 $na['objectClass']= array($ocname);
732 }
733 if (isset($classes[$ocname]['AUXILIARY'])){
734 $na['objectClass'][]= $classes[$ocname]['SUP'];
735 }
736 if ($type == "dc"){
737 /* This is bad actually, but - tell me a better way? */
738 $na['objectClass'][]= 'locality';
739 }
740 $na[$type]= $param;
741 if (is_array($classes[$ocname]['MUST'])){
742 foreach($classes[$ocname]['MUST'] as $attr){
743 $na[$attr]= "filled";
744 }
745 }
747 } else {
749 /* Use alternative add... */
750 switch ($type){
751 case 'ou':
752 if ($tag != ""){
753 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
754 $na["gosaUnitTag"]= $tag;
755 } else {
756 $na["objectClass"]= "organizationalUnit";
757 }
758 $na["ou"]= $param;
759 break;
760 case 'dc':
761 if ($tag != ""){
762 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
763 $na["gosaUnitTag"]= $tag;
764 } else {
765 $na["objectClass"]= array("dcObject", "top", "locality");
766 }
767 $na["dc"]= $param;
768 break;
769 default:
770 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
771 display_error_page();
772 }
774 }
775 $this->cd($cdn);
776 $this->add($na);
778 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
779 if (!preg_match('/success/i', $this->error)){
780 return FALSE;
781 }
782 }
783 }
785 return TRUE;
786 }
789 function recursive_remove()
790 {
791 $delarray= array();
793 /* Get sorted list of dn's to delete */
794 $this->search ("(objectClass=*)");
795 while ($this->fetch()){
796 $deldn= $this->getDN();
797 $delarray[$deldn]= strlen($deldn);
798 }
799 arsort ($delarray);
800 reset ($delarray);
802 /* Delete all dn's in subtree */
803 foreach ($delarray as $key => $value){
804 $this->rmdir($key);
805 }
806 }
808 function get_attribute($dn, $name,$r_array=0)
809 {
810 $data= "";
811 if ($this->reconnect) $this->connect();
812 $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
814 /* fill data from LDAP */
815 if ($sr) {
816 $ei= @ldap_first_entry($this->cid, $sr);
817 if ($ei) {
818 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
819 $data= $info[0];
820 }
821 }
822 }
823 if($r_array==0)
824 return ($data);
825 else
826 return ($info);
829 }
833 function get_additional_error()
834 {
835 $error= "";
836 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
837 return ($error);
838 }
840 function get_error()
841 {
842 if ($this->error == 'Success'){
843 return $this->error;
844 } else {
845 $adderror= $this->get_additional_error();
846 if ($adderror != ""){
847 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
848 } else {
849 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
850 }
851 return $error;
852 }
853 }
855 function get_credentials($url, $referrals= NULL)
856 {
857 $ret= array();
858 $url= preg_replace('!\?\?.*$!', '', $url);
859 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
861 if ($referrals === NULL){
862 $referrals= $this->referrals;
863 }
865 if (isset($referrals[$server])){
866 return ($referrals[$server]);
867 } else {
868 $ret['ADMIN']= LDAP::fix($this->binddn);
869 $ret['PASSWORD']= $this->bindpw;
870 }
872 return ($ret);
873 }
876 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
877 {
878 $display= "";
880 if ($recursive){
881 $this->cd($dn);
882 $this->ls($filter,$dn, array('dn','objectClass'));
883 $deps = array();
885 $display .= $this->gen_one_entry($dn)."\n";
887 while ($attrs= $this->fetch()){
888 $deps[] = $attrs['dn'];
889 }
890 foreach($deps as $dn){
891 $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
892 }
893 } else {
894 $display.= $this->gen_one_entry($dn);
895 }
896 return ($display);
897 }
900 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
901 {
902 $display= array();
904 $this->cd($dn);
905 $this->search("$filter");
907 $i=0;
908 while ($attrs= $this->fetch()){
909 $j=0;
911 foreach ($attributes as $at){
912 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
913 $j++;
914 }
916 $i++;
917 }
919 return ($display);
920 }
923 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
924 {
925 $ret = "";
926 $data = "";
927 if($this->reconnect){
928 $this->connect();
929 }
931 /* Searching Ldap Tree */
932 $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
934 /* Get the first entry */
935 $entry= @ldap_first_entry($this->cid, $sr);
937 /* Get all attributes related to that Objekt */
938 $atts = array();
940 /* Assemble dn */
941 $atts[0]['name'] = "dn";
942 $atts[0]['value'] = array('count' => 1, 0 => $dn);
944 /* Reset index */
945 $i = 1 ;
946 $identifier = array();
947 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
948 while ($attribute) {
949 $i++;
950 $atts[$i]['name'] = $attribute;
951 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
953 /* Next one */
954 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
955 }
957 foreach($atts as $at)
958 {
959 for ($i= 0; $i<$at['value']['count']; $i++){
961 /* Check if we must encode the data */
962 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
963 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
964 } else {
965 $ret .= $at['name'].": ".$at['value'][$i]."\n";
966 }
967 }
968 }
970 return($ret);
971 }
974 function dn_exists($dn)
975 {
976 return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
977 }
981 /* This funktion imports ldifs
983 If DeleteOldEntries is true, the destination entry will be deleted first.
984 If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
985 if JustMofify id false the destination dn will be overwritten by the new ldif.
986 */
988 function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
989 {
990 if($this->reconnect) $this->connect();
992 /* First we have to splitt the string ito detect empty lines
993 An empty line indicates an new Entry */
994 $entries = split("\n",$str_attr);
996 $data = "";
997 $cnt = 0;
998 $current_line = 0;
1000 /* FIX ldif */
1001 $last = "";
1002 $tmp = "";
1003 $i = 0;
1004 foreach($entries as $entry){
1005 if(preg_match("/^ /",$entry)){
1006 $tmp[$i] .= trim($entry);
1007 }else{
1008 $i ++;
1009 $tmp[$i] = trim($entry);
1010 }
1011 }
1013 /* Every single line ... */
1014 foreach($tmp as $entry) {
1015 $current_line ++;
1017 /* Removing Spaces to ..
1018 .. test if a new entry begins */
1019 $tmp = str_replace(" ","",$data );
1021 /* .. prevent empty lines in an entry */
1022 $tmp2 = str_replace(" ","",$entry);
1024 /* If the Block ends (Empty Line) */
1025 if((empty($entry))&&(!empty($tmp))) {
1026 /* Add collected lines as a complete block */
1027 $all[$cnt] = $data;
1028 $cnt ++;
1029 $data ="";
1030 } else {
1032 /* Append lines ... */
1033 if(!empty($tmp2)) {
1034 /* check if we need base64_decode for this line */
1035 if(ereg("::",$tmp2))
1036 {
1037 $encoded = split("::",$entry);
1038 $attr = trim($encoded[0]);
1039 $value = base64_decode(trim($encoded[1]));
1040 /* Add linenumber */
1041 $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
1042 }
1043 else
1044 {
1045 /* Add Linenumber */
1046 $data .= $current_line."#".base64_encode($entry)."\n";
1047 }
1048 }
1049 }
1050 }
1052 /* The Data we collected is not in the array all[];
1053 For example the Data is stored like this..
1055 all[0] = "1#dn : .... \n
1056 2#ObjectType: person \n ...."
1058 Now we check every insertblock and try to insert */
1059 foreach ( $all as $single) {
1060 $lineone = split("\n",$single);
1061 $ndn = split("#", $lineone[0]);
1062 $line = base64_decode($ndn[1]);
1064 $dnn = split (":",$line,2);
1065 $current_line = $ndn[0];
1066 $dn = $dnn[0];
1067 $value = $dnn[1];
1069 /* Every block must begin with a dn */
1070 if($dn != "dn") {
1071 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1072 return -2;
1073 }
1075 /* Should we use Modify instead of Add */
1076 $usemodify= false;
1078 /* Delete before insert */
1079 $usermdir= false;
1081 /* The dn address already exists, Don't delete destination entry, overwrite it */
1082 if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1084 $usermdir = $usemodify = false;
1086 /* Delete old entry first, then add new */
1087 } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1089 /* Delete first, then add */
1090 $usermdir = true;
1092 } elseif(($this->dn_exists($value))&&($JustModify)) {
1094 /* Modify instead of Add */
1095 $usemodify = true;
1096 }
1098 /* If we can't Import, return with a file error */
1099 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1100 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1101 $current_line);
1102 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
1103 }
1105 return (INSERT_OK);
1106 }
1109 /* Imports a single entry
1110 If $delete is true; The old entry will be deleted if it exists.
1111 if $modify is true; All variables that are not touched by the new ldif will be kept.
1112 if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1113 */
1114 function import_single_entry($str_attr,$modify,$delete)
1115 {
1116 global $config;
1118 if(!$config){
1119 trigger_error("Can't import ldif, can't read config object.");
1120 }
1123 if($this->reconnect) $this->connect();
1125 $ret = false;
1126 $rows= split("\n",$str_attr);
1127 $data= false;
1129 foreach($rows as $row) {
1131 /* Check if we use Linenumbers (when import_complete_ldif is called we use
1132 Linenumbers) Linenumbers are use like this 123#attribute : value */
1133 if(!empty($row)) {
1134 if(strpos($row,"#")!=FALSE) {
1136 /* We are using line numbers
1137 Because there is a # before a : */
1138 $tmp1= split("#",$row);
1139 $current_line= $tmp1[0];
1140 $row= base64_decode($tmp1[1]);
1141 }
1143 /* Split the line into attribute and value */
1144 $attr = split(":", $row,2);
1145 $attr[0]= trim($attr[0]); /* attribute */
1146 $attr[1]= $attr[1]; /* value */
1148 /* Check :: was used to indicate base64_encoded strings */
1149 if($attr[1][0] == ":"){
1150 $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1151 $attr[1]=base64_decode($attr[1]);
1152 }
1154 $attr[1] = trim($attr[1]);
1156 /* Check for attributes that are used more than once */
1157 if(!isset($data[$attr[0]])) {
1158 $data[$attr[0]]=$attr[1];
1159 } else {
1160 $tmp = $data[$attr[0]];
1162 if(!is_array($tmp)) {
1163 $new[0]=$tmp;
1164 $new[1]=$attr[1];
1165 $datas[$attr[0]]['count']=1;
1166 $data[$attr[0]]=$new;
1167 } else {
1168 $cnt = $datas[$attr[0]]['count'];
1169 $cnt ++;
1170 $data[$attr[0]][$cnt]=$attr[1];
1171 $datas[$attr[0]]['count'] = $cnt;
1172 }
1173 }
1174 }
1175 }
1177 /* If dn is an index of data, we should try to insert the data */
1178 if(isset($data['dn'])) {
1180 /* Fix dn */
1181 $tmp = gosa_ldap_explode_dn($data['dn']);
1182 unset($tmp['count']);
1183 $newdn ="";
1184 foreach($tmp as $tm){
1185 $newdn.= trim($tm).",";
1186 }
1187 $newdn = preg_replace("/,$/","",$newdn);
1188 $data['dn'] = $newdn;
1190 /* Creating Entry */
1191 $this->cd($data['dn']);
1193 /* Delete existing entry */
1194 if($delete){
1195 $this->rmdir_recursive($data['dn']);
1196 }
1198 /* Create missing trees */
1199 $this->cd ($this->basedn);
1200 $this->cd($config->current['BASE']);
1201 $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1202 $this->cd($data['dn']);
1204 $dn = $data['dn'];
1205 unset($data['dn']);
1207 if(!$modify){
1209 $this->cat($dn);
1210 if($this->count()){
1212 /* The destination entry exists, overwrite it with the new entry */
1213 $attrs = $this->fetch();
1214 foreach($attrs as $name => $value ){
1215 if(!is_numeric($name)){
1216 if(in_array($name,array("dn","count"))) continue;
1217 if(!isset($data[$name])){
1218 $data[$name] = array();
1219 }
1220 }
1221 }
1222 $ret = $this->modify($data);
1224 }else{
1226 /* The destination entry doesn't exists, create it */
1227 $ret = $this->add($data);
1228 }
1230 } else {
1232 /* Keep all vars that aren't touched by this ldif */
1233 $ret = $this->modify($data);
1234 }
1235 }
1236 show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1237 return($ret);
1238 }
1241 function importcsv($str)
1242 {
1243 $lines = split("\n",$str);
1244 foreach($lines as $line)
1245 {
1246 /* continue if theres a comment */
1247 if(substr(trim($line),0,1)=="#"){
1248 continue;
1249 }
1251 $line= str_replace ("\t\t","\t",$line);
1252 $line= str_replace ("\t" ,"," ,$line);
1253 echo $line;
1255 $cells = split(",",$line ) ;
1256 $linet= str_replace ("\t\t",",",$line);
1257 $cells = split("\t",$line);
1258 $count = count($cells);
1259 }
1261 }
1263 function get_objectclasses()
1264 {
1265 $objectclasses = array();
1266 global $config;
1268 /* Only read schema if it is allowed */
1269 if(isset($config) && preg_match("/config/i",get_class($config))){
1270 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1271 return($objectclasses);
1272 }
1273 }
1275 # Get base to look for schema
1276 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1277 if(!$sr){
1278 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1279 }
1281 $attr = @ldap_get_entries($this->cid,$sr);
1282 if (!isset($attr[0]['subschemasubentry'][0])){
1283 return array();
1284 }
1286 /* Get list of objectclasses and fill array */
1287 $nb= $attr[0]['subschemasubentry'][0];
1288 $objectclasses= array();
1289 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1290 $attrs= ldap_get_entries($this->cid,$sr);
1291 if (!isset($attrs[0])){
1292 return array();
1293 }
1294 foreach ($attrs[0]['objectclasses'] as $val){
1295 if (preg_match('/^[0-9]+$/', $val)){
1296 continue;
1297 }
1298 $name= "OID";
1299 $pattern= split(' ', $val);
1300 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1301 $objectclasses[$ocname]= array();
1303 foreach($pattern as $chunk){
1304 switch($chunk){
1306 case '(':
1307 $value= "";
1308 break;
1310 case ')': if ($name != ""){
1311 $objectclasses[$ocname][$name]= $this->value2container($value);
1312 }
1313 $name= "";
1314 $value= "";
1315 break;
1317 case 'NAME':
1318 case 'DESC':
1319 case 'SUP':
1320 case 'STRUCTURAL':
1321 case 'ABSTRACT':
1322 case 'AUXILIARY':
1323 case 'MUST':
1324 case 'MAY':
1325 if ($name != ""){
1326 $objectclasses[$ocname][$name]= $this->value2container($value);
1327 }
1328 $name= $chunk;
1329 $value= "";
1330 break;
1332 default: $value.= $chunk." ";
1333 }
1334 }
1336 }
1337 return $objectclasses;
1338 }
1341 function value2container($value)
1342 {
1343 /* Set emtpy values to "true" only */
1344 if (preg_match('/^\s*$/', $value)){
1345 return true;
1346 }
1348 /* Remove ' and " if needed */
1349 $value= preg_replace('/^[\'"]/', '', $value);
1350 $value= preg_replace('/[\'"] *$/', '', $value);
1352 /* Convert to array if $ is inside... */
1353 if (preg_match('/\$/', $value)){
1354 $container= preg_split('/\s*\$\s*/', $value);
1355 } else {
1356 $container= chop($value);
1357 }
1359 return ($container);
1360 }
1363 function log($string)
1364 {
1365 if (session::is_set('config')){
1366 $cfg= $_SESSION['config'];
1367 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1368 syslog (LOG_INFO, $string);
1369 }
1370 }
1371 }
1373 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1374 function getCn($dn){
1375 $simple= split(",", $dn);
1377 foreach($simple as $piece) {
1378 $partial= split("=", $piece);
1380 if($partial[0] == "cn"){
1381 return $partial[1];
1382 }
1383 }
1384 }
1387 function get_naming_contexts($server, $admin= "", $password= "")
1388 {
1389 /* Build LDAP connection */
1390 $ds= ldap_connect ($server);
1391 if (!$ds) {
1392 die ("Can't bind to LDAP. No check possible!");
1393 }
1394 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1395 $r= ldap_bind ($ds, $admin, $password);
1397 /* Get base to look for naming contexts */
1398 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1399 $attr= @ldap_get_entries($ds,$sr);
1401 return ($attr[0]['namingcontexts']);
1402 }
1405 function get_root_dse($server, $admin= "", $password= "")
1406 {
1407 /* Build LDAP connection */
1408 $ds= ldap_connect ($server);
1409 if (!$ds) {
1410 die ("Can't bind to LDAP. No check possible!");
1411 }
1412 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1413 $r= ldap_bind ($ds, $admin, $password);
1415 /* Get base to look for naming contexts */
1416 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1417 $attr= @ldap_get_entries($ds,$sr);
1419 /* Return empty array, if nothing was set */
1420 if (!isset($attr[0])){
1421 return array();
1422 }
1424 /* Rework array... */
1425 $result= array();
1426 for ($i= 0; $i<$attr[0]['count']; $i++){
1427 $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1428 unset($result[$attr[0][$i]]['count']);
1429 }
1431 return ($result);
1432 }
1435 }
1437 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1438 ?>