dolibarr  18.0.0
ldap.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
4  * Copyright (C) 2005-2021 Regis Houssin <regis.houssin@inodbox.com>
5  * Copyright (C) 2006-2021 Laurent Destailleur <eldy@users.sourceforge.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  * or see https://www.gnu.org/
20  */
21 
34 class Ldap
35 {
39  public $error = '';
40 
44  public $errors = array();
45 
49  public $server = array();
50 
55 
59  public $dn;
63  public $serverType;
71  public $domain;
72 
73  public $domainFQDN;
74 
79  public $searchUser;
88  public $people;
92  public $groups;
101 
102 
103  //Fetch user
104  public $name;
105  public $firstname;
106  public $login;
107  public $phone;
108  public $skype;
109  public $fax;
110  public $mail;
111  public $mobile;
112 
113  public $uacf;
114  public $pwdlastset;
115 
116  public $ldapcharset = 'UTF-8'; // LDAP should be UTF-8 encoded
117 
118 
122  public $connection;
126  public $result;
127 
131  const SYNCHRO_NONE = 0;
132 
137 
142 
143 
147  public function __construct()
148  {
149  global $conf;
150 
151  // Server
152  if (!empty($conf->global->LDAP_SERVER_HOST)) {
153  $this->server[] = $conf->global->LDAP_SERVER_HOST;
154  }
155  if (!empty($conf->global->LDAP_SERVER_HOST_SLAVE)) {
156  $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE;
157  }
158  $this->serverPort = getDolGlobalInt('LDAP_SERVER_PORT', 389);
159  $this->ldapProtocolVersion = getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION');
160  $this->dn = getDolGlobalString('LDAP_SERVER_DN');
161  $this->serverType = getDolGlobalString('LDAP_SERVER_TYPE');
162 
163  $this->domain = getDolGlobalString('LDAP_SERVER_DN');
164  $this->searchUser = getDolGlobalString('LDAP_ADMIN_DN');
165  $this->searchPassword = getDolGlobalString('LDAP_ADMIN_PASS');
166  $this->people = getDolGlobalString('LDAP_USER_DN');
167  $this->groups = getDolGlobalString('LDAP_GROUP_DN');
168 
169  $this->filter = getDolGlobalString('LDAP_FILTER_CONNECTION'); // Filter on user
170  $this->filtergroup = getDolGlobalString('LDAP_GROUP_FILTER'); // Filter on groups
171  $this->filtermember = getDolGlobalString('LDAP_MEMBER_FILTER'); // Filter on member
172 
173  // Users
174  $this->attr_login = getDolGlobalString('LDAP_FIELD_LOGIN'); //unix
175  $this->attr_sambalogin = getDolGlobalString('LDAP_FIELD_LOGIN_SAMBA'); //samba, activedirectory
176  $this->attr_name = getDolGlobalString('LDAP_FIELD_NAME');
177  $this->attr_firstname = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
178  $this->attr_mail = getDolGlobalString('LDAP_FIELD_MAIL');
179  $this->attr_phone = getDolGlobalString('LDAP_FIELD_PHONE');
180  $this->attr_skype = getDolGlobalString('LDAP_FIELD_SKYPE');
181  $this->attr_fax = getDolGlobalString('LDAP_FIELD_FAX');
182  $this->attr_mobile = getDolGlobalString('LDAP_FIELD_MOBILE');
183  }
184 
185  // Connection handling methods -------------------------------------------
186 
187  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
195  public function connect_bind()
196  {
197  // phpcs:enable
198  global $conf;
199  global $dolibarr_main_auth_ldap_debug;
200 
201  $connected = 0;
202  $this->bind = 0;
203  $this->error = 0;
204  $this->connectedServer = '';
205 
206  $ldapdebug = ((empty($dolibarr_main_auth_ldap_debug) || $dolibarr_main_auth_ldap_debug == "false") ? false : true);
207 
208  if ($ldapdebug) {
209  dol_syslog(get_class($this)."::connect_bind");
210  print "DEBUG: connect_bind<br>\n";
211  }
212 
213  // Check parameters
214  if (count($this->server) == 0 || empty($this->server[0])) {
215  $this->error = 'LDAP setup (file conf.php) is not complete';
216  dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
217  return -1;
218  }
219 
220  if (!function_exists("ldap_connect")) {
221  $this->error = 'LDAPFunctionsNotAvailableOnPHP';
222  dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
223  $return = -1;
224  }
225 
226  if (empty($this->error)) {
227  // Loop on each ldap server
228  foreach ($this->server as $host) {
229  if ($connected) {
230  break;
231  }
232  if (empty($host)) {
233  continue;
234  }
235 
236  if ($this->serverPing($host, $this->serverPort) === true) {
237  if ($ldapdebug) {
238  dol_syslog(get_class($this)."::connect_bind serverPing true, we try ldap_connect to ".$host);
239  }
240  $this->connection = ldap_connect($host, $this->serverPort);
241  } else {
242  if (preg_match('/^ldaps/i', $host)) {
243  // With host = ldaps://server, the serverPing to ssl://server sometimes fails, even if the ldap_connect succeed, so
244  // we test this case and continue in such a case even if serverPing fails.
245  if ($ldapdebug) {
246  dol_syslog(get_class($this)."::connect_bind serverPing false, we try ldap_connect to ".$host);
247  }
248  $this->connection = ldap_connect($host, $this->serverPort);
249  } else {
250  continue;
251  }
252  }
253 
254  if (is_resource($this->connection) || is_object($this->connection)) {
255  if ($ldapdebug) {
256  dol_syslog(get_class($this)."::connect_bind this->connection is ok", LOG_DEBUG);
257  }
258 
259  // Upgrade connexion to TLS, if requested by the configuration
260  if (!empty($conf->global->LDAP_SERVER_USE_TLS)) {
261  // For test/debug
262  //ldap_set_option($this->connection, LDAP_OPT_DEBUG_LEVEL, 7);
263  //ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3);
264  //ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
265 
266  $resulttls = ldap_start_tls($this->connection);
267  if (!$resulttls) {
268  dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING);
269  $this->error = 'ldap_start_tls Failed to start TLS '.ldap_errno($this->connection).' '.ldap_error($this->connection);
270  $connected = 0;
271  $this->unbind();
272  }
273  }
274 
275  // Execute the ldap_set_option here (after connect and before bind)
276  $this->setVersion();
277  ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true.
278 
279 
280  if ($this->serverType == "activedirectory") {
281  $result = $this->setReferrals();
282  dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG);
283  $this->result = $this->bindauth($this->searchUser, $this->searchPassword);
284  if ($this->result) {
285  $this->bind = $this->result;
286  $connected = 2;
287  $this->connectedServer = $host;
288  break;
289  } else {
290  $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
291  }
292  } else {
293  // Try in auth mode
294  if ($this->searchUser && $this->searchPassword) {
295  dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG);
296  $this->result = $this->bindauth($this->searchUser, $this->searchPassword);
297  if ($this->result) {
298  $this->bind = $this->result;
299  $connected = 2;
300  $this->connectedServer = $host;
301  break;
302  } else {
303  $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
304  }
305  }
306  // Try in anonymous
307  if (!$this->bind) {
308  dol_syslog(get_class($this)."::connect_bind try bind anonymously on ".$host, LOG_DEBUG);
309  $result = $this->bind();
310  if ($result) {
311  $this->bind = $this->result;
312  $connected = 1;
313  $this->connectedServer = $host;
314  break;
315  } else {
316  $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
317  }
318  }
319  }
320  }
321 
322  if (!$connected) {
323  $this->unbind();
324  }
325  } // End loop on each server
326  }
327 
328  if ($connected) {
329  $return = $connected;
330  dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG);
331  } else {
332  $this->error = 'Failed to connect to LDAP'.($this->error ? ': '.$this->error : '');
333  $return = -1;
334  dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING);
335  }
336 
337  return $return;
338  }
339 
348  public function close()
349  {
350  return $this->unbind();
351  }
352 
359  public function bind()
360  {
361  if (!$this->result = @ldap_bind($this->connection)) {
362  $this->ldapErrorCode = ldap_errno($this->connection);
363  $this->ldapErrorText = ldap_error($this->connection);
364  $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
365  return false;
366  } else {
367  return true;
368  }
369  }
370 
381  public function bindauth($bindDn, $pass)
382  {
383  if (!$this->result = @ldap_bind($this->connection, $bindDn, $pass)) {
384  $this->ldapErrorCode = ldap_errno($this->connection);
385  $this->ldapErrorText = ldap_error($this->connection);
386  $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
387  return false;
388  } else {
389  return true;
390  }
391  }
392 
399  public function unbind()
400  {
401  $this->result = true;
402  if (is_resource($this->connection) || is_object($this->connection)) {
403  $this->result = @ldap_unbind($this->connection);
404  }
405  if ($this->result) {
406  return true;
407  } else {
408  return false;
409  }
410  }
411 
412 
418  public function getVersion()
419  {
420  $version = 0;
421  $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
422  return $version;
423  }
424 
430  public function setVersion()
431  {
432  // LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17
433  $ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion);
434  return $ldapsetversion;
435  }
436 
442  public function setReferrals()
443  {
444  // LDAP_OPT_REFERRALS est une constante qui vaut ?
445  $ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
446  return $ldapreferrals;
447  }
448 
449 
459  public function add($dn, $info, $user)
460  {
461  dol_syslog(get_class($this)."::add dn=".$dn." info=".json_encode($info));
462 
463  // Check parameters
464  if (!$this->connection) {
465  $this->error = "NotConnected";
466  return -2;
467  }
468  if (!$this->bind) {
469  $this->error = "NotConnected";
470  return -3;
471  }
472 
473  // Encode to LDAP page code
474  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
475  foreach ($info as $key => $val) {
476  if (!is_array($val)) {
477  $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
478  }
479  }
480 
481  $this->dump($dn, $info);
482 
483  //print_r($info);
484  $result = @ldap_add($this->connection, $dn, $info);
485 
486  if ($result) {
487  dol_syslog(get_class($this)."::add successfull", LOG_DEBUG);
488  return 1;
489  } else {
490  $this->ldapErrorCode = @ldap_errno($this->connection);
491  $this->ldapErrorText = @ldap_error($this->connection);
492  $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
493  dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR);
494  return -1;
495  }
496  }
497 
507  public function modify($dn, $info, $user)
508  {
509  dol_syslog(get_class($this)."::modify dn=".$dn." info=".join(',', $info));
510 
511  // Check parameters
512  if (!$this->connection) {
513  $this->error = "NotConnected";
514  return -2;
515  }
516  if (!$this->bind) {
517  $this->error = "NotConnected";
518  return -3;
519  }
520 
521  // Encode to LDAP page code
522  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
523  foreach ($info as $key => $val) {
524  if (!is_array($val)) {
525  $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
526  }
527  }
528 
529  $this->dump($dn, $info);
530 
531  //print_r($info);
532 
533  // For better compatibility with Samba4 AD
534  if ($this->serverType == "activedirectory") {
535  unset($info['cn']); // To avoid error : Operation not allowed on RDN (Code 67)
536 
537  // To avoid error : LDAP Error: 53 (Unwilling to perform)
538  if (isset($info['unicodePwd'])) {
539  $info['unicodePwd'] = mb_convert_encoding("\"".$info['unicodePwd']."\"", "UTF-16LE", "UTF-8");
540  }
541  }
542  $result = @ldap_modify($this->connection, $dn, $info);
543 
544  if ($result) {
545  dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG);
546  return 1;
547  } else {
548  $this->error = @ldap_error($this->connection);
549  dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR);
550  return -1;
551  }
552  }
553 
565  public function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true)
566  {
567  dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn ? 1 : 0));
568 
569  // Check parameters
570  if (!$this->connection) {
571  $this->error = "NotConnected";
572  return -2;
573  }
574  if (!$this->bind) {
575  $this->error = "NotConnected";
576  return -3;
577  }
578 
579  // Encode to LDAP page code
580  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
581  $newrdn = $this->convFromOutputCharset($newrdn, $this->ldapcharset);
582  $newparent = $this->convFromOutputCharset($newparent, $this->ldapcharset);
583 
584  //print_r($info);
585  $result = @ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
586 
587  if ($result) {
588  dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG);
589  return 1;
590  } else {
591  $this->error = @ldap_error($this->connection);
592  dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR);
593  return -1;
594  }
595  }
596 
609  public function update($dn, $info, $user, $olddn, $newrdn = false, $newparent = false)
610  {
611  dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn);
612 
613  // Check parameters
614  if (!$this->connection) {
615  $this->error = "NotConnected";
616  return -2;
617  }
618  if (!$this->bind) {
619  $this->error = "NotConnected";
620  return -3;
621  }
622 
623  if (!$olddn || $olddn != $dn) {
624  if (!empty($olddn) && !empty($newrdn) && !empty($newparent) && $this->ldapProtocolVersion === '3') {
625  // This function currently only works with LDAPv3
626  $result = $this->rename($olddn, $newrdn, $newparent, $user, true);
627  $result = $this->modify($dn, $info, $user); // We force "modify" for avoid some fields not modify
628  } else {
629  // If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one.
630  $result = $this->add($dn, $info, $user);
631  if ($result > 0 && $olddn && $olddn != $dn) {
632  $result = $this->delete($olddn); // If add fails, we do not try to delete old one
633  }
634  }
635  } else {
636  //$result = $this->delete($olddn);
637  $result = $this->add($dn, $info, $user); // If record has been deleted from LDAP, we recreate it. We ignore error if it already exists.
638  $result = $this->modify($dn, $info, $user); // We use add/modify instead of delete/add when olddn is received
639  }
640  if ($result <= 0) {
641  $this->error = ldap_error($this->connection).' (Code '.ldap_errno($this->connection).") ".$this->error;
642  dol_syslog(get_class($this)."::update ".$this->error, LOG_ERR);
643  //print_r($info);
644  return -1;
645  } else {
646  dol_syslog(get_class($this)."::update done successfully");
647  return 1;
648  }
649  }
650 
651 
659  public function delete($dn)
660  {
661  dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn);
662 
663  // Check parameters
664  if (!$this->connection) {
665  $this->error = "NotConnected";
666  return -2;
667  }
668  if (!$this->bind) {
669  $this->error = "NotConnected";
670  return -3;
671  }
672 
673  // Encode to LDAP page code
674  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
675 
676  $result = @ldap_delete($this->connection, $dn);
677 
678  if ($result) {
679  return 1;
680  }
681  return -1;
682  }
683 
684  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
692  public function dump_content($dn, $info)
693  {
694  // phpcs:enable
695  $content = '';
696 
697  // Create file content
698  if (preg_match('/^ldap/', $this->server[0])) {
699  $target = "-H ".join(',', $this->server);
700  } else {
701  $target = "-h ".join(',', $this->server)." -p ".$this->serverPort;
702  }
703  $content .= "# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
704  $content .= "# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
705  $content .= "# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
706  if (in_array('localhost', $this->server)) {
707  $content .= "# If commands fails to connect, try without -h and -p\n";
708  }
709  $content .= "dn: ".$dn."\n";
710  foreach ($info as $key => $value) {
711  if (!is_array($value)) {
712  $content .= "$key: $value\n";
713  } else {
714  foreach ($value as $valuevalue) {
715  $content .= "$key: $valuevalue\n";
716  }
717  }
718  }
719  return $content;
720  }
721 
729  public function dump($dn, $info)
730  {
731  global $conf;
732 
733  // Create content
734  $content = $this->dump_content($dn, $info);
735 
736  //Create file
737  $result = dol_mkdir($conf->ldap->dir_temp);
738 
739  $outputfile = $conf->ldap->dir_temp.'/ldapinput.in';
740  $fp = fopen($outputfile, "w");
741  if ($fp) {
742  fputs($fp, $content);
743  fclose($fp);
744  dolChmod($outputfile);
745  return 1;
746  } else {
747  return -1;
748  }
749  }
750 
759  public function serverPing($host, $port = 389, $timeout = 1)
760  {
761  $regs = array();
762  if (preg_match('/^ldaps:\/\/([^\/]+)\/?$/', $host, $regs)) {
763  // Replace ldaps:// by ssl://
764  $host = 'ssl://'.$regs[1];
765  } elseif (preg_match('/^ldap:\/\/([^\/]+)\/?$/', $host, $regs)) {
766  // Remove ldap://
767  $host = $regs[1];
768  }
769 
770  //var_dump($newhostforstream); var_dump($host); var_dump($port);
771  //$host = 'ssl://ldap.test.local:636';
772  //$port = 636;
773 
774  $errno = $errstr = 0;
775  /*
776  if ($methodtochecktcpconnect == 'socket') {
777  Try to use socket_create() method.
778  Method that use stream_context_create() works only on registered listed in stream stream_get_wrappers(): http, https, ftp, ...
779  }
780  */
781 
782  // Use the method fsockopen to test tcp connect. No way to ignore ssl certificate errors with this method !
783  $op = @fsockopen($host, $port, $errno, $errstr, $timeout);
784 
785  //var_dump($op);
786  if (!$op) {
787  return false; //DC is N/A
788  } else {
789  fclose($op); //explicitly close open socket connection
790  return true; //DC is up & running, we can safely connect with ldap_connect
791  }
792  }
793 
794 
795  // Attribute methods -----------------------------------------------------
796 
806  public function addAttribute($dn, $info, $user)
807  {
808  dol_syslog(get_class($this)."::addAttribute dn=".$dn." info=".join(',', $info));
809 
810  // Check parameters
811  if (!$this->connection) {
812  $this->error = "NotConnected";
813  return -2;
814  }
815  if (!$this->bind) {
816  $this->error = "NotConnected";
817  return -3;
818  }
819 
820  // Encode to LDAP page code
821  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
822  foreach ($info as $key => $val) {
823  if (!is_array($val)) {
824  $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
825  }
826  }
827 
828  $this->dump($dn, $info);
829 
830  //print_r($info);
831  $result = @ldap_mod_add($this->connection, $dn, $info);
832 
833  if ($result) {
834  dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG);
835  return 1;
836  } else {
837  $this->error = @ldap_error($this->connection);
838  dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR);
839  return -1;
840  }
841  }
842 
852  public function updateAttribute($dn, $info, $user)
853  {
854  dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',', $info));
855 
856  // Check parameters
857  if (!$this->connection) {
858  $this->error = "NotConnected";
859  return -2;
860  }
861  if (!$this->bind) {
862  $this->error = "NotConnected";
863  return -3;
864  }
865 
866  // Encode to LDAP page code
867  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
868  foreach ($info as $key => $val) {
869  if (!is_array($val)) {
870  $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
871  }
872  }
873 
874  $this->dump($dn, $info);
875 
876  //print_r($info);
877  $result = @ldap_mod_replace($this->connection, $dn, $info);
878 
879  if ($result) {
880  dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG);
881  return 1;
882  } else {
883  $this->error = @ldap_error($this->connection);
884  dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR);
885  return -1;
886  }
887  }
888 
898  public function deleteAttribute($dn, $info, $user)
899  {
900  dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',', $info));
901 
902  // Check parameters
903  if (!$this->connection) {
904  $this->error = "NotConnected";
905  return -2;
906  }
907  if (!$this->bind) {
908  $this->error = "NotConnected";
909  return -3;
910  }
911 
912  // Encode to LDAP page code
913  $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
914  foreach ($info as $key => $val) {
915  if (!is_array($val)) {
916  $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
917  }
918  }
919 
920  $this->dump($dn, $info);
921 
922  //print_r($info);
923  $result = @ldap_mod_del($this->connection, $dn, $info);
924 
925  if ($result) {
926  dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG);
927  return 1;
928  } else {
929  $this->error = @ldap_error($this->connection);
930  dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR);
931  return -1;
932  }
933  }
934 
942  public function getAttribute($dn, $filter)
943  {
944  // Check parameters
945  if (!$this->connection) {
946  $this->error = "NotConnected";
947  return -2;
948  }
949  if (!$this->bind) {
950  $this->error = "NotConnected";
951  return -3;
952  }
953 
954  $search = @ldap_search($this->connection, $dn, $filter);
955 
956  // Only one entry should ever be returned
957  $entry = @ldap_first_entry($this->connection, $search);
958 
959  if (!$entry) {
960  $this->ldapErrorCode = -1;
961  $this->ldapErrorText = "Couldn't find entry";
962  return 0; // Couldn't find entry...
963  }
964 
965  // Get values
966  if (!($values = ldap_get_attributes($this->connection, $entry))) {
967  $this->ldapErrorCode = ldap_errno($this->connection);
968  $this->ldapErrorText = ldap_error($this->connection);
969  return 0; // No matching attributes
970  }
971 
972  // Return an array containing the attributes.
973  return $values;
974  }
975 
983  public function getAttributeValues($filterrecord, $attribute)
984  {
985  $attributes = array();
986  $attributes[0] = $attribute;
987 
988  // We need to search for this user in order to get their entry.
989  $this->result = @ldap_search($this->connection, $this->people, $filterrecord, $attributes);
990 
991  // Pourquoi cette ligne ?
992  //$info = ldap_get_entries($this->connection, $this->result);
993 
994  // Only one entry should ever be returned (no user will have the same uid)
995  $entry = ldap_first_entry($this->connection, $this->result);
996 
997  if (!$entry) {
998  $this->ldapErrorCode = -1;
999  $this->ldapErrorText = "Couldn't find user";
1000  return false; // Couldn't find the user...
1001  }
1002 
1003  // Get values
1004  if (!$values = @ldap_get_values($this->connection, $entry, $attribute)) {
1005  $this->ldapErrorCode = ldap_errno($this->connection);
1006  $this->ldapErrorText = ldap_error($this->connection);
1007  return false; // No matching attributes
1008  }
1009 
1010  // Return an array containing the attributes.
1011  return $values;
1012  }
1013 
1026  public function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter = 0, $attributeAsArray = array())
1027  {
1028  $fulllist = array();
1029 
1030  dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',', $attributeArray).") activefilter=".$activefilter);
1031 
1032  // if the directory is AD, then bind first with the search user first
1033  if ($this->serverType == "activedirectory") {
1034  $this->bindauth($this->searchUser, $this->searchPassword);
1035  dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser);
1036  }
1037 
1038  // Define filter
1039  if (!empty($activefilter)) { // Use a predefined trusted filter (defined into setup by admin).
1040  if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter) {
1041  $filter = '('.$this->filter.')';
1042  } elseif (((string) $activefilter == 'group') && $this->filtergroup ) {
1043  $filter = '('.$this->filtergroup.')';
1044  } elseif (((string) $activefilter == 'member') && $this->filter) {
1045  $filter = '('.$this->filtermember.')';
1046  } else {
1047  // If this->filter/this->filtergroup is empty, make fiter on * (all)
1048  $filter = '('.ldap_escape($useridentifier, '', LDAP_ESCAPE_FILTER).'=*)';
1049  }
1050  } else { // Use a filter forged using the $search value
1051  $filter = '('.ldap_escape($useridentifier, '', LDAP_ESCAPE_FILTER).'='.ldap_escape($search, '', LDAP_ESCAPE_FILTER).')';
1052  }
1053 
1054  if (is_array($attributeArray)) {
1055  // Return list with required fields
1056  $attributeArray = array_values($attributeArray); // This is to force to have index reordered from 0 (not make ldap_search fails)
1057  dol_syslog(get_class($this)."::getRecords connection=".$this->connectedServer.":".$this->serverPort." userDn=".$userDn." filter=".$filter." attributeArray=(".join(',', $attributeArray).")");
1058  //var_dump($attributeArray);
1059  $this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray);
1060  } else {
1061  // Return list with fields selected by default
1062  dol_syslog(get_class($this)."::getRecords connection=".$this->connectedServer.":".$this->serverPort." userDn=".$userDn." filter=".$filter);
1063  $this->result = @ldap_search($this->connection, $userDn, $filter);
1064  }
1065  if (!$this->result) {
1066  $this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection);
1067  return -1;
1068  }
1069 
1070  $info = @ldap_get_entries($this->connection, $this->result);
1071 
1072  // Warning: Dans info, les noms d'attributs sont en minuscule meme si passe
1073  // a ldap_search en majuscule !!!
1074  //print_r($info);
1075 
1076  for ($i = 0; $i < $info["count"]; $i++) {
1077  $recordid = $this->convToOutputCharset($info[$i][strtolower($useridentifier)][0], $this->ldapcharset);
1078  if ($recordid) {
1079  //print "Found record with key $useridentifier=".$recordid."<br>\n";
1080  $fulllist[$recordid][$useridentifier] = $recordid;
1081 
1082  // Add to the array for each attribute in my list
1083  $num = count($attributeArray);
1084  for ($j = 0; $j < $num; $j++) {
1085  $keyattributelower = strtolower($attributeArray[$j]);
1086  //print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n";
1087 
1088  //permet de recuperer le SID avec Active Directory
1089  if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid") {
1090  $objectsid = $this->getObjectSid($recordid);
1091  $fulllist[$recordid][$attributeArray[$j]] = $objectsid;
1092  } else {
1093  if (in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) {
1094  $valueTab = array();
1095  foreach ($info[$i][$keyattributelower] as $key => $value) {
1096  $valueTab[$key] = $this->convToOutputCharset($value, $this->ldapcharset);
1097  }
1098  $fulllist[$recordid][$attributeArray[$j]] = $valueTab;
1099  } else {
1100  $fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0], $this->ldapcharset);
1101  }
1102  }
1103  }
1104  }
1105  }
1106 
1107  asort($fulllist);
1108  return $fulllist;
1109  }
1110 
1118  public function littleEndian($hex)
1119  {
1120  $result = '';
1121  for ($x = dol_strlen($hex) - 2; $x >= 0; $x = $x - 2) {
1122  $result .= substr($hex, $x, 2);
1123  }
1124  return $result;
1125  }
1126 
1127 
1135  public function getObjectSid($ldapUser)
1136  {
1137  $criteria = '('.$this->getUserIdentifier().'='.$ldapUser.')';
1138  $justthese = array("objectsid");
1139 
1140  // if the directory is AD, then bind first with the search user first
1141  if ($this->serverType == "activedirectory") {
1142  $this->bindauth($this->searchUser, $this->searchPassword);
1143  }
1144 
1145  $i = 0;
1146  $searchDN = $this->people;
1147 
1148  while ($i <= 2) {
1149  $ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese);
1150 
1151  if (!$ldapSearchResult) {
1152  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1153  return -1;
1154  }
1155 
1156  $entry = ldap_first_entry($this->connection, $ldapSearchResult);
1157 
1158  if (!$entry) {
1159  // Si pas de resultat on cherche dans le domaine
1160  $searchDN = $this->domain;
1161  $i++;
1162  } else {
1163  $i++;
1164  $i++;
1165  }
1166  }
1167 
1168  if ($entry) {
1169  $ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid");
1170  $SIDText = $this->binSIDtoText($ldapBinary[0]);
1171  return $SIDText;
1172  } else {
1173  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1174  return '?';
1175  }
1176  }
1177 
1185  public function binSIDtoText($binsid)
1186  {
1187  $hex_sid = bin2hex($binsid);
1188  $rev = hexdec(substr($hex_sid, 0, 2)); // Get revision-part of SID
1189  $subcount = hexdec(substr($hex_sid, 2, 2)); // Get count of sub-auth entries
1190  $auth = hexdec(substr($hex_sid, 4, 12)); // SECURITY_NT_AUTHORITY
1191  $result = "$rev-$auth";
1192  for ($x = 0; $x < $subcount; $x++) {
1193  $result .= "-".hexdec($this->littleEndian(substr($hex_sid, 16 + ($x * 8), 8))); // get all SECURITY_NT_AUTHORITY
1194  }
1195  return $result;
1196  }
1197 
1198 
1210  public function search($checkDn, $filter)
1211  {
1212  dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter);
1213 
1214  $checkDn = $this->convFromOutputCharset($checkDn, $this->ldapcharset);
1215  $filter = $this->convFromOutputCharset($filter, $this->ldapcharset);
1216 
1217  // if the directory is AD, then bind first with the search user first
1218  if ($this->serverType == "activedirectory") {
1219  $this->bindauth($this->searchUser, $this->searchPassword);
1220  }
1221 
1222  $this->result = @ldap_search($this->connection, $checkDn, $filter);
1223 
1224  $result = @ldap_get_entries($this->connection, $this->result);
1225  if (!$result) {
1226  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1227  return -1;
1228  } else {
1229  ldap_free_result($this->result);
1230  return $result;
1231  }
1232  }
1233 
1234 
1243  public function fetch($user, $filter)
1244  {
1245  // Perform the search and get the entry handles
1246 
1247  // if the directory is AD, then bind first with the search user first
1248  if ($this->serverType == "activedirectory") {
1249  $this->bindauth($this->searchUser, $this->searchPassword);
1250  }
1251 
1252  $searchDN = $this->people; // TODO Why searching in people then domain ?
1253 
1254  $result = '';
1255  $i = 0;
1256  while ($i <= 2) {
1257  dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter);
1258  $this->result = @ldap_search($this->connection, $searchDN, $filter);
1259  if ($this->result) {
1260  $result = @ldap_get_entries($this->connection, $this->result);
1261  if ($result['count'] > 0) {
1262  dol_syslog('Ldap::fetch search found '.$result['count'].' records');
1263  } else {
1264  dol_syslog('Ldap::fetch search returns but found no records');
1265  }
1266  //var_dump($result);exit;
1267  } else {
1268  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1269  dol_syslog(get_class($this)."::fetch search fails");
1270  return -1;
1271  }
1272 
1273  if (!$result) {
1274  // Si pas de resultat on cherche dans le domaine
1275  $searchDN = $this->domain;
1276  $i++;
1277  } else {
1278  break;
1279  }
1280  }
1281 
1282  if (!$result) {
1283  $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1284  return -1;
1285  } else {
1286  $this->name = $this->convToOutputCharset($result[0][$this->attr_name][0], $this->ldapcharset);
1287  $this->firstname = $this->convToOutputCharset($result[0][$this->attr_firstname][0], $this->ldapcharset);
1288  $this->login = $this->convToOutputCharset($result[0][$this->attr_login][0], $this->ldapcharset);
1289  $this->phone = $this->convToOutputCharset($result[0][$this->attr_phone][0], $this->ldapcharset);
1290  $this->fax = $this->convToOutputCharset($result[0][$this->attr_fax][0], $this->ldapcharset);
1291  $this->mail = $this->convToOutputCharset($result[0][$this->attr_mail][0], $this->ldapcharset);
1292  $this->mobile = $this->convToOutputCharset($result[0][$this->attr_mobile][0], $this->ldapcharset);
1293 
1294  $this->uacf = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0], $this->ldapcharset));
1295  if (isset($result[0]["pwdlastset"][0])) { // If expiration on password exists
1296  $this->pwdlastset = ($result[0]["pwdlastset"][0] != 0) ? $this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0], $this->ldapcharset)) : 0;
1297  } else {
1298  $this->pwdlastset = -1;
1299  }
1300  if (!$this->name && !$this->login) {
1301  $this->pwdlastset = -1;
1302  }
1303  $this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0], $this->ldapcharset));
1304 
1305  // FQDN domain
1306  $domain = str_replace('dc=', '', $this->domain);
1307  $domain = str_replace(',', '.', $domain);
1308  $this->domainFQDN = $domain;
1309 
1310  // Set ldapUserDn (each user can have a different dn)
1311  //var_dump($result[0]);exit;
1312  $this->ldapUserDN = $result[0]['dn'];
1313 
1314  ldap_free_result($this->result);
1315  return 1;
1316  }
1317  }
1318 
1319 
1320  // helper methods
1321 
1327  public function getUserIdentifier()
1328  {
1329  if ($this->serverType == "activedirectory") {
1330  return $this->attr_sambalogin;
1331  } else {
1332  return $this->attr_login;
1333  }
1334  }
1335 
1342  public function parseUACF($uacf)
1343  {
1344  //All flags array
1345  $flags = array(
1346  "TRUSTED_TO_AUTH_FOR_DELEGATION" => 16777216,
1347  "PASSWORD_EXPIRED" => 8388608,
1348  "DONT_REQ_PREAUTH" => 4194304,
1349  "USE_DES_KEY_ONLY" => 2097152,
1350  "NOT_DELEGATED" => 1048576,
1351  "TRUSTED_FOR_DELEGATION" => 524288,
1352  "SMARTCARD_REQUIRED" => 262144,
1353  "MNS_LOGON_ACCOUNT" => 131072,
1354  "DONT_EXPIRE_PASSWORD" => 65536,
1355  "SERVER_TRUST_ACCOUNT" => 8192,
1356  "WORKSTATION_TRUST_ACCOUNT" => 4096,
1357  "INTERDOMAIN_TRUST_ACCOUNT" => 2048,
1358  "NORMAL_ACCOUNT" => 512,
1359  "TEMP_DUPLICATE_ACCOUNT" => 256,
1360  "ENCRYPTED_TEXT_PWD_ALLOWED" => 128,
1361  "PASSWD_CANT_CHANGE" => 64,
1362  "PASSWD_NOTREQD" => 32,
1363  "LOCKOUT" => 16,
1364  "HOMEDIR_REQUIRED" => 8,
1365  "ACCOUNTDISABLE" => 2,
1366  "SCRIPT" => 1
1367  );
1368 
1369  //Parse flags to text
1370  $retval = array();
1371  //while (list($flag, $val) = each($flags)) {
1372  foreach ($flags as $flag => $val) {
1373  if ($uacf >= $val) {
1374  $uacf -= $val;
1375  $retval[$val] = $flag;
1376  }
1377  }
1378 
1379  //Return human friendly flags
1380  return $retval;
1381  }
1382 
1389  public function parseSAT($samtype)
1390  {
1391  $stypes = array(
1392  805306368 => "NORMAL_ACCOUNT",
1393  805306369 => "WORKSTATION_TRUST",
1394  805306370 => "INTERDOMAIN_TRUST",
1395  268435456 => "SECURITY_GLOBAL_GROUP",
1396  268435457 => "DISTRIBUTION_GROUP",
1397  536870912 => "SECURITY_LOCAL_GROUP",
1398  536870913 => "DISTRIBUTION_LOCAL_GROUP"
1399  );
1400 
1401  $retval = "";
1402  while (list($sat, $val) = each($stypes)) {
1403  if ($samtype == $sat) {
1404  $retval = $val;
1405  break;
1406  }
1407  }
1408  if (empty($retval)) {
1409  $retval = "UNKNOWN_TYPE_".$samtype;
1410  }
1411 
1412  return $retval;
1413  }
1414 
1415  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1422  public function convert_time($value)
1423  {
1424  // phpcs:enable
1425  $dateLargeInt = $value; // nano secondes depuis 1601 !!!!
1426  $secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601
1427  $ADToUnixConvertor = ((1970 - 1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes
1428  $unixTimeStamp = intval($secsAfterADEpoch - $ADToUnixConvertor); // Unix time stamp
1429  return $unixTimeStamp;
1430  }
1431 
1432 
1440  private function convToOutputCharset($str, $pagecodefrom = 'UTF-8')
1441  {
1442  global $conf;
1443  if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') {
1444  $str = utf8_encode($str);
1445  }
1446  if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') {
1447  $str = utf8_decode($str);
1448  }
1449  return $str;
1450  }
1451 
1459  public function convFromOutputCharset($str, $pagecodeto = 'UTF-8')
1460  {
1461  global $conf;
1462  if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') {
1463  $str = utf8_decode($str);
1464  }
1465  if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') {
1466  $str = utf8_encode($str);
1467  }
1468  return $str;
1469  }
1470 
1471 
1478  public function getNextGroupGid($keygroup = 'LDAP_KEY_GROUPS')
1479  {
1480  global $conf;
1481 
1482  if (empty($keygroup)) {
1483  $keygroup = 'LDAP_KEY_GROUPS';
1484  }
1485 
1486  $search = '('.$conf->global->$keygroup.'=*)';
1487  $result = $this->search($this->groups, $search);
1488  if ($result) {
1489  $c = $result['count'];
1490  $gids = array();
1491  for ($i = 0; $i < $c; $i++) {
1492  $gids[] = $result[$i]['gidnumber'][0];
1493  }
1494  rsort($gids);
1495 
1496  return $gids[0] + 1;
1497  }
1498 
1499  return 0;
1500  }
1501 }
Class to manage LDAP features.
Definition: ldap.class.php:35
add($dn, $info, $user)
Add a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:459
connect_bind()
Connect and bind Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType,...
Definition: ldap.class.php:195
$ldapErrorCode
Code erreur retourne par le serveur Ldap.
Definition: ldap.class.php:96
modify($dn, $info, $user)
Modify a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:507
deleteAttribute($dn, $info, $user)
Delete a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:898
$connection
The internal LDAP connection handle.
Definition: ldap.class.php:122
setVersion()
Change ldap protocol version to use.
Definition: ldap.class.php:430
convToOutputCharset($str, $pagecodefrom='UTF-8')
Convert a string into output/memory charset.
$server
Tableau des serveurs (IP addresses ou nom d'hotes)
Definition: ldap.class.php:49
littleEndian($hex)
Converts a little-endian hex-number to one, that 'hexdec' can convert Required by Active Directory.
fetch($user, $filter)
Load all attribute of a LDAP user.
getObjectSid($ldapUser)
Recupere le SID de l'utilisateur Required by Active Directory.
updateAttribute($dn, $info, $user)
Update a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:852
update($dn, $info, $user, $olddn, $newrdn=false, $newparent=false)
Modify a LDAP entry (to use if dn != olddn) Ldap object connect and bind must have been done.
Definition: ldap.class.php:609
$ldapErrorText
Message texte de l'erreur.
Definition: ldap.class.php:100
getUserIdentifier()
Returns the correct user identifier to use, based on the ldap server type.
getAttribute($dn, $filter)
Returns an array containing attributes and values for first record.
Definition: ldap.class.php:942
$searchPassword
Mot de passe de l'administrateur Active Directory ne supporte pas les connexions anonymes.
Definition: ldap.class.php:84
close()
Simply closes the connection set up earlier.
Definition: ldap.class.php:348
$ldapProtocolVersion
Version du protocole ldap.
Definition: ldap.class.php:67
parseSAT($samtype)
SamAccountType value to text.
rename($dn, $newrdn, $newparent, $user, $deleteoldrdn=true)
Rename a LDAP entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:565
getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
Return available value of group GID.
$serverType
type de serveur, actuellement OpenLdap et Active Directory
Definition: ldap.class.php:63
binSIDtoText($binsid)
Returns the textual SID Indispensable pour Active Directory.
setReferrals()
changement du referrals.
Definition: ldap.class.php:442
$domain
Server DN.
Definition: ldap.class.php:71
search($checkDn, $filter)
Fonction de recherche avec filtre this->connection doit etre defini donc la methode bind ou bindauth ...
getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter=0, $attributeAsArray=array())
Returns an array containing a details or list of LDAP record(s).
getVersion()
Verification de la version du serveur ldap.
Definition: ldap.class.php:418
convert_time($value)
Convertit le temps ActiveDirectory en Unix timestamp.
$searchUser
User administrateur Ldap Active Directory ne supporte pas les connexions anonymes.
Definition: ldap.class.php:79
const SYNCHRO_NONE
No Ldap synchronization.
Definition: ldap.class.php:131
$connectedServer
Current connected server.
Definition: ldap.class.php:54
$groups
DN des groupes.
Definition: ldap.class.php:92
dump_content($dn, $info)
Build a LDAP message.
Definition: ldap.class.php:692
getAttributeValues($filterrecord, $attribute)
Returns an array containing values for an attribute and for first record matching filterrecord.
Definition: ldap.class.php:983
parseUACF($uacf)
UserAccountControl Flgs to more human understandable form...
__construct()
Constructor.
Definition: ldap.class.php:147
const SYNCHRO_LDAP_TO_DOLIBARR
Ldap to Dolibarr synchronization.
Definition: ldap.class.php:141
convFromOutputCharset($str, $pagecodeto='UTF-8')
Convert a string from output/memory charset.
$people
DN des utilisateurs.
Definition: ldap.class.php:88
serverPing($host, $port=389, $timeout=1)
Ping a server before ldap_connect for avoid waiting.
Definition: ldap.class.php:759
bind()
Anonymously binds to the connection.
Definition: ldap.class.php:359
unbind()
Unbind of LDAP server (close connection).
Definition: ldap.class.php:399
bindauth($bindDn, $pass)
Binds as an authenticated user, which usually allows for write access.
Definition: ldap.class.php:381
$result
Result of any connections etc.
Definition: ldap.class.php:126
dump($dn, $info)
Dump a LDAP message to ldapinput.in file.
Definition: ldap.class.php:729
addAttribute($dn, $info, $user)
Add a LDAP attribute in entry Ldap object connect and bind must have been done.
Definition: ldap.class.php:806
const SYNCHRO_DOLIBARR_TO_LDAP
Dolibarr to Ldap synchronization.
Definition: ldap.class.php:136
$dn
Base DN (e.g.
Definition: ldap.class.php:59
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:123