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 $reconnect=false;
35 var $tls = false;
36 var $cid;
37 var $hasres = array();
38 var $sr = array();
39 var $re = array();
40 var $basedn ="";
41 var $start = array(); // 0 if we are fetching the first entry, otherwise 1
42 var $error = ""; // Any error messages to be returned can be put here
43 var $srp = 0;
44 var $objectClasses = array(); // Information read from slapd.oc.conf
45 var $binddn = "";
46 var $bindpw = "";
47 var $hostname = "";
48 var $follow_referral = FALSE;
49 var $referrals= array();
50 var $max_ldap_query_time = 0; // 0, empty or negative values will disable this check
52 function LDAP($binddn,$bindpw, $hostname, $follow_referral= FALSE, $tls= FALSE)
53 {
54 global $config;
55 $this->follow_referral= $follow_referral;
56 $this->tls=$tls;
57 $this->binddn=LDAP::convert($binddn);
59 $this->bindpw=$bindpw;
60 $this->hostname=$hostname;
62 /* Check if MAX_LDAP_QUERY_TIME is defined */
63 if(isset($config->data['MAIN']['MAX_LDAP_QUERY_TIME'])){
64 $str = $config->data['MAIN']['MAX_LDAP_QUERY_TIME'];
65 $this->max_ldap_query_time = (float)($str);
66 }
68 $this->connect();
69 }
72 function getSearchResource()
73 {
74 $this->sr[$this->srp]= NULL;
75 $this->start[$this->srp]= 0;
76 $this->hasres[$this->srp]= false;
77 return $this->srp++;
78 }
81 /* Function to replace all problematic characters inside a DN by \001XX, where
82 \001 is decoded to chr(1) [ctrl+a]. It is not impossible, but very unlikely
83 that this character is inside a DN.
85 Currently used codes:
86 , => CO
87 \2C => CO
88 ( => OB
89 ) => CB
90 / => SL */
91 static function convert($dn)
92 {
93 if (SPECIALS_OVERRIDE == TRUE){
94 $tmp= preg_replace(array("/\\\\,/", "/\\\\2C/", "/\(/", "/\)/", "/\//"),
95 array("\001CO", "\001CO", "\001OB", "\001CB", "\001SL"),
96 $dn);
97 return (preg_replace('/,\s+/', ',', $tmp));
98 } else {
99 return ($dn);
100 }
101 }
104 /* Function to fix all problematic characters inside a DN by replacing \001XX
105 codes to their original values. See "convert" for mor information.
106 ',' characters are always expanded to \, (not \2C), since all tested LDAP
107 servers seem to take it the correct way. */
108 static function fix($dn)
109 {
110 if (SPECIALS_OVERRIDE == TRUE){
111 return (preg_replace(array("/\001CO/", "/\001OB/", "/\001CB/", "/\001SL/"),
112 array("\,", "(", ")", "/"),
113 $dn));
114 } else {
115 return ($dn);
116 }
117 }
120 /* Function to fix problematic characters in DN's that are used for search
121 requests. I.e. member=.... */
122 static function prepare4filter($dn)
123 {
124 return normalizeLdap(preg_replace('/\\\\/', '\\\\\\', LDAP::fix($dn)));
125 }
128 function connect()
129 {
130 $this->hascon=false;
131 $this->reconnect=false;
132 if ($this->cid= @ldap_connect($this->hostname)) {
133 @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
134 if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
135 @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
136 @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
137 }
138 if (function_exists("ldap_start_tls") && $this->tls){
139 @ldap_start_tls($this->cid);
140 }
142 $this->error = "No Error";
143 if ($bid = @ldap_bind($this->cid, LDAP::fix($this->binddn), $this->bindpw)) {
144 $this->error = "Success";
145 $this->hascon=true;
146 } else {
147 if ($this->reconnect){
148 if ($this->error != "Success"){
149 $this->error = "Could not rebind to " . $this->binddn;
150 }
151 } else {
152 $this->error = "Could not bind to " . $this->binddn;
153 }
154 }
155 } else {
156 $this->error = "Could not connect to LDAP server";
157 }
158 }
160 function rebind($ldap, $referral)
161 {
162 $credentials= $this->get_credentials($referral);
163 if (@ldap_bind($ldap, LDAP::fix($credentials['ADMIN']), $credentials['PASSWORD'])) {
164 $this->error = "Success";
165 $this->hascon=true;
166 $this->reconnect= true;
167 return (0);
168 } else {
169 $this->error = "Could not bind to " . $credentials['ADMIN'];
170 return NULL;
171 }
172 }
174 function reconnect()
175 {
176 if ($this->reconnect){
177 @ldap_unbind($this->cid);
178 $this->cid = NULL;
179 }
180 }
182 function unbind()
183 {
184 @ldap_unbind($this->cid);
185 $this->cid = NULL;
186 }
188 function disconnect()
189 {
190 if($this->hascon){
191 @ldap_close($this->cid);
192 $this->hascon=false;
193 }
194 }
196 function cd($dir)
197 {
198 if ($dir == ".."){
199 $this->basedn = $this->getParentDir();
200 } else {
201 $this->basedn = LDAP::convert($dir);
202 }
203 }
205 function getParentDir($basedn = "")
206 {
207 if ($basedn==""){
208 $basedn = $this->basedn;
209 } else {
210 $basedn = LDAP::convert($this->basedn);
211 }
212 return(ereg_replace("[^,]*[,]*[ ]*(.*)", "\\1", $basedn));
213 }
216 function search($srp, $filter, $attrs= array())
217 {
218 if($this->hascon){
219 if ($this->reconnect) $this->connect();
221 $start = microtime();
222 $this->clearResult($srp);
223 $this->sr[$srp] = @ldap_search($this->cid, LDAP::fix($this->basedn), $filter, $attrs);
224 $this->error = @ldap_error($this->cid);
225 $this->resetResult($srp);
226 $this->hasres[$srp]=true;
228 /* Check if query took longer as specified in max_ldap_query_time */
229 if($this->max_ldap_query_time){
230 $diff = get_MicroTimeDiff($start,microtime());
231 if($diff > $this->max_ldap_query_time){
232 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
233 }
234 }
236 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=search('".LDAP::fix($this->basedn)."', '$filter')");
237 return($this->sr[$srp]);
238 }else{
239 $this->error = "Could not connect to LDAP server";
240 return("");
241 }
242 }
244 function ls($srp, $filter = "(objectclass=*)", $basedn = "",$attrs = array("*"))
245 {
246 if($this->hascon){
247 if ($this->reconnect) $this->connect();
249 $this->clearResult($srp);
250 if ($basedn == "")
251 $basedn = $this->basedn;
252 else
253 $basedn= LDAP::convert($basedn);
255 $start = microtime();
256 $this->sr[$srp] = @ldap_list($this->cid, LDAP::fix($basedn), $filter,$attrs);
257 $this->error = @ldap_error($this->cid);
258 $this->resetResult($srp);
259 $this->hasres[$srp]=true;
261 /* Check if query took longer as specified in max_ldap_query_time */
262 if($this->max_ldap_query_time){
263 $diff = get_MicroTimeDiff($start,microtime());
264 if($diff > $this->max_ldap_query_time){
265 msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
266 }
267 }
269 $this->log("LDAP operation: time=".get_MicroTimeDiff($start,microtime())." operation=ls('".LDAP::fix($basedn)."', '$filter')");
271 return($this->sr[$srp]);
272 }else{
273 $this->error = "Could not connect to LDAP server";
274 return("");
275 }
276 }
278 function cat($srp, $dn,$attrs= array("*"))
279 {
280 if($this->hascon){
281 if ($this->reconnect) $this->connect();
283 $this->clearResult($srp);
284 $filter = "(objectclass=*)";
285 $this->sr[$srp] = @ldap_read($this->cid, LDAP::fix($dn), $filter,$attrs);
286 $this->error = @ldap_error($this->cid);
287 $this->resetResult($srp);
288 $this->hasres[$srp]=true;
289 return($this->sr[$srp]);
290 }else{
291 $this->error = "Could not connect to LDAP server";
292 return("");
293 }
294 }
296 function set_size_limit($size)
297 {
298 /* Ignore zero settings */
299 if ($size == 0){
300 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
301 }
302 if($this->hascon){
303 @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
304 } else {
305 $this->error = "Could not connect to LDAP server";
306 }
307 }
309 function fetch($srp)
310 {
311 $att= array();
312 if($this->hascon){
313 if($this->hasres[$srp]){
314 if ($this->start[$srp] == 0)
315 {
316 if ($this->sr[$srp]){
317 $this->start[$srp] = 1;
318 $this->re[$srp]= @ldap_first_entry($this->cid, $this->sr[$srp]);
319 } else {
320 return array();
321 }
322 } else {
323 $this->re[$srp]= @ldap_next_entry($this->cid, $this->re[$srp]);
324 }
325 if ($this->re[$srp])
326 {
327 $att= @ldap_get_attributes($this->cid, $this->re[$srp]);
328 $att['dn']= trim(LDAP::convert(@ldap_get_dn($this->cid, $this->re[$srp])));
329 }
330 $this->error = @ldap_error($this->cid);
331 if (!isset($att)){
332 $att= array();
333 }
334 return($att);
335 }else{
336 $this->error = "Perform a fetch with no search";
337 return("");
338 }
339 }else{
340 $this->error = "Could not connect to LDAP server";
341 return("");
342 }
343 }
345 function resetResult($srp)
346 {
347 $this->start[$srp] = 0;
348 }
350 function clearResult($srp)
351 {
352 if($this->hasres[$srp]){
353 $this->hasres[$srp] = false;
354 @ldap_free_result($this->sr[$srp]);
355 }
356 }
358 function getDN($srp)
359 {
360 if($this->hascon){
361 if($this->hasres[$srp]){
363 if(!$this->re[$srp])
364 {
365 $this->error = "Perform a Fetch with no valid Result";
366 }
367 else
368 {
369 $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
371 $this->error = @ldap_error($this->cid);
372 return(trim(LDAP::convert($rv)));
373 }
374 }else{
375 $this->error = "Perform a Fetch with no Search";
376 return("");
377 }
378 }else{
379 $this->error = "Could not connect to LDAP server";
380 return("");
381 }
382 }
384 function count($srp)
385 {
386 if($this->hascon){
387 if($this->hasres[$srp]){
388 $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
389 $this->error = @ldap_error($this->cid);
390 return($rv);
391 }else{
392 $this->error = "Perform a Fetch with no Search";
393 return("");
394 }
395 }else{
396 $this->error = "Could not connect to LDAP server";
397 return("");
398 }
399 }
401 function rm($attrs = "", $dn = "")
402 {
403 if($this->hascon){
404 if ($this->reconnect) $this->connect();
405 if ($dn == "")
406 $dn = $this->basedn;
408 $r = @ldap_mod_del($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 rename($attrs, $dn = "")
418 {
419 if($this->hascon){
420 if ($this->reconnect) $this->connect();
421 if ($dn == "")
422 $dn = $this->basedn;
424 $r = @ldap_mod_replace($this->cid, LDAP::fix($dn), $attrs);
425 $this->error = @ldap_error($this->cid);
426 return($r);
427 }else{
428 $this->error = "Could not connect to LDAP server";
429 return("");
430 }
431 }
433 function rmdir($deletedn)
434 {
435 if($this->hascon){
436 if ($this->reconnect) $this->connect();
437 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
438 $this->error = @ldap_error($this->cid);
439 return($r ? $r : 0);
440 }else{
441 $this->error = "Could not connect to LDAP server";
442 return("");
443 }
444 }
446 /**
447 * Function rmdir_recursive
448 *
449 * Description: Based in recursive_remove, adding two thing: full subtree remove, and delete own node.
450 * Parameters: The dn to delete
451 * GiveBack: True on sucessfull , 0 in error, and "" when we don't get a ldap conection
452 *
453 */
455 function rmdir_recursive($srp, $deletedn)
456 {
457 if($this->hascon){
458 if ($this->reconnect) $this->connect();
459 $delarray= array();
461 /* Get sorted list of dn's to delete */
462 $this->ls ($srp, "(objectClass=*)",$deletedn);
463 while ($this->fetch($srp)){
464 $deldn= $this->getDN($srp);
465 $delarray[$deldn]= strlen($deldn);
466 }
467 arsort ($delarray);
468 reset ($delarray);
470 /* Really Delete ALL dn's in subtree */
471 foreach ($delarray as $key => $value){
472 $this->rmdir_recursive($srp, $key);
473 }
475 /* Finally Delete own Node */
476 $r = @ldap_delete($this->cid, LDAP::fix($deletedn));
477 $this->error = @ldap_error($this->cid);
478 return($r ? $r : 0);
479 }else{
480 $this->error = "Could not connect to LDAP server";
481 return("");
482 }
483 }
486 function modify($attrs)
487 {
488 if(count($attrs) == 0){
489 return (0);
490 }
491 if($this->hascon){
492 if ($this->reconnect) $this->connect();
493 $r = @ldap_modify($this->cid, LDAP::fix($this->basedn), $attrs);
494 $this->error = @ldap_error($this->cid);
495 return($r ? $r : 0);
496 }else{
497 $this->error = "Could not connect to LDAP server";
498 return("");
499 }
500 }
502 function add($attrs)
503 {
504 if($this->hascon){
505 if ($this->reconnect) $this->connect();
506 $r = @ldap_add($this->cid, LDAP::fix($this->basedn), $attrs);
507 $this->error = @ldap_error($this->cid);
508 return($r ? $r : 0);
509 }else{
510 $this->error = "Could not connect to LDAP server";
511 return("");
512 }
513 }
515 function create_missing_trees($srp, $target)
516 {
517 global $config;
519 $real_path= substr($target, 0, strlen($target) - strlen($this->basedn) -1 );
521 if ($target == $this->basedn){
522 $l= array("dummy");
523 } else {
524 $l= array_reverse(gosa_ldap_explode_dn($real_path));
525 }
526 unset($l['count']);
527 $cdn= $this->basedn;
528 $tag= "";
530 /* Load schema if available... */
531 $classes= $this->get_objectclasses();
533 foreach ($l as $part){
534 if ($part != "dummy"){
535 $cdn= "$part,$cdn";
536 }
538 /* Ignore referrals */
539 $found= false;
540 foreach($this->referrals as $ref){
541 $base= preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URL']);
542 if ($base == $cdn){
543 $found= true;
544 break;
545 }
546 }
547 if ($found){
548 continue;
549 }
551 $this->cat ($srp, $cdn);
552 $attrs= $this->fetch($srp);
554 /* Create missing entry? */
555 if (count ($attrs)){
557 /* Catch the tag - if present */
558 if (isset($attrs['gosaUnitTag'][0])){
559 $tag= $attrs['gosaUnitTag'][0];
560 }
562 } else {
563 $type= preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
564 $param= preg_replace('/^[^=]+=([^,]+),.*$/', '\\1', $cdn);
566 $na= array();
568 /* Automatic or traditional? */
569 if(count($classes)){
571 /* Get name of first matching objectClass */
572 $ocname= "";
573 foreach($classes as $class){
574 if (isset($class['MUST']) && $class['MUST'] == "$type"){
576 /* Look for first classes that is structural... */
577 if (isset($class['STRUCTURAL'])){
578 $ocname= $class['NAME'];
579 break;
580 }
582 /* Look for classes that are auxiliary... */
583 if (isset($class['AUXILIARY'])){
584 $ocname= $class['NAME'];
585 }
586 }
587 }
589 /* Bail out, if we've nothing to do... */
590 if ($ocname == ""){
591 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': no object class found!"),$type), FATAL_ERROR_DIALOG);
592 exit();
593 }
595 /* Assemble_entry */
596 if ($tag != ""){
597 $na['objectClass']= array($ocname, "gosaAdministrativeUnitTag");
598 $na["gosaUnitTag"]= $tag;
599 } else {
600 $na['objectClass']= array($ocname);
601 }
602 if (isset($classes[$ocname]['AUXILIARY'])){
603 $na['objectClass'][]= $classes[$ocname]['SUP'];
604 }
605 if ($type == "dc"){
606 /* This is bad actually, but - tell me a better way? */
607 $na['objectClass'][]= 'locality';
608 }
609 $na[$type]= $param;
610 if (is_array($classes[$ocname]['MUST'])){
611 foreach($classes[$ocname]['MUST'] as $attr){
612 $na[$attr]= "filled";
613 }
614 }
616 } else {
618 /* Use alternative add... */
619 switch ($type){
620 case 'ou':
621 if ($tag != ""){
622 $na["objectClass"]= array("organizationalUnit", "gosaAdministrativeUnitTag");
623 $na["gosaUnitTag"]= $tag;
624 } else {
625 $na["objectClass"]= "organizationalUnit";
626 }
627 $na["ou"]= $param;
628 break;
629 case 'dc':
630 if ($tag != ""){
631 $na["objectClass"]= array("dcObject", "top", "locality", "gosaAdministrativeUnitTag");
632 $na["gosaUnitTag"]= $tag;
633 } else {
634 $na["objectClass"]= array("dcObject", "top", "locality");
635 }
636 $na["dc"]= $param;
637 break;
638 default:
639 msg_dialog::display(_("Internal error"), sprintf(_("Cannot automatically create subtrees with RDN '%s': not supported"),$type), FATAL_ERROR_DIALOG);
640 exit();
641 }
643 }
644 $this->cd($cdn);
645 $this->add($na);
647 if (!$this->success()){
648 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()));
649 return FALSE;
650 }
651 }
652 }
654 return TRUE;
655 }
658 function recursive_remove($srp)
659 {
660 $delarray= array();
662 /* Get sorted list of dn's to delete */
663 $this->search ($srp, "(objectClass=*)");
664 while ($this->fetch($srp)){
665 $deldn= $this->getDN($srp);
666 $delarray[$deldn]= strlen($deldn);
667 }
668 arsort ($delarray);
669 reset ($delarray);
671 /* Delete all dn's in subtree */
672 foreach ($delarray as $key => $value){
673 $this->rmdir($key);
674 }
675 }
677 function get_attribute($dn, $name,$r_array=0)
678 {
679 $data= "";
680 if ($this->reconnect) $this->connect();
681 $sr= @ldap_read($this->cid, LDAP::fix($dn), "objectClass=*", array("$name"));
683 /* fill data from LDAP */
684 if ($sr) {
685 $ei= @ldap_first_entry($this->cid, $sr);
686 if ($ei) {
687 if ($info= @ldap_get_values_len($this->cid, $ei, "$name")){
688 $data= $info[0];
689 }
690 }
691 }
692 if($r_array==0)
693 return ($data);
694 else
695 return ($info);
698 }
702 function get_additional_error()
703 {
704 $error= "";
705 @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
706 return ($error);
707 }
710 function success()
711 {
712 return (preg_match('/Success/i', $this->error));
713 }
716 function get_error()
717 {
718 if ($this->error == 'Success'){
719 return $this->error;
720 } else {
721 $adderror= $this->get_additional_error();
722 if ($adderror != ""){
723 $error= $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
724 } else {
725 $error= $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
726 }
727 return $error;
728 }
729 }
731 function get_credentials($url, $referrals= NULL)
732 {
733 $ret= array();
734 $url= preg_replace('!\?\?.*$!', '', $url);
735 $server= preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
737 if ($referrals === NULL){
738 $referrals= $this->referrals;
739 }
741 if (isset($referrals[$server])){
742 return ($referrals[$server]);
743 } else {
744 $ret['ADMIN']= LDAP::fix($this->binddn);
745 $ret['PASSWORD']= $this->bindpw;
746 }
748 return ($ret);
749 }
752 function gen_ldif ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE)
753 {
754 $display= "";
756 if ($recursive){
757 $this->cd($dn);
758 $this->ls($srp, $filter,$dn, array('dn','objectClass'));
759 $deps = array();
761 $display .= $this->gen_one_entry($dn)."\n";
763 while ($attrs= $this->fetch($srp)){
764 $deps[] = $attrs['dn'];
765 }
766 foreach($deps as $dn){
767 $display .= $this->gen_ldif($srp, $dn, $filter,$attributes,$recursive);
768 }
769 } else {
770 $display.= $this->gen_one_entry($dn);
771 }
772 return ($display);
773 }
776 function gen_xls ($srp, $dn, $filter= "(objectClass=*)", $attributes= array('*'), $recursive= TRUE,$r_array=0)
777 {
778 $display= array();
780 $this->cd($dn);
781 $this->search($srp, "$filter");
783 $i=0;
784 while ($attrs= $this->fetch($srp)){
785 $j=0;
787 foreach ($attributes as $at){
788 $display[$i][$j]= $this->get_attribute($attrs['dn'], $at,$r_array);
789 $j++;
790 }
792 $i++;
793 }
795 return ($display);
796 }
799 function gen_one_entry($dn, $filter= "(objectClass=*)" , $name= array("*"))
800 {
801 $ret = "";
802 $data = "";
803 if($this->reconnect){
804 $this->connect();
805 }
807 /* Searching Ldap Tree */
808 $sr= @ldap_read($this->cid, LDAP::fix($dn), $filter, $name);
810 /* Get the first entry */
811 $entry= @ldap_first_entry($this->cid, $sr);
813 /* Get all attributes related to that Objekt */
814 $atts = array();
816 /* Assemble dn */
817 $atts[0]['name'] = "dn";
818 $atts[0]['value'] = array('count' => 1, 0 => $dn);
820 /* Reset index */
821 $i = 1 ;
822 $identifier = array();
823 $attribute= @ldap_first_attribute($this->cid,$entry,$identifier);
824 while ($attribute) {
825 $i++;
826 $atts[$i]['name'] = $attribute;
827 $atts[$i]['value'] = @ldap_get_values_len($this->cid, $entry, "$attribute");
829 /* Next one */
830 $attribute= @ldap_next_attribute($this->cid,$entry,$identifier);
831 }
833 foreach($atts as $at)
834 {
835 for ($i= 0; $i<$at['value']['count']; $i++){
837 /* Check if we must encode the data */
838 if(!preg_match('/^[a-z0-9+@#.=, \/ -]+$/i', $at['value'][$i])) {
839 $ret .= $at['name'].":: ".base64_encode($at['value'][$i])."\n";
840 } else {
841 $ret .= $at['name'].": ".$at['value'][$i]."\n";
842 }
843 }
844 }
846 return($ret);
847 }
850 function dn_exists($dn)
851 {
852 return @ldap_list($this->cid, LDAP::fix($dn), "(objectClass=*)", array("objectClass"));
853 }
857 /* This funktion imports ldifs
859 If DeleteOldEntries is true, the destination entry will be deleted first.
860 If JustModify is true the destination entry will only be touched by the attributes specified in the ldif.
861 if JustMofify id false the destination dn will be overwritten by the new ldif.
862 */
864 function import_complete_ldif($srp, $str_attr,&$error,$JustModify,$DeleteOldEntries)
865 {
866 if($this->reconnect) $this->connect();
868 /* First we have to splitt the string ito detect empty lines
869 An empty line indicates an new Entry */
870 $entries = split("\n",$str_attr);
872 $data = "";
873 $cnt = 0;
874 $current_line = 0;
876 /* FIX ldif */
877 $last = "";
878 $tmp = "";
879 $i = 0;
880 foreach($entries as $entry){
881 if(preg_match("/^ /",$entry)){
882 $tmp[$i] .= trim($entry);
883 }else{
884 $i ++;
885 $tmp[$i] = trim($entry);
886 }
887 }
889 /* Every single line ... */
890 foreach($tmp as $entry) {
891 $current_line ++;
893 /* Removing Spaces to ..
894 .. test if a new entry begins */
895 $tmp = str_replace(" ","",$data );
897 /* .. prevent empty lines in an entry */
898 $tmp2 = str_replace(" ","",$entry);
900 /* If the Block ends (Empty Line) */
901 if((empty($entry))&&(!empty($tmp))) {
902 /* Add collected lines as a complete block */
903 $all[$cnt] = $data;
904 $cnt ++;
905 $data ="";
906 } else {
908 /* Append lines ... */
909 if(!empty($tmp2)) {
910 /* check if we need base64_decode for this line */
911 if(ereg("::",$tmp2))
912 {
913 $encoded = split("::",$entry);
914 $attr = trim($encoded[0]);
915 $value = base64_decode(trim($encoded[1]));
916 /* Add linenumber */
917 $data .= $current_line."#".base64_encode($attr.":".$value)."\n";
918 }
919 else
920 {
921 /* Add Linenumber */
922 $data .= $current_line."#".base64_encode($entry)."\n";
923 }
924 }
925 }
926 }
928 /* The Data we collected is not in the array all[];
929 For example the Data is stored like this..
931 all[0] = "1#dn : .... \n
932 2#ObjectType: person \n ...."
934 Now we check every insertblock and try to insert */
935 foreach ( $all as $single) {
936 $lineone = split("\n",$single);
937 $ndn = split("#", $lineone[0]);
938 $line = base64_decode($ndn[1]);
940 $dnn = split (":",$line,2);
941 $current_line = $ndn[0];
942 $dn = $dnn[0];
943 $value = $dnn[1];
945 /* Every block must begin with a dn */
946 if($dn != "dn") {
947 $error= sprintf(_("This is not a valid DN: '%s'. A block for import should begin with 'dn: ...' in line %s"), $line, $current_line);
948 return -2;
949 }
951 /* Should we use Modify instead of Add */
952 $usemodify= false;
954 /* Delete before insert */
955 $usermdir= false;
957 /* The dn address already exists, Don't delete destination entry, overwrite it */
958 if (($this->dn_exists($value))&&((!$JustModify)&&(!$DeleteOldEntries))) {
960 $usermdir = $usemodify = false;
962 /* Delete old entry first, then add new */
963 } elseif(($this->dn_exists($value))&&($DeleteOldEntries)){
965 /* Delete first, then add */
966 $usermdir = true;
968 } elseif(($this->dn_exists($value))&&($JustModify)) {
970 /* Modify instead of Add */
971 $usemodify = true;
972 }
974 /* If we can't Import, return with a file error */
975 if(!$this->import_single_entry($srp, $single,$usemodify,$usermdir) ) {
976 $error= sprintf(_("Error while importing dn: '%s', please check your LDIF from line %s on!"), $line,
977 $current_line);
978 return UNKNOWN_TOKEN_IN_LDIF_FILE; }
979 }
981 return (INSERT_OK);
982 }
985 /* Imports a single entry
986 If $delete is true; The old entry will be deleted if it exists.
987 if $modify is true; All variables that are not touched by the new ldif will be kept.
988 if $modify is false; The new ldif overwrites the old entry, and all untouched attributes get lost.
989 */
990 function import_single_entry($srp, $str_attr,$modify,$delete)
991 {
992 global $config;
994 if(!$config){
995 trigger_error("Can't import ldif, can't read config object.");
996 }
999 if($this->reconnect) $this->connect();
1001 $ret = false;
1002 $rows= split("\n",$str_attr);
1003 $data= false;
1005 foreach($rows as $row) {
1007 /* Check if we use Linenumbers (when import_complete_ldif is called we use
1008 Linenumbers) Linenumbers are use like this 123#attribute : value */
1009 if(!empty($row)) {
1010 if(strpos($row,"#")!=FALSE) {
1012 /* We are using line numbers
1013 Because there is a # before a : */
1014 $tmp1= split("#",$row);
1015 $current_line= $tmp1[0];
1016 $row= base64_decode($tmp1[1]);
1017 }
1019 /* Split the line into attribute and value */
1020 $attr = split(":", $row,2);
1021 $attr[0]= trim($attr[0]); /* attribute */
1022 $attr[1]= $attr[1]; /* value */
1024 /* Check :: was used to indicate base64_encoded strings */
1025 if($attr[1][0] == ":"){
1026 $attr[1]=trim(preg_replace("/^:/","",$attr[1]));
1027 $attr[1]=base64_decode($attr[1]);
1028 }
1030 $attr[1] = trim($attr[1]);
1032 /* Check for attributes that are used more than once */
1033 if(!isset($data[$attr[0]])) {
1034 $data[$attr[0]]=$attr[1];
1035 } else {
1036 $tmp = $data[$attr[0]];
1038 if(!is_array($tmp)) {
1039 $new[0]=$tmp;
1040 $new[1]=$attr[1];
1041 $datas[$attr[0]]['count']=1;
1042 $data[$attr[0]]=$new;
1043 } else {
1044 $cnt = $datas[$attr[0]]['count'];
1045 $cnt ++;
1046 $data[$attr[0]][$cnt]=$attr[1];
1047 $datas[$attr[0]]['count'] = $cnt;
1048 }
1049 }
1050 }
1051 }
1053 /* If dn is an index of data, we should try to insert the data */
1054 if(isset($data['dn'])) {
1056 /* Fix dn */
1057 $tmp = gosa_ldap_explode_dn($data['dn']);
1058 unset($tmp['count']);
1059 $newdn ="";
1060 foreach($tmp as $tm){
1061 $newdn.= trim($tm).",";
1062 }
1063 $newdn = preg_replace("/,$/","",$newdn);
1064 $data['dn'] = $newdn;
1066 /* Creating Entry */
1067 $this->cd($data['dn']);
1069 /* Delete existing entry */
1070 if($delete){
1071 $this->rmdir_recursive($srp, $data['dn']);
1072 }
1074 /* Create missing trees */
1075 $this->cd ($this->basedn);
1076 $this->cd($config->current['BASE']);
1077 $this->create_missing_trees($srp, preg_replace("/^[^,]+,/","",$data['dn']));
1078 $this->cd($data['dn']);
1080 $dn = $data['dn'];
1081 unset($data['dn']);
1083 if(!$modify){
1085 $this->cat($srp, $dn);
1086 if($this->count($srp)){
1088 /* The destination entry exists, overwrite it with the new entry */
1089 $attrs = $this->fetch($srp);
1090 foreach($attrs as $name => $value ){
1091 if(!is_numeric($name)){
1092 if(in_array($name,array("dn","count"))) continue;
1093 if(!isset($data[$name])){
1094 $data[$name] = array();
1095 }
1096 }
1097 }
1098 $ret = $this->modify($data);
1100 }else{
1102 /* The destination entry doesn't exists, create it */
1103 $ret = $this->add($data);
1104 }
1106 } else {
1108 /* Keep all vars that aren't touched by this ldif */
1109 $ret = $this->modify($data);
1110 }
1111 }
1113 if (!$this->success()){
1114 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($this->get_error(), $dn, "", get_class()));
1115 }
1117 return($ret);
1118 }
1121 function importcsv($str)
1122 {
1123 $lines = split("\n",$str);
1124 foreach($lines as $line)
1125 {
1126 /* continue if theres a comment */
1127 if(substr(trim($line),0,1)=="#"){
1128 continue;
1129 }
1131 $line= str_replace ("\t\t","\t",$line);
1132 $line= str_replace ("\t" ,"," ,$line);
1133 echo $line;
1135 $cells = split(",",$line ) ;
1136 $linet= str_replace ("\t\t",",",$line);
1137 $cells = split("\t",$line);
1138 $count = count($cells);
1139 }
1141 }
1143 function get_objectclasses()
1144 {
1145 $objectclasses = array();
1146 global $config;
1148 /* Only read schema if it is allowed */
1149 if(isset($config) && preg_match("/config/i",get_class($config))){
1150 if(!isset($config->data['MAIN']['SCHEMA_CHECK']) || !preg_match("/true/i",$config->data['MAIN']['SCHEMA_CHECK'])){
1151 return($objectclasses);
1152 }
1153 }
1155 /* Return the cached results. */
1156 if(class_available('session') && session::is_set("LDAP_CACHE::get_objectclasses")){
1157 $objectclasses = session::get("LDAP_CACHE::get_objectclasses");
1158 return($objectclasses);
1159 }
1161 # Get base to look for schema
1162 $sr = @ldap_read ($this->cid, NULL, "objectClass=*", array("subschemaSubentry"));
1163 if(!$sr){
1164 $sr = @ldap_read ($this->cid, "", "objectClass=*", array("subschemaSubentry"));
1165 }
1167 $attr = @ldap_get_entries($this->cid,$sr);
1168 if (!isset($attr[0]['subschemasubentry'][0])){
1169 return array();
1170 }
1172 /* Get list of objectclasses and fill array */
1173 $nb= $attr[0]['subschemasubentry'][0];
1174 $objectclasses= array();
1175 $sr= ldap_read ($this->cid, $nb, "objectClass=*", array("objectclasses"));
1176 $attrs= ldap_get_entries($this->cid,$sr);
1177 if (!isset($attrs[0])){
1178 return array();
1179 }
1180 foreach ($attrs[0]['objectclasses'] as $val){
1181 if (preg_match('/^[0-9]+$/', $val)){
1182 continue;
1183 }
1184 $name= "OID";
1185 $pattern= split(' ', $val);
1186 $ocname= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1187 $objectclasses[$ocname]= array();
1189 foreach($pattern as $chunk){
1190 switch($chunk){
1192 case '(':
1193 $value= "";
1194 break;
1196 case ')': if ($name != ""){
1197 $objectclasses[$ocname][$name]= $this->value2container($value);
1198 }
1199 $name= "";
1200 $value= "";
1201 break;
1203 case 'NAME':
1204 case 'DESC':
1205 case 'SUP':
1206 case 'STRUCTURAL':
1207 case 'ABSTRACT':
1208 case 'AUXILIARY':
1209 case 'MUST':
1210 case 'MAY':
1211 if ($name != ""){
1212 $objectclasses[$ocname][$name]= $this->value2container($value);
1213 }
1214 $name= $chunk;
1215 $value= "";
1216 break;
1218 default: $value.= $chunk." ";
1219 }
1220 }
1222 }
1223 if(class_available("session")){
1224 session::set("LDAP_CACHE::get_objectclasses",$objectclasses);
1225 }
1226 return $objectclasses;
1227 }
1230 function value2container($value)
1231 {
1232 /* Set emtpy values to "true" only */
1233 if (preg_match('/^\s*$/', $value)){
1234 return true;
1235 }
1237 /* Remove ' and " if needed */
1238 $value= preg_replace('/^[\'"]/', '', $value);
1239 $value= preg_replace('/[\'"] *$/', '', $value);
1241 /* Convert to array if $ is inside... */
1242 if (preg_match('/\$/', $value)){
1243 $container= preg_split('/\s*\$\s*/', $value);
1244 } else {
1245 $container= chop($value);
1246 }
1248 return ($container);
1249 }
1252 function log($string)
1253 {
1254 if (session::is_set('config')){
1255 $cfg = session::get('config');
1256 if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])){
1257 syslog (LOG_INFO, $string);
1258 }
1259 }
1260 }
1262 /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1263 function getCn($dn){
1264 $simple= split(",", $dn);
1266 foreach($simple as $piece) {
1267 $partial= split("=", $piece);
1269 if($partial[0] == "cn"){
1270 return $partial[1];
1271 }
1272 }
1273 }
1276 function get_naming_contexts($server, $admin= "", $password= "")
1277 {
1278 /* Build LDAP connection */
1279 $ds= ldap_connect ($server);
1280 if (!$ds) {
1281 die ("Can't bind to LDAP. No check possible!");
1282 }
1283 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1284 $r= ldap_bind ($ds, $admin, $password);
1286 /* Get base to look for naming contexts */
1287 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1288 $attr= @ldap_get_entries($ds,$sr);
1290 return ($attr[0]['namingcontexts']);
1291 }
1294 function get_root_dse($server, $admin= "", $password= "")
1295 {
1296 /* Build LDAP connection */
1297 $ds= ldap_connect ($server);
1298 if (!$ds) {
1299 die ("Can't bind to LDAP. No check possible!");
1300 }
1301 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1302 $r= ldap_bind ($ds, $admin, $password);
1304 /* Get base to look for naming contexts */
1305 $sr = @ldap_read ($ds, "", "objectClass=*", array("+"));
1306 $attr= @ldap_get_entries($ds,$sr);
1308 /* Return empty array, if nothing was set */
1309 if (!isset($attr[0])){
1310 return array();
1311 }
1313 /* Rework array... */
1314 $result= array();
1315 for ($i= 0; $i<$attr[0]['count']; $i++){
1316 $result[$attr[0][$i]]= $attr[0][$attr[0][$i]];
1317 unset($result[$attr[0][$i]]['count']);
1318 }
1320 return ($result);
1321 }
1323 }
1324 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1325 ?>