dolibarr 19.0.4
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
34class Ldap
35{
39 public $error = '';
40
44 public $errors = array();
45
49 public $server = array();
50
55
59 public $serverPort;
60
64 public $dn;
76 public $domain;
77
78 public $domainFQDN;
79
83 public $bind;
84
98 public $people;
102 public $groups;
111
115 public $filter;
119 public $filtergroup;
123 public $filtermember;
124
128 public $attr_login;
129
133 public $attr_sambalogin;
134
138 public $attr_name;
139
143 public $attr_firstname;
144
148 public $attr_mail;
149
153 public $attr_phone;
154
158 public $attr_fax;
159
163 public $attr_mobile;
164
168 public $badpwdtime;
169
173 public $ldapUserDN;
174
175 //Fetch user
176 public $name;
177 public $firstname;
178 public $login;
179 public $phone;
180 public $fax;
181 public $mail;
182 public $mobile;
183
184 public $uacf;
185 public $pwdlastset;
186
187 public $ldapcharset = 'UTF-8'; // LDAP should be UTF-8 encoded
188
189
197 public $result;
198
202 const SYNCHRO_NONE = 0;
203
208
213
214
218 public function __construct()
219 {
220 global $conf;
221
222 // Server
223 if (getDolGlobalString('LDAP_SERVER_HOST')) {
224 $this->server[] = $conf->global->LDAP_SERVER_HOST;
225 }
226 if (getDolGlobalString('LDAP_SERVER_HOST_SLAVE')) {
227 $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE;
228 }
229 $this->serverPort = getDolGlobalInt('LDAP_SERVER_PORT', 389);
230 $this->ldapProtocolVersion = getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION');
231 $this->dn = getDolGlobalString('LDAP_SERVER_DN');
232 $this->serverType = getDolGlobalString('LDAP_SERVER_TYPE');
233
234 $this->domain = getDolGlobalString('LDAP_SERVER_DN');
235 $this->searchUser = getDolGlobalString('LDAP_ADMIN_DN');
236 $this->searchPassword = getDolGlobalString('LDAP_ADMIN_PASS');
237 $this->people = getDolGlobalString('LDAP_USER_DN');
238 $this->groups = getDolGlobalString('LDAP_GROUP_DN');
239
240 $this->filter = getDolGlobalString('LDAP_FILTER_CONNECTION'); // Filter on user
241 $this->filtergroup = getDolGlobalString('LDAP_GROUP_FILTER'); // Filter on groups
242 $this->filtermember = getDolGlobalString('LDAP_MEMBER_FILTER'); // Filter on member
243
244 // Users
245 $this->attr_login = getDolGlobalString('LDAP_FIELD_LOGIN'); //unix
246 $this->attr_sambalogin = getDolGlobalString('LDAP_FIELD_LOGIN_SAMBA'); //samba, activedirectory
247 $this->attr_name = getDolGlobalString('LDAP_FIELD_NAME');
248 $this->attr_firstname = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
249 $this->attr_mail = getDolGlobalString('LDAP_FIELD_MAIL');
250 $this->attr_phone = getDolGlobalString('LDAP_FIELD_PHONE');
251 $this->attr_fax = getDolGlobalString('LDAP_FIELD_FAX');
252 $this->attr_mobile = getDolGlobalString('LDAP_FIELD_MOBILE');
253 }
254
255 // Connection handling methods -------------------------------------------
256
257 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
265 public function connect_bind()
266 {
267 // phpcs:enable
268 global $conf;
269 global $dolibarr_main_auth_ldap_debug;
270
271 $connected = 0;
272 $this->bind = 0;
273 $this->error = 0;
274 $this->connectedServer = '';
275
276 $ldapdebug = ((empty($dolibarr_main_auth_ldap_debug) || $dolibarr_main_auth_ldap_debug == "false") ? false : true);
277
278 if ($ldapdebug) {
279 dol_syslog(get_class($this)."::connect_bind");
280 print "DEBUG: connect_bind<br>\n";
281 }
282
283 // Check parameters
284 if (count($this->server) == 0 || empty($this->server[0])) {
285 $this->error = 'LDAP setup (file conf.php) is not complete';
286 dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
287 return -1;
288 }
289
290 if (!function_exists("ldap_connect")) {
291 $this->error = 'LDAPFunctionsNotAvailableOnPHP';
292 dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
293 $return = -1;
294 }
295
296 if (empty($this->error)) {
297 // Loop on each ldap server
298 foreach ($this->server as $host) {
299 if ($connected) {
300 break;
301 }
302 if (empty($host)) {
303 continue;
304 }
305
306 if ($this->serverPing($host, $this->serverPort) === true) {
307 if ($ldapdebug) {
308 dol_syslog(get_class($this)."::connect_bind serverPing true, we try ldap_connect to ".$host);
309 }
310 if (version_compare(PHP_VERSION, '8.3.0', '>=')) {
311 $uri = $host.':'.$this->serverPort;
312 $this->connection = ldap_connect($uri);
313 } else {
314 $this->connection = ldap_connect($host, $this->serverPort);
315 }
316 } else {
317 if (preg_match('/^ldaps/i', $host)) {
318 // With host = ldaps://server, the serverPing to ssl://server sometimes fails, even if the ldap_connect succeed, so
319 // we test this case and continue in such a case even if serverPing fails.
320 if ($ldapdebug) {
321 dol_syslog(get_class($this)."::connect_bind serverPing false, we try ldap_connect to ".$host);
322 }
323 if (version_compare(PHP_VERSION, '8.3.0', '>=')) {
324 $uri = $host.':'.$this->serverPort;
325 $this->connection = ldap_connect($uri);
326 } else {
327 $this->connection = ldap_connect($host, $this->serverPort);
328 }
329 } else {
330 continue;
331 }
332 }
333
334 if (is_resource($this->connection) || is_object($this->connection)) {
335 if ($ldapdebug) {
336 dol_syslog(get_class($this)."::connect_bind this->connection is ok", LOG_DEBUG);
337 }
338
339 // Upgrade connexion to TLS, if requested by the configuration
340 if (getDolGlobalString('LDAP_SERVER_USE_TLS')) {
341 // For test/debug
342 //ldap_set_option($this->connection, LDAP_OPT_DEBUG_LEVEL, 7);
343 //ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3);
344 //ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
345
346 $resulttls = ldap_start_tls($this->connection);
347 if (!$resulttls) {
348 dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING);
349 $this->error = 'ldap_start_tls Failed to start TLS '.ldap_errno($this->connection).' '.ldap_error($this->connection);
350 $connected = 0;
351 $this->unbind();
352 }
353 }
354
355 // Execute the ldap_set_option here (after connect and before bind)
356 $this->setVersion();
357 ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true.
358
359
360 if ($this->serverType == "activedirectory") {
361 $result = $this->setReferrals();
362 dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG);
363 $this->result = $this->bindauth($this->searchUser, $this->searchPassword);
364 if ($this->result) {
365 $this->bind = $this->result;
366 $connected = 2;
367 $this->connectedServer = $host;
368 break;
369 } else {
370 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
371 }
372 } else {
373 // Try in auth mode
374 if ($this->searchUser && $this->searchPassword) {
375 dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG);
376 $this->result = $this->bindauth($this->searchUser, $this->searchPassword);
377 if ($this->result) {
378 $this->bind = $this->result;
379 $connected = 2;
380 $this->connectedServer = $host;
381 break;
382 } else {
383 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
384 }
385 }
386 // Try in anonymous
387 if (!$this->bind) {
388 dol_syslog(get_class($this)."::connect_bind try bind anonymously on ".$host, LOG_DEBUG);
389 $result = $this->bind();
390 if ($result) {
391 $this->bind = $this->result;
392 $connected = 1;
393 $this->connectedServer = $host;
394 break;
395 } else {
396 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection);
397 }
398 }
399 }
400 }
401
402 if (!$connected) {
403 $this->unbind();
404 }
405 } // End loop on each server
406 }
407
408 if ($connected) {
409 $return = $connected;
410 dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG);
411 } else {
412 $this->error = 'Failed to connect to LDAP'.($this->error ? ': '.$this->error : '');
413 $return = -1;
414 dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING);
415 }
416
417 return $return;
418 }
419
428 public function close()
429 {
430 return $this->unbind();
431 }
432
439 public function bind()
440 {
441 if (!$this->result = @ldap_bind($this->connection)) {
442 $this->ldapErrorCode = ldap_errno($this->connection);
443 $this->ldapErrorText = ldap_error($this->connection);
444 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
445 return false;
446 } else {
447 return true;
448 }
449 }
450
461 public function bindauth($bindDn, $pass)
462 {
463 if (!$this->result = @ldap_bind($this->connection, $bindDn, $pass)) {
464 $this->ldapErrorCode = ldap_errno($this->connection);
465 $this->ldapErrorText = ldap_error($this->connection);
466 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
467 return false;
468 } else {
469 return true;
470 }
471 }
472
479 public function unbind()
480 {
481 $this->result = true;
482 if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
483 if (is_object($this->connection)) {
484 try {
485 $this->result = ldap_unbind($this->connection);
486 } catch (Throwable $exception) {
487 $this->error = 'Failed to unbind LDAP connection: '.$exception;
488 $this->result = false;
489 dol_syslog(get_class($this).'::unbind - '.$this->error, LOG_WARNING);
490 }
491 }
492 } else {
493 if (is_resource($this->connection)) {
494 $this->result = @ldap_unbind($this->connection);
495 }
496 }
497 if ($this->result) {
498 return true;
499 } else {
500 return false;
501 }
502 }
503
504
510 public function getVersion()
511 {
512 $version = 0;
513 $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
514 return $version;
515 }
516
522 public function setVersion()
523 {
524 // LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17
525 $ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion);
526 return $ldapsetversion;
527 }
528
534 public function setReferrals()
535 {
536 // LDAP_OPT_REFERRALS est une constante qui vaut ?
537 $ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
538 return $ldapreferrals;
539 }
540
541
551 public function add($dn, $info, $user)
552 {
553 dol_syslog(get_class($this)."::add dn=".$dn." info=".print_r($info, true));
554
555 // Check parameters
556 if (!$this->connection) {
557 $this->error = "NotConnected";
558 return -2;
559 }
560 if (!$this->bind) {
561 $this->error = "NotConnected";
562 return -3;
563 }
564
565 // Encode to LDAP page code
566 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
567 foreach ($info as $key => $val) {
568 if (!is_array($val)) {
569 $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
570 }
571 }
572
573 $this->dump($dn, $info);
574
575 //print_r($info);
576 $result = @ldap_add($this->connection, $dn, $info);
577
578 if ($result) {
579 dol_syslog(get_class($this)."::add successfull", LOG_DEBUG);
580 return 1;
581 } else {
582 $this->ldapErrorCode = @ldap_errno($this->connection);
583 $this->ldapErrorText = @ldap_error($this->connection);
584 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText;
585 dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR);
586 return -1;
587 }
588 }
589
599 public function modify($dn, $info, $user)
600 {
601 dol_syslog(get_class($this)."::modify dn=".$dn." info=".print_r($info, true));
602
603 // Check parameters
604 if (!$this->connection) {
605 $this->error = "NotConnected";
606 return -2;
607 }
608 if (!$this->bind) {
609 $this->error = "NotConnected";
610 return -3;
611 }
612
613 // Encode to LDAP page code
614 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
615 foreach ($info as $key => $val) {
616 if (!is_array($val)) {
617 $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
618 }
619 }
620
621 $this->dump($dn, $info);
622
623 //print_r($info);
624
625 // For better compatibility with Samba4 AD
626 if ($this->serverType == "activedirectory") {
627 unset($info['cn']); // To avoid error : Operation not allowed on RDN (Code 67)
628
629 // To avoid error : LDAP Error: 53 (Unwilling to perform)
630 if (isset($info['unicodePwd'])) {
631 $info['unicodePwd'] = mb_convert_encoding("\"".$info['unicodePwd']."\"", "UTF-16LE", "UTF-8");
632 }
633 }
634 $result = @ldap_modify($this->connection, $dn, $info);
635
636 if ($result) {
637 dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG);
638 return 1;
639 } else {
640 $this->error = @ldap_error($this->connection);
641 dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR);
642 return -1;
643 }
644 }
645
657 public function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true)
658 {
659 dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn ? 1 : 0));
660
661 // Check parameters
662 if (!$this->connection) {
663 $this->error = "NotConnected";
664 return -2;
665 }
666 if (!$this->bind) {
667 $this->error = "NotConnected";
668 return -3;
669 }
670
671 // Encode to LDAP page code
672 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
673 $newrdn = $this->convFromOutputCharset($newrdn, $this->ldapcharset);
674 $newparent = $this->convFromOutputCharset($newparent, $this->ldapcharset);
675
676 //print_r($info);
677 $result = @ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
678
679 if ($result) {
680 dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG);
681 return 1;
682 } else {
683 $this->error = @ldap_error($this->connection);
684 dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR);
685 return -1;
686 }
687 }
688
701 public function update($dn, $info, $user, $olddn, $newrdn = false, $newparent = false)
702 {
703 dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn);
704
705 // Check parameters
706 if (!$this->connection) {
707 $this->error = "NotConnected";
708 return -2;
709 }
710 if (!$this->bind) {
711 $this->error = "NotConnected";
712 return -3;
713 }
714
715 if (!$olddn || $olddn != $dn) {
716 if (!empty($olddn) && !empty($newrdn) && !empty($newparent) && $this->ldapProtocolVersion === '3') {
717 // This function currently only works with LDAPv3
718 $result = $this->rename($olddn, $newrdn, $newparent, $user, true);
719 $result = $this->modify($dn, $info, $user); // We force "modify" for avoid some fields not modify
720 } else {
721 // If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one.
722 $result = $this->add($dn, $info, $user);
723 if ($result > 0 && $olddn && $olddn != $dn) {
724 $result = $this->delete($olddn); // If add fails, we do not try to delete old one
725 }
726 }
727 } else {
728 //$result = $this->delete($olddn);
729 $result = $this->add($dn, $info, $user); // If record has been deleted from LDAP, we recreate it. We ignore error if it already exists.
730 $result = $this->modify($dn, $info, $user); // We use add/modify instead of delete/add when olddn is received
731 }
732 if ($result <= 0) {
733 $this->error = ldap_error($this->connection).' (Code '.ldap_errno($this->connection).") ".$this->error;
734 dol_syslog(get_class($this)."::update ".$this->error, LOG_ERR);
735 //print_r($info);
736 return -1;
737 } else {
738 dol_syslog(get_class($this)."::update done successfully");
739 return 1;
740 }
741 }
742
743
751 public function delete($dn)
752 {
753 dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn);
754
755 // Check parameters
756 if (!$this->connection) {
757 $this->error = "NotConnected";
758 return -2;
759 }
760 if (!$this->bind) {
761 $this->error = "NotConnected";
762 return -3;
763 }
764
765 // Encode to LDAP page code
766 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
767
768 $result = @ldap_delete($this->connection, $dn);
769
770 if ($result) {
771 return 1;
772 }
773 return -1;
774 }
775
776 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
784 public function dump_content($dn, $info)
785 {
786 // phpcs:enable
787 $content = '';
788
789 // Create file content
790 if (preg_match('/^ldap/', $this->server[0])) {
791 $target = "-H ".join(',', $this->server);
792 } else {
793 $target = "-h ".join(',', $this->server)." -p ".$this->serverPort;
794 }
795 $content .= "# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
796 $content .= "# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
797 $content .= "# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
798 if (in_array('localhost', $this->server)) {
799 $content .= "# If commands fails to connect, try without -h and -p\n";
800 }
801 $content .= "dn: ".$dn."\n";
802 foreach ($info as $key => $value) {
803 if (!is_array($value)) {
804 $content .= "$key: $value\n";
805 } else {
806 foreach ($value as $valuevalue) {
807 $content .= "$key: $valuevalue\n";
808 }
809 }
810 }
811 return $content;
812 }
813
821 public function dump($dn, $info)
822 {
823 global $conf;
824
825 // Create content
826 $content = $this->dump_content($dn, $info);
827
828 //Create file
829 $result = dol_mkdir($conf->ldap->dir_temp);
830
831 $outputfile = $conf->ldap->dir_temp.'/ldapinput.in';
832 $fp = fopen($outputfile, "w");
833 if ($fp) {
834 fputs($fp, $content);
835 fclose($fp);
836 dolChmod($outputfile);
837 return 1;
838 } else {
839 return -1;
840 }
841 }
842
851 public function serverPing($host, $port = 389, $timeout = 1)
852 {
853 $regs = array();
854 if (preg_match('/^ldaps:\/\/([^\/]+)\/?$/', $host, $regs)) {
855 // Replace ldaps:// by ssl://
856 $host = 'ssl://'.$regs[1];
857 } elseif (preg_match('/^ldap:\/\/([^\/]+)\/?$/', $host, $regs)) {
858 // Remove ldap://
859 $host = $regs[1];
860 }
861
862 //var_dump($newhostforstream); var_dump($host); var_dump($port);
863 //$host = 'ssl://ldap.test.local:636';
864 //$port = 636;
865
866 $errno = $errstr = 0;
867 /*
868 if ($methodtochecktcpconnect == 'socket') {
869 Try to use socket_create() method.
870 Method that use stream_context_create() works only on registered listed in stream stream_get_wrappers(): http, https, ftp, ...
871 }
872 */
873
874 // Use the method fsockopen to test tcp connect. No way to ignore ssl certificate errors with this method !
875 $op = @fsockopen($host, $port, $errno, $errstr, $timeout);
876
877 //var_dump($op);
878 if (!$op) {
879 return false; //DC is N/A
880 } else {
881 fclose($op); //explicitly close open socket connection
882 return true; //DC is up & running, we can safely connect with ldap_connect
883 }
884 }
885
886
887 // Attribute methods -----------------------------------------------------
888
898 public function addAttribute($dn, $info, $user)
899 {
900 dol_syslog(get_class($this)."::addAttribute 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_add($this->connection, $dn, $info);
924
925 if ($result) {
926 dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG);
927 return 1;
928 } else {
929 $this->error = @ldap_error($this->connection);
930 dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR);
931 return -1;
932 }
933 }
934
944 public function updateAttribute($dn, $info, $user)
945 {
946 dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',', $info));
947
948 // Check parameters
949 if (!$this->connection) {
950 $this->error = "NotConnected";
951 return -2;
952 }
953 if (!$this->bind) {
954 $this->error = "NotConnected";
955 return -3;
956 }
957
958 // Encode to LDAP page code
959 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
960 foreach ($info as $key => $val) {
961 if (!is_array($val)) {
962 $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
963 }
964 }
965
966 $this->dump($dn, $info);
967
968 //print_r($info);
969 $result = @ldap_mod_replace($this->connection, $dn, $info);
970
971 if ($result) {
972 dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG);
973 return 1;
974 } else {
975 $this->error = @ldap_error($this->connection);
976 dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR);
977 return -1;
978 }
979 }
980
990 public function deleteAttribute($dn, $info, $user)
991 {
992 dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',', $info));
993
994 // Check parameters
995 if (!$this->connection) {
996 $this->error = "NotConnected";
997 return -2;
998 }
999 if (!$this->bind) {
1000 $this->error = "NotConnected";
1001 return -3;
1002 }
1003
1004 // Encode to LDAP page code
1005 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset);
1006 foreach ($info as $key => $val) {
1007 if (!is_array($val)) {
1008 $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset);
1009 }
1010 }
1011
1012 $this->dump($dn, $info);
1013
1014 //print_r($info);
1015 $result = @ldap_mod_del($this->connection, $dn, $info);
1016
1017 if ($result) {
1018 dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG);
1019 return 1;
1020 } else {
1021 $this->error = @ldap_error($this->connection);
1022 dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR);
1023 return -1;
1024 }
1025 }
1026
1034 public function getAttribute($dn, $filter)
1035 {
1036 // Check parameters
1037 if (!$this->connection) {
1038 $this->error = "NotConnected";
1039 return -2;
1040 }
1041 if (!$this->bind) {
1042 $this->error = "NotConnected";
1043 return -3;
1044 }
1045
1046 $search = @ldap_search($this->connection, $dn, $filter);
1047
1048 // Only one entry should ever be returned
1049 $entry = @ldap_first_entry($this->connection, $search);
1050
1051 if (!$entry) {
1052 $this->ldapErrorCode = -1;
1053 $this->ldapErrorText = "Couldn't find entry";
1054 return 0; // Couldn't find entry...
1055 }
1056
1057 // Get values
1058 if (!($values = ldap_get_attributes($this->connection, $entry))) {
1059 $this->ldapErrorCode = ldap_errno($this->connection);
1060 $this->ldapErrorText = ldap_error($this->connection);
1061 return 0; // No matching attributes
1062 }
1063
1064 // Return an array containing the attributes.
1065 return $values;
1066 }
1067
1075 public function getAttributeValues($filterrecord, $attribute)
1076 {
1077 $attributes = array();
1078 $attributes[0] = $attribute;
1079
1080 // We need to search for this user in order to get their entry.
1081 $this->result = @ldap_search($this->connection, $this->people, $filterrecord, $attributes);
1082
1083 // Pourquoi cette ligne ?
1084 //$info = ldap_get_entries($this->connection, $this->result);
1085
1086 // Only one entry should ever be returned (no user will have the same uid)
1087 $entry = ldap_first_entry($this->connection, $this->result);
1088
1089 if (!$entry) {
1090 $this->ldapErrorCode = -1;
1091 $this->ldapErrorText = "Couldn't find user";
1092 return false; // Couldn't find the user...
1093 }
1094
1095 // Get values
1096 if (!$values = @ldap_get_values($this->connection, $entry, $attribute)) {
1097 $this->ldapErrorCode = ldap_errno($this->connection);
1098 $this->ldapErrorText = ldap_error($this->connection);
1099 return false; // No matching attributes
1100 }
1101
1102 // Return an array containing the attributes.
1103 return $values;
1104 }
1105
1118 public function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter = 0, $attributeAsArray = array())
1119 {
1120 $fulllist = array();
1121
1122 dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',', $attributeArray).") activefilter=".$activefilter);
1123
1124 // if the directory is AD, then bind first with the search user first
1125 if ($this->serverType == "activedirectory") {
1126 $this->bindauth($this->searchUser, $this->searchPassword);
1127 dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser);
1128 }
1129
1130 // Define filter
1131 if (!empty($activefilter)) { // Use a predefined trusted filter (defined into setup by admin).
1132 if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter) {
1133 $filter = '('.$this->filter.')';
1134 } elseif (((string) $activefilter == 'group') && $this->filtergroup) {
1135 $filter = '('.$this->filtergroup.')';
1136 } elseif (((string) $activefilter == 'member') && $this->filter) {
1137 $filter = '('.$this->filtermember.')';
1138 } else {
1139 // If this->filter/this->filtergroup is empty, make fiter on * (all)
1140 $filter = '('.ldap_escape($useridentifier, '', LDAP_ESCAPE_FILTER).'=*)';
1141 }
1142 } else { // Use a filter forged using the $search value
1143 $filter = '('.ldap_escape($useridentifier, '', LDAP_ESCAPE_FILTER).'='.ldap_escape($search, '', LDAP_ESCAPE_FILTER).')';
1144 }
1145
1146 if (is_array($attributeArray)) {
1147 // Return list with required fields
1148 $attributeArray = array_values($attributeArray); // This is to force to have index reordered from 0 (not make ldap_search fails)
1149 dol_syslog(get_class($this)."::getRecords connection=".$this->connectedServer.":".$this->serverPort." userDn=".$userDn." filter=".$filter." attributeArray=(".join(',', $attributeArray).")");
1150 //var_dump($attributeArray);
1151 $this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray);
1152 } else {
1153 // Return list with fields selected by default
1154 dol_syslog(get_class($this)."::getRecords connection=".$this->connectedServer.":".$this->serverPort." userDn=".$userDn." filter=".$filter);
1155 $this->result = @ldap_search($this->connection, $userDn, $filter);
1156 }
1157 if (!$this->result) {
1158 $this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection);
1159 return -1;
1160 }
1161
1162 $info = @ldap_get_entries($this->connection, $this->result);
1163
1164 // Warning: Dans info, les noms d'attributs sont en minuscule meme si passe
1165 // a ldap_search en majuscule !!!
1166 //print_r($info);
1167
1168 for ($i = 0; $i < $info["count"]; $i++) {
1169 $recordid = $this->convToOutputCharset($info[$i][strtolower($useridentifier)][0], $this->ldapcharset);
1170 if ($recordid) {
1171 //print "Found record with key $useridentifier=".$recordid."<br>\n";
1172 $fulllist[$recordid][$useridentifier] = $recordid;
1173
1174 // Add to the array for each attribute in my list
1175 $num = count($attributeArray);
1176 for ($j = 0; $j < $num; $j++) {
1177 $keyattributelower = strtolower($attributeArray[$j]);
1178 //print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n";
1179
1180 //permet de recuperer le SID avec Active Directory
1181 if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid") {
1182 $objectsid = $this->getObjectSid($recordid);
1183 $fulllist[$recordid][$attributeArray[$j]] = $objectsid;
1184 } else {
1185 if (in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) {
1186 $valueTab = array();
1187 foreach ($info[$i][$keyattributelower] as $key => $value) {
1188 $valueTab[$key] = $this->convToOutputCharset($value, $this->ldapcharset);
1189 }
1190 $fulllist[$recordid][$attributeArray[$j]] = $valueTab;
1191 } else {
1192 $fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0], $this->ldapcharset);
1193 }
1194 }
1195 }
1196 }
1197 }
1198
1199 asort($fulllist);
1200 return $fulllist;
1201 }
1202
1210 public function littleEndian($hex)
1211 {
1212 $result = '';
1213 for ($x = dol_strlen($hex) - 2; $x >= 0; $x = $x - 2) {
1214 $result .= substr($hex, $x, 2);
1215 }
1216 return $result;
1217 }
1218
1219
1227 public function getObjectSid($ldapUser)
1228 {
1229 $criteria = '('.$this->getUserIdentifier().'='.$ldapUser.')';
1230 $justthese = array("objectsid");
1231
1232 // if the directory is AD, then bind first with the search user first
1233 if ($this->serverType == "activedirectory") {
1234 $this->bindauth($this->searchUser, $this->searchPassword);
1235 }
1236
1237 $i = 0;
1238 $searchDN = $this->people;
1239
1240 while ($i <= 2) {
1241 $ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese);
1242
1243 if (!$ldapSearchResult) {
1244 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1245 return -1;
1246 }
1247
1248 $entry = ldap_first_entry($this->connection, $ldapSearchResult);
1249
1250 if (!$entry) {
1251 // Si pas de resultat on cherche dans le domaine
1252 $searchDN = $this->domain;
1253 $i++;
1254 } else {
1255 $i++;
1256 $i++;
1257 }
1258 }
1259
1260 if ($entry) {
1261 $ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid");
1262 $SIDText = $this->binSIDtoText($ldapBinary[0]);
1263 return $SIDText;
1264 } else {
1265 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1266 return '?';
1267 }
1268 }
1269
1277 public function binSIDtoText($binsid)
1278 {
1279 $hex_sid = bin2hex($binsid);
1280 $rev = hexdec(substr($hex_sid, 0, 2)); // Get revision-part of SID
1281 $subcount = hexdec(substr($hex_sid, 2, 2)); // Get count of sub-auth entries
1282 $auth = hexdec(substr($hex_sid, 4, 12)); // SECURITY_NT_AUTHORITY
1283 $result = "$rev-$auth";
1284 for ($x = 0; $x < $subcount; $x++) {
1285 $result .= "-".hexdec($this->littleEndian(substr($hex_sid, 16 + ($x * 8), 8))); // get all SECURITY_NT_AUTHORITY
1286 }
1287 return $result;
1288 }
1289
1290
1302 public function search($checkDn, $filter)
1303 {
1304 dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter);
1305
1306 $checkDn = $this->convFromOutputCharset($checkDn, $this->ldapcharset);
1307 $filter = $this->convFromOutputCharset($filter, $this->ldapcharset);
1308
1309 // if the directory is AD, then bind first with the search user first
1310 if ($this->serverType == "activedirectory") {
1311 $this->bindauth($this->searchUser, $this->searchPassword);
1312 }
1313
1314 $this->result = @ldap_search($this->connection, $checkDn, $filter);
1315
1316 $result = @ldap_get_entries($this->connection, $this->result);
1317 if (!$result) {
1318 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1319 return -1;
1320 } else {
1321 ldap_free_result($this->result);
1322 return $result;
1323 }
1324 }
1325
1326
1335 public function fetch($user, $filter)
1336 {
1337 // Perform the search and get the entry handles
1338
1339 // if the directory is AD, then bind first with the search user first
1340 if ($this->serverType == "activedirectory") {
1341 $this->bindauth($this->searchUser, $this->searchPassword);
1342 }
1343
1344 $searchDN = $this->people; // TODO Why searching in people then domain ?
1345
1346 $result = '';
1347 $i = 0;
1348 while ($i <= 2) {
1349 dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter);
1350 $this->result = @ldap_search($this->connection, $searchDN, $filter);
1351 if ($this->result) {
1352 $result = @ldap_get_entries($this->connection, $this->result);
1353 if ($result['count'] > 0) {
1354 dol_syslog('Ldap::fetch search found '.$result['count'].' records');
1355 } else {
1356 dol_syslog('Ldap::fetch search returns but found no records');
1357 }
1358 //var_dump($result);exit;
1359 } else {
1360 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1361 dol_syslog(get_class($this)."::fetch search fails");
1362 return -1;
1363 }
1364
1365 if (!$result) {
1366 // Si pas de resultat on cherche dans le domaine
1367 $searchDN = $this->domain;
1368 $i++;
1369 } else {
1370 break;
1371 }
1372 }
1373
1374 if (!$result) {
1375 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1376 return -1;
1377 } else {
1378 $this->name = $this->convToOutputCharset($result[0][$this->attr_name][0], $this->ldapcharset);
1379 $this->firstname = $this->convToOutputCharset($result[0][$this->attr_firstname][0], $this->ldapcharset);
1380 $this->login = $this->convToOutputCharset($result[0][$this->attr_login][0], $this->ldapcharset);
1381 $this->phone = $this->convToOutputCharset($result[0][$this->attr_phone][0], $this->ldapcharset);
1382 $this->fax = $this->convToOutputCharset($result[0][$this->attr_fax][0], $this->ldapcharset);
1383 $this->mail = $this->convToOutputCharset($result[0][$this->attr_mail][0], $this->ldapcharset);
1384 $this->mobile = $this->convToOutputCharset($result[0][$this->attr_mobile][0], $this->ldapcharset);
1385
1386 $this->uacf = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0], $this->ldapcharset));
1387 if (isset($result[0]["pwdlastset"][0])) { // If expiration on password exists
1388 $this->pwdlastset = ($result[0]["pwdlastset"][0] != 0) ? $this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0], $this->ldapcharset)) : 0;
1389 } else {
1390 $this->pwdlastset = -1;
1391 }
1392 if (!$this->name && !$this->login) {
1393 $this->pwdlastset = -1;
1394 }
1395 $this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0], $this->ldapcharset));
1396
1397 // FQDN domain
1398 $domain = str_replace('dc=', '', $this->domain);
1399 $domain = str_replace(',', '.', $domain);
1400 $this->domainFQDN = $domain;
1401
1402 // Set ldapUserDn (each user can have a different dn)
1403 //var_dump($result[0]);exit;
1404 $this->ldapUserDN = $result[0]['dn'];
1405
1406 ldap_free_result($this->result);
1407 return 1;
1408 }
1409 }
1410
1411
1412 // helper methods
1413
1419 public function getUserIdentifier()
1420 {
1421 if ($this->serverType == "activedirectory") {
1422 return $this->attr_sambalogin;
1423 } else {
1424 return $this->attr_login;
1425 }
1426 }
1427
1434 public function parseUACF($uacf)
1435 {
1436 //All flags array
1437 $flags = array(
1438 "TRUSTED_TO_AUTH_FOR_DELEGATION" => 16777216,
1439 "PASSWORD_EXPIRED" => 8388608,
1440 "DONT_REQ_PREAUTH" => 4194304,
1441 "USE_DES_KEY_ONLY" => 2097152,
1442 "NOT_DELEGATED" => 1048576,
1443 "TRUSTED_FOR_DELEGATION" => 524288,
1444 "SMARTCARD_REQUIRED" => 262144,
1445 "MNS_LOGON_ACCOUNT" => 131072,
1446 "DONT_EXPIRE_PASSWORD" => 65536,
1447 "SERVER_TRUST_ACCOUNT" => 8192,
1448 "WORKSTATION_TRUST_ACCOUNT" => 4096,
1449 "INTERDOMAIN_TRUST_ACCOUNT" => 2048,
1450 "NORMAL_ACCOUNT" => 512,
1451 "TEMP_DUPLICATE_ACCOUNT" => 256,
1452 "ENCRYPTED_TEXT_PWD_ALLOWED" => 128,
1453 "PASSWD_CANT_CHANGE" => 64,
1454 "PASSWD_NOTREQD" => 32,
1455 "LOCKOUT" => 16,
1456 "HOMEDIR_REQUIRED" => 8,
1457 "ACCOUNTDISABLE" => 2,
1458 "SCRIPT" => 1
1459 );
1460
1461 //Parse flags to text
1462 $retval = array();
1463 //while (list($flag, $val) = each($flags)) {
1464 foreach ($flags as $flag => $val) {
1465 if ($uacf >= $val) {
1466 $uacf -= $val;
1467 $retval[$val] = $flag;
1468 }
1469 }
1470
1471 //Return human friendly flags
1472 return $retval;
1473 }
1474
1481 public function parseSAT($samtype)
1482 {
1483 $stypes = array(
1484 805306368 => "NORMAL_ACCOUNT",
1485 805306369 => "WORKSTATION_TRUST",
1486 805306370 => "INTERDOMAIN_TRUST",
1487 268435456 => "SECURITY_GLOBAL_GROUP",
1488 268435457 => "DISTRIBUTION_GROUP",
1489 536870912 => "SECURITY_LOCAL_GROUP",
1490 536870913 => "DISTRIBUTION_LOCAL_GROUP"
1491 );
1492
1493 $retval = "";
1494 foreach ($stypes as $sat => $val) {
1495 if ($samtype == $sat) {
1496 $retval = $val;
1497 break;
1498 }
1499 }
1500 if (empty($retval)) {
1501 $retval = "UNKNOWN_TYPE_".$samtype;
1502 }
1503
1504 return $retval;
1505 }
1506
1507 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1514 public function convert_time($value)
1515 {
1516 // phpcs:enable
1517 $dateLargeInt = $value; // nano secondes depuis 1601 !!!!
1518 $secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601
1519 $ADToUnixConvertor = ((1970 - 1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes
1520 $unixTimeStamp = intval($secsAfterADEpoch - $ADToUnixConvertor); // Unix time stamp
1521 return $unixTimeStamp;
1522 }
1523
1524
1532 private function convToOutputCharset($str, $pagecodefrom = 'UTF-8')
1533 {
1534 global $conf;
1535 if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') {
1536 $str = mb_convert_encoding($str, 'UTF-8', 'ISO-8859-1');
1537 }
1538 if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') {
1539 $str = mb_convert_encoding($str, 'ISO-8859-1');
1540 }
1541 return $str;
1542 }
1543
1551 public function convFromOutputCharset($str, $pagecodeto = 'UTF-8')
1552 {
1553 global $conf;
1554 if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') {
1555 $str = mb_convert_encoding($str, 'ISO-8859-1');
1556 }
1557 if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') {
1558 $str = mb_convert_encoding($str, 'UTF-8', 'ISO-8859-1');
1559 }
1560 return $str;
1561 }
1562
1563
1570 public function getNextGroupGid($keygroup = 'LDAP_KEY_GROUPS')
1571 {
1572 global $conf;
1573
1574 if (empty($keygroup)) {
1575 $keygroup = 'LDAP_KEY_GROUPS';
1576 }
1577
1578 $search = '(' . getDolGlobalString($keygroup).'=*)';
1579 $result = $this->search($this->groups, $search);
1580 if ($result) {
1581 $c = $result['count'];
1582 $gids = array();
1583 for ($i = 0; $i < $c; $i++) {
1584 $gids[] = $result[$i]['gidnumber'][0];
1585 }
1586 rsort($gids);
1587
1588 return $gids[0] + 1;
1589 }
1590
1591 return 0;
1592 }
1593}
Class to manage LDAP features.
add($dn, $info, $user)
Add a LDAP entry Ldap object connect and bind must have been done.
connect_bind()
Connect and bind Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType,...
$ldapErrorCode
Code erreur retourne par le serveur Ldap.
modify($dn, $info, $user)
Modify a LDAP entry Ldap object connect and bind must have been done.
deleteAttribute($dn, $info, $user)
Delete a LDAP attribute in entry Ldap object connect and bind must have been done.
$connection
The internal LDAP connection handle.
setVersion()
Change ldap protocol version to use.
convToOutputCharset($str, $pagecodefrom='UTF-8')
Convert a string into output/memory charset.
$server
Tableau des serveurs (IP addresses ou nom d'hotes)
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.
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.
$ldapErrorText
Message texte de l'erreur.
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.
$searchPassword
Mot de passe de l'administrateur Active Directory ne supporte pas les connexions anonymes.
close()
Simply closes the connection set up earlier.
$ldapProtocolVersion
Version du protocole ldap.
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.
getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
Return available value of group GID.
$serverType
type de serveur, actuellement OpenLdap et Active Directory
binSIDtoText($binsid)
Returns the textual SID Indispensable pour Active Directory.
setReferrals()
changement du referrals.
$domain
Server DN.
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.
convert_time($value)
Convertit le temps ActiveDirectory en Unix timestamp.
$searchUser
User administrateur Ldap Active Directory ne supporte pas les connexions anonymes.
const SYNCHRO_NONE
No Ldap synchronization.
$connectedServer
Current connected server.
$groups
DN des groupes.
dump_content($dn, $info)
Build a LDAP message.
getAttributeValues($filterrecord, $attribute)
Returns an array containing values for an attribute and for first record matching filterrecord.
parseUACF($uacf)
UserAccountControl Flgs to more human understandable form...
__construct()
Constructor.
const SYNCHRO_LDAP_TO_DOLIBARR
Ldap to Dolibarr synchronization.
convFromOutputCharset($str, $pagecodeto='UTF-8')
Convert a string from output/memory charset.
$people
DN des utilisateurs.
serverPing($host, $port=389, $timeout=1)
Ping a server before ldap_connect for avoid waiting.
bind()
Anonymously binds to the connection.
unbind()
Unbind of LDAP server (close connection).
bindauth($bindDn, $pass)
Binds as an authenticated user, which usually allows for write access.
$result
Result of any connections etc.
dump($dn, $info)
Dump a LDAP message to ldapinput.in file.
addAttribute($dn, $info, $user)
Add a LDAP attribute in entry Ldap object connect and bind must have been done.
const SYNCHRO_DOLIBARR_TO_LDAP
Dolibarr to Ldap synchronization.
$dn
Base DN (e.g.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a 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:124