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=$this->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 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 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, $this->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, $this->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 = $this->convert($dir);
168 }
170 function getParentDir($basedn = "")
171 {
172 if ($basedn=="")
173 $basedn = $this->basedn;
174 else
175 $basedn = $this->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, $this->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('".$this->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= $this->convert($basedn);
218 $start = microtime();
220 $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
221 $this->error = @ldap_error($this->cid);
222 $this->resetResult();
223 $this->hasres=true;
225 /* Check if query took longer as specified in max_ldap_query_time */
226 if($this->max_ldap_query_time){
227 $diff = get_MicroTimeDiff($start,microtime());
228 if($diff > $this->max_ldap_query_time){
229 print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
230 }
231 }
233 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
235 return($this->sr);
236 }else{
237 $this->error = "Could not connect to LDAP server";
238 return("");
239 }
240 }
242 function cat($dn,$attrs= array("*"))
243 {
244 if($this->hascon){
245 if ($this->reconnect) $this->connect();
246 $this->clearResult();
247 $filter = "(objectclass=*)";
248 $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
249 $this->error = @ldap_error($this->cid);
250 $this->resetResult();
251 $this->hasres=true;
252 return($this->sr);
253 }else{
254 $this->error = "Could not connect to LDAP server";
255 return("");
256 }
257 }
259 function set_size_limit($size)
260 {
261 /* Ignore zero settings */
262 if ($size == 0){
263 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
264 }
265 if($this->hascon){
266 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
267 } else {
268 $this->error = "Could not connect to LDAP server";
269 }
270 }
272 function fetch()
273 {
274 $att= array();
275 if($this->hascon){
276 if($this->hasres){
277 if ($this->start == 0)
278 {
279 $this->start = 1;
280 $this->re= @ldap_first_entry($this->cid, $this->sr);
281 } else {
282 $this->re= @ldap_next_entry($this->cid, $this->re);
283 }
284 if ($this->re)
285 {
286 $att= @ldap_get_attributes($this->cid, $this->re);
287 $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
288 }
289 $this->error = @ldap_error($this->cid);
290 if (!isset($att)){
291 $att= array();
292 }
293 return($att);
294 }else{
295 $this->error = "Perform a Fetch with no Search";
296 return("");
297 }
298 }else{
299 $this->error = "Could not connect to LDAP server";
300 return("");
301 }
302 }
304 function resetResult()
305 {
306 $this->start = 0;
307 }
309 function clearResult()
310 {
311 if($this->hasres){
312 $this->hasres = false;
313 @ldap_free_result($this->sr);
314 }
315 }
317 function getDN()
318 {
319 if($this->hascon){
320 if($this->hasres){
322 if(!$this->re)
323 {
324 $this->error = "Perform a Fetch with no valid Result";
325 }
326 else
327 {
328 $rv = @ldap_get_dn($this->cid, $this->re);
330 $this->error = @ldap_error($this->cid);
331 return(trim($this->convert($rv)));
332 }
333 }else{
334 $this->error = "Perform a Fetch with no Search";
335 return("");
336 }
337 }else{
338 $this->error = "Could not connect to LDAP server";
339 return("");
340 }
341 }
343 function count()
344 {
345 if($this->hascon){
346 if($this->hasres){
347 $rv = @ldap_count_entries($this->cid, $this->sr);
348 $this->error = @ldap_error($this->cid);
349 return($rv);
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 rm($attrs = "", $dn = "")
361 {
362 if($this->hascon){
363 if ($this->reconnect) $this->connect();
364 if ($dn == "")
365 $dn = $this->basedn;
367 $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
368 $this->error = @ldap_error($this->cid);
369 return($r);
370 }else{
371 $this->error = "Could not connect to LDAP server";
372 return("");
373 }
374 }
376 function rename($attrs, $dn = "")
377 {
378 if($this->hascon){
379 if ($this->reconnect) $this->connect();
380 if ($dn == "")
381 $dn = $this->basedn;
383 $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
384 $this->error = @ldap_error($this->cid);
385 return($r);
386 }else{
387 $this->error = "Could not connect to LDAP server";
388 return("");
389 }
390 }
392 function rmdir($deletedn)
393 {
394 if($this->hascon){
395 if ($this->reconnect) $this->connect();
396 $r = @ldap_delete($this->cid, $this->fix($deletedn));
397 $this->error = @ldap_error($this->cid);
398 return($r ? $r : 0);
399 }else{
400 $this->error = "Could not connect to LDAP server";
401 return("");
402 }
403 }
405 /**
406 * Function rmdir_recursive
407 *
408 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
409 * Parameters: The dn to delete
410 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
411 *
412 */
414 function rmdir_recursive($deletedn)
415 {
416 if($this->hascon){
417 if ($this->reconnect) $this->connect();
418 $delarray= array();
420 /* Get sorted list of dn's to delete */
421 $this->ls ("(objectClass=*)",$deletedn);
422 while ($this->fetch()){
423 $deldn= $this->getDN();
424 $delarray[$deldn]= strlen($deldn);
425 }
426 arsort ($delarray);
427 reset ($delarray);
429 /* Really Delete ALL dn's in subtree */
430 foreach ($delarray as $key => $value){
431 $this->rmdir_recursive($key);
432 }
434 /* Finally Delete own Node */
435 $r = @ldap_delete($this->cid, $this->fix($deletedn));
436 $this->error = @ldap_error($this->cid);
437 return($r ? $r : 0);
438 }else{
439 $this->error = "Could not connect to LDAP server";
440 return("");
441 }
442 }
444 /* Copy given attributes and sub-dns with attributes to destination dn
445 */
446 function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
447 {
448 error_reporting(E_ALL);
450 if($is_first){
451 echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
452 }else{
453 if(preg_match("/^ou=/",$sourcedn)){
454 echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
455 }else{
456 $tmp = split(",",$sourcedn);
458 echo " <b>"._("Object").":</b> ";
460 $deststr = @LDAP::fix($destinationdn);
461 if(strlen($deststr) > 96){
462 $deststr = substr($deststr,0,96)."...";
463 }
465 echo $deststr."<br>";
466 }
467 }
469 flush();
471 if($this->hascon){
472 if ($this->reconnect) $this->connect();
474 /* Save base dn */
475 $basedn= $this->basedn;
476 $delarray= array();
478 /* Check if destination entry already exists */
479 $this->cat($destinationdn);
481 if($this->count()){
482 return;
483 }else{
485 $this->clearResult();
487 /* Get source entry */
488 $this->cd($basedn);
489 $this->cat($sourcedn);
490 $attr = $this->fetch();
492 /* Error while fetching object / attribute abort*/
493 if((!$attr) || (count($attr)) ==0) {
494 echo _("Error while fetching source dn - aborted!");
495 return;
496 }
498 /* check if this is a department */
499 if(in_array("organizationalUnit",$attr['objectClass'])){
500 $attr['dn'] = $this->convert($destinationdn);
501 $this->cd($basedn);
502 $this->create_missing_trees($destinationdn);
503 $this->cd($destinationdn);
505 /* If is first entry, append FAIbranch to department entry */
506 if($is_first){
507 $this->cat($destinationdn);
508 $attr= $this->fetch();
510 /* Filter unneeded informations */
511 foreach($attr as $key => $value){
512 if(is_numeric($key)) unset($attr[$key]);
513 if(isset($attr[$key]['count'])){
514 if(is_array($attr[$key])){
515 unset($attr[$key]['count']);
516 }
517 }
518 }
520 unset($attr['count']);
521 unset($attr['dn']);
523 /* Add marking attribute */
524 $attr['objectClass'][] = "FAIbranch";
526 /* Add this entry */
527 $this->modify($attr);
528 }
529 }else{
531 /* If this is no department */
532 foreach($attr as $key => $value){
533 if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon","gotoMimeIcon"))){
534 $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
535 $ei= ldap_first_entry($this->cid, $sr);
536 if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
537 $attr[$key] = $tmp;
538 }
539 }
541 if(is_numeric($key)) unset($attr[$key]);
542 if(isset($attr[$key]['count'])){
543 if(is_array($attr[$key])){
544 unset($attr[$key]['count']);
545 }
546 }
547 }
548 unset($attr['count']);
549 unset($attr['dn']);
551 if((!in_array("gosaApplication" , $attr['objectClass'])) && (!in_array("gotoMimeType", $attr['objectClass']))){
552 $attr['FAIdebianRelease'] = $destinationName;
553 if($type=="branch"){
554 $attr['FAIstate'] ="branch";
555 }elseif($type=="freeze"){
556 $attr['FAIstate'] ="freeze";
557 }else{
558 print_red(_("Unknown FAIstate %s"),$type);
559 }
560 }
562 /* Replace FAIdebianRelease with new release name */
563 if(in_array("FAIpackageList" , $attr['objectClass'])){
564 $attr['FAIdebianRelease'] = $destinationName;
565 }
567 /* Add entry */
568 $this->cd($destinationdn);
569 $this->cat($destinationdn);
570 $a = $this->fetch();
571 if(!count($a)){
572 $this->add($attr);
573 }
575 if($this->error != "Success"){
576 /* Some error occurred */
577 print "---------------------------------------------";
578 print $this->get_error()."<br>";
579 print $sourcedn."<br>";
580 print $destinationdn."<br>";
581 print_a( $attr);
582 exit();
583 }
584 }
585 }
587 echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
589 $this->ls ("(objectClass=*)",$sourcedn);
590 while ($this->fetch()){
591 $deldn= $this->getDN();
592 $delarray[$deldn]= strlen($deldn);
593 }
594 asort ($delarray);
595 reset ($delarray);
597 $depth ++;
598 foreach($delarray as $dn => $bla){
599 if($dn != $destinationdn){
600 $this->cd($basedn);
601 $item = $this->fetch($this->cat($dn));
602 if(!in_array("FAIbranch",$item['objectClass'])){
603 $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
604 }
605 }
606 }
607 }
608 if($is_first){
609 echo "<p class='seperator'> </p>";
610 }
612 }
614 function modify($attrs)
615 {
616 if(count($attrs) == 0){
617 return (0);
618 }
619 if($this->hascon){
620 if ($this->reconnect) $this->connect();
621 $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
622 $this->error = @ldap_error($this->cid);
623 return($r ? $r : 0);
624 }else{
625 $this->error = "Could not connect to LDAP server";
626 return("");
627 }
628 }
630 function add($attrs)
631 {
632 if($this->hascon){
633 if ($this->reconnect) $this->connect();
634 $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
635 $this->error = @ldap_error($this->cid);
636 return($r ? $r : 0);
637 }else{
638 $this->error = "Could not connect to LDAP server";
639 return("");
640 }
641 }
643 function create_missing_trees($target)
644 {
645 global $config;
647 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
649 if ($target == $this->basedn){
650 $l= array("dummy");
651 } else {
652 $l= array_reverse(gosa_ldap_explode_dn($real_path));
653 }
654 unset($l['count']);
655 $cdn= $this->basedn;
656 $tag= "";
658 /* Load schema if available... */
659 $classes= $this->get_objectclasses();
661 foreach ($l as $part){
662 if ($part != "dummy"){
663 $cdn= "$part,$cdn";
664 }
666 /* Ignore referrals */
667 $found= false;
668 foreach($this->referrals as $ref){
669 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
670 if ($base == $cdn){
671 $found= true;
672 break;
673 }
674 }
675 if ($found){
676 continue;
677 }
679 $this->cat ($cdn);
680 $attrs= $this->fetch();
682 /* Create missing entry? */
683 if (count ($attrs)){
685 /* Catch the tag - if present */
686 if (isset($attrs['gosaUnitTag'][0])){
687 $tag= $attrs['gosaUnitTag'][0];
688 }
690 } else {
691 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
692 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
694 $na= array();
696 /* Automatic or traditional? */
697 if(count($classes)){
699 /* Get name of first matching objectClass */
700 $ocname= "";
701 foreach($classes as $class){
702 if (isset($class['MUST']) && $class['MUST'] == "$type"){
704 /* Look for first classes that is structural... */
705 if (isset($class['STRUCTURAL'])){
706 $ocname= $class['NAME'];
707 break;
708 }
710 /* Look for classes that are auxiliary... */
711 if (isset($class['AUXILIARY'])){
712 $ocname= $class['NAME'];
713 }
714 }
715 }
717 /* Bail out, if we've nothing to do... */
718 if ($ocname == ""){
719 print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
720 echo $_SESSION['errors'];
721 exit;
722 }
724 /* Assemble_entry */
725 if ($tag != ""){
726 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
727 } else {
728 $na['objectClass']= array($ocname);
729 }
730 if (isset($classes[$ocname]['AUXILIARY'])){
731 $na['objectClass'][]= $classes[$ocname]['SUP'];
732 }
733 if ($type == "dc"){
734 /* This is bad actually, but - tell me a better way? */
735 $na['objectClass'][]= 'locality';
736 }
737 $na[$type]= $param;
738 if (is_array($classes[$ocname]['MUST'])){
739 foreach($classes[$ocname]['MUST'] as $attr){
740 $na[$attr]= "filled";
741 }
742 }
744 } else {
746 /* Use alternative add... */
747 switch ($type){
748 case 'ou':
749 if ($tag != ""){
750 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
751 $na["gosaUnitTag"]= $tag;
752 } else {
753 $na["objectClass"]= "organizationalUnit";
754 }
755 $na["ou"]= $param;
756 break;
757 case 'dc':
758 if ($tag != ""){
759 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
760 $na["gosaUnitTag"]= $tag;
761 } else {
762 $na["objectClass"]= array("dcObject", "top", "locality");
763 }
764 $na["dc"]= $param;
765 break;
766 default:
767 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
768 echo $_SESSION['errors'];
769 exit;
770 }
772 }
773 $this->cd($cdn);
774 $this->add($na);
776 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
777 if (!preg_match('/success/i', $this->error)){
778 return FALSE;
779 }
780 }
781 }
783 return TRUE;
784 }
787 function recursive_remove()
788 {
789 $delarray= array();
791 /* Get sorted list of dn's to delete */
792 $this->search ("(objectClass=*)");
793 while ($this->fetch()){
794 $deldn= $this->getDN();
795 $delarray[$deldn]= strlen($deldn);
796 }
797 arsort ($delarray);
798 reset ($delarray);
800 /* Delete all dn's in subtree */
801 foreach ($delarray as $key => $value){
802 $this->rmdir($key);
803 }
804 }
806 function get_attribute($dn, $name,$r_array=0)
807 {
808 $data= "";
809 if ($this->reconnect) $this->connect();
810 $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
812 /* fill data from LDAP */
813 if ($sr) {
814 $ei= @ldap_first_entry($this->cid, $sr);
815 if ($ei) {
816 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
817 $data= $info[0];
818 }
820 }
821 }
822 if($r_array==0)
823 return ($data);
824 else
825 return ($info);
828 }
832 function get_additional_error()
833 {
834 $error= "";
835 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
836 return ($error);
837 }
839 function get_error()
840 {
841 if ($this->error == 'Success'){
842 return $this->error;
843 } else {
844 $adderror= $this->get_additional_error();
845 if ($adderror != ""){
846 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
847 } else {
848 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
849 }
850 return $error;
851 }
852 }
854 function get_credentials($url, $referrals= NULL)
855 {
856 $ret= array();
857 $url= preg_replace('!\?\?.*$!', '', $url);
858 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
860 if ($referrals == NULL){
861 $referrals= $this->referrals;
862 }
864 if (isset($referrals[$server])){
865 return ($referrals[$server]);
866 } else {
867 $ret['ADMIN']= $this->fix($this->binddn);
868 $ret['PASSWORD']= $this->bindpw;
869 }
871 return ($ret);
872 }
875 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
876 {
877 $display= "";
879 if ($recursive){
880 $this->cd($dn);
881 $this->ls($filter,$dn, array('dn','objectClass'));
882 $deps = array();
884 $display .= $this->gen_one_entry($dn)."\n";
886 while ($attrs= $this->fetch()){
887 $deps[] = $attrs['dn'];
888 }
889 foreach($deps as $dn){
890 $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
891 }
892 } else {
893 $display.= $this->gen_one_entry($dn);
894 }
895 return ($display);
896 }
899 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
900 {
901 $display= array();
903 $this->cd($dn);
904 $this->search("$filter");
906 $i=0;
907 while ($attrs= $this->fetch()){
908 $j=0;
910 foreach ($attributes as $at){
911 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
912 $j++;
913 }
915 $i++;
916 }
918 return ($display);
919 }
922 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
923 {
924 $ret = "";
925 $data = "";
926 if($this->reconnect){
927 $this->connect();
928 }
930 /* Searching Ldap Tree */
931 $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
933 /* Get the first entry */
934 $entry= @ldap_first_entry($this->cid, $sr);
936 /* Get all attributes related to that Objekt */
937 $atts = array();
939 /* Assemble dn */
940 $atts[0]['name'] = "dn";
941 $atts[0]['value'] = array('count' => 1, 0 => $dn);
943 /* Reset index */
944 $i = 1 ;
945 $identifier = array();
946 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
947 while ($attribute) {
948 $i++;
949 $atts[$i]['name'] = $attribute;
950 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
952 /* Next one */
953 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
954 }
956 foreach($atts as $at)
957 {
958 for ($i= 0; $i<$at['value']['count']; $i++){
960 /* Check if we must encode the data */
961 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
962 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
963 } else {
964 $ret .= $at['name'].": ".$at['value'][$i]."\n";
965 }
966 }
967 }
969 return($ret);
970 }
973 function dn_exists($dn)
974 {
975 return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
976 }
980 /* This funktion imports ldifs
982 If DeleteOldEntries is true, the destination entry will be deleted first.
983 If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
984 if JustMofify id false the destination dn will be overwritten by the new ldif.
985 */
987 function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
988 {
989 if($this->reconnect) $this->connect();
991 /* First we have to splitt the string ito detect empty lines
992 An empty line indicates an new Entry */
993 $entries = split("\n",$str_attr);
995 $data = "";
996 $cnt = 0;
997 $current_line = 0;
999 /* FIX ldif */
1000 $last = "";
1001 $tmp = "";
1002 $i = 0;
1003 foreach($entries as $entry){
1004 if(preg_match("/^ /",$entry)){
1005 $tmp[$i] .= trim($entry);
1006 }else{
1007 $i ++;
1008 $tmp[$i] = trim($entry);
1009 }
1010 }
1012 /* Every single line ... */
1013 foreach($tmp as $entry) {
1014 $current_line ++;
1016 /* Removing Spaces to ..
1017 .. test if a new entry begins */
1018 $tmp = str_replace(" ","",$data );
1020 /* .. prevent empty lines in an entry */
1021 $tmp2 = str_replace(" ","",$entry);
1023 /* If the Block ends (Empty Line) */
1024 if((empty($entry))&&(!empty($tmp))) {
1025 /* Add collected lines as a complete block */
1026 $all[$cnt] = $data;
1027 $cnt ++;
1028 $data ="";
1029 } else {
1031 /* Append lines ... */
1032 if(!empty($tmp2)) {
1033 /* check if we need base64_decode for this line */
1034 if(ereg("::",$tmp2))
1035 {
1036 $encoded = split("::",$entry);
1037 $attr = trim($encoded[0]);
1038 $value = base64_decode(trim($encoded[1]));
1039 /* Add linenumber */
1040 $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
1041 }
1042 else
1043 {
1044 /* Add Linenumber */
1045 $data .= $current_line."#".base64_encode($entry)."\n";
1046 }
1047 }
1048 }
1049 }
1051 /* The Data we collected is not in the array all[];
1052 For example the Data is stored like this..
1054 all[0] = "1#dn : .... \n
1055 2#ObjectType: person \n ...."
1057 Now we check every insertblock and try to insert */
1058 foreach ( $all as $single) {
1059 $lineone = split("\n",$single);
1060 $ndn = split("#", $lineone[0]);
1061 $line = base64_decode($ndn[1]);
1063 $dnn = split (":",$line,2);
1064 $current_line = $ndn[0];
1065 $dn = $dnn[0];
1066 $value = $dnn[1];
1068 /* Every block must begin with a dn */
1069 if($dn != "dn") {
1070 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1071 return -2;
1072 }
1074 /* Should we use Modify instead of Add */
1075 $usemodify= false;
1077 /* Delete before insert */
1078 $usermdir= false;
1080 /* The dn address already exists, Don't delete destination entry, overwrite it */
1081 if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
1083 $usermdir = $usemodify = false;
1085 /* Delete old entry first, then add new */
1086 } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
1088 /* Delete first, then add */
1089 $usermdir = true;
1091 } elseif(($this->dn_exists($value))&&($JustModify)) {
1093 /* Modify instead of Add */
1094 $usemodify = true;
1095 }
1097 /* If we can't Import, return with a file error */
1098 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1099 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1100 $current_line);
1101 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
1102 }
1104 return (INSERT_OK);
1105 }
1108 /* Imports a single entry
1109 If $delete is true; The old entry will be deleted if it exists.
1110 if $modify is true; All variables that are not touched by the new ldif will be kept.
1111 if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
1112 */
1113 function import_single_entry($str_attr,$modify,$delete)
1114 {
1115 global $config;
1117 if(!$config){
1118 trigger_error("Can't import ldif, can't read config object.");
1119 }
1122 if($this->reconnect) $this->connect();
1124 $ret = false;
1125 $rows= split("\n",$str_attr);
1126 $data= false;
1128 foreach($rows as $row) {
1130 /* Check if we use Linenumbers (when import_complete_ldif is called we use
1131 Linenumbers) Linenumbers are use like this 123#attribute : value */
1132 if(!empty($row)) {
1133 if(strpos($row,"#")!=FALSE) {
1135 /* We are using line numbers
1136 Because there is a # before a : */
1137 $tmp1= split("#",$row);
1138 $current_line= $tmp1[0];
1139 $row= base64_decode($tmp1[1]);
1140 }
1142 /* Split the line into attribute and value */
1143 $attr = split(":", $row,2);
1144 $attr[0]= trim($attr[0]); /* attribute */
1145 $attr[1]= $attr[1]; /* value */
1147 /* Check :: was used to indicate base64_encoded strings */
1148 if($attr[1][0] == ":"){
1149 $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1150 $attr[1]=base64_decode($attr[1]);
1151 }
1153 $attr[1] = trim($attr[1]);
1155 /* Check for attributes that are used more than once */
1156 if(!isset($data[$attr[0]])) {
1157 $data[$attr[0]]=$attr[1];
1158 } else {
1159 $tmp = $data[$attr[0]];
1161 if(!is_array($tmp)) {
1162 $new[0]=$tmp;
1163 $new[1]=$attr[1];
1164 $datas[$attr[0]]['count']=1;
1165 $data[$attr[0]]=$new;
1166 } else {
1167 $cnt = $datas[$attr[0]]['count'];
1168 $cnt ++;
1169 $data[$attr[0]][$cnt]=$attr[1];
1170 $datas[$attr[0]]['count'] = $cnt;
1171 }
1172 }
1173 }
1174 }
1176 /* If dn is an index of data, we should try to insert the data */
1177 if(isset($data['dn'])) {
1179 /* Fix dn */
1180 $tmp = gosa_ldap_explode_dn($data['dn']);
1181 unset($tmp['count']);
1182 $newdn ="";
1183 foreach($tmp as $tm){
1184 $newdn.= trim($tm).",";
1185 }
1186 $newdn = preg_replace("/,$/","",$newdn);
1187 $data['dn'] = $newdn;
1189 /* Creating Entry */
1190 $this->cd($data['dn']);
1192 /* Delete existing entry */
1193 if($delete){
1194 $this->rmdir_recursive($data['dn']);
1195 }
1197 /* Create missing trees */
1198 $this->cd ($this->basedn);
1199 $this->cd($config->current['BASE']);
1200 $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1201 $this->cd($data['dn']);
1203 $dn = $data['dn'];
1204 unset($data['dn']);
1206 if(!$modify){
1208 $this->cat($dn);
1209 if($this->count()){
1211 /* The destination entry exists, overwrite it with the new entry */
1212 $attrs = $this->fetch();
1213 foreach($attrs as $name => $value ){
1214 if(!is_numeric($name)){
1215 if(in_array($name,array("dn","count"))) continue;
1216 if(!isset($data[$name])){
1217 $data[$name] = array();
1218 }
1219 }
1220 }
1221 $ret = $this->modify($data);
1223 }else{
1225 /* The destination entry doesn't exists, create it */
1226 $ret = $this->add($data);
1227 }
1229 } else {
1231 /* Keep all vars that aren't touched by this ldif */
1232 $ret = $this->modify($data);
1233 }
1234 }
1235 show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1236 return($ret);
1237 }
1240 function importcsv($str)
1241 {
1242 $lines = split("\n",$str);
1243 foreach($lines as $line)
1244 {
1245 /* continue if theres a comment */
1246 if(substr(trim($line),0,1)=="#"){
1247 continue;
1248 }
1250 $line= str_replace ("\t\t","\t",$line);
1251 $line= str_replace ("\t" ,"," ,$line);
1252 echo $line;
1254 $cells = split(",",$line ) ;
1255 $linet= str_replace ("\t\t",",",$line);
1256 $cells = split("\t",$line);
1257 $count = count($cells);
1258 }
1260 }
1262 function get_objectclasses()
1263 {
1264 $objectclasses = array();
1265 global $config;
1267 /* Only read schema if it is allowed */
1268 if(isset($config) && preg_match("/config/i",get_class($config))){
1269 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1270 return($objectclasses);
1271 }
1272 }
1274 # Get base to look for schema
1275 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1276 if(!$sr){
1277 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1278 }
1280 $attr = @ldap_get_entries($this->cid,$sr);
1281 if (!isset($attr[0]['subschemasubentry'][0])){
1282 return array();
1283 }
1285 /* Get list of objectclasses and fill array */
1286 $nb= $attr[0]['subschemasubentry'][0];
1287 $objectclasses= array();
1288 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1289 $attrs= ldap_get_entries($this->cid,$sr);
1290 if (!isset($attrs[0])){
1291 return array();
1292 }
1293 foreach ($attrs[0]['objectclasses'] as $val){
1294 if (preg_match('/^[0-9]+$/', $val)){
1295 continue;
1296 }
1297 $name= "OID";
1298 $pattern= split(' ', $val);
1299 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1300 $objectclasses[$ocname]= array();
1302 foreach($pattern as $chunk){
1303 switch($chunk){
1305 case '(':
1306 $value= "";
1307 break;
1309 case ')': if ($name != ""){
1310 $objectclasses[$ocname][$name]= $this->value2container($value);
1311 }
1312 $name= "";
1313 $value= "";
1314 break;
1316 case 'NAME':
1317 case 'DESC':
1318 case 'SUP':
1319 case 'STRUCTURAL':
1320 case 'ABSTRACT':
1321 case 'AUXILIARY':
1322 case 'MUST':
1323 case 'MAY':
1324 if ($name != ""){
1325 $objectclasses[$ocname][$name]= $this->value2container($value);
1326 }
1327 $name= $chunk;
1328 $value= "";
1329 break;
1331 default: $value.= $chunk." ";
1332 }
1333 }
1335 }
1336 return $objectclasses;
1337 }
1340 function value2container($value)
1341 {
1342 /* Set emtpy values to "true" only */
1343 if (preg_match('/^\s*$/', $value)){
1344 return true;
1345 }
1347 /* Remove ' and " if needed */
1348 $value= preg_replace('/^[\'"]/', '', $value);
1349 $value= preg_replace('/[\'"] *$/', '', $value);
1351 /* Convert to array if $ is inside... */
1352 if (preg_match('/\$/', $value)){
1353 $container= preg_split('/\s*\$\s*/', $value);
1354 } else {
1355 $container= chop($value);
1356 }
1358 return ($container);
1359 }
1362 function log($string)
1363 {
1364 if (isset($_SESSION['config'])){
1365 $cfg= $_SESSION['config'];
1366 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1367 syslog (LOG_INFO, $string);
1368 }
1369 }
1370 }
1372 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1373 function getCn($dn){
1374 $simple= split(",", $dn);
1376 foreach($simple as $piece) {
1377 $partial= split("=", $piece);
1379 if($partial[0] == "cn"){
1380 return $partial[1];
1381 }
1382 }
1383 }
1386 function get_naming_contexts($server, $admin= "", $password= "")
1387 {
1388 /* Build LDAP connection */
1389 $ds= ldap_connect ($server);
1390 if (!$ds) {
1391 die ("Can't bind to LDAP. No check possible!");
1392 }
1393 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1394 $r= ldap_bind ($ds, $admin, $password);
1396 /* Get base to look for naming contexts */
1397 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1398 $attr= @ldap_get_entries($ds,$sr);
1400 return ($attr[0]['namingcontexts']);
1401 }
1404 function get_root_dse($server, $admin= "", $password= "")
1405 {
1406 /* Build LDAP connection */
1407 $ds= ldap_connect ($server);
1408 if (!$ds) {
1409 die ("Can't bind to LDAP. No check possible!");
1410 }
1411 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1412 $r= ldap_bind ($ds, $admin, $password);
1414 /* Get base to look for naming contexts */
1415 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1416 $attr= @ldap_get_entries($ds,$sr);
1418 /* Return empty array, if nothing was set */
1419 if (!isset($attr[0])){
1420 return array();
1421 }
1423 /* Rework array... */
1424 $result= array();
1425 for ($i= 0; $i<$attr[0]['count']; $i++){
1426 $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1427 unset($result[$attr[0][$i]]['count']);
1428 }
1430 return ($result);
1431 }
1434 }
1436 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1437 ?>