FusionDirectory
 All Data Structures Files Functions Variables
class_ldap.inc
Go to the documentation of this file.
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2003-2010 Cajus Pollmeier
5  Copyright (C) 2003 Alejandro Escanero Blanco <aescanero@chaosdimension.org>
6  Copyright (C) 1998 Eric Kilfoil <eric@ipass.net>
7  Copyright (C) 2011-2016 FusionDirectory
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
34 class LDAP
35 {
36  var $hascon = FALSE;
37  var $reconnect = FALSE;
38  var $tls = FALSE;
39 
40  /* connection identifier */
41  var $cid;
42 
43  var $hasres = array();
44  var $sr = array();
45  var $re = array();
46  var $basedn = "";
47 
48  /* 0 if we are fetching the first entry, otherwise 1 */
49  var $start = array();
50 
51  /* Any error messages to be returned can be put here */
52  var $error = "";
53 
54  var $srp = 0;
55 
56  /* Information read from slapd.oc.conf */
57  var $objectClasses = array();
58  /* the dn for the bind */
59  var $binddn = "";
60  /* the dn's password for the bind */
61  var $bindpw = "";
62  var $hostname = "";
63  var $follow_referral = FALSE;
64  var $referrals = array();
65 
66  /* 0, empty or negative values will disable this check */
67  var $max_ldap_query_time = 0;
68 
82  function __construct($binddn, $bindpw, $hostname, $follow_referral = FALSE, $tls = FALSE)
83  {
84  global $config;
85  $this->follow_referral = $follow_referral;
86  $this->tls = $tls;
87  $this->binddn = $binddn;
88  $this->bindpw = $bindpw;
89  $this->hostname = $hostname;
90 
91  /* Check if MAX_LDAP_QUERY_TIME is defined */
92  if (is_object($config) && ($config->get_cfg_value("ldapMaxQueryTime") != "")) {
93  $str = $config->get_cfg_value("ldapMaxQueryTime");
94  $this->max_ldap_query_time = (float)($str);
95  }
96 
97  $this->connect();
98  }
99 
105  function getSearchResource()
106  {
107  $this->sr[$this->srp] = NULL;
108  $this->start[$this->srp] = 0;
109  $this->hasres[$this->srp] = FALSE;
110  return $this->srp++;
111  }
112 
118  static function prepare4filter($dn)
119  {
120  trigger_error('deprecated, use ldap_escape_f instead');
121  return ldap_escape_f($dn);
122  }
123 
129  function connect()
130  {
131  $this->hascon = FALSE;
132  $this->reconnect = FALSE;
133  if ($this->cid = @ldap_connect($this->hostname)) {
134  @ldap_set_option($this->cid, LDAP_OPT_PROTOCOL_VERSION, 3);
135  if (function_exists("ldap_set_rebind_proc") && $this->follow_referral) {
136  @ldap_set_option($this->cid, LDAP_OPT_REFERRALS, 1);
137  @ldap_set_rebind_proc($this->cid, array(&$this, "rebind"));
138  }
139  if (function_exists("ldap_start_tls") && $this->tls) {
140  @ldap_start_tls($this->cid);
141  }
142 
143  $this->error = "No Error";
144  if (@ldap_bind($this->cid, $this->binddn, $this->bindpw)) {
145  $this->error = "Success";
146  $this->hascon = TRUE;
147  } else {
148  if ($this->reconnect) {
149  if ($this->error != "Success") {
150  $this->error = "Could not rebind to " . $this->binddn;
151  }
152  } else {
153  $this->error = "Could not bind to " . $this->binddn;
154  }
155  }
156  } else {
157  $this->error = "Could not connect to LDAP server";
158  }
159  }
160 
164  function rebind($ldap, $referral)
165  {
166  $credentials = $this->get_credentials($referral);
167  if (@ldap_bind($ldap, $credentials['ADMINDN'], $credentials['ADMINPASSWORD'])) {
168  $this->error = "Success";
169  $this->hascon = TRUE;
170  $this->reconnect = TRUE;
171  return 0;
172  } else {
173  $this->error = "Could not bind to " . $credentials['ADMINDN'];
174  return NULL;
175  }
176  }
177 
181  function reconnect()
182  {
183  if ($this->reconnect) {
184  $this->unbind();
185  }
186  }
187 
191  function unbind()
192  {
193  @ldap_unbind($this->cid);
194  $this->cid = NULL;
195  }
196 
200  function disconnect()
201  {
202  if ($this->hascon) {
203  @ldap_close($this->cid);
204  $this->hascon = FALSE;
205  }
206  }
207 
213  function cd($dir)
214  {
215  if ($dir == '..') {
216  $this->basedn = $this->getParentDir();
217  } else {
218  $this->basedn = $dir;
219  }
220  }
221 
229  function getParentDir($basedn = '')
230  {
231  if ($basedn == '') {
232  $basedn = $this->basedn;
233  }
234  return preg_replace("/[^,]*[,]*[ ]*(.*)/", "$1", $basedn);
235  }
236 
246  function search($srp, $filter, $attrs = array(), $scope = 'subtree')
247  {
248  if ($this->hascon) {
249  if ($this->reconnect) {
250  $this->connect();
251  }
252 
253  $start = microtime(TRUE);
254  $this->clearResult($srp);
255  switch (strtolower($scope)) {
256  case 'base':
257  throw new FusionDirectoryException('not implemented');
258  case 'one':
259  $this->sr[$srp] = @ldap_list($this->cid, $this->basedn, $filter, $attrs);
260  break;
261  default:
262  case 'subtree':
263  $this->sr[$srp] = @ldap_search($this->cid, $this->basedn, $filter, $attrs);
264  break;
265  }
266  $this->error = @ldap_error($this->cid);
267  $this->resetResult($srp);
268  $this->hasres[$srp] = TRUE;
269 
270  /* Check if query took longer as specified in max_ldap_query_time */
271  if ($this->max_ldap_query_time) {
272  $diff = microtime(TRUE) - $start;
273  if ($diff > $this->max_ldap_query_time) {
274  msg_dialog::display(_("Performance warning"), sprintf(_("LDAP performance is poor: last query took about %.2fs!"), $diff), WARNING_DIALOG);
275  }
276  }
277 
278  $this->log("LDAP operation: time=".(microtime(TRUE) - $start)." operation=search('".$this->basedn."', '$filter')");
279  return $this->sr[$srp];
280  } else {
281  $this->error = "Could not connect to LDAP server";
282  return "";
283  }
284  }
285 
286  /*
287  * \brief List
288  *
289  * \param integer $srp
290  *
291  * \param string $filter Initialized at "(objectclass=*)"
292  *
293  * \param string $basedn Empty string
294  *
295  * \param array $attrs
296  */
297  function ls($srp, $filter = "(objectclass=*)", $basedn = "", $attrs = array("*"))
298  {
299  trigger_error('deprecated');
300  $this->cd($basedn);
301  return $this->search($srp, $filter, $attrs, 'one');
302  }
303 
304  /*
305  * \brief Concatenate
306  *
307  * \param integer $srp
308  *
309  * \param string $dn The DN
310  *
311  * \param array $attrs
312  *
313  * \param string $filter Initialized at "(objectclass=*)"
314  */
315  function cat($srp, $dn, $attrs = array("*"), $filter = "(objectclass=*)")
316  {
317  if ($this->hascon) {
318  if ($this->reconnect) {
319  $this->connect();
320  }
321 
322  $this->clearResult($srp);
323  $this->sr[$srp] = @ldap_read($this->cid, $dn, $filter, $attrs);
324  $this->error = @ldap_error($this->cid);
325  $this->resetResult($srp);
326  $this->hasres[$srp] = TRUE;
327  return $this->sr[$srp];
328  } else {
329  $this->error = "Could not connect to LDAP server";
330  return "";
331  }
332  }
333 
341  function object_match_filter($dn, $filter)
342  {
343  if ($this->hascon) {
344  if ($this->reconnect) {
345  $this->connect();
346  }
347  $res = @ldap_read($this->cid, $dn, $filter, array("objectClass"));
348  $rv = @ldap_count_entries($this->cid, $res);
349  return $rv;
350  } else {
351  $this->error = "Could not connect to LDAP server";
352  return FALSE;
353  }
354  }
355 
361  function set_size_limit($size)
362  {
363  /* Ignore zero settings */
364  if ($size == 0) {
365  @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, 10000000);
366  }
367  if ($this->hascon) {
368  @ldap_set_option($this->cid, LDAP_OPT_SIZELIMIT, $size);
369  } else {
370  $this->error = "Could not connect to LDAP server";
371  }
372  }
373 
379  function fetch($srp)
380  {
381  $att = array();
382  if ($this->hascon) {
383  if ($this->hasres[$srp]) {
384  if ($this->start[$srp] == 0) {
385  if ($this->sr[$srp]) {
386  $this->start[$srp] = 1;
387  $this->re[$srp] = @ldap_first_entry($this->cid, $this->sr[$srp]);
388  } else {
389  return array();
390  }
391  } else {
392  $this->re[$srp] = @ldap_next_entry($this->cid, $this->re[$srp]);
393  }
394  if ($this->re[$srp]) {
395  $att = @ldap_get_attributes($this->cid, $this->re[$srp]);
396  $att['dn'] = trim(@ldap_get_dn($this->cid, $this->re[$srp]));
397  }
398  $this->error = @ldap_error($this->cid);
399  if (!isset($att)) {
400  $att = array();
401  }
402  return $att;
403  } else {
404  $this->error = "Perform a fetch with no search";
405  return "";
406  }
407  } else {
408  $this->error = "Could not connect to LDAP server";
409  return "";
410  }
411  }
412 
418  function resetResult($srp)
419  {
420  $this->start[$srp] = 0;
421  }
422 
428  function clearResult($srp)
429  {
430  if ($this->hasres[$srp]) {
431  $this->hasres[$srp] = FALSE;
432  @ldap_free_result($this->sr[$srp]);
433  }
434  }
435 
441  function getDN($srp)
442  {
443  if ($this->hascon) {
444  if ($this->hasres[$srp]) {
445  if (!$this->re[$srp]) {
446  $this->error = "Perform a Fetch with no valid Result";
447  } else {
448  $rv = @ldap_get_dn($this->cid, $this->re[$srp]);
449 
450  $this->error = @ldap_error($this->cid);
451  return trim($rv);
452  }
453  } else {
454  $this->error = "Perform a Fetch with no Search";
455  return "";
456  }
457  } else {
458  $this->error = "Could not connect to LDAP server";
459  return "";
460  }
461  }
462 
468  function count($srp)
469  {
470  if ($this->hascon) {
471  if ($this->hasres[$srp]) {
472  $rv = @ldap_count_entries($this->cid, $this->sr[$srp]);
473  $this->error = @ldap_error($this->cid);
474  return $rv;
475  } else {
476  $this->error = "Perform a Fetch with no Search";
477  return "";
478  }
479  } else {
480  $this->error = "Could not connect to LDAP server";
481  return "";
482  }
483  }
484 
485 
493  function rm($attrs = "", $dn = "")
494  {
495  if ($this->hascon) {
496  if ($this->reconnect) {
497  $this->connect();
498  }
499  if ($dn == '') {
500  $dn = $this->basedn;
501  }
502 
503  $r = ldap_mod_del($this->cid, $dn, $attrs);
504  $this->error = @ldap_error($this->cid);
505  return $r;
506  } else {
507  $this->error = 'Could not connect to LDAP server';
508  return '';
509  }
510  }
511 
512  function mod_add($attrs = "", $dn = "")
513  {
514  if ($this->hascon) {
515  if ($this->reconnect) {
516  $this->connect();
517  }
518  if ($dn == "") {
519  $dn = $this->basedn;
520  }
521 
522  $r = @ldap_mod_add($this->cid, $dn, $attrs);
523  $this->error = @ldap_error($this->cid);
524  return $r;
525  } else {
526  $this->error = "Could not connect to LDAP server";
527  return "";
528  }
529  }
530 
536  function rmdir($deletedn)
537  {
538  if ($this->hascon) {
539  if ($this->reconnect) {
540  $this->connect();
541  }
542  $r = @ldap_delete($this->cid, $deletedn);
543  $this->error = @ldap_error($this->cid);
544  return ($r ? $r : 0);
545  } else {
546  $this->error = "Could not connect to LDAP server";
547  return "";
548  }
549  }
550 
551 
561  function rename_dn($source, $dest)
562  {
563  /* Check if source and destination are the same entry */
564  if (strtolower($source) == strtolower($dest)) {
565  trigger_error("Source and destination can't be the same entry.");
566  $this->error = "Source and destination can't be the same entry.";
567  return FALSE;
568  }
569 
570  /* Check if destination entry exists */
571  if ($this->dn_exists($dest)) {
572  trigger_error("Destination '$dest' already exists.");
573  $this->error = "Destination '$dest' already exists.";
574  return FALSE;
575  }
576 
577  /* Extract the name and the parent part out ouf source dn.
578  e.g. cn=herbert,ou=department,dc=...
579  parent => ou=department,dc=...
580  dest_rdn => cn=herbert
581  */
582  $parent = preg_replace("/^[^,]+,/", "", $dest);
583  $dest_rdn = preg_replace("/,.*$/", "", $dest);
584 
585  if ($this->hascon) {
586  if ($this->reconnect) {
587  $this->connect();
588  }
589  /* We have to pass TRUE as deleteoldrdn in case the attribute is single-valued */
590  $r = ldap_rename($this->cid, $source, $dest_rdn, $parent, TRUE);
591  $this->error = ldap_error($this->cid);
592 
593  /* Check if destination dn exists, if not the
594  server may not support this operation */
595  $r &= is_resource($this->dn_exists($dest));
596  return $r;
597  } else {
598  $this->error = "Could not connect to LDAP server";
599  return FALSE;
600  }
601  }
602 
603 
615  function rmdir_recursive($srp, $deletedn)
616  {
617  if ($this->hascon) {
618  if ($this->reconnect) {
619  $this->connect();
620  }
621  $delarray = array();
622 
623  /* Get sorted list of dn's to delete */
624  $this->cd($deletedn);
625  $this->search($srp, '(objectClass=*)', array('dn'));
626  while ($attrs = $this->fetch($srp)) {
627  $delarray[$attrs['dn']] = strlen($attrs['dn']);
628  }
629  arsort($delarray);
630  reset($delarray);
631 
632  /* Really Delete ALL dn's in subtree */
633  foreach (array_keys($delarray) as $key) {
634  $r = @ldap_delete($this->cid, $key);
635  if ($r === FALSE) {
636  break;
637  }
638  }
639  $this->error = @ldap_error($this->cid);
640  return ($r ? $r : 0);
641  } else {
642  $this->error = "Could not connect to LDAP server";
643  return "";
644  }
645  }
646 
647  function makeReadableErrors($error, $attrs)
648  {
649  if ($this->success()) {
650  return "";
651  }
652 
653  $str = "";
654  if (preg_match("/^objectClass: value #([0-9]*) invalid per syntax$/", $this->get_additional_error(), $m)) {
655  if (isset($attrs['objectClass'])) {
656  $ocs = $attrs['objectClass'];
657  if (!is_array($ocs)) {
658  $ocs = array($ocs);
659  }
660  if (isset($ocs[$m[1]])) {
661  $str .= " - <b>objectClass: ".$ocs[$m[1]]."</b>";
662  }
663  }
664  }
665  if ($error == "Undefined attribute type") {
666  $str = " - <b>attribute: ".preg_replace("/:.*$/", "", $this->get_additional_error())."</b>";
667  }
668 
669  @DEBUG(DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $attrs, "Erroneous data");
670 
671  return $str;
672  }
673 
679  function modify($attrs)
680  {
681  if (count($attrs) == 0) {
682  return 0;
683  }
684  if ($this->hascon) {
685  if ($this->reconnect) {
686  $this->connect();
687  }
688  $r = @ldap_modify($this->cid, $this->basedn, $attrs);
689  $this->error = @ldap_error($this->cid);
690  if (!$this->success()) {
691  $this->error .= $this->makeReadableErrors($this->error, $attrs);
692  }
693  return ($r ? $r : 0);
694  } else {
695  $this->error = "Could not connect to LDAP server";
696  return "";
697  }
698  }
699 
705  function add($attrs)
706  {
707  if ($this->hascon) {
708  if ($this->reconnect) {
709  $this->connect();
710  }
711  $r = @ldap_add($this->cid, $this->basedn, $attrs);
712  $this->error = @ldap_error($this->cid);
713  if (!$this->success()) {
714  $this->error .= $this->makeReadableErrors($this->error, $attrs);
715  }
716  return ($r ? $r : 0);
717  } else {
718  $this->error = "Could not connect to LDAP server";
719  return "";
720  }
721  }
722 
723  /*
724  * $target is a dn, i.e. "ou=example,ou=orga,dc=base"
725  *
726  * Creates missing trees, in our example ou=orga,dc=base will get created if not existing, same thing for ou=example,ou=orga,dc=base
727  * */
728  function create_missing_trees($srp, $target, $ignoreReferralBases = TRUE)
729  {
730  $real_path = substr($target, 0, strlen($target) - strlen($this->basedn) - 1);
731 
732  if ($target == $this->basedn) {
733  $l = array("dummy");
734  } else {
735  $l = array_reverse(ldap_explode_dn($real_path, 0));
736  }
737  unset($l['count']);
738  $cdn = $this->basedn;
739 
740  /* Load schema if available... */
741  $classes = $this->get_objectclasses();
742 
743  foreach ($l as $part) {
744  if ($part != "dummy") {
745  $cdn = "$part,$cdn";
746  }
747 
748  /* Ignore referrals */
749  if ($ignoreReferralBases) {
750  $found = FALSE;
751  foreach ($this->referrals as $ref) {
752  $base = preg_replace('!^[^:]+://[^/]+/([^?]+).*$!', '\\1', $ref['URI']);
753  if ($base == $cdn) {
754  $found = TRUE;
755  break;
756  }
757  }
758  if ($found) {
759  continue;
760  }
761  }
762 
763  $this->cat ($srp, $cdn);
764  $attrs = $this->fetch($srp);
765 
766  /* Create missing entry? */
767  if (!count($attrs)) {
768  $type = preg_replace('/^([^=]+)=.*$/', '\\1', $cdn);
769  $param = preg_replace('/^[^=]+=([^,]+).*$/', '\\1', $cdn);
770  $param = preg_replace(array('/\\\\,/','/\\\\"/'), array(',','"'), $param);
771 
772  $na = array();
773 
774  /* Automatic or traditional? */
775  if (count($classes)) {
776  /* Get name of first matching objectClass */
777  $ocname = "";
778  foreach ($classes as $class) {
779  if (isset($class['MUST']) && in_array($type, $class['MUST'])) {
780 
781  /* Look for first classes that is structural... */
782  if (isset($class['STRUCTURAL'])) {
783  $ocname = $class['NAME'];
784  break;
785  }
786 
787  /* Look for classes that are auxiliary... */
788  if (isset($class['AUXILIARY'])) {
789  $ocname = $class['NAME'];
790  }
791  }
792  }
793 
794  /* Bail out, if we've nothing to do... */
795  if ($ocname == '') {
796  msg_dialog::display(_('Internal error'), sprintf(_('Cannot automatically create subtrees with RDN "%s": no object class found!'), $type), FATAL_ERROR_DIALOG);
797  exit();
798  }
799 
800  /* Assemble_entry */
801  $na['objectClass'] = array($ocname);
802  if (isset($classes[$ocname]['AUXILIARY'])) {
803  $na['objectClass'][] = $classes[$ocname]['SUP'];
804  }
805  if ($type == 'dc') {
806  /* This is bad actually, but - tell me a better way? */
807  $na['objectClass'][] = 'organization';
808  $na['o'] = $param;
809  }
810  $na[$type] = $param;
811 
812  // Fill in MUST values - but do not overwrite existing ones.
813  if (is_array($classes[$ocname]['MUST'])) {
814  foreach ($classes[$ocname]['MUST'] as $attr) {
815  if (isset($na[$attr]) && !empty($na[$attr])) {
816  continue;
817  }
818  $na[$attr] = 'filled';
819  }
820  }
821  } else {
822  /* Use alternative add... */
823  switch ($type) {
824  case 'ou':
825  $na['objectClass'] = 'organizationalUnit';
826  $na['ou'] = $param;
827  break;
828  case 'dc':
829  $na['objectClass'] = array('dcObject', 'top', 'organization');
830  $na['dc'] = $param;
831  $na['o'] = $param;
832  break;
833  default:
834  msg_dialog::display(_('Internal error'), sprintf(_('Cannot automatically create subtrees with RDN "%s": not supported'), $type), FATAL_ERROR_DIALOG);
835  exit();
836  }
837 
838  }
839  $this->cd($cdn);
840  $this->add($na);
841 
842  if (!$this->success()) {
843  @DEBUG(DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $cdn, 'dn');
844  @DEBUG(DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $na, 'Content');
845  @DEBUG(DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $this->get_error(), 'LDAP error');
846 
847  msg_dialog::display(_('LDAP error'), msgPool::ldaperror($this->get_error(), $cdn, LDAP_ADD, get_class()), LDAP_ERROR);
848  return FALSE;
849  }
850  }
851  }
852 
853  return TRUE;
854  }
855 
865  function get_attribute($dn, $name, $r_array = 0)
866  {
867  $data = "";
868  if ($this->reconnect) {
869  $this->connect();
870  }
871  $sr = @ldap_read($this->cid, $dn, "objectClass=*", array("$name"));
872 
873  /* fill data from LDAP */
874  if ($sr) {
875  $ei = @ldap_first_entry($this->cid, $sr);
876  if ($ei) {
877  if ($info = @ldap_get_values_len($this->cid, $ei, "$name")) {
878  $data = $info[0];
879  }
880  }
881  }
882  if ($r_array == 0) {
883  return $data;
884  } else {
885  return $info;
886  }
887  }
888 
889 
896  {
897  $error = "";
898  @ldap_get_option ($this->cid, LDAP_OPT_ERROR_STRING, $error);
899  return $error;
900  }
901 
907  function success()
908  {
909  return preg_match('/Success/i', $this->error);
910  }
911 
915  function get_error()
916  {
917  if ($this->error == 'Success') {
918  return $this->error;
919  } else {
920  $adderror = $this->get_additional_error();
921  if ($adderror != "") {
922  $error = $this->error." (".$this->get_additional_error().", ".sprintf(_("while operating on '%s' using LDAP server '%s'"), $this->basedn, $this->hostname).")";
923  } else {
924  $error = $this->error." (".sprintf(_("while operating on LDAP server %s"), $this->hostname).")";
925  }
926  return $error;
927  }
928  }
929 
930  function get_credentials($url, $referrals = NULL)
931  {
932  $ret = array();
933  $url = preg_replace('!\?\?.*$!', '', $url);
934  $server = preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $url);
935 
936  if ($referrals === NULL) {
937  $referrals = $this->referrals;
938  }
939 
940  if (isset($referrals[$server])) {
941  return $referrals[$server];
942  } else {
943  $ret['ADMINDN'] = $this->binddn;
944  $ret['ADMINPASSWORD'] = $this->bindpw;
945  }
946 
947  return $ret;
948  }
949 
950 
964  function generateLdif ($dn, $filter = "(objectClass=*)", $scope = 'sub', $limit = 0)
965  {
966  // Ensure that limit is numeric if not skip here.
967  if (!empty($limit) && !is_numeric($limit)) {
968  trigger_error(sprintf("Invalid parameter for limit '%s', a numeric value is required."), $limit);
969  return NULL;
970  }
971  $limit = (!$limit)?'':' -z '.$limit;
972 
973  // Check scope values
974  $scope = trim($scope);
975  if (!empty($scope) && !in_array($scope, array('base', 'one', 'sub', 'children'))) {
976  trigger_error(sprintf("Invalid parameter for scope '%s', please use 'base', 'one', 'sub' or 'children'."), $scope);
977  return NULL;
978  }
979  $scope = (!empty($scope))?' -s '.$scope: '';
980 
981  // Prepare parameters to be valid for shell execution
982  $dn = escapeshellarg($dn);
983  $pwd = escapeshellarg($this->bindpw);
984  $host = escapeshellarg($this->hostname);
985  $admin = escapeshellarg($this->binddn);
986  $filter = escapeshellarg($filter);
987 
988  $cmd = "ldapsearch -x -LLLL -D {$admin} {$filter} {$limit} {$scope} -H {$host} -b {$dn} -w {$pwd} ";
989 
990  // Create list of process pipes
991  $descriptorspec = array(
992  0 => array("pipe", "r"), // stdin
993  1 => array("pipe", "w"), // stdout
994  2 => array("pipe", "w") // stderr
995  );
996 
997  // Try to open the process
998  $process = proc_open($cmd, $descriptorspec, $pipes);
999  if (is_resource($process)) {
1000  // Write the password to stdin
1001  fclose($pipes[0]);
1002 
1003  // Get results from stdout and stderr
1004  $res = stream_get_contents($pipes[1]);
1005  $err = stream_get_contents($pipes[2]);
1006  fclose($pipes[1]);
1007 
1008  // Close the process and check its return value
1009  if (proc_close($process) != 0) {
1010  $this->error = $err;
1011  return NULL;
1012  }
1013  } else {
1014  $this->error = _("proc_open failed to execute ldapsearch");
1015  return NULL;
1016  }
1017  return $res;
1018  }
1019 
1020  function dn_exists($dn)
1021  {
1022  return @ldap_read($this->cid, $dn, "(objectClass=*)", array("objectClass"));
1023  }
1024 
1042  function import_complete_ldif($srp, $str_attr, $JustModify, $DeleteOldEntries)
1043  {
1044  if ($this->reconnect) {
1045  $this->connect();
1046  }
1047 
1048  /* First we split the string into lines */
1049  $fileLines = preg_split("/\n/", $str_attr);
1050  if (end($fileLines) != '') {
1051  $fileLines[] = '';
1052  }
1053 
1054  /* Joining lines */
1055  $line = NULL;
1056  $entry = array();
1057  $entryStart = -1;
1058  foreach ($fileLines as $lineNumber => $fileLine) {
1059  if (preg_match('/^ /', $fileLine)) {
1060  if ($line === NULL) {
1061  throw new LDIFImportException(sprintf(_('Error line %s, first line of an entry cannot start with a space'), $lineNumber));
1062  }
1063  /* Append to current line */
1064  $line .= substr($fileLine, 1);
1065  } else {
1066  if ($line !== NULL) {
1067  if (preg_match('/^#/', $line)) {
1068  /* Ignore comment */
1069  } elseif (preg_match('/^version:/', $line) && empty($entry)) {
1070  /* Ignore version number */
1071  } else {
1072  /* Line has ended */
1073  list ($key, $value) = explode(':', $line, 2);
1074  $value = trim($value);
1075  if (preg_match('/^:/', $value)) {
1076  $value = base64_decode(trim(substr($value, 1)));
1077  }
1078  if (preg_match('/^</', $value)) {
1079  throw new LDIFImportException(sprintf(_('Error line %s, references to an external file are not supported'), $lineNumber));
1080  }
1081  if ($value === '') {
1082  throw new LDIFImportException(sprintf(_('Error line %s, attribute "%s" has no value'), $lineNumber, $key));
1083  }
1084  if ($key == 'dn') {
1085  if (!empty($entry)) {
1086  throw new LDIFImportException(sprintf(_('Error line %s, an entry bloc can only have one dn'), $lineNumber));
1087  }
1088  $entry['dn'] = $value;
1089  $entryStart = $lineNumber;
1090  } elseif (empty($entry)) {
1091  throw new LDIFImportException(sprintf(_('Error line %s, an entry bloc should start with the dn'), $lineNumber));
1092  } else {
1093  if (!isset($entry[$key])) {
1094  $entry[$key] = array();
1095  }
1096  $entry[$key][] = $value;
1097  }
1098  }
1099  }
1100  /* Start new line */
1101  $line = trim($fileLine);
1102  if ($line == '') {
1103  if (!empty($entry)) {
1104  /* Entry is finished */
1105  $entries[$entryStart] = $entry;
1106  }
1107  /* Start a new entry */
1108  $entry = array();
1109  $entryStart = -1;
1110  $line = NULL;
1111  }
1112  }
1113  }
1114 
1115  foreach ($entries as $startLine => $entry) {
1116  /* Delete before insert */
1117  $usermdir = ($this->dn_exists($entry['dn']) && $DeleteOldEntries);
1118  /* Should we use Modify instead of Add */
1119  $usemodify = ($this->dn_exists($entry['dn']) && $JustModify);
1120 
1121  /* If we can't Import, return with a file error */
1122  if (!$this->import_single_entry($srp, $entry, $usemodify, $usermdir)) {
1123  throw new LDIFImportException(sprintf(_('Error while importing dn: "%s", please check your LDIF from line %s on!'), $entry['dn'][0], $startLine));
1124  }
1125  }
1126  }
1127 
1142  protected function import_single_entry($srp, $data, $modify, $delete)
1143  {
1144  global $config;
1145 
1146  if (!$config) {
1147  trigger_error("Can't import ldif, can't read config object.");
1148  }
1149 
1150  if ($this->reconnect) {
1151  $this->connect();
1152  }
1153 
1154  $ret = FALSE;
1155 
1156  /* If dn is an index of data, we should try to insert the data */
1157  if (isset($data['dn'])) {
1158  /* Fix dn */
1159  $tmp = ldap_explode_dn($data['dn'], 0);
1160  unset($tmp['count']);
1161  $dn = '';
1162  foreach ($tmp as $tm) {
1163  $dn .= trim($tm).',';
1164  }
1165  $dn = preg_replace('/,$/', '', $dn);
1166  unset($data['dn']);
1167 
1168  /* Creating Entry */
1169  $this->cd($dn);
1170 
1171  /* Delete existing entry */
1172  if ($delete) {
1173  $this->rmdir_recursive($srp, $dn);
1174  }
1175 
1176  /* Create missing trees */
1177  $this->cd($config->current['BASE']);
1178  $this->create_missing_trees($srp, preg_replace('/^[^,]+,/', '', $dn));
1179  $this->cd($dn);
1180 
1181  if (!$modify) {
1182  $this->cat($srp, $dn);
1183  if ($this->count($srp)) {
1184  /* The destination entry exists, overwrite it with the new entry */
1185  $attrs = $this->fetch($srp);
1186  foreach (array_keys($attrs) as $name) {
1187  if (!is_numeric($name)) {
1188  if (in_array($name, array('dn','count'))) {
1189  continue;
1190  }
1191  if (!isset($data[$name])) {
1192  $data[$name] = array();
1193  }
1194  }
1195  }
1196  $ret = $this->modify($data);
1197  } else {
1198  /* The destination entry doesn't exists, create it */
1199  $ret = $this->add($data);
1200  }
1201  } else {
1202  /* Keep all vars that aren't touched by this ldif */
1203  $ret = $this->modify($data);
1204  }
1205  }
1206 
1207  if (!$this->success()) {
1208  msg_dialog::display(_('LDAP error'), msgPool::ldaperror($this->get_error(), $dn, '', get_class()), LDAP_ERROR);
1209  }
1210 
1211  return $ret;
1212  }
1213 
1219  function get_objectclasses($force_reload = FALSE)
1220  {
1221  $objectclasses = array();
1222 
1223  /* Return the cached results. */
1224  if (class_available('session') && session::global_is_set('LDAP_CACHE::get_objectclasses') && !$force_reload) {
1225  $objectclasses = session::global_get('LDAP_CACHE::get_objectclasses');
1226  return $objectclasses;
1227  }
1228 
1229  // Get base to look for schema
1230  $sr = @ldap_read($this->cid, '', 'objectClass=*', array('subschemaSubentry'));
1231  $attr = @ldap_get_entries($this->cid, $sr);
1232  if (!isset($attr[0]['subschemasubentry'][0])) {
1233  return array();
1234  }
1235 
1236  /* Get list of objectclasses and fill array */
1237  $nb = $attr[0]['subschemasubentry'][0];
1238  $objectclasses = array();
1239  $sr = ldap_read ($this->cid, $nb, 'objectClass=*', array('objectclasses'));
1240  $attrs = ldap_get_entries($this->cid, $sr);
1241  if (!isset($attrs[0])) {
1242  return array();
1243  }
1244  foreach ($attrs[0]['objectclasses'] as $val) {
1245  if (preg_match('/^[0-9]+$/', $val)) {
1246  continue;
1247  }
1248  $name = "OID";
1249  $pattern = explode(' ', $val);
1250  $ocname = preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
1251  $objectclasses[$ocname] = array();
1252 
1253  foreach ($pattern as $chunk) {
1254  switch ($chunk) {
1255 
1256  case '(':
1257  $value = '';
1258  break;
1259 
1260  case ')':
1261  if ($name != '') {
1262  $v = $this->value2container($value);
1263  if (in_array($name, array('MUST', 'MAY')) && !is_array($v)) {
1264  $v = array($v);
1265  }
1266  $objectclasses[$ocname][$name] = $v;
1267  }
1268  $name = '';
1269  $value = '';
1270  break;
1271 
1272  case 'NAME':
1273  case 'DESC':
1274  case 'SUP':
1275  case 'STRUCTURAL':
1276  case 'ABSTRACT':
1277  case 'AUXILIARY':
1278  case 'MUST':
1279  case 'MAY':
1280  if ($name != '') {
1281  $v = $this->value2container($value);
1282  if (in_array($name, array('MUST','MAY')) && !is_array($v)) {
1283  $v = array($v);
1284  }
1285  $objectclasses[$ocname][$name] = $v;
1286  }
1287  $name = $chunk;
1288  $value = '';
1289  break;
1290 
1291  default: $value .= $chunk.' ';
1292  }
1293  }
1294 
1295  }
1296  if (class_available('session')) {
1297  session::global_set('LDAP_CACHE::get_objectclasses', $objectclasses);
1298  }
1299 
1300  return $objectclasses;
1301  }
1302 
1303 
1304  function value2container($value)
1305  {
1306  /* Set emtpy values to "TRUE" only */
1307  if (preg_match('/^\s*$/', $value)) {
1308  return TRUE;
1309  }
1310 
1311  /* Remove ' and " if needed */
1312  $value = preg_replace('/^[\'"]/', '', $value);
1313  $value = preg_replace('/[\'"] *$/', '', $value);
1314 
1315  /* Convert to array if $ is inside... */
1316  if (preg_match('/\$/', $value)) {
1317  $container = preg_split('/\s*\$\s*/', $value);
1318  } else {
1319  $container = chop($value);
1320  }
1321 
1322  return $container;
1323  }
1324 
1330  function log($string)
1331  {
1332  if (session::global_is_set('config')) {
1333  $cfg = session::global_get('config');
1334  if (isset($cfg->current['LDAPSTATS']) && preg_match('/true/i', $cfg->current['LDAPSTATS'])) {
1335  syslog (LOG_INFO, $string);
1336  }
1337  }
1338  }
1339 
1340  /* added by Guido Serra aka Zeph <zeph@purotesto.it> */
1341 
1347  function getCn($dn)
1348  {
1349  $simple = explode(",", $dn);
1350 
1351  foreach ($simple as $piece) {
1352  $partial = explode("=", $piece);
1353 
1354  if ($partial[0] == "cn") {
1355  return $partial[1];
1356  }
1357  }
1358  }
1359 
1360  function get_naming_contexts($server, $admin = '', $password = '')
1361  {
1362  /* Build LDAP connection */
1363  $ds = ldap_connect ($server);
1364  if (!$ds) {
1365  die ('Can\'t bind to LDAP. No check possible!');
1366  }
1367  ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
1368  ldap_bind ($ds, $admin, $password);
1369 
1370  /* Get base to look for naming contexts */
1371  $sr = @ldap_read ($ds, '', 'objectClass=*', array('namingContexts'));
1372  $attr = @ldap_get_entries($ds, $sr);
1373 
1374  return $attr[0]['namingcontexts'];
1375  }
1376 }
1377 ?>
rename_dn($source, $dest)
Move the given Ldap entry from $source to $dest.
Definition: class_ldap.inc:561
generateLdif($dn, $filter="(objectClass=*)", $scope= 'sub', $limit=0)
Generates an ldif for all entries matching the filter settings, scope and limit.
Definition: class_ldap.inc:964
set_size_limit($size)
Set a size limit.
Definition: class_ldap.inc:361
object_match_filter($dn, $filter)
Search object from a filter.
Definition: class_ldap.inc:341
clearResult($srp)
Clear a result.
Definition: class_ldap.inc:428
getSearchResource()
Get the search ressource.
Definition: class_ldap.inc:105
cd($dir)
Change directory.
Definition: class_ldap.inc:213
static prepare4filter($dn)
Function to fix problematic characters in DN's that are used for search requests. I...
Definition: class_ldap.inc:118
rm($attrs="", $dn="")
Remove.
Definition: class_ldap.inc:493
const DEBUG_LDAP
Definition: functions.inc:37
static ldaperror($error, $dn= '', $type=0, $plugin= '')
Display LDAP error.
rmdir_recursive($srp, $deletedn)
Function rmdir_recursive.
Definition: class_ldap.inc:615
getParentDir($basedn= '')
Accessor of the parent directory of the basedn.
Definition: class_ldap.inc:229
modify($attrs)
Modify a entry of the directory LDAP.
Definition: class_ldap.inc:679
log($string)
Add a string in log file.
success()
Success.
Definition: class_ldap.inc:907
rebind($ldap, $referral)
Rebind.
Definition: class_ldap.inc:164
disconnect()
Disconnect to LDAP server.
Definition: class_ldap.inc:200
connect()
Create a connection to LDAP server.
Definition: class_ldap.inc:129
__construct($binddn, $bindpw, $hostname, $follow_referral=FALSE, $tls=FALSE)
Create a LDAP connection.
Definition: class_ldap.inc:82
static global_set($name, $value)
Set a value in a session.
getDN($srp)
Accessor of the DN.
Definition: class_ldap.inc:441
import_single_entry($srp, $data, $modify, $delete)
Function to Imports a single entry.
Parent class for all exceptions thrown in FusionDirectory.
This class contains all ldap function needed to make ldap operations easy.
Definition: class_ldap.inc:34
search($srp, $filter, $attrs=array(), $scope= 'subtree')
Search about filter.
Definition: class_ldap.inc:246
DEBUG($level, $line, $function, $file, $data, $info= '')
Debug level action.
Definition: functions.inc:219
fetch($srp)
Fetch.
Definition: class_ldap.inc:379
get_objectclasses($force_reload=FALSE)
Get the object classes.
get_error()
Get the error.
Definition: class_ldap.inc:915
static global_get($name)
Accessor of a session var.
count($srp)
Return the numbers of entries.
Definition: class_ldap.inc:468
static display($s_title, $s_message, $i_type=INFO_DIALOG)
Display a message dialog.
import_complete_ldif($srp, $str_attr, $JustModify, $DeleteOldEntries)
Function to imports ldifs.
add($attrs)
Add entry in the LDAP directory.
Definition: class_ldap.inc:705
resetResult($srp)
Reset the result.
Definition: class_ldap.inc:418
unbind()
Unbind to LDAP server.
Definition: class_ldap.inc:191
rmdir($deletedn)
Remove directory.
Definition: class_ldap.inc:536
get_additional_error()
Get the LDAP additional error.
Definition: class_ldap.inc:895
static global_is_set($name)
Check if a session is defined.
getCn($dn)
Function to get cn.
reconnect()
Reconnect to LDAP server.
Definition: class_ldap.inc:181
Exception class which can be thrown by LDAP if the LDIF format is broken.
get_attribute($dn, $name, $r_array=0)
Read a entry from a directory.
Definition: class_ldap.inc:865
class_available($name)
Checks if a class is available.
Definition: functions.inc:130