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-2017 Regis Houssin <regis.houssin@inodbox.com> 5 * Copyright (C) 2006-2015 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 22/** 23 * \file htdocs/core/class/ldap.class.php 24 * \brief File of class to manage LDAP features 25 */ 26 27/** 28 * Class to manage LDAP features 29 */ 30class Ldap 31{ 32 /** 33 * @var string Error code (or message) 34 */ 35 public $error = ''; 36 37 /** 38 * @var string[] Array of error strings 39 */ 40 public $errors = array(); 41 42 /** 43 * Tableau des serveurs (IP addresses ou nom d'hotes) 44 */ 45 public $server = array(); 46 47 /** 48 * Base DN (e.g. "dc=foo,dc=com") 49 */ 50 public $dn; 51 /** 52 * type de serveur, actuellement OpenLdap et Active Directory 53 */ 54 public $serverType; 55 /** 56 * Version du protocole ldap 57 */ 58 public $ldapProtocolVersion; 59 /** 60 * Server DN 61 */ 62 public $domain; 63 /** 64 * User administrateur Ldap 65 * Active Directory ne supporte pas les connexions anonymes 66 */ 67 public $searchUser; 68 /** 69 * Mot de passe de l'administrateur 70 * Active Directory ne supporte pas les connexions anonymes 71 */ 72 public $searchPassword; 73 /** 74 * DN des utilisateurs 75 */ 76 public $people; 77 /** 78 * DN des groupes 79 */ 80 public $groups; 81 /** 82 * Code erreur retourne par le serveur Ldap 83 */ 84 public $ldapErrorCode; 85 /** 86 * Message texte de l'erreur 87 */ 88 public $ldapErrorText; 89 90 91 //Fetch user 92 public $name; 93 public $firstname; 94 public $login; 95 public $phone; 96 public $skype; 97 public $fax; 98 public $mail; 99 public $mobile; 100 101 public $uacf; 102 public $pwdlastset; 103 104 public $ldapcharset = 'UTF-8'; // LDAP should be UTF-8 encoded 105 106 107 /** 108 * The internal LDAP connection handle 109 */ 110 public $connection; 111 /** 112 * Result of any connections etc. 113 */ 114 public $result; 115 116 117 /** 118 * Constructor 119 */ 120 public function __construct() 121 { 122 global $conf; 123 124 // Server 125 if (!empty($conf->global->LDAP_SERVER_HOST)) $this->server[] = $conf->global->LDAP_SERVER_HOST; 126 if (!empty($conf->global->LDAP_SERVER_HOST_SLAVE)) $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE; 127 $this->serverPort = $conf->global->LDAP_SERVER_PORT; 128 $this->ldapProtocolVersion = $conf->global->LDAP_SERVER_PROTOCOLVERSION; 129 $this->dn = $conf->global->LDAP_SERVER_DN; 130 $this->serverType = $conf->global->LDAP_SERVER_TYPE; 131 $this->domain = $conf->global->LDAP_SERVER_DN; 132 $this->searchUser = $conf->global->LDAP_ADMIN_DN; 133 $this->searchPassword = $conf->global->LDAP_ADMIN_PASS; 134 $this->people = $conf->global->LDAP_USER_DN; 135 $this->groups = $conf->global->LDAP_GROUP_DN; 136 137 $this->filter = $conf->global->LDAP_FILTER_CONNECTION; // Filter on user 138 $this->filtermember = $conf->global->LDAP_MEMBER_FILTER; // Filter on member 139 140 // Users 141 $this->attr_login = $conf->global->LDAP_FIELD_LOGIN; //unix 142 $this->attr_sambalogin = $conf->global->LDAP_FIELD_LOGIN_SAMBA; //samba, activedirectory 143 $this->attr_name = $conf->global->LDAP_FIELD_NAME; 144 $this->attr_firstname = $conf->global->LDAP_FIELD_FIRSTNAME; 145 $this->attr_mail = $conf->global->LDAP_FIELD_MAIL; 146 $this->attr_phone = $conf->global->LDAP_FIELD_PHONE; 147 $this->attr_skype = $conf->global->LDAP_FIELD_SKYPE; 148 $this->attr_fax = $conf->global->LDAP_FIELD_FAX; 149 $this->attr_mobile = $conf->global->LDAP_FIELD_MOBILE; 150 } 151 152 153 154 // Connection handling methods ------------------------------------------- 155 156 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 157 /** 158 * Connect and bind 159 * Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType, this->searchUser, this->searchPassword 160 * After return, this->connection and $this->bind are defined 161 * 162 * @return int <0 if KO, 1 if bind anonymous, 2 if bind auth 163 */ 164 public function connect_bind() 165 { 166 // phpcs:enable 167 global $conf; 168 169 $connected = 0; 170 $this->bind = 0; 171 172 // Check parameters 173 if (count($this->server) == 0 || empty($this->server[0])) 174 { 175 $this->error = 'LDAP setup (file conf.php) is not complete'; 176 dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING); 177 return -1; 178 } 179 180 if (!function_exists("ldap_connect")) 181 { 182 $this->error = 'LDAPFunctionsNotAvailableOnPHP'; 183 dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING); 184 $return = -1; 185 } 186 187 if (empty($this->error)) 188 { 189 // Loop on each ldap server 190 foreach ($this->server as $host) 191 { 192 if ($connected) break; 193 if (empty($host)) continue; 194 195 if ($this->serverPing($host, $this->serverPort) === true) { 196 $this->connection = ldap_connect($host, $this->serverPort); 197 } else continue; 198 199 if (is_resource($this->connection)) 200 { 201 // Begin TLS if requested by the configuration 202 if (!empty($conf->global->LDAP_SERVER_USE_TLS)) 203 { 204 if (!ldap_start_tls($this->connection)) 205 { 206 dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING); 207 $connected = 0; 208 $this->close(); 209 } 210 } 211 212 // Execute the ldap_set_option here (after connect and before bind) 213 $this->setVersion(); 214 ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true. 215 216 217 if ($this->serverType == "activedirectory") 218 { 219 $result = $this->setReferrals(); 220 dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG); 221 $this->result = $this->bindauth($this->searchUser, $this->searchPassword); 222 if ($this->result) 223 { 224 $this->bind = $this->result; 225 $connected = 2; 226 break; 227 } else { 228 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection); 229 } 230 } else { 231 // Try in auth mode 232 if ($this->searchUser && $this->searchPassword) 233 { 234 dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./', '*', $this->searchPassword), LOG_DEBUG); 235 $this->result = $this->bindauth($this->searchUser, $this->searchPassword); 236 if ($this->result) 237 { 238 $this->bind = $this->result; 239 $connected = 2; 240 break; 241 } else { 242 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection); 243 } 244 } 245 // Try in anonymous 246 if (!$this->bind) 247 { 248 dol_syslog(get_class($this)."::connect_bind try bind on ".$host, LOG_DEBUG); 249 $result = $this->bind(); 250 if ($result) 251 { 252 $this->bind = $this->result; 253 $connected = 1; 254 break; 255 } else { 256 $this->error = ldap_errno($this->connection).' '.ldap_error($this->connection); 257 } 258 } 259 } 260 } 261 262 if (!$connected) $this->close(); 263 } 264 } 265 266 if ($connected) 267 { 268 $return = $connected; 269 dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG); 270 } else { 271 $this->error = 'Failed to connect to LDAP'.($this->error ? ': '.$this->error : ''); 272 $return = -1; 273 dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING); 274 } 275 return $return; 276 } 277 278 279 280 /** 281 * Simply closes the connection set up earlier. 282 * Returns true if OK, false if there was an error. 283 * 284 * @return boolean true or false 285 */ 286 public function close() 287 { 288 if ($this->connection && !@ldap_close($this->connection)) 289 { 290 return false; 291 } else { 292 return true; 293 } 294 } 295 296 /** 297 * Anonymously binds to the connection. After this is done, 298 * queries and searches can be done - but read-only. 299 * 300 * @return boolean true or false 301 */ 302 public function bind() 303 { 304 if (!$this->result = @ldap_bind($this->connection)) 305 { 306 $this->ldapErrorCode = ldap_errno($this->connection); 307 $this->ldapErrorText = ldap_error($this->connection); 308 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText; 309 return false; 310 } else { 311 return true; 312 } 313 } 314 315 /** 316 * Binds as an authenticated user, which usually allows for write 317 * access. The FULL dn must be passed. For a directory manager, this is 318 * "cn=Directory Manager" under iPlanet. For a user, it will be something 319 * like "uid=jbloggs,ou=People,dc=foo,dc=com". 320 * 321 * @param string $bindDn DN 322 * @param string $pass Password 323 * @return boolean true or false 324 */ 325 public function bindauth($bindDn, $pass) 326 { 327 if (!$this->result = @ldap_bind($this->connection, $bindDn, $pass)) 328 { 329 $this->ldapErrorCode = ldap_errno($this->connection); 330 $this->ldapErrorText = ldap_error($this->connection); 331 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText; 332 return false; 333 } else { 334 return true; 335 } 336 } 337 338 /** 339 * Unbind du serveur ldap. 340 * 341 * @return boolean true or false 342 */ 343 public function unbind() 344 { 345 if (!$this->result = @ldap_unbind($this->connection)) 346 { 347 return false; 348 } else { 349 return true; 350 } 351 } 352 353 354 /** 355 * Verification de la version du serveur ldap. 356 * 357 * @return string version 358 */ 359 public function getVersion() 360 { 361 $version = 0; 362 $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version); 363 return $version; 364 } 365 366 /** 367 * Change ldap protocol version to use. 368 * 369 * @return boolean version 370 */ 371 public function setVersion() 372 { 373 // LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17 374 $ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion); 375 return $ldapsetversion; 376 } 377 378 /** 379 * changement du referrals. 380 * 381 * @return boolean referrals 382 */ 383 public function setReferrals() 384 { 385 // LDAP_OPT_REFERRALS est une constante qui vaut ? 386 $ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0); 387 return $ldapreferrals; 388 } 389 390 391 /** 392 * Add a LDAP entry 393 * Ldap object connect and bind must have been done 394 * 395 * @param string $dn DN entry key 396 * @param array $info Attributes array 397 * @param User $user Objet user that create 398 * @return int <0 if KO, >0 if OK 399 */ 400 public function add($dn, $info, $user) 401 { 402 dol_syslog(get_class($this)."::add dn=".$dn." info=".join(',', $info)); 403 404 // Check parameters 405 if (!$this->connection) 406 { 407 $this->error = "NotConnected"; 408 return -2; 409 } 410 if (!$this->bind) 411 { 412 $this->error = "NotConnected"; 413 return -3; 414 } 415 416 // Encode to LDAP page code 417 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 418 foreach ($info as $key => $val) 419 { 420 if (!is_array($val)) $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset); 421 } 422 423 $this->dump($dn, $info); 424 425 //print_r($info); 426 $result = @ldap_add($this->connection, $dn, $info); 427 428 if ($result) 429 { 430 dol_syslog(get_class($this)."::add successfull", LOG_DEBUG); 431 return 1; 432 } else { 433 $this->ldapErrorCode = @ldap_errno($this->connection); 434 $this->ldapErrorText = @ldap_error($this->connection); 435 $this->error = $this->ldapErrorCode." ".$this->ldapErrorText; 436 dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR); 437 return -1; 438 } 439 } 440 441 /** 442 * Modify a LDAP entry 443 * Ldap object connect and bind must have been done 444 * 445 * @param string $dn DN entry key 446 * @param array $info Attributes array 447 * @param User $user Objet user that modify 448 * @return int <0 if KO, >0 if OK 449 */ 450 public function modify($dn, $info, $user) 451 { 452 dol_syslog(get_class($this)."::modify dn=".$dn." info=".join(',', $info)); 453 454 // Check parameters 455 if (!$this->connection) 456 { 457 $this->error = "NotConnected"; 458 return -2; 459 } 460 if (!$this->bind) 461 { 462 $this->error = "NotConnected"; 463 return -3; 464 } 465 466 // Encode to LDAP page code 467 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 468 foreach ($info as $key => $val) 469 { 470 if (!is_array($val)) $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset); 471 } 472 473 $this->dump($dn, $info); 474 475 //print_r($info); 476 477 // For better compatibility with Samba4 AD 478 if ($this->serverType == "activedirectory") { 479 unset($info['cn']); // To avoid error : Operation not allowed on RDN (Code 67) 480 } 481 $result = @ldap_modify($this->connection, $dn, $info); 482 483 if ($result) 484 { 485 dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG); 486 return 1; 487 } else { 488 $this->error = @ldap_error($this->connection); 489 dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR); 490 return -1; 491 } 492 } 493 494 /** 495 * Rename a LDAP entry 496 * Ldap object connect and bind must have been done 497 * 498 * @param string $dn Old DN entry key (uid=qqq,ou=xxx,dc=aaa,dc=bbb) (before update) 499 * @param string $newrdn New RDN entry key (uid=qqq) 500 * @param string $newparent New parent (ou=xxx,dc=aaa,dc=bbb) 501 * @param User $user Objet user that modify 502 * @param bool $deleteoldrdn If true the old RDN value(s) is removed, else the old RDN value(s) is retained as non-distinguished values of the entry. 503 * @return int <0 if KO, >0 if OK 504 */ 505 public function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true) 506 { 507 dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn ? 1 : 0)); 508 509 // Check parameters 510 if (!$this->connection) 511 { 512 $this->error = "NotConnected"; 513 return -2; 514 } 515 if (!$this->bind) 516 { 517 $this->error = "NotConnected"; 518 return -3; 519 } 520 521 // Encode to LDAP page code 522 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 523 $newrdn = $this->convFromOutputCharset($newrdn, $this->ldapcharset); 524 $newparent = $this->convFromOutputCharset($newparent, $this->ldapcharset); 525 526 //print_r($info); 527 $result = @ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn); 528 529 if ($result) 530 { 531 dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG); 532 return 1; 533 } else { 534 $this->error = @ldap_error($this->connection); 535 dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR); 536 return -1; 537 } 538 } 539 540 /** 541 * Modify a LDAP entry (to use if dn != olddn) 542 * Ldap object connect and bind must have been done 543 * 544 * @param string $dn DN entry key 545 * @param array $info Attributes array 546 * @param User $user Objet user that update 547 * @param string $olddn Old DN entry key (before update) 548 * @param string $newrdn New RDN entry key (uid=qqq) (for ldap_rename) 549 * @param string $newparent New parent (ou=xxx,dc=aaa,dc=bbb) (for ldap_rename) 550 * @return int <0 if KO, >0 if OK 551 */ 552 public function update($dn, $info, $user, $olddn, $newrdn = false, $newparent = false) 553 { 554 dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn); 555 556 // Check parameters 557 if (!$this->connection) 558 { 559 $this->error = "NotConnected"; 560 return -2; 561 } 562 if (!$this->bind) 563 { 564 $this->error = "NotConnected"; 565 return -3; 566 } 567 568 if (!$olddn || $olddn != $dn) 569 { 570 if (!empty($olddn) && !empty($newrdn) && !empty($newparent) && $this->ldapProtocolVersion === '3') 571 { 572 // This function currently only works with LDAPv3 573 $result = $this->rename($olddn, $newrdn, $newparent, $user, true); 574 $result = $this->modify($dn, $info, $user); // We force "modify" for avoid some fields not modify 575 } else { 576 // If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one. 577 $result = $this->add($dn, $info, $user); 578 if ($result > 0 && $olddn && $olddn != $dn) $result = $this->delete($olddn); // If add fails, we do not try to delete old one 579 } 580 } else { 581 //$result = $this->delete($olddn); 582 $result = $this->add($dn, $info, $user); // If record has been deleted from LDAP, we recreate it. We ignore error if it already exists. 583 $result = $this->modify($dn, $info, $user); // We use add/modify instead of delete/add when olddn is received 584 } 585 if ($result <= 0) 586 { 587 $this->error = ldap_error($this->connection).' (Code '.ldap_errno($this->connection).") ".$this->error; 588 dol_syslog(get_class($this)."::update ".$this->error, LOG_ERR); 589 //print_r($info); 590 return -1; 591 } else { 592 dol_syslog(get_class($this)."::update done successfully"); 593 return 1; 594 } 595 } 596 597 598 /** 599 * Delete a LDAP entry 600 * Ldap object connect and bind must have been done 601 * 602 * @param string $dn DN entry key 603 * @return int <0 if KO, >0 if OK 604 */ 605 public function delete($dn) 606 { 607 dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn); 608 609 // Check parameters 610 if (!$this->connection) 611 { 612 $this->error = "NotConnected"; 613 return -2; 614 } 615 if (!$this->bind) 616 { 617 $this->error = "NotConnected"; 618 return -3; 619 } 620 621 // Encode to LDAP page code 622 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 623 624 $result = @ldap_delete($this->connection, $dn); 625 626 if ($result) return 1; 627 return -1; 628 } 629 630 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 631 /** 632 * Build a LDAP message 633 * 634 * @param string $dn DN entry key 635 * @param array $info Attributes array 636 * @return string Content of file 637 */ 638 public function dump_content($dn, $info) 639 { 640 // phpcs:enable 641 $content = ''; 642 643 // Create file content 644 if (preg_match('/^ldap/', $this->server[0])) 645 { 646 $target = "-H ".join(',', $this->server); 647 } else { 648 $target = "-h ".join(',', $this->server)." -p ".$this->serverPort; 649 } 650 $content .= "# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n"; 651 $content .= "# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n"; 652 $content .= "# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n"; 653 if (in_array('localhost', $this->server)) $content .= "# If commands fails to connect, try without -h and -p\n"; 654 $content .= "dn: ".$dn."\n"; 655 foreach ($info as $key => $value) 656 { 657 if (!is_array($value)) 658 { 659 $content .= "$key: $value\n"; 660 } else { 661 foreach ($value as $valuevalue) 662 { 663 $content .= "$key: $valuevalue\n"; 664 } 665 } 666 } 667 return $content; 668 } 669 670 /** 671 * Dump a LDAP message to ldapinput.in file 672 * 673 * @param string $dn DN entry key 674 * @param array $info Attributes array 675 * @return int <0 if KO, >0 if OK 676 */ 677 public function dump($dn, $info) 678 { 679 global $conf; 680 681 // Create content 682 $content = $this->dump_content($dn, $info); 683 684 //Create file 685 $result = dol_mkdir($conf->ldap->dir_temp); 686 687 $outputfile = $conf->ldap->dir_temp.'/ldapinput.in'; 688 $fp = fopen($outputfile, "w"); 689 if ($fp) 690 { 691 fputs($fp, $content); 692 fclose($fp); 693 if (!empty($conf->global->MAIN_UMASK)) 694 @chmod($outputfile, octdec($conf->global->MAIN_UMASK)); 695 return 1; 696 } else { 697 return -1; 698 } 699 } 700 701 /** 702 * Ping a server before ldap_connect for avoid waiting 703 * 704 * @param string $host Server host or address 705 * @param int $port Server port (default 389) 706 * @param int $timeout Timeout in second (default 1s) 707 * @return boolean true or false 708 */ 709 public function serverPing($host, $port = 389, $timeout = 1) 710 { 711 // Replace ldaps:// by ssl:// 712 if (preg_match('/^ldaps:\/\/([^\/]+)\/?$/', $host, $regs)) { 713 $host = 'ssl://'.$regs[1]; 714 } 715 // Remove ldap:// 716 if (preg_match('/^ldap:\/\/([^\/]+)\/?$/', $host, $regs)) { 717 $host = $regs[1]; 718 } 719 $op = @fsockopen($host, $port, $errno, $errstr, $timeout); 720 if (!$op) return false; //DC is N/A 721 else { 722 fclose($op); //explicitly close open socket connection 723 return true; //DC is up & running, we can safely connect with ldap_connect 724 } 725 } 726 727 728 // Attribute methods ----------------------------------------------------- 729 730 /** 731 * Add a LDAP attribute in entry 732 * Ldap object connect and bind must have been done 733 * 734 * @param string $dn DN entry key 735 * @param array $info Attributes array 736 * @param User $user Objet user that create 737 * @return int <0 if KO, >0 if OK 738 */ 739 public function addAttribute($dn, $info, $user) 740 { 741 dol_syslog(get_class($this)."::addAttribute dn=".$dn." info=".join(',', $info)); 742 743 // Check parameters 744 if (!$this->connection) 745 { 746 $this->error = "NotConnected"; 747 return -2; 748 } 749 if (!$this->bind) 750 { 751 $this->error = "NotConnected"; 752 return -3; 753 } 754 755 // Encode to LDAP page code 756 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 757 foreach ($info as $key => $val) 758 { 759 if (!is_array($val)) $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset); 760 } 761 762 $this->dump($dn, $info); 763 764 //print_r($info); 765 $result = @ldap_mod_add($this->connection, $dn, $info); 766 767 if ($result) 768 { 769 dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG); 770 return 1; 771 } else { 772 $this->error = @ldap_error($this->connection); 773 dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR); 774 return -1; 775 } 776 } 777 778 /** 779 * Update a LDAP attribute in entry 780 * Ldap object connect and bind must have been done 781 * 782 * @param string $dn DN entry key 783 * @param array $info Attributes array 784 * @param User $user Objet user that create 785 * @return int <0 if KO, >0 if OK 786 */ 787 public function updateAttribute($dn, $info, $user) 788 { 789 dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',', $info)); 790 791 // Check parameters 792 if (!$this->connection) 793 { 794 $this->error = "NotConnected"; 795 return -2; 796 } 797 if (!$this->bind) 798 { 799 $this->error = "NotConnected"; 800 return -3; 801 } 802 803 // Encode to LDAP page code 804 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 805 foreach ($info as $key => $val) 806 { 807 if (!is_array($val)) $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset); 808 } 809 810 $this->dump($dn, $info); 811 812 //print_r($info); 813 $result = @ldap_mod_replace($this->connection, $dn, $info); 814 815 if ($result) 816 { 817 dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG); 818 return 1; 819 } else { 820 $this->error = @ldap_error($this->connection); 821 dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR); 822 return -1; 823 } 824 } 825 826 /** 827 * Delete a LDAP attribute in entry 828 * Ldap object connect and bind must have been done 829 * 830 * @param string $dn DN entry key 831 * @param array $info Attributes array 832 * @param User $user Objet user that create 833 * @return int <0 if KO, >0 if OK 834 */ 835 public function deleteAttribute($dn, $info, $user) 836 { 837 dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',', $info)); 838 839 // Check parameters 840 if (!$this->connection) 841 { 842 $this->error = "NotConnected"; 843 return -2; 844 } 845 if (!$this->bind) 846 { 847 $this->error = "NotConnected"; 848 return -3; 849 } 850 851 // Encode to LDAP page code 852 $dn = $this->convFromOutputCharset($dn, $this->ldapcharset); 853 foreach ($info as $key => $val) 854 { 855 if (!is_array($val)) $info[$key] = $this->convFromOutputCharset($val, $this->ldapcharset); 856 } 857 858 $this->dump($dn, $info); 859 860 //print_r($info); 861 $result = @ldap_mod_del($this->connection, $dn, $info); 862 863 if ($result) 864 { 865 dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG); 866 return 1; 867 } else { 868 $this->error = @ldap_error($this->connection); 869 dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR); 870 return -1; 871 } 872 } 873 874 /** 875 * Returns an array containing attributes and values for first record 876 * 877 * @param string $dn DN entry key 878 * @param string $filter Filter 879 * @return int|array <0 or false if KO, array if OK 880 */ 881 public function getAttribute($dn, $filter) 882 { 883 // Check parameters 884 if (!$this->connection) 885 { 886 $this->error = "NotConnected"; 887 return -2; 888 } 889 if (!$this->bind) 890 { 891 $this->error = "NotConnected"; 892 return -3; 893 } 894 895 $search = ldap_search($this->connection, $dn, $filter); 896 897 // Only one entry should ever be returned 898 $entry = ldap_first_entry($this->connection, $search); 899 900 if (!$entry) 901 { 902 $this->ldapErrorCode = -1; 903 $this->ldapErrorText = "Couldn't find entry"; 904 return 0; // Couldn't find entry... 905 } 906 907 // Get values 908 if (!($values = ldap_get_attributes($this->connection, $entry))) 909 { 910 $this->ldapErrorCode = ldap_errno($this->connection); 911 $this->ldapErrorText = ldap_error($this->connection); 912 return 0; // No matching attributes 913 } 914 915 // Return an array containing the attributes. 916 return $values; 917 } 918 919 /** 920 * Returns an array containing values for an attribute and for first record matching filterrecord 921 * 922 * @param string $filterrecord Record 923 * @param string $attribute Attributes 924 * @return void 925 */ 926 public function getAttributeValues($filterrecord, $attribute) 927 { 928 $attributes = array(); 929 $attributes[0] = $attribute; 930 931 // We need to search for this user in order to get their entry. 932 $this->result = @ldap_search($this->connection, $this->people, $filterrecord, $attributes); 933 934 // Pourquoi cette ligne ? 935 //$info = ldap_get_entries($this->connection, $this->result); 936 937 // Only one entry should ever be returned (no user will have the same uid) 938 $entry = ldap_first_entry($this->connection, $this->result); 939 940 if (!$entry) 941 { 942 $this->ldapErrorCode = -1; 943 $this->ldapErrorText = "Couldn't find user"; 944 return false; // Couldn't find the user... 945 } 946 947 // Get values 948 if (!$values = @ldap_get_values($this->connection, $entry, $attribute)) 949 { 950 $this->ldapErrorCode = ldap_errno($this->connection); 951 $this->ldapErrorText = ldap_error($this->connection); 952 return false; // No matching attributes 953 } 954 955 // Return an array containing the attributes. 956 return $values; 957 } 958 959 /** 960 * Returns an array containing a details or list of LDAP record(s) 961 * ldapsearch -LLLx -hlocalhost -Dcn=admin,dc=parinux,dc=org -w password -b "ou=adherents,ou=people,dc=parinux,dc=org" userPassword 962 * 963 * @param string $search Value of fiel to search, '*' for all. Not used if $activefilter is set. 964 * @param string $userDn DN (Ex: ou=adherents,ou=people,dc=parinux,dc=org) 965 * @param string $useridentifier Name of key field (Ex: uid) 966 * @param array $attributeArray Array of fields required. Note this array must also contains field $useridentifier (Ex: sn,userPassword) 967 * @param int $activefilter '1' or 'user'=use field this->filter as filter instead of parameter $search, 'member'=use field this->filtermember as filter 968 * @param array $attributeAsArray Array of fields wanted as an array not a string 969 * @return array Array of [id_record][ldap_field]=value 970 */ 971 public function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter = 0, $attributeAsArray = array()) 972 { 973 $fulllist = array(); 974 975 dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',', $attributeArray).") activefilter=".$activefilter); 976 977 // if the directory is AD, then bind first with the search user first 978 if ($this->serverType == "activedirectory") 979 { 980 $this->bindauth($this->searchUser, $this->searchPassword); 981 dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser); 982 } 983 984 // Define filter 985 if (!empty($activefilter)) 986 { 987 if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter) { 988 $filter = '('.$this->filter.')'; 989 } elseif (((string) $activefilter == 'member') && $this->filter) { 990 $filter = '('.$this->filtermember.')'; 991 } else { 992 // If this->filter is empty, make fiter on * (all) 993 $filter = '('.$useridentifier.'=*)'; 994 } 995 } else { 996 $filter = '('.$useridentifier.'='.$search.')'; 997 } 998 999 if (is_array($attributeArray)) 1000 { 1001 // Return list with required fields 1002 $attributeArray = array_values($attributeArray); // This is to force to have index reordered from 0 (not make ldap_search fails) 1003 dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter." attributeArray=(".join(',', $attributeArray).")"); 1004 //var_dump($attributeArray); 1005 $this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray); 1006 } else { 1007 // Return list with fields selected by default 1008 dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter); 1009 $this->result = @ldap_search($this->connection, $userDn, $filter); 1010 } 1011 if (!$this->result) 1012 { 1013 $this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection); 1014 return -1; 1015 } 1016 1017 $info = @ldap_get_entries($this->connection, $this->result); 1018 1019 // Warning: Dans info, les noms d'attributs sont en minuscule meme si passe 1020 // a ldap_search en majuscule !!! 1021 //print_r($info); 1022 1023 for ($i = 0; $i < $info["count"]; $i++) 1024 { 1025 $recordid = $this->convToOutputCharset($info[$i][$useridentifier][0], $this->ldapcharset); 1026 if ($recordid) 1027 { 1028 //print "Found record with key $useridentifier=".$recordid."<br>\n"; 1029 $fulllist[$recordid][$useridentifier] = $recordid; 1030 1031 // Add to the array for each attribute in my list 1032 $num = count($attributeArray); 1033 for ($j = 0; $j < $num; $j++) 1034 { 1035 $keyattributelower = strtolower($attributeArray[$j]); 1036 //print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n"; 1037 1038 //permet de recuperer le SID avec Active Directory 1039 if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid") 1040 { 1041 $objectsid = $this->getObjectSid($recordid); 1042 $fulllist[$recordid][$attributeArray[$j]] = $objectsid; 1043 } else { 1044 if (in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) { 1045 $valueTab = array(); 1046 foreach ($info[$i][$keyattributelower] as $key => $value) { 1047 $valueTab[$key] = $this->convToOutputCharset($value, $this->ldapcharset); 1048 } 1049 $fulllist[$recordid][$attributeArray[$j]] = $valueTab; 1050 } else { 1051 $fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0], $this->ldapcharset); 1052 } 1053 } 1054 } 1055 } 1056 } 1057 1058 asort($fulllist); 1059 return $fulllist; 1060 } 1061 1062 /** 1063 * Converts a little-endian hex-number to one, that 'hexdec' can convert 1064 * Required by Active Directory 1065 * 1066 * @param string $hex Hex value 1067 * @return string Little endian 1068 */ 1069 public function littleEndian($hex) 1070 { 1071 $result = ''; 1072 for ($x = dol_strlen($hex) - 2; $x >= 0; $x = $x - 2) { 1073 $result .= substr($hex, $x, 2); 1074 } 1075 return $result; 1076 } 1077 1078 1079 /** 1080 * Recupere le SID de l'utilisateur 1081 * Required by Active Directory 1082 * 1083 * @param string $ldapUser Login de l'utilisateur 1084 * @return string Sid 1085 */ 1086 public function getObjectSid($ldapUser) 1087 { 1088 $criteria = '('.$this->getUserIdentifier().'='.$ldapUser.')'; 1089 $justthese = array("objectsid"); 1090 1091 // if the directory is AD, then bind first with the search user first 1092 if ($this->serverType == "activedirectory") 1093 { 1094 $this->bindauth($this->searchUser, $this->searchPassword); 1095 } 1096 1097 $i = 0; 1098 $searchDN = $this->people; 1099 1100 while ($i <= 2) 1101 { 1102 $ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese); 1103 1104 if (!$ldapSearchResult) 1105 { 1106 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection); 1107 return -1; 1108 } 1109 1110 $entry = ldap_first_entry($this->connection, $ldapSearchResult); 1111 1112 if (!$entry) 1113 { 1114 // Si pas de resultat on cherche dans le domaine 1115 $searchDN = $this->domain; 1116 $i++; 1117 } else { 1118 $i++; 1119 $i++; 1120 } 1121 } 1122 1123 if ($entry) 1124 { 1125 $ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid"); 1126 $SIDText = $this->binSIDtoText($ldapBinary[0]); 1127 return $SIDText; 1128 } else { 1129 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection); 1130 return '?'; 1131 } 1132 } 1133 1134 /** 1135 * Returns the textual SID 1136 * Indispensable pour Active Directory 1137 * 1138 * @param string $binsid Binary SID 1139 * @return string Textual SID 1140 */ 1141 public function binSIDtoText($binsid) 1142 { 1143 $hex_sid = bin2hex($binsid); 1144 $rev = hexdec(substr($hex_sid, 0, 2)); // Get revision-part of SID 1145 $subcount = hexdec(substr($hex_sid, 2, 2)); // Get count of sub-auth entries 1146 $auth = hexdec(substr($hex_sid, 4, 12)); // SECURITY_NT_AUTHORITY 1147 $result = "$rev-$auth"; 1148 for ($x = 0; $x < $subcount; $x++) 1149 { 1150 $result .= "-".hexdec($this->littleEndian(substr($hex_sid, 16 + ($x * 8), 8))); // get all SECURITY_NT_AUTHORITY 1151 } 1152 return $result; 1153 } 1154 1155 1156 /** 1157 * Fonction de recherche avec filtre 1158 * this->connection doit etre defini donc la methode bind ou bindauth doit avoir deja ete appelee 1159 * Ne pas utiliser pour recherche d'une liste donnee de proprietes 1160 * car conflit majuscule-minuscule. A n'utiliser que pour les pages 1161 * 'Fiche LDAP' qui affiche champ lisibles par defaut. 1162 * 1163 * @param string $checkDn DN de recherche (Ex: ou=users,cn=my-domain,cn=com) 1164 * @param string $filter Search filter (ex: (sn=nom_personne) ) 1165 * @return array|int Array with answers (key lowercased - value) 1166 */ 1167 public function search($checkDn, $filter) 1168 { 1169 dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter); 1170 1171 $checkDn = $this->convFromOutputCharset($checkDn, $this->ldapcharset); 1172 $filter = $this->convFromOutputCharset($filter, $this->ldapcharset); 1173 1174 // if the directory is AD, then bind first with the search user first 1175 if ($this->serverType == "activedirectory") { 1176 $this->bindauth($this->searchUser, $this->searchPassword); 1177 } 1178 1179 $this->result = @ldap_search($this->connection, $checkDn, $filter); 1180 1181 $result = @ldap_get_entries($this->connection, $this->result); 1182 if (!$result) 1183 { 1184 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection); 1185 return -1; 1186 } else { 1187 ldap_free_result($this->result); 1188 return $result; 1189 } 1190 } 1191 1192 1193 /** 1194 * Load all attribute of a LDAP user 1195 * 1196 * @param User $user User to search for. Not used if a filter is provided. 1197 * @param string $filter Filter for search. Must start with &. 1198 * Examples: &(objectClass=inetOrgPerson) &(objectClass=user)(objectCategory=person) &(isMemberOf=cn=Sales,ou=Groups,dc=opencsi,dc=com) 1199 * @return int >0 if OK, <0 if KO 1200 */ 1201 public function fetch($user, $filter) 1202 { 1203 // Perform the search and get the entry handles 1204 1205 // if the directory is AD, then bind first with the search user first 1206 if ($this->serverType == "activedirectory") { 1207 $this->bindauth($this->searchUser, $this->searchPassword); 1208 } 1209 1210 $searchDN = $this->people; // TODO Why searching in people then domain ? 1211 1212 $result = ''; 1213 $i = 0; 1214 while ($i <= 2) 1215 { 1216 dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter); 1217 $this->result = @ldap_search($this->connection, $searchDN, $filter); 1218 if ($this->result) 1219 { 1220 $result = @ldap_get_entries($this->connection, $this->result); 1221 if ($result['count'] > 0) dol_syslog('Ldap::fetch search found '.$result['count'].' records'); 1222 else dol_syslog('Ldap::fetch search returns but found no records'); 1223 //var_dump($result);exit; 1224 } else { 1225 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection); 1226 dol_syslog(get_class($this)."::fetch search fails"); 1227 return -1; 1228 } 1229 1230 if (!$result) 1231 { 1232 // Si pas de resultat on cherche dans le domaine 1233 $searchDN = $this->domain; 1234 $i++; 1235 } else { 1236 break; 1237 } 1238 } 1239 1240 if (!$result) 1241 { 1242 $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection); 1243 return -1; 1244 } else { 1245 $this->name = $this->convToOutputCharset($result[0][$this->attr_name][0], $this->ldapcharset); 1246 $this->firstname = $this->convToOutputCharset($result[0][$this->attr_firstname][0], $this->ldapcharset); 1247 $this->login = $this->convToOutputCharset($result[0][$this->attr_login][0], $this->ldapcharset); 1248 $this->phone = $this->convToOutputCharset($result[0][$this->attr_phone][0], $this->ldapcharset); 1249 $this->skype = $this->convToOutputCharset($result[0][$this->attr_skype][0], $this->ldapcharset); 1250 $this->fax = $this->convToOutputCharset($result[0][$this->attr_fax][0], $this->ldapcharset); 1251 $this->mail = $this->convToOutputCharset($result[0][$this->attr_mail][0], $this->ldapcharset); 1252 $this->mobile = $this->convToOutputCharset($result[0][$this->attr_mobile][0], $this->ldapcharset); 1253 1254 $this->uacf = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0], $this->ldapcharset)); 1255 if (isset($result[0]["pwdlastset"][0])) // If expiration on password exists 1256 { 1257 $this->pwdlastset = ($result[0]["pwdlastset"][0] != 0) ? $this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0], $this->ldapcharset)) : 0; 1258 } else { 1259 $this->pwdlastset = -1; 1260 } 1261 if (!$this->name && !$this->login) $this->pwdlastset = -1; 1262 $this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0], $this->ldapcharset)); 1263 1264 // FQDN domain 1265 $domain = str_replace('dc=', '', $this->domain); 1266 $domain = str_replace(',', '.', $domain); 1267 $this->domainFQDN = $domain; 1268 1269 // Set ldapUserDn (each user can have a different dn) 1270 //var_dump($result[0]);exit; 1271 $this->ldapUserDN = $result[0]['dn']; 1272 1273 ldap_free_result($this->result); 1274 return 1; 1275 } 1276 } 1277 1278 1279 // helper methods 1280 1281 /** 1282 * Returns the correct user identifier to use, based on the ldap server type 1283 * 1284 * @return string Login 1285 */ 1286 public function getUserIdentifier() 1287 { 1288 if ($this->serverType == "activedirectory") { 1289 return $this->attr_sambalogin; 1290 } else { 1291 return $this->attr_login; 1292 } 1293 } 1294 1295 /** 1296 * UserAccountControl Flgs to more human understandable form... 1297 * 1298 * @param string $uacf UACF 1299 * @return void 1300 */ 1301 public function parseUACF($uacf) 1302 { 1303 //All flags array 1304 $flags = array( 1305 "TRUSTED_TO_AUTH_FOR_DELEGATION" => 16777216, 1306 "PASSWORD_EXPIRED" => 8388608, 1307 "DONT_REQ_PREAUTH" => 4194304, 1308 "USE_DES_KEY_ONLY" => 2097152, 1309 "NOT_DELEGATED" => 1048576, 1310 "TRUSTED_FOR_DELEGATION" => 524288, 1311 "SMARTCARD_REQUIRED" => 262144, 1312 "MNS_LOGON_ACCOUNT" => 131072, 1313 "DONT_EXPIRE_PASSWORD" => 65536, 1314 "SERVER_TRUST_ACCOUNT" => 8192, 1315 "WORKSTATION_TRUST_ACCOUNT" => 4096, 1316 "INTERDOMAIN_TRUST_ACCOUNT" => 2048, 1317 "NORMAL_ACCOUNT" => 512, 1318 "TEMP_DUPLICATE_ACCOUNT" => 256, 1319 "ENCRYPTED_TEXT_PWD_ALLOWED" => 128, 1320 "PASSWD_CANT_CHANGE" => 64, 1321 "PASSWD_NOTREQD" => 32, 1322 "LOCKOUT" => 16, 1323 "HOMEDIR_REQUIRED" => 8, 1324 "ACCOUNTDISABLE" => 2, 1325 "SCRIPT" => 1 1326 ); 1327 1328 //Parse flags to text 1329 $retval = array(); 1330 //while (list($flag, $val) = each($flags)) { 1331 foreach ($flags as $flag => $val) { 1332 if ($uacf >= $val) { 1333 $uacf -= $val; 1334 $retval[$val] = $flag; 1335 } 1336 } 1337 1338 //Return human friendly flags 1339 return($retval); 1340 } 1341 1342 /** 1343 * SamAccountType value to text 1344 * 1345 * @param string $samtype SamType 1346 * @return string Sam string 1347 */ 1348 public function parseSAT($samtype) 1349 { 1350 $stypes = array( 1351 805306368 => "NORMAL_ACCOUNT", 1352 805306369 => "WORKSTATION_TRUST", 1353 805306370 => "INTERDOMAIN_TRUST", 1354 268435456 => "SECURITY_GLOBAL_GROUP", 1355 268435457 => "DISTRIBUTION_GROUP", 1356 536870912 => "SECURITY_LOCAL_GROUP", 1357 536870913 => "DISTRIBUTION_LOCAL_GROUP" 1358 ); 1359 1360 $retval = ""; 1361 while (list($sat, $val) = each($stypes)) { 1362 if ($samtype == $sat) { 1363 $retval = $val; 1364 break; 1365 } 1366 } 1367 if (empty($retval)) $retval = "UNKNOWN_TYPE_".$samtype; 1368 1369 return($retval); 1370 } 1371 1372 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1373 /** 1374 * Convertit le temps ActiveDirectory en Unix timestamp 1375 * 1376 * @param string $value AD time to convert 1377 * @return integer Unix timestamp 1378 */ 1379 public function convert_time($value) 1380 { 1381 // phpcs:enable 1382 $dateLargeInt = $value; // nano secondes depuis 1601 !!!! 1383 $secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601 1384 $ADToUnixConvertor = ((1970 - 1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes 1385 $unixTimeStamp = intval($secsAfterADEpoch - $ADToUnixConvertor); // Unix time stamp 1386 return $unixTimeStamp; 1387 } 1388 1389 1390 /** 1391 * Convert a string into output/memory charset 1392 * 1393 * @param string $str String to convert 1394 * @param string $pagecodefrom Page code of src string 1395 * @return string Converted string 1396 */ 1397 private function convToOutputCharset($str, $pagecodefrom = 'UTF-8') 1398 { 1399 global $conf; 1400 if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str = utf8_encode($str); 1401 if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str = utf8_decode($str); 1402 return $str; 1403 } 1404 1405 /** 1406 * Convert a string from output/memory charset 1407 * 1408 * @param string $str String to convert 1409 * @param string $pagecodeto Page code for result string 1410 * @return string Converted string 1411 */ 1412 public function convFromOutputCharset($str, $pagecodeto = 'UTF-8') 1413 { 1414 global $conf; 1415 if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str = utf8_decode($str); 1416 if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str = utf8_encode($str); 1417 return $str; 1418 } 1419 1420 1421 /** 1422 * Return available value of group GID 1423 * 1424 * @param string $keygroup Key of group 1425 * @return int gid number 1426 */ 1427 public function getNextGroupGid($keygroup = 'LDAP_KEY_GROUPS') 1428 { 1429 global $conf; 1430 1431 if (empty($keygroup)) $keygroup = 'LDAP_KEY_GROUPS'; 1432 1433 $search = '('.$conf->global->$keygroup.'=*)'; 1434 $result = $this->search($this->groups, $search); 1435 if ($result) 1436 { 1437 $c = $result['count']; 1438 $gids = array(); 1439 for ($i = 0; $i < $c; $i++) 1440 { 1441 $gids[] = $result[$i]['gidnumber'][0]; 1442 } 1443 rsort($gids); 1444 1445 return $gids[0] + 1; 1446 } 1447 1448 return 0; 1449 } 1450} 1451