1 <?php
2 /*****************************************************************************
3 newldap.inc - version 1.0
4 Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
5 Copyright (C) 2004-2006 Cajus Pollmeier <pollmeier@gonicus.de>
7 Based in code of ldap.inc of
8 Copyright (C) 1998 Eric Kilfoil <eric@ipass.net>
9 *****************************************************************************/
11 define("ALREADY_EXISTING_ENTRY",-10001);
12 define("UNKNOWN_TOKEN_IN_LDIF_FILE",-10002);
13 define("NO_FILE_UPLOADED",10003);
14 define("INSERT_OK",10000);
15 define("SPECIALS_OVERRIDE", TRUE);
17 class LDAP{
19 var $hascon =false;
20 var $hasres =false;
21 var $reconnect=false;
22 var $tls = false;
23 var $basedn ="";
24 var $cid;
25 var $error = ""; // Any error messages to be returned can be put here
26 var $start = 0; // 0 if we are fetching the first entry, otherwise 1
27 var $objectClasses = array(); // Information read from slapd.oc.conf
28 var $binddn = "";
29 var $bindpw = "";
30 var $hostname = "";
31 var $follow_referral = FALSE;
32 var $referrals= array();
33 var $max_ldap_query_time = 0; // 0, empty or negative values will disable this check
35 function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
36 {
37 global $config;
38 $this->follow_referral= $follow_referral;
39 $this->tls=$tls;
40 $this->binddn=LDAP::convert($binddn);
42 $this->bindpw=$bindpw;
43 $this->hostname=$hostname;
45 /* Check if MAX_LDAP_QUERY_TIME is defined */
46 if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
47 $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
48 $this->max_ldap_query_time = (float)($str);
49 }
51 $this->connect();
52 }
55 /* Function to replace all problematic characters inside a DN by \001XX, where
56 \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
57 that this character is inside a DN.
59 Currently used codes:
60 , => CO
61 \2C => CO
62 ( => OB
63 ) => CB
64 / => SL */
65 static function convert($dn)
66 {
67 if (SPECIALS_OVERRIDE == TRUE){
68 $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
69 array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
70 $dn);
71 return (preg_replace('/,\s+/', ',', $tmp));
72 } else {
73 return ($dn);
74 }
75 }
78 /* Function to fix all problematic characters inside a DN by replacing \001XX
79 codes to their original values. See "convert" for mor information.
80 ',' characters are always expanded to \, (not \2C), since all tested LDAP
81 servers seem to take it the correct way. */
82 static function fix($dn)
83 {
84 if (SPECIALS_OVERRIDE == TRUE){
85 return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
86 array("\,", "(", ")", "/"),
87 $dn));
88 } else {
89 return ($dn);
90 }
91 }
94 function connect()
95 {
96 $this->hascon=false;
97 $this->reconnect=false;
98 if ($this->cid= @ldap_connect($this->hostname)) {
99 @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
100 if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
101 @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
102 @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
103 }
104 if (function_exists("ldap_start_tls") && $this->tls){
105 @ldap_start_tls($this->cid);
106 }
108 $this->error = "No Error";
109 if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
110 $this->error = "Success";
111 $this->hascon=true;
112 } else {
113 if ($this->reconnect){
114 if ($this->error != "Success"){
115 $this->error = "Could not rebind to " . $this->binddn;
116 }
117 } else {
118 $this->error = "Could not bind to " . $this->binddn;
119 }
120 }
121 } else {
122 $this->error = "Could not connect to LDAP server";
123 }
124 }
126 function rebind($ldap, $referral)
127 {
128 $credentials= $this->get_credentials($referral);
129 if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
130 $this->error = "Success";
131 $this->hascon=true;
132 $this->reconnect= true;
133 return (0);
134 } else {
135 $this->error = "Could not bind to " . $credentials['ADMIN'];
136 return NULL;
137 }
138 }
140 function reconnect()
141 {
142 if ($this->reconnect){
143 @ldap_unbind($this->cid);
144 $this->cid = NULL;
145 }
146 }
148 function unbind()
149 {
150 @ldap_unbind($this->cid);
151 $this->cid = NULL;
152 }
154 function disconnect()
155 {
156 if($this->hascon){
157 @ldap_close($this->cid);
158 $this->hascon=false;
159 }
160 }
162 function cd($dir)
163 {
164 if ($dir == "..")
165 $this->basedn = $this->getParentDir();
166 else
167 $this->basedn = LDAP::convert($dir);
168 }
170 function getParentDir($basedn = "")
171 {
172 if ($basedn=="")
173 $basedn = $this->basedn;
174 else
175 $basedn = LDAP::convert($this->basedn);
176 return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
177 }
179 function search($filter, $attrs= array())
180 {
181 if($this->hascon){
182 if ($this->reconnect) $this->connect();
184 $start = microtime();
186 $this->clearResult();
187 $this->sr = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
188 $this->error = @ldap_error($this->cid);
189 $this->resetResult();
190 $this->hasres=true;
192 /* Check if query took longer as specified in max_ldap_query_time */
193 if($this->max_ldap_query_time){
194 $diff = get_MicroTimeDiff($start,microtime());
195 if($diff > $this->max_ldap_query_time){
196 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
197 }
198 }
200 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
201 return($this->sr);
202 }else{
203 $this->error = "Could not connect to LDAP server";
204 return("");
205 }
206 }
208 function ls($filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
209 {
210 if($this->hascon){
211 if ($this->reconnect) $this->connect();
212 $this->clearResult();
213 if ($basedn == "")
214 $basedn = $this->basedn;
215 else
216 $basedn= LDAP::convert($basedn);
218 $start = microtime();
219 $this->sr = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
220 $this->error = @ldap_error($this->cid);
221 $this->resetResult();
222 $this->hasres=true;
224 /* Check if query took longer as specified in max_ldap_query_time */
225 if($this->max_ldap_query_time){
226 $diff = get_MicroTimeDiff($start,microtime());
227 if($diff > $this->max_ldap_query_time){
228 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
229 }
230 }
232 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
234 return($this->sr);
235 }else{
236 $this->error = "Could not connect to LDAP server";
237 return("");
238 }
239 }
241 function cat($dn,$attrs= array("*"))
242 {
243 if($this->hascon){
244 if ($this->reconnect) $this->connect();
245 $this->clearResult();
246 $filter = "(objectclass=*)";
247 $this->sr = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
248 $this->error = @ldap_error($this->cid);
249 $this->resetResult();
250 $this->hasres=true;
251 return($this->sr);
252 }else{
253 $this->error = "Could not connect to LDAP server";
254 return("");
255 }
256 }
258 function set_size_limit($size)
259 {
260 /* Ignore zero settings */
261 if ($size == 0){
262 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
263 }
264 if($this->hascon){
265 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
266 } else {
267 $this->error = "Could not connect to LDAP server";
268 }
269 }
271 function fetch()
272 {
273 $att= array();
274 if($this->hascon){
275 if($this->hasres){
276 if ($this->start == 0)
277 {
278 if ($this->sr){
279 $this->start = 1;
280 $this->re= @ldap_first_entry($this->cid, $this->sr);
281 } else {
282 return array();
283 }
284 } else {
285 $this->re= @ldap_next_entry($this->cid, $this->re);
286 }
287 if ($this->re)
288 {
289 $att= @ldap_get_attributes($this->cid, $this->re);
290 $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re)));
291 }
292 $this->error = @ldap_error($this->cid);
293 if (!isset($att)){
294 $att= array();
295 }
296 return($att);
297 }else{
298 $this->error = "Perform a Fetch with no Search";
299 return("");
300 }
301 }else{
302 $this->error = "Could not connect to LDAP server";
303 return("");
304 }
305 }
307 function resetResult()
308 {
309 $this->start = 0;
310 }
312 function clearResult()
313 {
314 if($this->hasres){
315 $this->hasres = false;
316 @ldap_free_result($this->sr);
317 }
318 }
320 function getDN()
321 {
322 if($this->hascon){
323 if($this->hasres){
325 if(!$this->re)
326 {
327 $this->error = "Perform a Fetch with no valid Result";
328 }
329 else
330 {
331 $rv = @ldap_get_dn($this->cid, $this->re);
333 $this->error = @ldap_error($this->cid);
334 return(trim(LDAP::convert($rv)));
335 }
336 }else{
337 $this->error = "Perform a Fetch with no Search";
338 return("");
339 }
340 }else{
341 $this->error = "Could not connect to LDAP server";
342 return("");
343 }
344 }
346 function count()
347 {
348 if($this->hascon){
349 if($this->hasres){
350 $rv = @ldap_count_entries($this->cid, $this->sr);
351 $this->error = @ldap_error($this->cid);
352 return($rv);
353 }else{
354 $this->error = "Perform a Fetch with no Search";
355 return("");
356 }
357 }else{
358 $this->error = "Could not connect to LDAP server";
359 return("");
360 }
361 }
363 function rm($attrs = "", $dn = "")
364 {
365 if($this->hascon){
366 if ($this->reconnect) $this->connect();
367 if ($dn == "")
368 $dn = $this->basedn;
370 $r = @ldap_mod_del($this->cid, LDAP::fix($dn), $attrs);
371 $this->error = @ldap_error($this->cid);
372 return($r);
373 }else{
374 $this->error = "Could not connect to LDAP server";
375 return("");
376 }
377 }
379 function rename($attrs, $dn = "")
380 {
381 if($this->hascon){
382 if ($this->reconnect) $this->connect();
383 if ($dn == "")
384 $dn = $this->basedn;
386 $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
387 $this->error = @ldap_error($this->cid);
388 return($r);
389 }else{
390 $this->error = "Could not connect to LDAP server";
391 return("");
392 }
393 }
395 function rmdir($deletedn)
396 {
397 if($this->hascon){
398 if ($this->reconnect) $this->connect();
399 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
400 $this->error = @ldap_error($this->cid);
401 return($r ? $r : 0);
402 }else{
403 $this->error = "Could not connect to LDAP server";
404 return("");
405 }
406 }
408 /**
409 * Function rmdir_recursive
410 *
411 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
412 * Parameters: The dn to delete
413 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
414 *
415 */
417 function rmdir_recursive($deletedn)
418 {
419 if($this->hascon){
420 if ($this->reconnect) $this->connect();
421 $delarray= array();
423 /* Get sorted list of dn's to delete */
424 $this->ls ("(objectClass=*)",$deletedn);
425 while ($this->fetch()){
426 $deldn= $this->getDN();
427 $delarray[$deldn]= strlen($deldn);
428 }
429 arsort ($delarray);
430 reset ($delarray);
432 /* Really Delete ALL dn's in subtree */
433 foreach ($delarray as $key => $value){
434 $this->rmdir_recursive($key);
435 }
437 /* Finally Delete own Node */
438 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
439 $this->error = @ldap_error($this->cid);
440 return($r ? $r : 0);
441 }else{
442 $this->error = "Could not connect to LDAP server";
443 return("");
444 }
445 }
448 function modify($attrs)
449 {
450 if(count($attrs) == 0){
451 return (0);
452 }
453 if($this->hascon){
454 if ($this->reconnect) $this->connect();
455 $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
456 $this->error = @ldap_error($this->cid);
457 return($r ? $r : 0);
458 }else{
459 $this->error = "Could not connect to LDAP server";
460 return("");
461 }
462 }
464 function add($attrs)
465 {
466 if($this->hascon){
467 if ($this->reconnect) $this->connect();
468 $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
469 $this->error = @ldap_error($this->cid);
470 return($r ? $r : 0);
471 }else{
472 $this->error = "Could not connect to LDAP server";
473 return("");
474 }
475 }
477 function create_missing_trees($target)
478 {
479 global $config;
481 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
483 if ($target == $this->basedn){
484 $l= array("dummy");
485 } else {
486 $l= array_reverse(gosa_ldap_explode_dn($real_path));
487 }
488 unset($l['count']);
489 $cdn= $this->basedn;
490 $tag= "";
492 /* Load schema if available... */
493 $classes= $this->get_objectclasses();
495 foreach ($l as $part){
496 if ($part != "dummy"){
497 $cdn= "$part,$cdn";
498 }
500 /* Ignore referrals */
501 $found= false;
502 foreach($this->referrals as $ref){
503 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
504 if ($base == $cdn){
505 $found= true;
506 break;
507 }
508 }
509 if ($found){
510 continue;
511 }
513 $this->cat ($cdn);
514 $attrs= $this->fetch();
516 /* Create missing entry? */
517 if (count ($attrs)){
519 /* Catch the tag - if present */
520 if (isset($attrs['gosaUnitTag'][0])){
521 $tag= $attrs['gosaUnitTag'][0];
522 }
524 } else {
525 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
526 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
528 $na= array();
530 /* Automatic or traditional? */
531 if(count($classes)){
533 /* Get name of first matching objectClass */
534 $ocname= "";
535 foreach($classes as $class){
536 if (isset($class['MUST']) && $class['MUST'] == "$type"){
538 /* Look for first classes that is structural... */
539 if (isset($class['STRUCTURAL'])){
540 $ocname= $class['NAME'];
541 break;
542 }
544 /* Look for classes that are auxiliary... */
545 if (isset($class['AUXILIARY'])){
546 $ocname= $class['NAME'];
547 }
548 }
549 }
551 /* Bail out, if we've nothing to do... */
552 if ($ocname == ""){
553 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found"),$type), ERROR_DIALOG);
554 display_error_page();
555 }
557 /* Assemble_entry */
558 if ($tag != ""){
559 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
560 $na["gosaUnitTag"]= $tag;
561 } else {
562 $na['objectClass']= array($ocname);
563 }
564 if (isset($classes[$ocname]['AUXILIARY'])){
565 $na['objectClass'][]= $classes[$ocname]['SUP'];
566 }
567 if ($type == "dc"){
568 /* This is bad actually, but - tell me a better way? */
569 $na['objectClass'][]= 'locality';
570 }
571 $na[$type]= $param;
572 if (is_array($classes[$ocname]['MUST'])){
573 foreach($classes[$ocname]['MUST'] as $attr){
574 $na[$attr]= "filled";
575 }
576 }
578 } else {
580 /* Use alternative add... */
581 switch ($type){
582 case 'ou':
583 if ($tag != ""){
584 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
585 $na["gosaUnitTag"]= $tag;
586 } else {
587 $na["objectClass"]= "organizationalUnit";
588 }
589 $na["ou"]= $param;
590 break;
591 case 'dc':
592 if ($tag != ""){
593 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
594 $na["gosaUnitTag"]= $tag;
595 } else {
596 $na["objectClass"]= array("dcObject", "top", "locality");
597 }
598 $na["dc"]= $param;
599 break;
600 default:
601 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), ERROR_DIALOG);
602 display_error_page();
603 }
605 }
606 $this->cd($cdn);
607 $this->add($na);
609 show_ldap_error($this->get_error(), sprintf(_("Creating subtree '%s' failed."),$cdn));
610 if (!preg_match('/success/i', $this->error)){
611 return FALSE;
612 }
613 }
614 }
616 return TRUE;
617 }
620 function recursive_remove()
621 {
622 $delarray= array();
624 /* Get sorted list of dn's to delete */
625 $this->search ("(objectClass=*)");
626 while ($this->fetch()){
627 $deldn= $this->getDN();
628 $delarray[$deldn]= strlen($deldn);
629 }
630 arsort ($delarray);
631 reset ($delarray);
633 /* Delete all dn's in subtree */
634 foreach ($delarray as $key => $value){
635 $this->rmdir($key);
636 }
637 }
639 function get_attribute($dn, $name,$r_array=0)
640 {
641 $data= "";
642 if ($this->reconnect) $this->connect();
643 $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
645 /* fill data from LDAP */
646 if ($sr) {
647 $ei= @ldap_first_entry($this->cid, $sr);
648 if ($ei) {
649 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
650 $data= $info[0];
651 }
652 }
653 }
654 if($r_array==0)
655 return ($data);
656 else
657 return ($info);
660 }
664 function get_additional_error()
665 {
666 $error= "";
667 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
668 return ($error);
669 }
671 function get_error()
672 {
673 if ($this->error == 'Success'){
674 return $this->error;
675 } else {
676 $adderror= $this->get_additional_error();
677 if ($adderror != ""){
678 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
679 } else {
680 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
681 }
682 return $error;
683 }
684 }
686 function get_credentials($url, $referrals= NULL)
687 {
688 $ret= array();
689 $url= preg_replace('!\?\?.*$!', '', $url);
690 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
692 if ($referrals === NULL){
693 $referrals= $this->referrals;
694 }
696 if (isset($referrals[$server])){
697 return ($referrals[$server]);
698 } else {
699 $ret['ADMIN']= LDAP::fix($this->binddn);
700 $ret['PASSWORD']= $this->bindpw;
701 }
703 return ($ret);
704 }
707 function gen_ldif ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
708 {
709 $display= "";
711 if ($recursive){
712 $this->cd($dn);
713 $this->ls($filter,$dn, array('dn','objectClass'));
714 $deps = array();
716 $display .= $this->gen_one_entry($dn)."\n";
718 while ($attrs= $this->fetch()){
719 $deps[] = $attrs['dn'];
720 }
721 foreach($deps as $dn){
722 $display .= $this->gen_ldif($dn, $filter,$attributes,$recursive);
723 }
724 } else {
725 $display.= $this->gen_one_entry($dn);
726 }
727 return ($display);
728 }
731 function gen_xls ($dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
732 {
733 $display= array();
735 $this->cd($dn);
736 $this->search("$filter");
738 $i=0;
739 while ($attrs= $this->fetch()){
740 $j=0;
742 foreach ($attributes as $at){
743 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
744 $j++;
745 }
747 $i++;
748 }
750 return ($display);
751 }
754 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
755 {
756 $ret = "";
757 $data = "";
758 if($this->reconnect){
759 $this->connect();
760 }
762 /* Searching Ldap Tree */
763 $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
765 /* Get the first entry */
766 $entry= @ldap_first_entry($this->cid, $sr);
768 /* Get all attributes related to that Objekt */
769 $atts = array();
771 /* Assemble dn */
772 $atts[0]['name'] = "dn";
773 $atts[0]['value'] = array('count' => 1, 0 => $dn);
775 /* Reset index */
776 $i = 1 ;
777 $identifier = array();
778 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
779 while ($attribute) {
780 $i++;
781 $atts[$i]['name'] = $attribute;
782 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
784 /* Next one */
785 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
786 }
788 foreach($atts as $at)
789 {
790 for ($i= 0; $i<$at['value']['count']; $i++){
792 /* Check if we must encode the data */
793 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
794 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
795 } else {
796 $ret .= $at['name'].": ".$at['value'][$i]."\n";
797 }
798 }
799 }
801 return($ret);
802 }
805 function dn_exists($dn)
806 {
807 return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
808 }
812 /* This funktion imports ldifs
814 If DeleteOldEntries is true, the destination entry will be deleted first.
815 If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
816 if JustMofify id false the destination dn will be overwritten by the new ldif.
817 */
819 function import_complete_ldif($str_attr,&$error,$JustModify,$DeleteOldEntries)
820 {
821 if($this->reconnect) $this->connect();
823 /* First we have to splitt the string ito detect empty lines
824 An empty line indicates an new Entry */
825 $entries = split("\n",$str_attr);
827 $data = "";
828 $cnt = 0;
829 $current_line = 0;
831 /* FIX ldif */
832 $last = "";
833 $tmp = "";
834 $i = 0;
835 foreach($entries as $entry){
836 if(preg_match("/^ /",$entry)){
837 $tmp[$i] .= trim($entry);
838 }else{
839 $i ++;
840 $tmp[$i] = trim($entry);
841 }
842 }
844 /* Every single line ... */
845 foreach($tmp as $entry) {
846 $current_line ++;
848 /* Removing Spaces to ..
849 .. test if a new entry begins */
850 $tmp = str_replace(" ","",$data );
852 /* .. prevent empty lines in an entry */
853 $tmp2 = str_replace(" ","",$entry);
855 /* If the Block ends (Empty Line) */
856 if((empty($entry))&&(!empty($tmp))) {
857 /* Add collected lines as a complete block */
858 $all[$cnt] = $data;
859 $cnt ++;
860 $data ="";
861 } else {
863 /* Append lines ... */
864 if(!empty($tmp2)) {
865 /* check if we need base64_decode for this line */
866 if(ereg("::",$tmp2))
867 {
868 $encoded = split("::",$entry);
869 $attr = trim($encoded[0]);
870 $value = base64_decode(trim($encoded[1]));
871 /* Add linenumber */
872 $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
873 }
874 else
875 {
876 /* Add Linenumber */
877 $data .= $current_line."#".base64_encode($entry)."\n";
878 }
879 }
880 }
881 }
883 /* The Data we collected is not in the array all[];
884 For example the Data is stored like this..
886 all[0] = "1#dn : .... \n
887 2#ObjectType: person \n ...."
889 Now we check every insertblock and try to insert */
890 foreach ( $all as $single) {
891 $lineone = split("\n",$single);
892 $ndn = split("#", $lineone[0]);
893 $line = base64_decode($ndn[1]);
895 $dnn = split (":",$line,2);
896 $current_line = $ndn[0];
897 $dn = $dnn[0];
898 $value = $dnn[1];
900 /* Every block must begin with a dn */
901 if($dn != "dn") {
902 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
903 return -2;
904 }
906 /* Should we use Modify instead of Add */
907 $usemodify= false;
909 /* Delete before insert */
910 $usermdir= false;
912 /* The dn address already exists, Don't delete destination entry, overwrite it */
913 if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
915 $usermdir = $usemodify = false;
917 /* Delete old entry first, then add new */
918 } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
920 /* Delete first, then add */
921 $usermdir = true;
923 } elseif(($this->dn_exists($value))&&($JustModify)) {
925 /* Modify instead of Add */
926 $usemodify = true;
927 }
929 /* If we can't Import, return with a file error */
930 if(!$this->import_single_entry($single,$usemodify,$usermdir) ) {
931 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
932 $current_line);
933 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
934 }
936 return (INSERT_OK);
937 }
940 /* Imports a single entry
941 If $delete is true; The old entry will be deleted if it exists.
942 if $modify is true; All variables that are not touched by the new ldif will be kept.
943 if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
944 */
945 function import_single_entry($str_attr,$modify,$delete)
946 {
947 global $config;
949 if(!$config){
950 trigger_error("Can't import ldif, can't read config object.");
951 }
954 if($this->reconnect) $this->connect();
956 $ret = false;
957 $rows= split("\n",$str_attr);
958 $data= false;
960 foreach($rows as $row) {
962 /* Check if we use Linenumbers (when import_complete_ldif is called we use
963 Linenumbers) Linenumbers are use like this 123#attribute : value */
964 if(!empty($row)) {
965 if(strpos($row,"#")!=FALSE) {
967 /* We are using line numbers
968 Because there is a # before a : */
969 $tmp1= split("#",$row);
970 $current_line= $tmp1[0];
971 $row= base64_decode($tmp1[1]);
972 }
974 /* Split the line into attribute and value */
975 $attr = split(":", $row,2);
976 $attr[0]= trim($attr[0]); /* attribute */
977 $attr[1]= $attr[1]; /* value */
979 /* Check :: was used to indicate base64_encoded strings */
980 if($attr[1][0] == ":"){
981 $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
982 $attr[1]=base64_decode($attr[1]);
983 }
985 $attr[1] = trim($attr[1]);
987 /* Check for attributes that are used more than once */
988 if(!isset($data[$attr[0]])) {
989 $data[$attr[0]]=$attr[1];
990 } else {
991 $tmp = $data[$attr[0]];
993 if(!is_array($tmp)) {
994 $new[0]=$tmp;
995 $new[1]=$attr[1];
996 $datas[$attr[0]]['count']=1;
997 $data[$attr[0]]=$new;
998 } else {
999 $cnt = $datas[$attr[0]]['count'];
1000 $cnt ++;
1001 $data[$attr[0]][$cnt]=$attr[1];
1002 $datas[$attr[0]]['count'] = $cnt;
1003 }
1004 }
1005 }
1006 }
1008 /* If dn is an index of data, we should try to insert the data */
1009 if(isset($data['dn'])) {
1011 /* Fix dn */
1012 $tmp = gosa_ldap_explode_dn($data['dn']);
1013 unset($tmp['count']);
1014 $newdn ="";
1015 foreach($tmp as $tm){
1016 $newdn.= trim($tm).",";
1017 }
1018 $newdn = preg_replace("/,$/","",$newdn);
1019 $data['dn'] = $newdn;
1021 /* Creating Entry */
1022 $this->cd($data['dn']);
1024 /* Delete existing entry */
1025 if($delete){
1026 $this->rmdir_recursive($data['dn']);
1027 }
1029 /* Create missing trees */
1030 $this->cd ($this->basedn);
1031 $this->cd($config->current['BASE']);
1032 $this->create_missing_trees(preg_replace("/^[^,]+,/","",$data['dn']));
1033 $this->cd($data['dn']);
1035 $dn = $data['dn'];
1036 unset($data['dn']);
1038 if(!$modify){
1040 $this->cat($dn);
1041 if($this->count()){
1043 /* The destination entry exists, overwrite it with the new entry */
1044 $attrs = $this->fetch();
1045 foreach($attrs as $name => $value ){
1046 if(!is_numeric($name)){
1047 if(in_array($name,array("dn","count"))) continue;
1048 if(!isset($data[$name])){
1049 $data[$name] = array();
1050 }
1051 }
1052 }
1053 $ret = $this->modify($data);
1055 }else{
1057 /* The destination entry doesn't exists, create it */
1058 $ret = $this->add($data);
1059 }
1061 } else {
1063 /* Keep all vars that aren't touched by this ldif */
1064 $ret = $this->modify($data);
1065 }
1066 }
1067 show_ldap_error($this->get_error(), sprintf(_("Ldap import with dn '%s' failed."),$dn));
1068 return($ret);
1069 }
1072 function importcsv($str)
1073 {
1074 $lines = split("\n",$str);
1075 foreach($lines as $line)
1076 {
1077 /* continue if theres a comment */
1078 if(substr(trim($line),0,1)=="#"){
1079 continue;
1080 }
1082 $line= str_replace ("\t\t","\t",$line);
1083 $line= str_replace ("\t" ,"," ,$line);
1084 echo $line;
1086 $cells = split(",",$line ) ;
1087 $linet= str_replace ("\t\t",",",$line);
1088 $cells = split("\t",$line);
1089 $count = count($cells);
1090 }
1092 }
1094 function get_objectclasses()
1095 {
1096 $objectclasses = array();
1097 global $config;
1099 /* Only read schema if it is allowed */
1100 if(isset($config) && preg_match("/config/i",get_class($config))){
1101 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1102 return($objectclasses);
1103 }
1104 }
1106 # Get base to look for schema
1107 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1108 if(!$sr){
1109 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1110 }
1112 $attr = @ldap_get_entries($this->cid,$sr);
1113 if (!isset($attr[0]['subschemasubentry'][0])){
1114 return array();
1115 }
1117 /* Get list of objectclasses and fill array */
1118 $nb= $attr[0]['subschemasubentry'][0];
1119 $objectclasses= array();
1120 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1121 $attrs= ldap_get_entries($this->cid,$sr);
1122 if (!isset($attrs[0])){
1123 return array();
1124 }
1125 foreach ($attrs[0]['objectclasses'] as $val){
1126 if (preg_match('/^[0-9]+$/', $val)){
1127 continue;
1128 }
1129 $name= "OID";
1130 $pattern= split(' ', $val);
1131 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1132 $objectclasses[$ocname]= array();
1134 foreach($pattern as $chunk){
1135 switch($chunk){
1137 case '(':
1138 $value= "";
1139 break;
1141 case ')': if ($name != ""){
1142 $objectclasses[$ocname][$name]= $this->value2container($value);
1143 }
1144 $name= "";
1145 $value= "";
1146 break;
1148 case 'NAME':
1149 case 'DESC':
1150 case 'SUP':
1151 case 'STRUCTURAL':
1152 case 'ABSTRACT':
1153 case 'AUXILIARY':
1154 case 'MUST':
1155 case 'MAY':
1156 if ($name != ""){
1157 $objectclasses[$ocname][$name]= $this->value2container($value);
1158 }
1159 $name= $chunk;
1160 $value= "";
1161 break;
1163 default: $value.= $chunk." ";
1164 }
1165 }
1167 }
1168 return $objectclasses;
1169 }
1172 function value2container($value)
1173 {
1174 /* Set emtpy values to "true" only */
1175 if (preg_match('/^\s*$/', $value)){
1176 return true;
1177 }
1179 /* Remove ' and " if needed */
1180 $value= preg_replace('/^[\'"]/', '', $value);
1181 $value= preg_replace('/[\'"] *$/', '', $value);
1183 /* Convert to array if $ is inside... */
1184 if (preg_match('/\$/', $value)){
1185 $container= preg_split('/\s*\$\s*/', $value);
1186 } else {
1187 $container= chop($value);
1188 }
1190 return ($container);
1191 }
1194 function log($string)
1195 {
1196 if (session::is_set('config')){
1197 $cfg = session::get('config');
1198 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1199 syslog (LOG_INFO, $string);
1200 }
1201 }
1202 }
1204 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1205 function getCn($dn){
1206 $simple= split(",", $dn);
1208 foreach($simple as $piece) {
1209 $partial= split("=", $piece);
1211 if($partial[0] == "cn"){
1212 return $partial[1];
1213 }
1214 }
1215 }
1218 function get_naming_contexts($server, $admin= "", $password= "")
1219 {
1220 /* Build LDAP connection */
1221 $ds= ldap_connect ($server);
1222 if (!$ds) {
1223 die ("Can't bind to LDAP. No check possible!");
1224 }
1225 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1226 $r= ldap_bind ($ds, $admin, $password);
1228 /* Get base to look for naming contexts */
1229 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1230 $attr= @ldap_get_entries($ds,$sr);
1232 return ($attr[0]['namingcontexts']);
1233 }
1236 function get_root_dse($server, $admin= "", $password= "")
1237 {
1238 /* Build LDAP connection */
1239 $ds= ldap_connect ($server);
1240 if (!$ds) {
1241 die ("Can't bind to LDAP. No check possible!");
1242 }
1243 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1244 $r= ldap_bind ($ds, $admin, $password);
1246 /* Get base to look for naming contexts */
1247 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1248 $attr= @ldap_get_entries($ds,$sr);
1250 /* Return empty array, if nothing was set */
1251 if (!isset($attr[0])){
1252 return array();
1253 }
1255 /* Rework array... */
1256 $result= array();
1257 for ($i= 0; $i<$attr[0]['count']; $i++){
1258 $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1259 unset($result[$attr[0][$i]]['count']);
1260 }
1262 return ($result);
1263 }
1264 }
1265 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1266 ?>