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 }
193 /* Checks if there is still unfetched data
194 */
195 function checkResult()
196 {
197 /* Check if we have started a search before */
198 if(is_resource(@ldap_first_entry($this->cid, $this->sr))){
200 /* Check if there are still unfetched elements */
201 if(is_resource(@ldap_next_entry($this->cid, $this->re))){
202 trigger_error("A new search was initiated while the an older search wasn't fetched completely.");
203 }
204 }
205 }
207 function search($filter, $attrs= array())
208 {
209 if($this->hascon){
210 if ($this->reconnect) $this->connect();
212 /* Check if there are still unfetched objects from last search
213 */
214 $this->checkResult();
216 $start = microtime();
217 $this->clearResult();
218 $this->sr = @ldap_search($this->cid, $this->fix($this->basedn), $filter, $attrs);
219 $this->error = @ldap_error($this->cid);
220 $this->resetResult();
221 $this->hasres=true;
223 /* Check if query took longer as specified in max_ldap_query_time */
224 if($this->max_ldap_query_time){
225 $diff = get_MicroTimeDiff($start,microtime());
226 if($diff > $this->max_ldap_query_time){
227 print_red(sprintf(_("The LDAP server is slow (%.2fs for the last query). This may be responsible for performance breakdowns."),$diff)) ;
228 }
229 }
231 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".$this->fix($this->basedn)."', '$filter')");
233 return($this->sr);
234 }else{
235 $this->error = "Could not connect to LDAP server";
236 return("");
237 }
238 }
240 function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
241 {
242 if($this->hascon){
243 if ($this->reconnect) $this->connect();
245 /* Check if there are still unfetched objects from last search
246 */
247 $this->checkResult();
249 $this->clearResult();
250 if ($basedn == "")
251 $basedn = $this->basedn;
252 else
253 $basedn= $this->convert($basedn);
255 $start = microtime();
257 $this->sr = @ldap_list($this->cid, $this->fix($basedn), $filter,$attrs);
258 $this->error = @ldap_error($this->cid);
259 $this->resetResult();
260 $this->hasres=true;
262 /* Check if query took longer as specified in max_ldap_query_time */
263 if($this->max_ldap_query_time){
264 $diff = get_MicroTimeDiff($start,microtime());
265 if($diff > $this->max_ldap_query_time){
266 print_red(sprintf(_("The ldapserver is answering very slow (%.2f), this may be responsible for performance breakdowns."),$diff)) ;
267 }
268 }
270 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".$this->fix($basedn)."', '$filter')");
272 return($this->sr);
273 }else{
274 $this->error = "Could not connect to LDAP server";
275 return("");
276 }
277 }
279 function cat($dn,$attrs= array("*"))
280 {
281 if($this->hascon){
282 if ($this->reconnect) $this->connect();
284 /* Check if there are still unfetched objects from last search
285 */
286 $this->checkResult();
288 $start = microtime();
289 $this->clearResult();
290 $filter = "(objectclass=*)";
291 $this->sr = @ldap_read($this->cid, $this->fix($dn), $filter,$attrs);
292 $this->error = @ldap_error($this->cid);
293 $this->resetResult();
294 $this->hasres=true;
295 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=cat('".$this->fix($dn)."')");
296 return($this->sr);
297 }else{
298 $this->error = "Could not connect to LDAP server";
299 return("");
300 }
301 }
303 function set_size_limit($size)
304 {
305 /* Ignore zero settings */
306 if ($size == 0){
307 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
308 }
309 if($this->hascon){
310 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
311 } else {
312 $this->error = "Could not connect to LDAP server";
313 }
314 }
316 function fetch()
317 {
318 $att= array();
319 if($this->hascon){
320 if($this->hasres){
321 if ($this->start == 0)
322 {
323 $this->start = 1;
324 $this->re= @ldap_first_entry($this->cid, $this->sr);
325 } else {
326 $this->re= @ldap_next_entry($this->cid, $this->re);
327 }
328 if ($this->re)
329 {
330 $att= @ldap_get_attributes($this->cid, $this->re);
331 $att['dn']= trim($this->convert(@ldap_get_dn($this->cid, $this->re)));
332 }
333 $this->error = @ldap_error($this->cid);
334 if (!isset($att)){
335 $att= array();
336 }
337 return($att);
338 }else{
339 $this->error = "Perform a Fetch with no Search";
340 return("");
341 }
342 }else{
343 $this->error = "Could not connect to LDAP server";
344 return("");
345 }
346 }
348 function resetResult()
349 {
350 $this->start = 0;
351 }
353 function clearResult()
354 {
355 if($this->hasres){
356 $this->hasres = false;
357 @ldap_free_result($this->sr);
358 }
359 }
361 function getDN()
362 {
363 if($this->hascon){
364 if($this->hasres){
366 if(!$this->re)
367 {
368 $this->error = "Perform a Fetch with no valid Result";
369 }
370 else
371 {
372 $rv = @ldap_get_dn($this->cid, $this->re);
374 $this->error = @ldap_error($this->cid);
375 return(trim($this->convert($rv)));
376 }
377 }else{
378 $this->error = "Perform a Fetch with no Search";
379 return("");
380 }
381 }else{
382 $this->error = "Could not connect to LDAP server";
383 return("");
384 }
385 }
387 function count()
388 {
389 if($this->hascon){
390 if($this->hasres){
391 $rv = @ldap_count_entries($this->cid, $this->sr);
392 $this->error = @ldap_error($this->cid);
393 return($rv);
394 }else{
395 $this->error = "Perform a Fetch with no Search";
396 return("");
397 }
398 }else{
399 $this->error = "Could not connect to LDAP server";
400 return("");
401 }
402 }
404 function rm($attrs = "", $dn = "")
405 {
406 if($this->hascon){
407 if ($this->reconnect) $this->connect();
408 if ($dn == "")
409 $dn = $this->basedn;
411 $r = @ldap_mod_del($this->cid, $this->fix($dn), $attrs);
412 $this->error = @ldap_error($this->cid);
413 return($r);
414 }else{
415 $this->error = "Could not connect to LDAP server";
416 return("");
417 }
418 }
420 function rename($attrs, $dn = "")
421 {
422 if($this->hascon){
423 if ($this->reconnect) $this->connect();
424 if ($dn == "")
425 $dn = $this->basedn;
427 $r = @ldap_mod_replace($this->cid, $this->fix($dn), $attrs);
428 $this->error = @ldap_error($this->cid);
429 return($r);
430 }else{
431 $this->error = "Could not connect to LDAP server";
432 return("");
433 }
434 }
436 function rmdir($deletedn)
437 {
438 if($this->hascon){
439 if ($this->reconnect) $this->connect();
440 $start= microtime();
441 $r = @ldap_delete($this->cid, $this->fix($deletedn));
442 $this->error = @ldap_error($this->cid);
443 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=delete('".$this->fix($deletedn)."')");
444 return($r ? $r : 0);
445 }else{
446 $this->error = "Could not connect to LDAP server";
447 return("");
448 }
449 }
451 /**
452 * Function rmdir_recursive
453 *
454 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
455 * Parameters: The dn to delete
456 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
457 *
458 */
460 function rmdir_recursive($deletedn)
461 {
462 if($this->hascon){
463 if ($this->reconnect) $this->connect();
464 $delarray= array();
466 /* Get sorted list of dn's to delete */
467 $this->ls ("(objectClass=*)",$deletedn);
468 while ($this->fetch()){
469 $deldn= $this->getDN();
470 $delarray[$deldn]= strlen($deldn);
471 }
472 arsort ($delarray);
473 reset ($delarray);
475 /* Really Delete ALL dn's in subtree */
476 foreach ($delarray as $key => $value){
477 $this->rmdir_recursive($key);
478 }
480 /* Finally Delete own Node */
481 $r = @ldap_delete($this->cid, $this->fix($deletedn));
482 $this->error = @ldap_error($this->cid);
483 return($r ? $r : 0);
484 }else{
485 $this->error = "Could not connect to LDAP server";
486 return("");
487 }
488 }
490 /* Copy given attributes and sub-dns with attributes to destination dn
491 */
492 function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
493 {
494 error_reporting(E_ALL);
496 if($is_first){
497 echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".@LDAP::fix($sourcedn)."</i>")."</h2>";
498 }else{
499 if(preg_match("/^ou=/",$sourcedn)){
500 echo "<h3>"._("Processing")." <i>".@LDAP::fix($destinationdn)."</i></h3>";
501 }else{
502 $tmp = split(",",$sourcedn);
504 echo " <b>"._("Object").":</b> ";
506 $deststr = @LDAP::fix($destinationdn);
507 if(strlen($deststr) > 96){
508 $deststr = substr($deststr,0,96)."...";
509 }
511 echo $deststr."<br>";
512 }
513 }
515 flush();
517 if($this->hascon){
518 if ($this->reconnect) $this->connect();
520 /* Save base dn */
521 $basedn= $this->basedn;
522 $delarray= array();
524 /* Check if destination entry already exists */
525 $this->cat($destinationdn);
527 if($this->count()){
528 return;
529 }else{
531 $this->clearResult();
533 /* Get source entry */
534 $this->cd($basedn);
535 $this->cat($sourcedn);
536 $attr = $this->fetch();
538 /* Error while fetching object / attribute abort*/
539 if((!$attr) || (count($attr)) ==0) {
540 echo _("Error while fetching source dn - aborted!");
541 return;
542 }
544 /* check if this is a department */
545 if(in_array("organizationalUnit",$attr['objectClass'])){
546 $attr['dn'] = $this->convert($destinationdn);
547 $this->cd($basedn);
548 $this->create_missing_trees($destinationdn);
549 $this->cd($destinationdn);
551 /* If is first entry, append FAIbranch to department entry */
552 if($is_first){
553 $this->cat($destinationdn);
554 $attr= $this->fetch();
556 /* Filter unneeded informations */
557 foreach($attr as $key => $value){
558 if(is_numeric($key)) unset($attr[$key]);
559 if(isset($attr[$key]['count'])){
560 if(is_array($attr[$key])){
561 unset($attr[$key]['count']);
562 }
563 }
564 }
566 unset($attr['count']);
567 unset($attr['dn']);
569 /* Add marking attribute */
570 $attr['objectClass'][] = "FAIbranch";
572 /* Add this entry */
573 $this->modify($attr);
574 }
575 }else{
577 /* If this is no department */
578 foreach($attr as $key => $value){
579 if(in_array($key ,array("FAItemplateFile","FAIscript", "gotoLogonScript", "gosaApplicationIcon"))){
580 $sr= ldap_read($this->cid, $this->fix($sourcedn), "$key=*", array($key));
581 $ei= ldap_first_entry($this->cid, $sr);
582 if ($tmp= @ldap_get_values_len($this->cid, $ei,$key)){
583 $attr[$key] = $tmp;
584 }
585 }
587 if(is_numeric($key)) unset($attr[$key]);
588 if(isset($attr[$key]['count'])){
589 if(is_array($attr[$key])){
590 unset($attr[$key]['count']);
591 }
592 }
593 }
594 unset($attr['count']);
595 unset($attr['dn']);
597 if(!in_array("gosaApplication" , $attr['objectClass'])){
598 if($type=="branch"){
599 $attr['FAIstate'] ="branch";
600 }elseif($type=="freeze"){
601 $attr['FAIstate'] ="freeze";
602 }else{
603 print_red(_("Unknown FAIstate %s"),$type);
604 }
605 }elseif(in_array("gosaApplication",$attr['objectClass'])){
606 if(!in_array("FAIobject",$attr['objectClass'])){
607 $attr['objectClass'][] = "FAIobject";
608 }
609 $attr['FAIstate'] = $type;
610 }
612 /* Replace FAIdebianRelease with new release name */
613 if(in_array("FAIpackageList" , $attr['objectClass'])){
614 $attr['FAIdebianRelease'] = $destinationName;
615 if($type=="branch"){
616 $attr['FAIstate'] ="branch";
617 }elseif($type=="freeze"){
618 $attr['FAIstate'] ="freeze";
619 }else{
620 print_red(_("Unknown FAIstate %s"),$type);
621 }
622 }
624 /* Add entry */
625 $this->cd($destinationdn);
626 $this->cat($destinationdn);
627 $a = $this->fetch();
628 if(!count($a)){
629 $this->add($attr);
630 }
632 if($this->error != "Success"){
633 /* Some error occurred */
634 print "---------------------------------------------";
635 print $this->get_error()."<br>";
636 print $sourcedn."<br>";
637 print $destinationdn."<br>";
638 print_a( $attr);
639 exit();
640 }
641 }
642 }
644 echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
646 $this->ls ("(objectClass=*)",$sourcedn);
647 while ($this->fetch()){
648 $deldn= $this->getDN();
649 $delarray[$deldn]= strlen($deldn);
650 }
651 asort ($delarray);
652 reset ($delarray);
654 $depth ++;
655 foreach($delarray as $dn => $bla){
656 if($dn != $destinationdn){
657 $this->cd($basedn);
658 $item = $this->fetch($this->cat($dn));
659 if(!in_array("FAIbranch",$item['objectClass'])){
660 $this->copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
661 }
662 }
663 }
664 }
665 if($is_first){
666 echo "<p class='seperator'> </p>";
667 }
669 }
671 function modify($attrs)
672 {
673 if(count($attrs) == 0){
674 return (0);
675 }
676 if($this->hascon){
677 if ($this->reconnect) $this->connect();
678 $start= microtime();
679 $r = @ldap_modify($this->cid, $this->fix($this->basedn), $attrs);
680 $this->error = @ldap_error($this->cid);
681 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=modify('$this->basedn')");
682 return($r ? $r : 0);
683 }else{
684 $this->error = "Could not connect to LDAP server";
685 return("");
686 }
687 }
689 function add($attrs)
690 {
691 if($this->hascon){
692 if ($this->reconnect) $this->connect();
693 $start= microtime();
694 $r = @ldap_add($this->cid, $this->fix($this->basedn), $attrs);
695 $this->error = @ldap_error($this->cid);
696 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=add('$this->basedn')");
697 return($r ? $r : 0);
698 }else{
699 $this->error = "Could not connect to LDAP server";
700 return("");
701 }
702 }
705 function create_missing_trees($target)
706 {
707 global $config;
709 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
711 if ($target == $this->basedn){
712 $l= array("dummy");
713 } else {
714 $l= array_reverse(gosa_ldap_explode_dn($real_path));
715 }
716 unset($l['count']);
717 $cdn= $this->basedn;
718 $tag= "";
720 /* Load schema if available... */
721 $classes= $this->get_objectclasses();
723 foreach ($l as $part){
724 if ($part != "dummy"){
725 $cdn= "$part,$cdn";
726 }
728 /* Ignore referrals */
729 $found= false;
730 foreach($this->referrals as $ref){
731 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
732 if ($base == $cdn){
733 $found= true;
734 break;
735 }
736 }
737 if ($found){
738 continue;
739 }
741 $this->cat ($cdn);
742 $attrs= $this->fetch();
744 /* Create missing entry? */
745 if (count ($attrs)){
746 /* Catch the tag - if present */
747 if (isset($attrs['gosaUnitTag'][0])){
748 $tag= $attrs['gosaUnitTag'][0];
749 }
751 } else {
752 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
753 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
755 $na= array();
757 /* Automatic or traditional? */
758 if(count($classes)){
760 /* Get name of first matching objectClass */
761 $ocname= "";
762 foreach($classes as $class){
763 if (isset($class['MUST']) && $class['MUST'] == "$type"){
765 /* Look for first classes that is structural... */
766 if (isset($class['STRUCTURAL'])){
767 $ocname= $class['NAME'];
768 break;
769 }
771 /* Look for classes that are auxiliary... */
772 if (isset($class['AUXILIARY'])){
773 $ocname= $class['NAME'];
774 }
775 }
776 }
778 /* Bail out, if we've nothing to do... */
779 if ($ocname == ""){
780 print_red(sprintf(_("Autocreation of subtree failed. No objectClass found for attribute '%s'."), $type));
781 echo $_SESSION['errors'];
782 exit;
783 }
785 /* Assemble_entry */
786 if ($tag != ""){
787 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
788 $na["gosaUnitTag"]= $tag;
789 } else {
790 $na['objectClass']= array($ocname);
791 }
792 if (isset($classes[$ocname]['AUXILIARY'])){
793 $na['objectClass'][]= $classes[$ocname]['SUP'];
794 }
795 if ($type == "dc"){
796 /* This is bad actually, but - tell me a better way? */
797 $na['objectClass'][]= 'locality';
798 }
799 $na[$type]= $param;
800 if (is_array($classes[$ocname]['MUST'])){
801 foreach($classes[$ocname]['MUST'] as $attr){
802 $na[$attr]= "filled";
803 }
804 }
806 } else {
808 /* Use alternative add... */
809 switch ($type){
810 case 'ou':
811 if ($tag != ""){
812 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
813 $na["gosaUnitTag"]= $tag;
814 } else {
815 $na["objectClass"]= "organizationalUnit";
816 }
817 $na["ou"]= $param;
818 break;
819 case 'dc':
820 if ($tag != ""){
821 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
822 $na["gosaUnitTag"]= $tag;
823 } else {
824 $na["objectClass"]= array("dcObject", "top", "locality");
825 }
826 $na["dc"]= $param;
827 break;
828 default:
829 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
830 echo $_SESSION['errors'];
831 exit;
832 }
834 }
835 $this->cd($cdn);
836 $this->add($na);
837 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
838 if (!preg_match('/success/i', $this->error)){
839 return FALSE;
840 }
841 }
842 }
844 return TRUE;
845 }
848 function create_missing_trees_old($target)
849 {
850 /* Ignore create_missing trees if the base equals target */
851 if ($target == $this->basedn){
852 return;
853 }
855 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
856 $tmp = ldap_explode_dn($real_path,0);
857 if(!$tmp){
858 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)));
859 return;
860 }
862 $l= array_reverse($tmp);
863 unset($l['count']);
864 $cdn= $this->basedn;
865 $tag= "";
867 foreach ($l as $part){
868 $cdn= "$part,$cdn";
870 /* Ignore referrals */
871 $found= false;
872 foreach($this->referrals as $ref){
873 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
874 if ($base == $cdn){
875 $found= true;
876 break;
877 }
878 }
879 if ($found){
880 continue;
881 }
883 $this->cat ($cdn);
884 $attrs= $this->fetch();
886 /* Create missing entry? */
887 if (count ($attrs)){
889 /* Catch the tag - if present */
890 if (isset($attrs['gosaUnitTag'][0])){
891 $tag= $attrs['gosaUnitTag'][0];
892 }
894 } else {
895 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
896 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
898 $na= array();
899 switch ($type){
900 case 'ou':
901 if ($tag != ""){
902 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
903 $na["gosaUnitTag"]= $tag;
904 } else {
905 $na["objectClass"]= "organizationalUnit";
906 }
907 $na["ou"]= $param;
908 break;
909 case 'dc':
910 if ($tag != ""){
911 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
912 $na["gosaUnitTag"]= $tag;
913 } else {
914 $na["objectClass"]= array("dcObject", "top", "locality");
915 }
916 $na["dc"]= $param;
917 break;
918 default:
919 print_red(sprintf(_("Autocreation of type '%s' is currently not supported. Please report to the GOsa team."), $type));
920 echo $_SESSION['errors'];
921 exit;
922 }
923 $this->cd($cdn);
924 $this->add($na);
925 }
926 }
927 }
929 function recursive_remove()
930 {
931 $delarray= array();
933 /* Get sorted list of dn's to delete */
934 $this->search ("(objectClass=*)");
935 while ($this->fetch()){
936 $deldn= $this->getDN();
937 $delarray[$deldn]= strlen($deldn);
938 }
939 arsort ($delarray);
940 reset ($delarray);
942 /* Delete all dn's in subtree */
943 foreach ($delarray as $key => $value){
944 $this->rmdir($key);
945 }
946 }
948 function get_attribute($dn, $name,$r_array=0)
949 {
950 $data= "";
951 if ($this->reconnect) $this->connect();
952 $sr= @ldap_read($this->cid, $this->fix($dn), "objectClass=*", array("$name"));
954 /* fill data from LDAP */
955 if ($sr) {
956 $ei= @ldap_first_entry($this->cid, $sr);
957 if ($ei) {
958 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
959 $data= $info[0];
960 }
962 }
963 }
964 if($r_array==0)
965 return ($data);
966 else
967 return ($info);
970 }
974 function get_additional_error()
975 {
976 $error= "";
977 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
978 return ($error);
979 }
981 function get_error()
982 {
983 if ($this->error == 'Success'){
984 return $this->error;
985 } else {
986 $adderror= $this->get_additional_error();
987 if ($adderror != ""){
988 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
989 } else {
990 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
991 }
992 return $error;
993 }
994 }
996 function get_credentials($url, $referrals= NULL)
997 {
998 $ret= array();
999 $url= preg_replace('!\?\?.*$!', '', $url);
1000 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
1002 if ($referrals == NULL){
1003 $referrals= $this->referrals;
1004 }
1006 if (isset($referrals[$server])){
1007 return ($referrals[$server]);
1008 } else {
1009 $ret['ADMIN']= $this->fix($this->binddn);
1010 $ret['PASSWORD']= $this->bindpw;
1011 }
1013 return ($ret);
1014 }
1017 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
1018 {
1019 $display= "";
1021 if ($recursive){
1022 $this->cd($dn);
1023 $this->search("$filter", array('dn'));
1024 while ($attrs= $this->fetch()){
1025 $display.= $this->gen_one_entry($attrs['dn'], $filter, $attributes);
1026 $display.= "\n";
1027 }
1028 } else {
1029 $display.= $this->gen_one_entry($dn);
1030 }
1032 return ($display);
1033 }
1035 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
1036 {
1037 $display= "";
1039 $this->cd($dn);
1040 $this->search("$filter");
1042 $i=0;
1043 while ($attrs= $this->fetch()){
1044 $j=0;
1046 foreach ($attributes as $at){
1047 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
1048 $j++;
1049 }
1051 $i++;
1052 }
1054 return ($display);
1055 }
1058 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
1059 {
1060 $ret = "";
1061 $data = "";
1062 if($this->reconnect){
1063 $this->connect();
1064 }
1066 /* Searching Ldap Tree */
1067 $sr= @ldap_read($this->cid, $this->fix($dn), $filter, $name);
1069 /* Get the first entry */
1070 $entry= @ldap_first_entry($this->cid, $sr);
1072 /* Get all attributes related to that Objekt */
1073 $atts = array();
1075 /* Assemble dn */
1076 $atts[0]['name'] = "dn";
1077 $atts[0]['value'] = array('count' => 1, 0 => $dn);
1079 /* Reset index */
1080 $i = 1 ;
1081 $identifier = array();
1082 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
1083 while ($attribute) {
1084 $i++;
1085 $atts[$i]['name'] = $attribute;
1086 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
1088 /* Next one */
1089 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
1090 }
1092 foreach($atts as $at)
1093 {
1094 for ($i= 0; $i<$at['value']['count']; $i++){
1096 /* Check if we must encode the data */
1097 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
1098 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
1099 } else {
1100 $ret .= $at['name'].": ".$at['value'][$i]."\n";
1101 }
1102 }
1103 }
1105 return($ret);
1106 }
1109 function dn_exists($dn)
1110 {
1111 return @ldap_list($this->cid, $this->fix($dn), "(objectClass=*)", array("objectClass"));
1112 }
1116 function import_complete_ldif($str_attr,&$error,$overwrite,$cleanup)
1117 {
1118 if($this->reconnect) $this->connect();
1120 /* First we have to splitt the string ito detect empty lines
1121 An empty line indicates an new Entry */
1122 $entries = split("\n",$str_attr);
1124 $data = "";
1125 $cnt = 0;
1126 $current_line = 0;
1128 /* Every single line ... */
1129 foreach($entries as $entry) {
1130 $current_line ++;
1132 /* Removing Spaces to ..
1133 .. test if a new entry begins */
1134 $tmp = str_replace(" ","",$data );
1136 /* .. prevent empty lines in an entry */
1137 $tmp2 = str_replace(" ","",$entry);
1139 /* If the Block ends (Empty Line) */
1140 if((empty($entry))&&(!empty($tmp))) {
1141 /* Add collected lines as a complete block */
1142 $all[$cnt] = $data;
1143 $cnt ++;
1144 $data ="";
1145 } else {
1147 /* Append lines ... */
1148 if(!empty($tmp2)) {
1149 /* check if we need base64_decode for this line */
1150 if(ereg("::",$tmp2))
1151 {
1152 $encoded = split("::",$entry);
1153 $attr = $encoded[0];
1154 $value = base64_decode($encoded[1]);
1155 /* Add linenumber */
1156 $data .= $current_line."#".$attr.":".$value."\n";
1157 }
1158 else
1159 {
1160 /* Add Linenumber */
1161 $data .= $current_line."#".$entry."\n";
1162 }
1163 }
1164 }
1165 }
1167 /* The Data we collected is not in the array all[];
1168 For example the Data is stored like this..
1170 all[0] = "1#dn : .... \n
1171 2#ObjectType: person \n ...."
1173 Now we check every insertblock and try to insert */
1174 foreach ( $all as $single) {
1175 $lineone = split("\n",$single);
1176 $ndn = split("#", $lineone[0]);
1177 $line = $ndn[1];
1179 $dnn = split (":",$line,2);
1180 $current_line = $ndn[0];
1181 $dn = $dnn[0];
1182 $value = $dnn[1];
1184 /* Every block must begin with a dn */
1185 if($dn != "dn") {
1186 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
1187 return -2;
1188 }
1190 /* Should we use Modify instead of Add */
1191 $usemodify= false;
1193 /* Delete before insert */
1194 $usermdir= false;
1196 /* The dn address already exists! */
1197 if (($this->dn_exists($value))&&((!$overwrite)&&(!$cleanup))) {
1199 $error= sprintf(_("The dn: '%s' (from line %s) already exists in the LDAP database."), $line, $current_line);
1200 return ALREADY_EXISTING_ENTRY;
1202 } elseif(($this->dn_exists($value))&&($cleanup)){
1204 /* Delete first, then add */
1205 $usermdir = true;
1207 } elseif(($this->dn_exists($value))&&($overwrite)) {
1209 /* Modify instead of Add */
1210 $usemodify = true;
1211 }
1213 /* If we can't Import, return with a file error */
1214 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
1215 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
1216 $current_line);
1217 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
1218 }
1220 return (INSERT_OK);
1221 }
1224 /* Imports a single entry */
1225 function import_single_entry($str_attr,$modify,$delete)
1226 {
1227 if($this->reconnect) $this->connect();
1229 $ret = false;
1230 $rows= split("\n",$str_attr);
1231 $data= false;
1233 foreach($rows as $row) {
1235 /* Check if we use Linenumbers (when import_complete_ldif is called we use
1236 Linenumbers) Linenumbers are use like this 123#attribute : value */
1237 if(!empty($row)) {
1238 if((strpos($row,"#")!=FALSE)&&(strpos($row,"#")<strpos($row,":"))) {
1240 /* We are using line numbers
1241 Because there is a # before a : */
1242 $tmp1= split("#",$row);
1243 $current_line= $tmp1[0];
1244 $row= $tmp1[1];
1245 }
1247 /* Split the line into attribute and value */
1248 $attr = split(":", $row,2);
1249 $attr[0]= trim($attr[0]); /* attribute */
1250 $attr[1]= trim($attr[1]); /* value */
1252 /* Check for attributes that are used more than once */
1253 if(!isset($data[$attr[0]])) {
1254 $data[$attr[0]]=$attr[1];
1255 } else {
1256 $tmp = $data[$attr[0]];
1258 if(!is_array($tmp)) {
1259 $new[0]=$tmp;
1260 $new[1]=$attr[1];
1261 $datas[$attr[0]]['count']=1;
1262 $data[$attr[0]]=$new;
1263 } else {
1264 $cnt = $datas[$attr[0]]['count'];
1265 $cnt ++;
1266 $data[$attr[0]][$cnt]=$attr[1];
1267 $datas[$attr[0]]['count'] = $cnt;
1268 }
1269 }
1270 }
1271 }
1273 /* If dn is an index of data, we should try to insert the data */
1274 if(isset($data['dn'])) {
1275 /* Creating Entry */
1276 $this->cd($data['dn']);
1278 /* Delete existing entry */
1279 if($delete){
1280 $this->rmdir($data['dn']);
1281 }
1283 /* Create missing trees */
1284 $this->create_missing_trees($data['dn']);
1285 unset($data['dn']);
1287 /* If entry exists use modify */
1288 if(!$modify){
1289 $ret = $this->add($data);
1290 } else {
1291 $ret = $this->modify($data);
1292 }
1293 }
1295 return($ret);
1296 }
1299 function importcsv($str)
1300 {
1301 $lines = split("\n",$str);
1302 foreach($lines as $line)
1303 {
1304 /* continue if theres a comment */
1305 if(substr(trim($line),0,1)=="#"){
1306 continue;
1307 }
1309 $line= str_replace ("\t\t","\t",$line);
1310 $line= str_replace ("\t" ,"," ,$line);
1311 echo $line;
1313 $cells = split(",",$line ) ;
1314 $linet= str_replace ("\t\t",",",$line);
1315 $cells = split("\t",$line);
1316 $count = count($cells);
1317 }
1319 }
1321 function get_objectclasses_old()
1322 {
1323 $objectclasses = array();
1325 # Get base to look for schema
1326 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1327 $attr = @ldap_get_entries($this->cid,$sr);
1329 if (!isset($attr[0]['subschemasubentry'][0])){
1330 $this->error = @ldap_error($this->cid);
1331 gosa_log($this->get_error());
1332 return array();
1333 }
1335 # Get list of objectclasses
1336 $nb= $attr[0]['subschemasubentry'][0];
1337 $objectclasses= array();
1338 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1339 $attrs= ldap_get_entries($this->cid,$sr);
1340 if (!isset($attrs[0])){
1341 return array();
1342 }
1343 foreach ($attrs[0]['objectclasses'] as $val){
1344 $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1345 if ($name != $val){
1346 $objectclasses[$name]= $val;
1347 }
1348 }
1350 return $objectclasses;
1351 }
1354 function get_objectclasses()
1355 {
1356 global $config;
1357 $objectclasses = array();
1359 if(isset($config) && preg_match("/config/i",get_class($config))){
1360 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1361 return($objectclasses);
1362 }
1363 }
1365 # Get base to look for schema
1366 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1367 if(!$sr){
1368 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1369 }
1371 $attr = @ldap_get_entries($this->cid,$sr);
1372 if (!isset($attr[0]['subschemasubentry'][0])){
1373 return array();
1374 }
1376 /* Get list of objectclasses and fill array */
1377 $nb= $attr[0]['subschemasubentry'][0];
1378 $objectclasses= array();
1379 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1380 $attrs= ldap_get_entries($this->cid,$sr);
1381 if (!isset($attrs[0])){
1382 return array();
1383 }
1384 foreach ($attrs[0]['objectclasses'] as $val){
1385 if (preg_match('/^[0-9]+$/', $val)){
1386 continue;
1387 }
1388 $name= "OID";
1389 $pattern= split(' ', $val);
1390 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1391 $objectclasses[$ocname]= array();
1393 foreach($pattern as $chunk){
1394 switch($chunk){
1396 case '(':
1397 $value= "";
1398 break;
1400 case ')': if ($name != ""){
1401 $objectclasses[$ocname][$name]= $this->value2container($value);
1402 }
1403 $name= "";
1404 $value= "";
1405 break;
1407 case 'NAME':
1408 case 'DESC':
1409 case 'SUP':
1410 case 'STRUCTURAL':
1411 case 'ABSTRACT':
1412 case 'AUXILIARY':
1413 case 'MUST':
1414 case 'MAY':
1415 if ($name != ""){
1416 $objectclasses[$ocname][$name]= $this->value2container($value);
1417 }
1418 $name= $chunk;
1419 $value= "";
1420 break;
1422 default: $value.= $chunk." ";
1423 }
1424 }
1426 }
1428 return $objectclasses;
1429 }
1431 function value2container($value)
1432 {
1433 /* Set emtpy values to "true" only */
1434 if (preg_match('/^\s*$/', $value)){
1435 return true;
1436 }
1438 /* Remove ' and " if needed */
1439 $value= preg_replace('/^[\'"]/', '', $value);
1440 $value= preg_replace('/[\'"] *$/', '', $value);
1442 /* Convert to array if $ is inside... */
1443 if (preg_match('/\$/', $value)){
1444 $container= preg_split('/\s*\$\s*/', $value);
1445 } else {
1446 $container= chop($value);
1447 }
1449 return ($container);
1450 }
1452 function log($string)
1453 {
1454 if (isset($_SESSION['config'])){
1455 $cfg= $_SESSION['config'];
1456 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1457 syslog (LOG_INFO, $string);
1458 }
1459 }
1460 }
1462 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1463 function getCn($dn){
1464 $simple= split(",", $dn);
1466 foreach($simple as $piece) {
1467 $partial= split("=", $piece);
1469 if($partial[0] == "cn"){
1470 return $partial[1];
1471 }
1472 }
1474 }
1476 function get_naming_contexts($server, $admin= "", $password= "")
1477 {
1478 /* Build LDAP connection */
1479 $ds= ldap_connect ($server);
1480 if (!$ds) {
1481 die ("Can't bind to LDAP. No check possible!");
1482 }
1483 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1484 $r= ldap_bind ($ds, $admin, $password);
1486 /* Get base to look for naming contexts */
1487 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1488 $attr= @ldap_get_entries($ds,$sr);
1490 return ($attr[0]['namingcontexts']);
1491 }
1493 }
1495 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1496 ?>