1<?php 2/** 3 * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY 4 * Version 4.0.4 5 * 6 * PHP Version 5 with SSL and LDAP support 7 * 8 * Written by Scott Barnett, Richard Hyland 9 * email: scott@wiggumworld.com, adldap@richardhyland.com 10 * http://adldap.sourceforge.net/ 11 * 12 * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland 13 * 14 * We'd appreciate any improvements or additions to be submitted back 15 * to benefit the entire community :) 16 * 17 * This library is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU Lesser General Public 19 * License as published by the Free Software Foundation; either 20 * version 2.1 of the License. 21 * 22 * This library is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * Lesser General Public License for more details. 26 * 27 * @category ToolsAndUtilities 28 * @package adLDAP 29 * @author Scott Barnett, Richard Hyland 30 * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland 31 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1 32 * @revision $Revision: 169 $ 33 * @version 4.0.4 34 * @link http://adldap.sourceforge.net/ 35 */ 36 37/** 38* Main adLDAP class 39* 40* Can be initialised using $adldap = new adLDAP(); 41* 42* Something to keep in mind is that Active Directory is a permissions 43* based directory. If you bind as a domain user, you can't fetch as 44* much information on other users as you could as a domain admin. 45* 46* Before asking questions, please read the Documentation at 47* http://adldap.sourceforge.net/wiki/doku.php?id=api 48*/ 49require_once(dirname(__FILE__) . '/collections/adLDAPCollection.php'); 50require_once(dirname(__FILE__) . '/classes/adLDAPGroups.php'); 51require_once(dirname(__FILE__) . '/classes/adLDAPUsers.php'); 52require_once(dirname(__FILE__) . '/classes/adLDAPFolders.php'); 53require_once(dirname(__FILE__) . '/classes/adLDAPUtils.php'); 54require_once(dirname(__FILE__) . '/classes/adLDAPContacts.php'); 55require_once(dirname(__FILE__) . '/classes/adLDAPExchange.php'); 56require_once(dirname(__FILE__) . '/classes/adLDAPComputers.php'); 57 58class adLDAP { 59 60 /** 61 * Define the different types of account in AD 62 */ 63 const ADLDAP_NORMAL_ACCOUNT = 805306368; 64 const ADLDAP_WORKSTATION_TRUST = 805306369; 65 const ADLDAP_INTERDOMAIN_TRUST = 805306370; 66 const ADLDAP_SECURITY_GLOBAL_GROUP = 268435456; 67 const ADLDAP_DISTRIBUTION_GROUP = 268435457; 68 const ADLDAP_SECURITY_LOCAL_GROUP = 536870912; 69 const ADLDAP_DISTRIBUTION_LOCAL_GROUP = 536870913; 70 const ADLDAP_FOLDER = 'OU'; 71 const ADLDAP_CONTAINER = 'CN'; 72 73 /** 74 * The default port for LDAP non-SSL connections 75 */ 76 const ADLDAP_LDAP_PORT = '389'; 77 /** 78 * The default port for LDAPS SSL connections 79 */ 80 const ADLDAP_LDAPS_PORT = '636'; 81 82 /** 83 * The account suffix for your domain, can be set when the class is invoked 84 * 85 * @var string 86 */ 87 protected $accountSuffix = "@mydomain.local"; 88 89 /** 90 * The base dn for your domain 91 * 92 * If this is set to null then adLDAP will attempt to obtain this automatically from the rootDSE 93 * 94 * @var string 95 */ 96 protected $baseDn = "DC=mydomain,DC=local"; 97 98 /** 99 * Port used to talk to the domain controllers. 100 * 101 * @var int 102 */ 103 protected $adPort = self::ADLDAP_LDAP_PORT; 104 105 /** 106 * Array of domain controllers. Specifiy multiple controllers if you 107 * would like the class to balance the LDAP queries amongst multiple servers 108 * 109 * @var array 110 */ 111 protected $domainControllers = array("dc01.mydomain.local"); 112 113 /** 114 * Optional account with higher privileges for searching 115 * This should be set to a domain admin account 116 * 117 * @var string 118 * @var string 119 */ 120 protected $adminUsername = NULL; 121 protected $adminPassword = NULL; 122 123 /** 124 * AD does not return the primary group. http://support.microsoft.com/?kbid=321360 125 * This tweak will resolve the real primary group. 126 * Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if 127 * someone's primary group is NOT domain users, this is obviously going to mess up the results 128 * 129 * @var bool 130 */ 131 protected $realPrimaryGroup = true; 132 133 /** 134 * Use SSL (LDAPS), your server needs to be setup, please see 135 * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl 136 * 137 * @var bool 138 */ 139 protected $useSSL = false; 140 141 /** 142 * Use TLS 143 * If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa 144 * 145 * @var bool 146 */ 147 protected $useTLS = false; 148 149 /** 150 * Use SSO 151 * To indicate to adLDAP to reuse password set by the brower through NTLM or Kerberos 152 * 153 * @var bool 154 */ 155 protected $useSSO = false; 156 157 /** 158 * When querying group memberships, do it recursively 159 * eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C 160 * user_ingroup("Fred","C") will returns true with this option turned on, false if turned off 161 * 162 * @var bool 163 */ 164 protected $recursiveGroups = true; 165 166 // You should not need to edit anything below this line 167 //****************************************************************************************** 168 169 /** 170 * Connection and bind default variables 171 * 172 * @var mixed 173 * @var mixed 174 */ 175 protected $ldapConnection; 176 protected $ldapBind; 177 178 /** 179 * Get the active LDAP Connection 180 * 181 * @return resource 182 */ 183 public function getLdapConnection() { 184 if ($this->ldapConnection) { 185 return $this->ldapConnection; 186 } 187 return false; 188 } 189 190 /** 191 * Get the bind status 192 * 193 * @return bool 194 */ 195 public function getLdapBind() { 196 return $this->ldapBind; 197 } 198 199 /** 200 * Get the current base DN 201 * 202 * @return string 203 */ 204 public function getBaseDn() { 205 return $this->baseDn; 206 } 207 208 /** 209 * The group class 210 * 211 * @var adLDAPGroups 212 */ 213 protected $groupClass; 214 215 /** 216 * Get the group class interface 217 * 218 * @return adLDAPGroups 219 */ 220 public function group() { 221 if (!$this->groupClass) { 222 $this->groupClass = new adLDAPGroups($this); 223 } 224 return $this->groupClass; 225 } 226 227 /** 228 * The user class 229 * 230 * @var adLDAPUsers 231 */ 232 protected $userClass; 233 234 /** 235 * Get the userclass interface 236 * 237 * @return adLDAPUsers 238 */ 239 public function user() { 240 if (!$this->userClass) { 241 $this->userClass = new adLDAPUsers($this); 242 } 243 return $this->userClass; 244 } 245 246 /** 247 * The folders class 248 * 249 * @var adLDAPFolders 250 */ 251 protected $folderClass; 252 253 /** 254 * Get the folder class interface 255 * 256 * @return adLDAPFolders 257 */ 258 public function folder() { 259 if (!$this->folderClass) { 260 $this->folderClass = new adLDAPFolders($this); 261 } 262 return $this->folderClass; 263 } 264 265 /** 266 * The utils class 267 * 268 * @var adLDAPUtils 269 */ 270 protected $utilClass; 271 272 /** 273 * Get the utils class interface 274 * 275 * @return adLDAPUtils 276 */ 277 public function utilities() { 278 if (!$this->utilClass) { 279 $this->utilClass = new adLDAPUtils($this); 280 } 281 return $this->utilClass; 282 } 283 284 /** 285 * The contacts class 286 * 287 * @var adLDAPContacts 288 */ 289 protected $contactClass; 290 291 /** 292 * Get the contacts class interface 293 * 294 * @return adLDAPContacts 295 */ 296 public function contact() { 297 if (!$this->contactClass) { 298 $this->contactClass = new adLDAPContacts($this); 299 } 300 return $this->contactClass; 301 } 302 303 /** 304 * The exchange class 305 * 306 * @var adLDAPExchange 307 */ 308 protected $exchangeClass; 309 310 /** 311 * Get the exchange class interface 312 * 313 * @return adLDAPExchange 314 */ 315 public function exchange() { 316 if (!$this->exchangeClass) { 317 $this->exchangeClass = new adLDAPExchange($this); 318 } 319 return $this->exchangeClass; 320 } 321 322 /** 323 * The computers class 324 * 325 * @var adLDAPComputers 326 */ 327 protected $computersClass; 328 329 /** 330 * Get the computers class interface 331 * 332 * @return adLDAPComputers 333 */ 334 public function computer() { 335 if (!$this->computerClass) { 336 $this->computerClass = new adLDAPComputers($this); 337 } 338 return $this->computerClass; 339 } 340 341 /** 342 * Getters and Setters 343 */ 344 345 /** 346 * Set the account suffix 347 * 348 * @param string $accountSuffix 349 * @return void 350 */ 351 public function setAccountSuffix($accountSuffix) 352 { 353 $this->accountSuffix = $accountSuffix; 354 } 355 356 /** 357 * Get the account suffix 358 * 359 * @return string 360 */ 361 public function getAccountSuffix() 362 { 363 return $this->accountSuffix; 364 } 365 366 /** 367 * Set the domain controllers array 368 * 369 * @param array $domainControllers 370 * @return void 371 */ 372 public function setDomainControllers(array $domainControllers) 373 { 374 $this->domainControllers = $domainControllers; 375 } 376 377 /** 378 * Get the list of domain controllers 379 * 380 * @return void 381 */ 382 public function getDomainControllers() 383 { 384 return $this->domainControllers; 385 } 386 387 /** 388 * Sets the port number your domain controller communicates over 389 * 390 * @param int $adPort 391 */ 392 public function setPort($adPort) 393 { 394 $this->adPort = $adPort; 395 } 396 397 /** 398 * Gets the port number your domain controller communicates over 399 * 400 * @return int 401 */ 402 public function getPort() 403 { 404 return $this->adPort; 405 } 406 407 /** 408 * Set the username of an account with higher priviledges 409 * 410 * @param string $adminUsername 411 * @return void 412 */ 413 public function setAdminUsername($adminUsername) 414 { 415 $this->adminUsername = $adminUsername; 416 } 417 418 /** 419 * Get the username of the account with higher priviledges 420 * 421 * This will throw an exception for security reasons 422 */ 423 public function getAdminUsername() 424 { 425 throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); 426 } 427 428 /** 429 * Set the password of an account with higher priviledges 430 * 431 * @param string $adminPassword 432 * @return void 433 */ 434 public function setAdminPassword($adminPassword) 435 { 436 $this->adminPassword = $adminPassword; 437 } 438 439 /** 440 * Get the password of the account with higher priviledges 441 * 442 * This will throw an exception for security reasons 443 */ 444 public function getAdminPassword() 445 { 446 throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); 447 } 448 449 /** 450 * Set whether to detect the true primary group 451 * 452 * @param bool $realPrimaryGroup 453 * @return void 454 */ 455 public function setRealPrimaryGroup($realPrimaryGroup) 456 { 457 $this->realPrimaryGroup = $realPrimaryGroup; 458 } 459 460 /** 461 * Get the real primary group setting 462 * 463 * @return bool 464 */ 465 public function getRealPrimaryGroup() 466 { 467 return $this->realPrimaryGroup; 468 } 469 470 /** 471 * Set whether to use SSL 472 * 473 * @param bool $useSSL 474 * @return void 475 */ 476 public function setUseSSL($useSSL) 477 { 478 $this->useSSL = $useSSL; 479 // Set the default port correctly 480 if($this->useSSL) { 481 $this->setPort(self::ADLDAP_LDAPS_PORT); 482 } 483 else { 484 $this->setPort(self::ADLDAP_LDAP_PORT); 485 } 486 } 487 488 /** 489 * Get the SSL setting 490 * 491 * @return bool 492 */ 493 public function getUseSSL() 494 { 495 return $this->useSSL; 496 } 497 498 /** 499 * Set whether to use TLS 500 * 501 * @param bool $useTLS 502 * @return void 503 */ 504 public function setUseTLS($useTLS) 505 { 506 $this->useTLS = $useTLS; 507 } 508 509 /** 510 * Get the TLS setting 511 * 512 * @return bool 513 */ 514 public function getUseTLS() 515 { 516 return $this->useTLS; 517 } 518 519 /** 520 * Set whether to use SSO 521 * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined. 522 * 523 * @param bool $useSSO 524 * @return void 525 */ 526 public function setUseSSO($useSSO) 527 { 528 if ($useSSO === true && !$this->ldapSaslSupported()) { 529 throw new adLDAPException('No LDAP SASL support for PHP. See: http://php.net/ldap_sasl_bind'); 530 } 531 $this->useSSO = $useSSO; 532 } 533 534 /** 535 * Get the SSO setting 536 * 537 * @return bool 538 */ 539 public function getUseSSO() 540 { 541 return $this->useSSO; 542 } 543 544 /** 545 * Set whether to lookup recursive groups 546 * 547 * @param bool $recursiveGroups 548 * @return void 549 */ 550 public function setRecursiveGroups($recursiveGroups) 551 { 552 $this->recursiveGroups = $recursiveGroups; 553 } 554 555 /** 556 * Get the recursive groups setting 557 * 558 * @return bool 559 */ 560 public function getRecursiveGroups() 561 { 562 return $this->recursiveGroups; 563 } 564 565 /** 566 * Default Constructor 567 * 568 * Tries to bind to the AD domain over LDAP or LDAPs 569 * 570 * @param array $options Array of options to pass to the constructor 571 * @throws Exception - if unable to bind to Domain Controller 572 * @return bool 573 */ 574 function __construct($options = array()) { 575 // You can specifically overide any of the default configuration options setup above 576 if (count($options) > 0) { 577 if (array_key_exists("account_suffix",$options)){ $this->accountSuffix = $options["account_suffix"]; } 578 if (array_key_exists("base_dn",$options)){ $this->baseDn = $options["base_dn"]; } 579 if (array_key_exists("domain_controllers",$options)){ 580 if (!is_array($options["domain_controllers"])) { 581 throw new adLDAPException('[domain_controllers] option must be an array'); 582 } 583 $this->domainControllers = $options["domain_controllers"]; 584 } 585 if (array_key_exists("admin_username",$options)){ $this->adminUsername = $options["admin_username"]; } 586 if (array_key_exists("admin_password",$options)){ $this->adminPassword = $options["admin_password"]; } 587 if (array_key_exists("real_primarygroup",$options)){ $this->realPrimaryGroup = $options["real_primarygroup"]; } 588 if (array_key_exists("use_ssl",$options)){ $this->setUseSSL($options["use_ssl"]); } 589 if (array_key_exists("use_tls",$options)){ $this->useTLS = $options["use_tls"]; } 590 if (array_key_exists("recursive_groups",$options)){ $this->recursiveGroups = $options["recursive_groups"]; } 591 if (array_key_exists("ad_port",$options)){ $this->setPort($options["ad_port"]); } 592 if (array_key_exists("sso",$options)) { 593 $this->setUseSSO($options["sso"]); 594 if (!$this->ldapSaslSupported()) { 595 $this->setUseSSO(false); 596 } 597 } 598 } 599 600 if ($this->ldapSupported() === false) { 601 throw new adLDAPException('No LDAP support for PHP. See: http://php.net/ldap'); 602 } 603 604 return $this->connect(); 605 } 606 607 /** 608 * Default Destructor 609 * 610 * Closes the LDAP connection 611 * 612 * @return void 613 */ 614 function __destruct() { 615 $this->close(); 616 } 617 618 /** 619 * Connects and Binds to the Domain Controller 620 * 621 * @return bool 622 */ 623 public function connect() 624 { 625 // Connect to the AD/LDAP server as the username/password 626 $domainController = $this->randomController(); 627 if ($this->useSSL) { 628 $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort); 629 } else { 630 $this->ldapConnection = ldap_connect($domainController, $this->adPort); 631 } 632 633 // Set some ldap options for talking to AD 634 ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); 635 ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, 0); 636 637 if ($this->useTLS) { 638 ldap_start_tls($this->ldapConnection); 639 } 640 641 // Bind as a domain admin if they've set it up 642 if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) { 643 $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword); 644 if (!$this->ldapBind) { 645 if ($this->useSSL && !$this->useTLS) { 646 // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message 647 throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError()); 648 } 649 else { 650 throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError()); 651 } 652 } 653 } 654 if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) { 655 putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); 656 $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); 657 if (!$this->ldapBind){ 658 throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); 659 } 660 else { 661 return true; 662 } 663 } 664 665 666 if ($this->baseDn == NULL) { 667 $this->baseDn = $this->findBaseDn(); 668 } 669 670 return true; 671 } 672 673 /** 674 * Closes the LDAP connection 675 * 676 * @return void 677 */ 678 public function close() { 679 if ($this->ldapConnection) { 680 @ldap_close($this->ldapConnection); 681 } 682 } 683 684 /** 685 * Validate a user's login credentials 686 * 687 * @param string $username A user's AD username 688 * @param string $password A user's AD password 689 * @param bool optional $preventRebind 690 * @return bool 691 */ 692 public function authenticate($username, $password, $preventRebind = false) { 693 // Prevent null binding 694 if ($username === NULL || $password === NULL) { return false; } 695 if (empty($username) || empty($password)) { return false; } 696 697 // Allow binding over SSO for Kerberos 698 if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) { 699 putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); 700 $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); 701 if (!$this->ldapBind) { 702 throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); 703 } 704 else { 705 return true; 706 } 707 } 708 709 // Bind as the user 710 $ret = true; 711 $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password); 712 if (!$this->ldapBind){ 713 $ret = false; 714 } 715 716 // Cnce we've checked their details, kick back into admin mode if we have it 717 if ($this->adminUsername !== NULL && !$preventRebind) { 718 $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix , $this->adminPassword); 719 if (!$this->ldapBind){ 720 // This should never happen in theory 721 throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); 722 } 723 } 724 725 return $ret; 726 } 727 728 /** 729 * Find the Base DN of your domain controller 730 * 731 * @return string 732 */ 733 public function findBaseDn() 734 { 735 $namingContext = $this->getRootDse(array('defaultnamingcontext')); 736 return $namingContext[0]['defaultnamingcontext'][0]; 737 } 738 739 /** 740 * Get the RootDSE properties from a domain controller 741 * 742 * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext 743 * @return array 744 */ 745 public function getRootDse($attributes = array("*", "+")) { 746 if (!$this->ldapBind){ return (false); } 747 748 $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes); 749 $entries = @ldap_get_entries($this->ldapConnection, $sr); 750 return $entries; 751 } 752 753 /** 754 * Get last error from Active Directory 755 * 756 * This function gets the last message from Active Directory 757 * This may indeed be a 'Success' message but if you get an unknown error 758 * it might be worth calling this function to see what errors were raised 759 * 760 * return string 761 */ 762 public function getLastError() { 763 return @ldap_error($this->ldapConnection); 764 } 765 766 /** 767 * Detect LDAP support in php 768 * 769 * @return bool 770 */ 771 protected function ldapSupported() 772 { 773 if (!function_exists('ldap_connect')) { 774 return false; 775 } 776 return true; 777 } 778 779 /** 780 * Detect ldap_sasl_bind support in PHP 781 * 782 * @return bool 783 */ 784 protected function ldapSaslSupported() 785 { 786 if (!function_exists('ldap_sasl_bind')) { 787 return false; 788 } 789 return true; 790 } 791 792 /** 793 * Schema 794 * 795 * @param array $attributes Attributes to be queried 796 * @return array 797 */ 798 public function adldap_schema($attributes){ 799 800 // LDAP doesn't like NULL attributes, only set them if they have values 801 // If you wish to remove an attribute you should set it to a space 802 // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute 803 $mod=array(); 804 805 // Check every attribute to see if it contains 8bit characters and then UTF8 encode them 806 array_walk($attributes, array($this, 'encode8bit')); 807 808 if ($attributes["address_city"]){ $mod["l"][0]=$attributes["address_city"]; } 809 if ($attributes["address_code"]){ $mod["postalCode"][0]=$attributes["address_code"]; } 810 //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes? 811 if ($attributes["address_country"]){ $mod["c"][0]=$attributes["address_country"]; } 812 if ($attributes["address_pobox"]){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; } 813 if ($attributes["address_state"]){ $mod["st"][0]=$attributes["address_state"]; } 814 if ($attributes["address_street"]){ $mod["streetAddress"][0]=$attributes["address_street"]; } 815 if ($attributes["company"]){ $mod["company"][0]=$attributes["company"]; } 816 if ($attributes["change_password"]){ $mod["pwdLastSet"][0]=0; } 817 if ($attributes["department"]){ $mod["department"][0]=$attributes["department"]; } 818 if ($attributes["description"]){ $mod["description"][0]=$attributes["description"]; } 819 if ($attributes["display_name"]){ $mod["displayName"][0]=$attributes["display_name"]; } 820 if ($attributes["email"]){ $mod["mail"][0]=$attributes["email"]; } 821 if ($attributes["expires"]){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format? 822 if ($attributes["firstname"]){ $mod["givenName"][0]=$attributes["firstname"]; } 823 if ($attributes["home_directory"]){ $mod["homeDirectory"][0]=$attributes["home_directory"]; } 824 if ($attributes["home_drive"]){ $mod["homeDrive"][0]=$attributes["home_drive"]; } 825 if ($attributes["initials"]){ $mod["initials"][0]=$attributes["initials"]; } 826 if ($attributes["logon_name"]){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; } 827 if ($attributes["manager"]){ $mod["manager"][0]=$attributes["manager"]; } //UNTESTED ***Use DistinguishedName*** 828 if ($attributes["office"]){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; } 829 if ($attributes["password"]){ $mod["unicodePwd"][0]=$this->user()->encodePassword($attributes["password"]); } 830 if ($attributes["profile_path"]){ $mod["profilepath"][0]=$attributes["profile_path"]; } 831 if ($attributes["script_path"]){ $mod["scriptPath"][0]=$attributes["script_path"]; } 832 if ($attributes["surname"]){ $mod["sn"][0]=$attributes["surname"]; } 833 if ($attributes["title"]){ $mod["title"][0]=$attributes["title"]; } 834 if ($attributes["telephone"]){ $mod["telephoneNumber"][0]=$attributes["telephone"]; } 835 if ($attributes["mobile"]){ $mod["mobile"][0]=$attributes["mobile"]; } 836 if ($attributes["pager"]){ $mod["pager"][0]=$attributes["pager"]; } 837 if ($attributes["ipphone"]){ $mod["ipphone"][0]=$attributes["ipphone"]; } 838 if ($attributes["web_page"]){ $mod["wWWHomePage"][0]=$attributes["web_page"]; } 839 if ($attributes["fax"]){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; } 840 if ($attributes["enabled"]){ $mod["userAccountControl"][0]=$attributes["enabled"]; } 841 if ($attributes["homephone"]){ $mod["homephone"][0]=$attributes["homephone"]; } 842 843 // Distribution List specific schema 844 if ($attributes["group_sendpermission"]){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; } 845 if ($attributes["group_rejectpermission"]){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; } 846 847 // Exchange Schema 848 if ($attributes["exchange_homemdb"]){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; } 849 if ($attributes["exchange_mailnickname"]){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; } 850 if ($attributes["exchange_proxyaddress"]){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; } 851 if ($attributes["exchange_usedefaults"]){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; } 852 if ($attributes["exchange_policyexclude"]){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; } 853 if ($attributes["exchange_policyinclude"]){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; } 854 if ($attributes["exchange_addressbook"]){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; } 855 if ($attributes["exchange_altrecipient"]){ $mod["altRecipient"][0]=$attributes["exchange_altrecipient"]; } 856 if ($attributes["exchange_deliverandredirect"]){ $mod["deliverAndRedirect"][0]=$attributes["exchange_deliverandredirect"]; } 857 858 // This schema is designed for contacts 859 if ($attributes["exchange_hidefromlists"]){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; } 860 if ($attributes["contact_email"]){ $mod["targetAddress"][0]=$attributes["contact_email"]; } 861 862 //echo ("<pre>"); print_r($mod); 863 /* 864 // modifying a name is a bit fiddly 865 if ($attributes["firstname"] && $attributes["surname"]){ 866 $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"]; 867 $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"]; 868 $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"]; 869 } 870 */ 871 872 if (count($mod)==0){ return (false); } 873 return ($mod); 874 } 875 876 /** 877 * Convert 8bit characters e.g. accented characters to UTF8 encoded characters 878 */ 879 protected function encode8Bit(&$item, $key) { 880 $encode = false; 881 if (is_string($item)) { 882 for ($i=0; $i<strlen($item); $i++) { 883 if (ord($item[$i]) >> 7) { 884 $encode = true; 885 } 886 } 887 } 888 if ($encode === true && $key != 'password') { 889 $item = utf8_encode($item); 890 } 891 } 892 893 /** 894 * Select a random domain controller from your domain controller array 895 * 896 * @return string 897 */ 898 protected function randomController() 899 { 900 mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions 901 /*if (sizeof($this->domainControllers) > 1) { 902 $adController = $this->domainControllers[array_rand($this->domainControllers)]; 903 // Test if the controller is responding to pings 904 $ping = $this->pingController($adController); 905 if ($ping === false) { 906 // Find the current key in the domain controllers array 907 $key = array_search($adController, $this->domainControllers); 908 // Remove it so that we don't end up in a recursive loop 909 unset($this->domainControllers[$key]); 910 // Select a new controller 911 return $this->randomController(); 912 } 913 else { 914 return ($adController); 915 } 916 } */ 917 return $this->domainControllers[array_rand($this->domainControllers)]; 918 } 919 920 /** 921 * Test basic connectivity to controller 922 * 923 * @return bool 924 */ 925 protected function pingController($host) { 926 $port = $this->adPort; 927 fsockopen($host, $port, $errno, $errstr, 10); 928 if ($errno > 0) { 929 return false; 930 } 931 return true; 932 } 933 934} 935 936/** 937* adLDAP Exception Handler 938* 939* Exceptions of this type are thrown on bind failure or when SSL is required but not configured 940* Example: 941* try { 942* $adldap = new adLDAP(); 943* } 944* catch (adLDAPException $e) { 945* echo $e; 946* exit(); 947* } 948*/ 949class adLDAPException extends Exception {} 950