1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 * Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
6 * Copyright (C) 1998 Eric Kilfoil <eric@ipass.net>
7 *
8 * ID: $$Id$$
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
25 define("ALREADY_EXISTING_ENTRY",-10001);
26 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
27 define("NO_FILE_UPLOADED",10003);
28 define("INSERT_OK",10000);
29 define("SPECIALS_OVERRIDE", TRUE);
31 class LDAP{
33 var $hascon =false;
34 var $hasres =false;
35 var $reconnect=false;
36 var $tls = false;
37 var $basedn ="";
38 var $cid;
39 var $error = ""; // Any error messages to be returned can be put here
40 var $start = 0; // 0 if we are fetching the first entry, otherwise 1
41 var $objectClasses = array(); // Information read from slapd.oc.conf
42 var $binddn = "";
43 var $bindpw = "";
44 var $hostname = "";
45 var $follow_referral = FALSE;
46 var $referrals= array();
47 var $max_ldap_query_time = 0; // 0, empty or negative values will disable this check
49 function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
50 {
51 global $config;
52 $this->follow_referral= $follow_referral;
53 $this->tls=$tls;
54 $this->binddn=LDAP::convert($binddn);
56 $this->bindpw=$bindpw;
57 $this->hostname=$hostname;
59 /* Check if MAX_LDAP_QUERY_TIME is defined */
60 if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
61 $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
62 $this->max_ldap_query_time = (float)($str);
63 }
65 $this->connect();
66 }
69 /* Function to replace all problematic characters inside a DN by \001XX, where
70 \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
71 that this character is inside a DN.
73 Currently used codes:
74 , => CO
75 \2C => CO
76 ( => OB
77 ) => CB
78 / => SL */
79 static function convert($dn)
80 {
81 if (SPECIALS_OVERRIDE == TRUE){
82 $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
83 array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
84 $dn);
85 return (preg_replace('/,\s+/', ',', $tmp));
86 } else {
87 return ($dn);
88 }
89 }
92 /* Function to fix all problematic characters inside a DN by replacing \001XX
93 codes to their original values. See "convert" for mor information.
94 ',' characters are always expanded to \, (not \2C), since all tested LDAP
95 servers seem to take it the correct way. */
96 static function fix($dn)
97 {
98 if (SPECIALS_OVERRIDE == TRUE){
99 return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
100 array("\,", "(", ")", "/"),
101 $dn));
102 } else {
103 return ($dn);
104 }
105 }
108 /* Function to fix problematic characters in DN's that are used for search
109 requests. I.e. member=.... */
110 static function prepare4filter($dn)
111 {
112 return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
113 }
116 function connect()
117 {
118 $this->hascon=false;
119 $this->reconnect=false;
120 if ($this->cid= @ldap_connect($this->hostname)) {
121 @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
122 if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
123 @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
124 @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
125 }
126 if (function_exists("ldap_start_tls") && $this->tls){
127 @ldap_start_tls($this->cid);
128 }
130 $this->error = "No Error";
131 if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
132 $this->error = "Success";
133 $this->hascon=true;
134 } else {
135 if ($this->reconnect){
136 if ($this->error != "Success"){
137 $this->error = "Could not rebind to " . $this->binddn;
138 }
139 } else {
140 $this->error = "Could not bind to " . $this->binddn;
141 }
142 }
143 } else {
144 $this->error = "Could not connect to LDAP server";
145 }
146 }
148 function rebind($ldap, $referral)
149 {
150 $credentials= $this->get_credentials($referral);
151 if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
152 $this->error = "Success";
153 $this->hascon=true;
154 $this->reconnect= true;
155 return (0);
156 } else {
157 $this->error = "Could not bind to " . $credentials['ADMIN'];
158 return NULL;
159 }
160 }
162 function reconnect()
163 {
164 if ($this->reconnect){
165 @ldap_unbind($this->cid);
166 $this->cid = NULL;
167 }
168 }
170 function unbind()
171 {
172 @ldap_unbind($this->cid);
173 $this->cid = NULL;
174 }
176 function disconnect()
177 {
178 if($this->hascon){
179 @ldap_close($this->cid);
180 $this->hascon=false;
181 }
182 }
184 function cd($dir)
185 {
186 if ($dir == "..")
187 $this->basedn = $this->getParentDir();
188 else
189 $this->basedn = LDAP::convert($dir);
190 }
192 function getParentDir($basedn = "")
193 {
194 if ($basedn=="")
195 $basedn = $this->basedn;
196 else
197 $basedn = LDAP::convert($this->basedn);
198 return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
199 }
201 function search($filter, $attrs= array())
202 {
203 if($this->hascon){
204 if ($this->reconnect) $this->connect();
206 $start = microtime();
208 $this->clearResult();
209 $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
210 $this->error = @ldap_error($this->cid);
211 $this->resetResult();
212 $this->hasres=true;
214 /* Check if query took longer as specified in max_ldap_query_time */
215 if($this->max_ldap_query_time){
216 $diff = get_MicroTimeDiff($start,microtime());
217 if($diff > $this->max_ldap_query_time){
218 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
219 }
220 }
222 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
223 return($this->sr);
224 }else{
225 $this->error = "Could not connect to LDAP server";
226 return("");
227 }
228 }
230 function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
231 {
232 if($this->hascon){
233 if ($this->reconnect) $this->connect();
234 $this->clearResult();
235 if ($basedn == "")
236 $basedn = $this->basedn;
237 else
238 $basedn= LDAP::convert($basedn);
240 $start = microtime();
241 $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
242 $this->error = @ldap_error($this->cid);
243 $this->resetResult();
244 $this->hasres=true;
246 /* Check if query took longer as specified in max_ldap_query_time */
247 if($this->max_ldap_query_time){
248 $diff = get_MicroTimeDiff($start,microtime());
249 if($diff > $this->max_ldap_query_time){
250 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
251 }
252 }
254 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
256 return($this->sr);
257 }else{
258 $this->error = "Could not connect to LDAP server";
259 return("");
260 }
261 }
263 function cat($dn,$attrs= array("*"))
264 {
265 if($this->hascon){
266 if ($this->reconnect) $this->connect();
267 $this->clearResult();
268 $filter = "(objectclass=*)";
269 $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
270 $this->error = @ldap_error($this->cid);
271 $this->resetResult();
272 $this->hasres=true;
273 return($this->sr);
274 }else{
275 $this->error = "Could not connect to LDAP server";
276 return("");
277 }
278 }
280 function set_size_limit($size)
281 {
282 /* Ignore zero settings */
283 if ($size == 0){
284 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
285 }
286 if($this->hascon){
287 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
288 } else {
289 $this->error = "Could not connect to LDAP server";
290 }
291 }
293 function fetch()
294 {
295 $att= array();
296 if($this->hascon){
297 if($this->hasres){
298 if ($this->start == 0)
299 {
300 if ($this->sr){
301 $this->start = 1;
302 $this->re= @ldap_first_entry($this->cid, $this->sr);
303 } else {
304 return array();
305 }
306 } else {
307 $this->re= @ldap_next_entry($this->cid, $this->re);
308 }
309 if ($this->re)
310 {
311 $att= @ldap_get_attributes($this->cid, $this->re);
312 $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
313 }
314 $this->error = @ldap_error($this->cid);
315 if (!isset($att)){
316 $att= array();
317 }
318 return($att);
319 }else{
320 $this->error = "Perform a Fetch with no Search";
321 return("");
322 }
323 }else{
324 $this->error = "Could not connect to LDAP server";
325 return("");
326 }
327 }
329 function resetResult()
330 {
331 $this->start = 0;
332 }
334 function clearResult()
335 {
336 if($this->hasres){
337 $this->hasres = false;
338 @ldap_free_result($this->sr);
339 }
340 }
342 function getDN()
343 {
344 if($this->hascon){
345 if($this->hasres){
347 if(!$this->re)
348 {
349 $this->error = "Perform a Fetch with no valid Result";
350 }
351 else
352 {
353 $rv = @ldap_get_dn($this->cid, $this->re);
355 $this->error = @ldap_error($this->cid);
356 return(trim(LDAP::convert($rv)));
357 }
358 }else{
359 $this->error = "Perform a Fetch with no Search";
360 return("");
361 }
362 }else{
363 $this->error = "Could not connect to LDAP server";
364 return("");
365 }
366 }
368 function count()
369 {
370 if($this->hascon){
371 if($this->hasres){
372 $rv = @ldap_count_entries($this->cid, $this->sr);
373 $this->error = @ldap_error($this->cid);
374 return($rv);
375 }else{
376 $this->error = "Perform a Fetch with no Search";
377 return("");
378 }
379 }else{
380 $this->error = "Could not connect to LDAP server";
381 return("");
382 }
383 }
385 function rm($attrs = "", $dn = "")
386 {
387 if($this->hascon){
388 if ($this->reconnect) $this->connect();
389 if ($dn == "")
390 $dn = $this->basedn;
392 $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
393 $this->error = @ldap_error($this->cid);
394 return($r);
395 }else{
396 $this->error = "Could not connect to LDAP server";
397 return("");
398 }
399 }
401 function rename($attrs, $dn = "")
402 {
403 if($this->hascon){
404 if ($this->reconnect) $this->connect();
405 if ($dn == "")
406 $dn = $this->basedn;
408 $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
409 $this->error = @ldap_error($this->cid);
410 return($r);
411 }else{
412 $this->error = "Could not connect to LDAP server";
413 return("");
414 }
415 }
417 function rmdir($deletedn)
418 {
419 if($this->hascon){
420 if ($this->reconnect) $this->connect();
421 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
422 $this->error = @ldap_error($this->cid);
423 return($r ? $r : 0);
424 }else{
425 $this->error = "Could not connect to LDAP server";
426 return("");
427 }
428 }
430 /**
431 * Function rmdir_recursive
432 *
433 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
434 * Parameters: The dn to delete
435 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
436 *
437 */
439 function rmdir_recursive($deletedn)
440 {
441 if($this->hascon){
442 if ($this->reconnect) $this->connect();
443 $delarray= array();
445 /* Get sorted list of dn's to delete */
446 $this->ls ("(objectClass=*)",$deletedn);
447 while ($this->fetch()){
448 $deldn= $this->getDN();
449 $delarray[$deldn]= strlen($deldn);
450 }
451 arsort ($delarray);
452 reset ($delarray);
454 /* Really Delete ALL dn's in subtree */
455 foreach ($delarray as $key => $value){
456 $this->rmdir_recursive($key);
457 }
459 /* Finally Delete own Node */
460 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
461 $this->error = @ldap_error($this->cid);
462 return($r ? $r : 0);
463 }else{
464 $this->error = "Could not connect to LDAP server";
465 return("");
466 }
467 }
470 function modify($attrs)
471 {
472 if(count($attrs) == 0){
473 return (0);
474 }
475 if($this->hascon){
476 if ($this->reconnect) $this->connect();
477 $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
478 $this->error = @ldap_error($this->cid);
479 return($r ? $r : 0);
480 }else{
481 $this->error = "Could not connect to LDAP server";
482 return("");
483 }
484 }
486 function add($attrs)
487 {
488 if($this->hascon){
489 if ($this->reconnect) $this->connect();
490 $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
491 $this->error = @ldap_error($this->cid);
492 return($r ? $r : 0);
493 }else{
494 $this->error = "Could not connect to LDAP server";
495 return("");
496 }
497 }
499 function create_missing_trees($target)
500 {
501 global $config;
503 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
505 if ($target == $this->basedn){
506 $l= array("dummy");
507 } else {
508 $l= array_reverse(gosa_ldap_explode_dn($real_path));
509 }
510 unset($l['count']);
511 $cdn= $this->basedn;
512 $tag= "";
514 /* Load schema if available... */
515 $classes= $this->get_objectclasses();
517 foreach ($l as $part){
518 if ($part != "dummy"){
519 $cdn= "$part,$cdn";
520 }
522 /* Ignore referrals */
523 $found= false;
524 foreach($this->referrals as $ref){
525 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
526 if ($base == $cdn){
527 $found= true;
528 break;
529 }
530 }
531 if ($found){
532 continue;
533 }
535 $this->cat ($cdn);
536 $attrs= $this->fetch();
538 /* Create missing entry? */
539 if (count ($attrs)){
541 /* Catch the tag - if present */
542 if (isset($attrs['gosaUnitTag'][0])){
543 $tag= $attrs['gosaUnitTag'][0];
544 }
546 } else {
547 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
548 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
550 $na= array();
552 /* Automatic or traditional? */
553 if(count($classes)){
555 /* Get name of first matching objectClass */
556 $ocname= "";
557 foreach($classes as $class){
558 if (isset($class['MUST']) && $class['MUST'] == "$type"){
560 /* Look for first classes that is structural... */
561 if (isset($class['STRUCTURAL'])){
562 $ocname= $class['NAME'];
563 break;
564 }
566 /* Look for classes that are auxiliary... */
567 if (isset($class['AUXILIARY'])){
568 $ocname= $class['NAME'];
569 }
570 }
571 }
573 /* Bail out, if we've nothing to do... */
574 if ($ocname == ""){
575 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), FATAL_ERROR_DIALOG);
576 exit();
577 }
579 /* Assemble_entry */
580 if ($tag != ""){
581 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
582 $na["gosaUnitTag"]= $tag;
583 } else {
584 $na['objectClass']= array($ocname);
585 }
586 if (isset($classes[$ocname]['AUXILIARY'])){
587 $na['objectClass'][]= $classes[$ocname]['SUP'];
588 }
589 if ($type == "dc"){
590 /* This is bad actually, but - tell me a better way? */
591 $na['objectClass'][]= 'locality';
592 }
593 $na[$type]= $param;
594 if (is_array($classes[$ocname]['MUST'])){
595 foreach($classes[$ocname]['MUST'] as $attr){
596 $na[$attr]= "filled";
597 }
598 }
600 } else {
602 /* Use alternative add... */
603 switch ($type){
604 case 'ou':
605 if ($tag != ""){
606 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
607 $na["gosaUnitTag"]= $tag;
608 } else {
609 $na["objectClass"]= "organizationalUnit";
610 }
611 $na["ou"]= $param;
612 break;
613 case 'dc':
614 if ($tag != ""){
615 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
616 $na["gosaUnitTag"]= $tag;
617 } else {
618 $na["objectClass"]= array("dcObject", "top", "locality");
619 }
620 $na["dc"]= $param;
621 break;
622 default:
623 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
624 exit();
625 }
627 }
628 $this->cd($cdn);
629 $this->add($na);
631 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
632 if (!preg_match('/success/i', $this->error)){
633 return FALSE;
634 }
635 }
636 }
638 return TRUE;
639 }
642 function recursive_remove()
643 {
644 $delarray= array();
646 /* Get sorted list of dn's to delete */
647 $this->search ("(objectClass=*)");
648 while ($this->fetch()){
649 $deldn= $this->getDN();
650 $delarray[$deldn]= strlen($deldn);
651 }
652 arsort ($delarray);
653 reset ($delarray);
655 /* Delete all dn's in subtree */
656 foreach ($delarray as $key => $value){
657 $this->rmdir($key);
658 }
659 }
661 function get_attribute($dn, $name,$r_array=0)
662 {
663 $data= "";
664 if ($this->reconnect) $this->connect();
665 $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
667 /* fill data from LDAP */
668 if ($sr) {
669 $ei= @ldap_first_entry($this->cid, $sr);
670 if ($ei) {
671 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
672 $data= $info[0];
673 }
674 }
675 }
676 if($r_array==0)
677 return ($data);
678 else
679 return ($info);
682 }
686 function get_additional_error()
687 {
688 $error= "";
689 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
690 return ($error);
691 }
693 function get_error()
694 {
695 if ($this->error == 'Success'){
696 return $this->error;
697 } else {
698 $adderror= $this->get_additional_error();
699 if ($adderror != ""){
700 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
701 } else {
702 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
703 }
704 return $error;
705 }
706 }
708 function get_credentials($url, $referrals= NULL)
709 {
710 $ret= array();
711 $url= preg_replace('!\?\?.*$!', '', $url);
712 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
714 if ($referrals === NULL){
715 $referrals= $this->referrals;
716 }
718 if (isset($referrals[$server])){
719 return ($referrals[$server]);
720 } else {
721 $ret['ADMIN']= LDAP::fix($this->binddn);
722 $ret['PASSWORD']= $this->bindpw;
723 }
725 return ($ret);
726 }
729 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
730 {
731 $display= "";
733 if ($recursive){
734 $this->cd($dn);
735 $this->ls($filter,$dn, array('dn','objectClass'));
736 $deps = array();
738 $display .= $this->gen_one_entry($dn)."\n";
740 while ($attrs= $this->fetch()){
741 $deps[] = $attrs['dn'];
742 }
743 foreach($deps as $dn){
744 $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
745 }
746 } else {
747 $display.= $this->gen_one_entry($dn);
748 }
749 return ($display);
750 }
753 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
754 {
755 $display= array();
757 $this->cd($dn);
758 $this->search("$filter");
760 $i=0;
761 while ($attrs= $this->fetch()){
762 $j=0;
764 foreach ($attributes as $at){
765 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
766 $j++;
767 }
769 $i++;
770 }
772 return ($display);
773 }
776 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
777 {
778 $ret = "";
779 $data = "";
780 if($this->reconnect){
781 $this->connect();
782 }
784 /* Searching Ldap Tree */
785 $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
787 /* Get the first entry */
788 $entry= @ldap_first_entry($this->cid, $sr);
790 /* Get all attributes related to that Objekt */
791 $atts = array();
793 /* Assemble dn */
794 $atts[0]['name'] = "dn";
795 $atts[0]['value'] = array('count' => 1, 0 => $dn);
797 /* Reset index */
798 $i = 1 ;
799 $identifier = array();
800 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
801 while ($attribute) {
802 $i++;
803 $atts[$i]['name'] = $attribute;
804 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
806 /* Next one */
807 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
808 }
810 foreach($atts as $at)
811 {
812 for ($i= 0; $i<$at['value']['count']; $i++){
814 /* Check if we must encode the data */
815 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
816 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
817 } else {
818 $ret .= $at['name'].": ".$at['value'][$i]."\n";
819 }
820 }
821 }
823 return($ret);
824 }
827 function dn_exists($dn)
828 {
829 return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
830 }
834 /* This funktion imports ldifs
836 If DeleteOldEntries is true, the destination entry will be deleted first.
837 If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
838 if JustMofify id false the destination dn will be overwritten by the new ldif.
839 */
841 function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
842 {
843 if($this->reconnect) $this->connect();
845 /* First we have to splitt the string ito detect empty lines
846 An empty line indicates an new Entry */
847 $entries = split("\n",$str_attr);
849 $data = "";
850 $cnt = 0;
851 $current_line = 0;
853 /* FIX ldif */
854 $last = "";
855 $tmp = "";
856 $i = 0;
857 foreach($entries as $entry){
858 if(preg_match("/^ /",$entry)){
859 $tmp[$i] .= trim($entry);
860 }else{
861 $i ++;
862 $tmp[$i] = trim($entry);
863 }
864 }
866 /* Every single line ... */
867 foreach($tmp as $entry) {
868 $current_line ++;
870 /* Removing Spaces to ..
871 .. test if a new entry begins */
872 $tmp = str_replace(" ","",$data );
874 /* .. prevent empty lines in an entry */
875 $tmp2 = str_replace(" ","",$entry);
877 /* If the Block ends (Empty Line) */
878 if((empty($entry))&&(!empty($tmp))) {
879 /* Add collected lines as a complete block */
880 $all[$cnt] = $data;
881 $cnt ++;
882 $data ="";
883 } else {
885 /* Append lines ... */
886 if(!empty($tmp2)) {
887 /* check if we need base64_decode for this line */
888 if(ereg("::",$tmp2))
889 {
890 $encoded = split("::",$entry);
891 $attr = trim($encoded[0]);
892 $value = base64_decode(trim($encoded[1]));
893 /* Add linenumber */
894 $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
895 }
896 else
897 {
898 /* Add Linenumber */
899 $data .= $current_line."#".base64_encode($entry)."\n";
900 }
901 }
902 }
903 }
905 /* The Data we collected is not in the array all[];
906 For example the Data is stored like this..
908 all[0] = "1#dn : .... \n
909 2#ObjectType: person \n ...."
911 Now we check every insertblock and try to insert */
912 foreach ( $all as $single) {
913 $lineone = split("\n",$single);
914 $ndn = split("#", $lineone[0]);
915 $line = base64_decode($ndn[1]);
917 $dnn = split (":",$line,2);
918 $current_line = $ndn[0];
919 $dn = $dnn[0];
920 $value = $dnn[1];
922 /* Every block must begin with a dn */
923 if($dn != "dn") {
924 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
925 return -2;
926 }
928 /* Should we use Modify instead of Add */
929 $usemodify= false;
931 /* Delete before insert */
932 $usermdir= false;
934 /* The dn address already exists, Don't delete destination entry, overwrite it */
935 if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
937 $usermdir = $usemodify = false;
939 /* Delete old entry first, then add new */
940 } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
942 /* Delete first, then add */
943 $usermdir = true;
945 } elseif(($this->dn_exists($value))&&($JustModify)) {
947 /* Modify instead of Add */
948 $usemodify = true;
949 }
951 /* If we can't Import, return with a file error */
952 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
953 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
954 $current_line);
955 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
956 }
958 return (INSERT_OK);
959 }
962 /* Imports a single entry
963 If $delete is true; The old entry will be deleted if it exists.
964 if $modify is true; All variables that are not touched by the new ldif will be kept.
965 if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
966 */
967 function import_single_entry($str_attr,$modify,$delete)
968 {
969 global $config;
971 if(!$config){
972 trigger_error("Can't import ldif, can't read config object.");
973 }
976 if($this->reconnect) $this->connect();
978 $ret = false;
979 $rows= split("\n",$str_attr);
980 $data= false;
982 foreach($rows as $row) {
984 /* Check if we use Linenumbers (when import_complete_ldif is called we use
985 Linenumbers) Linenumbers are use like this 123#attribute : value */
986 if(!empty($row)) {
987 if(strpos($row,"#")!=FALSE) {
989 /* We are using line numbers
990 Because there is a # before a : */
991 $tmp1= split("#",$row);
992 $current_line= $tmp1[0];
993 $row= base64_decode($tmp1[1]);
994 }
996 /* Split the line into attribute and value */
997 $attr = split(":", $row,2);
998 $attr[0]= trim($attr[0]); /* attribute */
999 $attr[1]= $attr[1]; /* value */
1001 /* Check :: was used to indicate base64_encoded strings */
1002 if($attr[1][0] == ":"){
1003 $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1004 $attr[1]=base64_decode($attr[1]);
1005 }
1007 $attr[1] = trim($attr[1]);
1009 /* Check for attributes that are used more than once */
1010 if(!isset($data[$attr[0]])) {
1011 $data[$attr[0]]=$attr[1];
1012 } else {
1013 $tmp = $data[$attr[0]];
1015 if(!is_array($tmp)) {
1016 $new[0]=$tmp;
1017 $new[1]=$attr[1];
1018 $datas[$attr[0]]['count']=1;
1019 $data[$attr[0]]=$new;
1020 } else {
1021 $cnt = $datas[$attr[0]]['count'];
1022 $cnt ++;
1023 $data[$attr[0]][$cnt]=$attr[1];
1024 $datas[$attr[0]]['count'] = $cnt;
1025 }
1026 }
1027 }
1028 }
1030 /* If dn is an index of data, we should try to insert the data */
1031 if(isset($data['dn'])) {
1033 /* Fix dn */
1034 $tmp = gosa_ldap_explode_dn($data['dn']);
1035 unset($tmp['count']);
1036 $newdn ="";
1037 foreach($tmp as $tm){
1038 $newdn.= trim($tm).",";
1039 }
1040 $newdn = preg_replace("/,$/","",$newdn);
1041 $data['dn'] = $newdn;
1043 /* Creating Entry */
1044 $this->cd($data['dn']);
1046 /* Delete existing entry */
1047 if($delete){
1048 $this->rmdir_recursive($data['dn']);
1049 }
1051 /* Create missing trees */
1052 $this->cd ($this->basedn);
1053 $this->cd($config->current['BASE']);
1054 $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1055 $this->cd($data['dn']);
1057 $dn = $data['dn'];
1058 unset($data['dn']);
1060 if(!$modify){
1062 $this->cat($dn);
1063 if($this->count()){
1065 /* The destination entry exists, overwrite it with the new entry */
1066 $attrs = $this->fetch();
1067 foreach($attrs as $name => $value ){
1068 if(!is_numeric($name)){
1069 if(in_array($name,array("dn","count"))) continue;
1070 if(!isset($data[$name])){
1071 $data[$name] = array();
1072 }
1073 }
1074 }
1075 $ret = $this->modify($data);
1077 }else{
1079 /* The destination entry doesn't exists, create it */
1080 $ret = $this->add($data);
1081 }
1083 } else {
1085 /* Keep all vars that aren't touched by this ldif */
1086 $ret = $this->modify($data);
1087 }
1088 }
1089 show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1090 return($ret);
1091 }
1094 function importcsv($str)
1095 {
1096 $lines = split("\n",$str);
1097 foreach($lines as $line)
1098 {
1099 /* continue if theres a comment */
1100 if(substr(trim($line),0,1)=="#"){
1101 continue;
1102 }
1104 $line= str_replace ("\t\t","\t",$line);
1105 $line= str_replace ("\t" ,"," ,$line);
1106 echo $line;
1108 $cells = split(",",$line ) ;
1109 $linet= str_replace ("\t\t",",",$line);
1110 $cells = split("\t",$line);
1111 $count = count($cells);
1112 }
1114 }
1116 function get_objectclasses()
1117 {
1118 $objectclasses = array();
1119 global $config;
1121 /* Only read schema if it is allowed */
1122 if(isset($config) && preg_match("/config/i",get_class($config))){
1123 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1124 return($objectclasses);
1125 }
1126 }
1128 /* Return the cached results. */
1129 if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1130 $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1131 return($objectclasses);
1132 }
1134 # Get base to look for schema
1135 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1136 if(!$sr){
1137 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1138 }
1140 $attr = @ldap_get_entries($this->cid,$sr);
1141 if (!isset($attr[0]['subschemasubentry'][0])){
1142 return array();
1143 }
1145 /* Get list of objectclasses and fill array */
1146 $nb= $attr[0]['subschemasubentry'][0];
1147 $objectclasses= array();
1148 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1149 $attrs= ldap_get_entries($this->cid,$sr);
1150 if (!isset($attrs[0])){
1151 return array();
1152 }
1153 foreach ($attrs[0]['objectclasses'] as $val){
1154 if (preg_match('/^[0-9]+$/', $val)){
1155 continue;
1156 }
1157 $name= "OID";
1158 $pattern= split(' ', $val);
1159 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1160 $objectclasses[$ocname]= array();
1162 foreach($pattern as $chunk){
1163 switch($chunk){
1165 case '(':
1166 $value= "";
1167 break;
1169 case ')': if ($name != ""){
1170 $objectclasses[$ocname][$name]= $this->value2container($value);
1171 }
1172 $name= "";
1173 $value= "";
1174 break;
1176 case 'NAME':
1177 case 'DESC':
1178 case 'SUP':
1179 case 'STRUCTURAL':
1180 case 'ABSTRACT':
1181 case 'AUXILIARY':
1182 case 'MUST':
1183 case 'MAY':
1184 if ($name != ""){
1185 $objectclasses[$ocname][$name]= $this->value2container($value);
1186 }
1187 $name= $chunk;
1188 $value= "";
1189 break;
1191 default: $value.= $chunk." ";
1192 }
1193 }
1195 }
1196 if(class_available("session")){
1197 session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1198 }
1199 return $objectclasses;
1200 }
1203 function value2container($value)
1204 {
1205 /* Set emtpy values to "true" only */
1206 if (preg_match('/^\s*$/', $value)){
1207 return true;
1208 }
1210 /* Remove ' and " if needed */
1211 $value= preg_replace('/^[\'"]/', '', $value);
1212 $value= preg_replace('/[\'"] *$/', '', $value);
1214 /* Convert to array if $ is inside... */
1215 if (preg_match('/\$/', $value)){
1216 $container= preg_split('/\s*\$\s*/', $value);
1217 } else {
1218 $container= chop($value);
1219 }
1221 return ($container);
1222 }
1225 function log($string)
1226 {
1227 if (session::is_set('config')){
1228 $cfg = session::get('config');
1229 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1230 syslog (LOG_INFO, $string);
1231 }
1232 }
1233 }
1235 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1236 function getCn($dn){
1237 $simple= split(",", $dn);
1239 foreach($simple as $piece) {
1240 $partial= split("=", $piece);
1242 if($partial[0] == "cn"){
1243 return $partial[1];
1244 }
1245 }
1246 }
1249 function get_naming_contexts($server, $admin= "", $password= "")
1250 {
1251 /* Build LDAP connection */
1252 $ds= ldap_connect ($server);
1253 if (!$ds) {
1254 die ("Can't bind to LDAP. No check possible!");
1255 }
1256 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1257 $r= ldap_bind ($ds, $admin, $password);
1259 /* Get base to look for naming contexts */
1260 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1261 $attr= @ldap_get_entries($ds,$sr);
1263 return ($attr[0]['namingcontexts']);
1264 }
1267 function get_root_dse($server, $admin= "", $password= "")
1268 {
1269 /* Build LDAP connection */
1270 $ds= ldap_connect ($server);
1271 if (!$ds) {
1272 die ("Can't bind to LDAP. No check possible!");
1273 }
1274 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1275 $r= ldap_bind ($ds, $admin, $password);
1277 /* Get base to look for naming contexts */
1278 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1279 $attr= @ldap_get_entries($ds,$sr);
1281 /* Return empty array, if nothing was set */
1282 if (!isset($attr[0])){
1283 return array();
1284 }
1286 /* Rework array... */
1287 $result= array();
1288 for ($i= 0; $i<$attr[0]['count']; $i++){
1289 $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1290 unset($result[$attr[0][$i]]['count']);
1291 }
1293 return ($result);
1294 }
1295 }
1296 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1297 ?>