[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> adLDAP.php (source)

   1  <?php
   2  /**
   3   * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY
   4   * Version 3.3.2
   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-2010 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-2010 Scott Barnett, Richard Hyland
  31   * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1
  32   * @revision $Revision: 91 $
  33   * @version 3.3.2
  34   * @link http://adldap.sourceforge.net/
  35   */
  36  
  37  /**
  38   * Define the different types of account in AD
  39   */
  40  define ('ADLDAP_NORMAL_ACCOUNT', 805306368);
  41  define ('ADLDAP_WORKSTATION_TRUST', 805306369);
  42  define ('ADLDAP_INTERDOMAIN_TRUST', 805306370);
  43  define ('ADLDAP_SECURITY_GLOBAL_GROUP', 268435456);
  44  define ('ADLDAP_DISTRIBUTION_GROUP', 268435457);
  45  define ('ADLDAP_SECURITY_LOCAL_GROUP', 536870912);
  46  define ('ADLDAP_DISTRIBUTION_LOCAL_GROUP', 536870913);
  47  define ('ADLDAP_FOLDER', 'OU');
  48  define ('ADLDAP_CONTAINER', 'CN');
  49  
  50  /**
  51  * Main adLDAP class
  52  *
  53  * Can be initialised using $adldap = new adLDAP();
  54  *
  55  * Something to keep in mind is that Active Directory is a permissions
  56  * based directory. If you bind as a domain user, you can't fetch as
  57  * much information on other users as you could as a domain admin.
  58  *
  59  * Before asking questions, please read the Documentation at
  60  * http://adldap.sourceforge.net/wiki/doku.php?id=api
  61  */
  62  class adLDAP {
  63      /**
  64      * The account suffix for your domain, can be set when the class is invoked
  65      *
  66      * @var string
  67      */
  68      protected $_account_suffix = "@mydomain.local";
  69  
  70      /**
  71      * The base dn for your domain
  72      *
  73      * @var string
  74      */
  75      protected $_base_dn = "DC=mydomain,DC=local";
  76  
  77      /**
  78      * Array of domain controllers. Specifiy multiple controllers if you
  79      * would like the class to balance the LDAP queries amongst multiple servers
  80      *
  81      * @var array
  82      */
  83      protected $_domain_controllers = array ("dc01.mydomain.local");
  84  
  85      /**
  86      * Optional account with higher privileges for searching
  87      * This should be set to a domain admin account
  88      *
  89      * @var string
  90      * @var string
  91      */
  92      protected $_ad_username=NULL;
  93      protected $_ad_password=NULL;
  94  
  95      /**
  96      * AD does not return the primary group. http://support.microsoft.com/?kbid=321360
  97      * This tweak will resolve the real primary group.
  98      * Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if
  99      * someone's primary group is NOT domain users, this is obviously going to mess up the results
 100      *
 101      * @var bool
 102      */
 103      protected $_real_primarygroup=true;
 104  
 105      /**
 106      * Use SSL (LDAPS), your server needs to be setup, please see
 107      * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl
 108      *
 109      * @var bool
 110      */
 111      protected $_use_ssl=false;
 112  
 113      /**
 114      * Use TLS
 115      * If you wish to use TLS you should ensure that $_use_ssl is set to false and vice-versa
 116      *
 117      * @var bool
 118      */
 119      protected $_use_tls=false;
 120  
 121      /**
 122      * When querying group memberships, do it recursively
 123      * eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C
 124      * user_ingroup("Fred","C") will returns true with this option turned on, false if turned off
 125      *
 126      * @var bool
 127      */
 128      protected $_recursive_groups=true;
 129  
 130      // You should not need to edit anything below this line
 131      //******************************************************************************************
 132  
 133      /**
 134      * Connection and bind default variables
 135      *
 136      * @var mixed
 137      * @var mixed
 138      */
 139      protected $_conn;
 140      protected $_bind;
 141  
 142      /**
 143      * Getters and Setters
 144      */
 145  
 146      /**
 147      * Set the account suffix
 148      *
 149      * @param string $_account_suffix
 150      * @return void
 151      */
 152      public function set_account_suffix($_account_suffix)
 153      {
 154            $this->_account_suffix = $_account_suffix;
 155      }
 156  
 157      /**
 158      * Get the account suffix
 159      *
 160      * @return string
 161      */
 162      public function get_account_suffix()
 163      {
 164            return $this->_account_suffix;
 165      }
 166  
 167      /**
 168      * Set the domain controllers array
 169      *
 170      * @param array $_domain_controllers
 171      * @return void
 172      */
 173      public function set_domain_controllers(array $_domain_controllers)
 174      {
 175            $this->_domain_controllers = $_domain_controllers;
 176      }
 177  
 178      /**
 179      * Get the list of domain controllers
 180      *
 181      * @return void
 182      */
 183      public function get_domain_controllers()
 184      {
 185            return $this->_domain_controllers;
 186      }
 187  
 188      /**
 189      * Set the username of an account with higher priviledges
 190      *
 191      * @param string $_ad_username
 192      * @return void
 193      */
 194      public function set_ad_username($_ad_username)
 195      {
 196            $this->_ad_username = $_ad_username;
 197      }
 198  
 199      /**
 200      * Get the username of the account with higher priviledges
 201      *
 202      * This will throw an exception for security reasons
 203      */
 204      public function get_ad_username()
 205      {
 206            throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
 207      }
 208  
 209      /**
 210      * Set the password of an account with higher priviledges
 211      *
 212      * @param string $_ad_password
 213      * @return void
 214      */
 215      public function set_ad_password($_ad_password)
 216      {
 217            $this->_ad_password = $_ad_password;
 218      }
 219  
 220      /**
 221      * Get the password of the account with higher priviledges
 222      *
 223      * This will throw an exception for security reasons
 224      */
 225      public function get_ad_password()
 226      {
 227            throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
 228      }
 229  
 230      /**
 231      * Set whether to detect the true primary group
 232      *
 233      * @param bool $_real_primary_group
 234      * @return void
 235      */
 236      public function set_real_primarygroup($_real_primarygroup)
 237      {
 238            $this->_real_primarygroup = $_real_primarygroup;
 239      }
 240  
 241      /**
 242      * Get the real primary group setting
 243      *
 244      * @return bool
 245      */
 246      public function get_real_primarygroup()
 247      {
 248            return $this->_real_primarygroup;
 249      }
 250  
 251      /**
 252      * Set whether to use SSL
 253      *
 254      * @param bool $_use_ssl
 255      * @return void
 256      */
 257      public function set_use_ssl($_use_ssl)
 258      {
 259            $this->_use_ssl = $_use_ssl;
 260      }
 261  
 262      /**
 263      * Get the SSL setting
 264      *
 265      * @return bool
 266      */
 267      public function get_use_ssl()
 268      {
 269            return $this->_use_ssl;
 270      }
 271  
 272      /**
 273      * Set whether to use TLS
 274      *
 275      * @param bool $_use_tls
 276      * @return void
 277      */
 278      public function set_use_tls($_use_tls)
 279      {
 280            $this->_use_tls = $_use_tls;
 281      }
 282  
 283      /**
 284      * Get the TLS setting
 285      *
 286      * @return bool
 287      */
 288      public function get_use_tls()
 289      {
 290            return $this->_use_tls;
 291      }
 292  
 293      /**
 294      * Set whether to lookup recursive groups
 295      *
 296      * @param bool $_recursive_groups
 297      * @return void
 298      */
 299      public function set_recursive_groups($_recursive_groups)
 300      {
 301            $this->_recursive_groups = $_recursive_groups;
 302      }
 303  
 304      /**
 305      * Get the recursive groups setting
 306      *
 307      * @return bool
 308      */
 309      public function get_recursive_groups()
 310      {
 311            return $this->_recursive_groups;
 312      }
 313  
 314      /**
 315      * Default Constructor
 316      *
 317      * Tries to bind to the AD domain over LDAP or LDAPs
 318      *
 319      * @param array $options Array of options to pass to the constructor
 320      * @throws Exception - if unable to bind to Domain Controller
 321      * @return bool
 322      */
 323      function __construct($options=array()){
 324          // You can specifically overide any of the default configuration options setup above
 325          if (count($options)>0){
 326              if (array_key_exists("account_suffix",$options)){ $this->_account_suffix=$options["account_suffix"]; }
 327              if (array_key_exists("base_dn",$options)){ $this->_base_dn=$options["base_dn"]; }
 328              if (array_key_exists("domain_controllers",$options)){ $this->_domain_controllers=$options["domain_controllers"]; }
 329              if (array_key_exists("ad_username",$options)){ $this->_ad_username=$options["ad_username"]; }
 330              if (array_key_exists("ad_password",$options)){ $this->_ad_password=$options["ad_password"]; }
 331              if (array_key_exists("real_primarygroup",$options)){ $this->_real_primarygroup=$options["real_primarygroup"]; }
 332              if (array_key_exists("use_ssl",$options)){ $this->_use_ssl=$options["use_ssl"]; }
 333              if (array_key_exists("use_tls",$options)){ $this->_use_tls=$options["use_tls"]; }
 334              if (array_key_exists("recursive_groups",$options)){ $this->_recursive_groups=$options["recursive_groups"]; }
 335          }
 336  
 337          if ($this->ldap_supported() === false) {
 338              throw new adLDAPException('No LDAP support for PHP.  See: http://www.php.net/ldap');
 339          }
 340  
 341          return $this->connect();
 342      }
 343  
 344      /**
 345      * Default Destructor
 346      *
 347      * Closes the LDAP connection
 348      *
 349      * @return void
 350      */
 351      function __destruct(){ $this->close(); }
 352  
 353      /**
 354      * Connects and Binds to the Domain Controller
 355      *
 356      * @return bool
 357      */
 358      public function connect() {
 359          // Connect to the AD/LDAP server as the username/password
 360          $dc=$this->random_controller();
 361          if ($this->_use_ssl){
 362              $this->_conn = ldap_connect("ldaps://".$dc, 636);
 363          } else {
 364              $this->_conn = ldap_connect($dc);
 365          }
 366  
 367          // Set some ldap options for talking to AD
 368          ldap_set_option($this->_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
 369          ldap_set_option($this->_conn, LDAP_OPT_REFERRALS, 0);
 370  
 371          if ($this->_use_tls) {
 372              ldap_start_tls($this->_conn);
 373          }
 374  
 375          // Bind as a domain admin if they've set it up
 376          if ($this->_ad_username!=NULL && $this->_ad_password!=NULL){
 377              $this->_bind = @ldap_bind($this->_conn,$this->_ad_username.$this->_account_suffix,$this->_ad_password);
 378              if (!$this->_bind){
 379                  if ($this->_use_ssl && !$this->_use_tls){
 380                      // If you have problems troubleshooting, remove the @ character from the ldap_bind command above to get the actual error message
 381                      throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->get_last_error());
 382                  } else {
 383                      throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->get_last_error());
 384                  }
 385              }
 386          }
 387  
 388          if ($this->_base_dn == NULL) {
 389              $this->_base_dn = $this->find_base_dn();
 390          }
 391  
 392          return (true);
 393      }
 394  
 395      /**
 396      * Closes the LDAP connection
 397      *
 398      * @return void
 399      */
 400      public function close() {
 401          ldap_close ($this->_conn);
 402      }
 403  
 404      /**
 405      * Validate a user's login credentials
 406      *
 407      * @param string $username A user's AD username
 408      * @param string $password A user's AD password
 409      * @param bool optional $prevent_rebind
 410      * @return bool
 411      */
 412      public function authenticate($username, $password, $prevent_rebind = false) {
 413          // Prevent null binding
 414          if ($username === NULL || $password === NULL) { return false; }
 415          if (empty($username) || empty($password)) { return false; }
 416  
 417          // Bind as the user
 418          $ret = true;
 419          $this->_bind = @ldap_bind($this->_conn, $username . $this->_account_suffix, $password);
 420          if (!$this->_bind){ $ret = false; }
 421  
 422          // Cnce we've checked their details, kick back into admin mode if we have it
 423          if ($this->_ad_username !== NULL && !$prevent_rebind) {
 424              $this->_bind = @ldap_bind($this->_conn, $this->_ad_username . $this->_account_suffix , $this->_ad_password);
 425              if (!$this->_bind){
 426                  // This should never happen in theory
 427                  throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->get_last_error());
 428              }
 429          }
 430  
 431          return $ret;
 432      }
 433  
 434      //*****************************************************************************************************************
 435      // GROUP FUNCTIONS
 436  
 437      /**
 438      * Add a group to a group
 439      *
 440      * @param string $parent The parent group name
 441      * @param string $child The child group name
 442      * @return bool
 443      */
 444      public function group_add_group($parent,$child){
 445  
 446          // Find the parent group's dn
 447          $parent_group=$this->group_info($parent,array("cn"));
 448          if ($parent_group[0]["dn"]===NULL){ return (false); }
 449          $parent_dn=$parent_group[0]["dn"];
 450  
 451          // Find the child group's dn
 452          $child_group=$this->group_info($child,array("cn"));
 453          if ($child_group[0]["dn"]===NULL){ return (false); }
 454          $child_dn=$child_group[0]["dn"];
 455  
 456          $add=array();
 457          $add["member"] = $child_dn;
 458  
 459          $result=@ldap_mod_add($this->_conn,$parent_dn,$add);
 460          if ($result==false){ return (false); }
 461          return (true);
 462      }
 463  
 464      /**
 465      * Add a user to a group
 466      *
 467      * @param string $group The group to add the user to
 468      * @param string $user The user to add to the group
 469      * @param bool $isGUID Is the username passed a GUID or a samAccountName
 470      * @return bool
 471      */
 472      public function group_add_user($group,$user,$isGUID=false){
 473          // Adding a user is a bit fiddly, we need to get the full DN of the user
 474          // and add it using the full DN of the group
 475  
 476          // Find the user's dn
 477          $user_dn=$this->user_dn($user,$isGUID);
 478          if ($user_dn===false){ return (false); }
 479  
 480          // Find the group's dn
 481          $group_info=$this->group_info($group,array("cn"));
 482          if ($group_info[0]["dn"]===NULL){ return (false); }
 483          $group_dn=$group_info[0]["dn"];
 484  
 485          $add=array();
 486          $add["member"] = $user_dn;
 487  
 488          $result=@ldap_mod_add($this->_conn,$group_dn,$add);
 489          if ($result==false){ return (false); }
 490          return (true);
 491      }
 492  
 493      /**
 494      * Add a contact to a group
 495      *
 496      * @param string $group The group to add the contact to
 497      * @param string $contact_dn The DN of the contact to add
 498      * @return bool
 499      */
 500      public function group_add_contact($group,$contact_dn){
 501          // To add a contact we take the contact's DN
 502          // and add it using the full DN of the group
 503  
 504          // Find the group's dn
 505          $group_info=$this->group_info($group,array("cn"));
 506          if ($group_info[0]["dn"]===NULL){ return (false); }
 507          $group_dn=$group_info[0]["dn"];
 508  
 509          $add=array();
 510          $add["member"] = $contact_dn;
 511  
 512          $result=@ldap_mod_add($this->_conn,$group_dn,$add);
 513          if ($result==false){ return (false); }
 514          return (true);
 515      }
 516  
 517      /**
 518      * Create a group
 519      *
 520      * @param array $attributes Default attributes of the group
 521      * @return bool
 522      */
 523      public function group_create($attributes){
 524          if (!is_array($attributes)){ return ("Attributes must be an array"); }
 525          if (!array_key_exists("group_name",$attributes)){ return ("Missing compulsory field [group_name]"); }
 526          if (!array_key_exists("container",$attributes)){ return ("Missing compulsory field [container]"); }
 527          if (!array_key_exists("description",$attributes)){ return ("Missing compulsory field [description]"); }
 528          if (!is_array($attributes["container"])){ return ("Container attribute must be an array."); }
 529          $attributes["container"]=array_reverse($attributes["container"]);
 530  
 531          //$member_array = array();
 532          //$member_array[0] = "cn=user1,cn=Users,dc=yourdomain,dc=com";
 533          //$member_array[1] = "cn=administrator,cn=Users,dc=yourdomain,dc=com";
 534  
 535          $add=array();
 536          $add["cn"] = $attributes["group_name"];
 537          $add["samaccountname"] = $attributes["group_name"];
 538          $add["objectClass"] = "Group";
 539          $add["description"] = $attributes["description"];
 540          //$add["member"] = $member_array; UNTESTED
 541  
 542          $container="OU=".implode(",OU=",$attributes["container"]);
 543          $result=ldap_add($this->_conn,"CN=".$add["cn"].", ".$container.",".$this->_base_dn,$add);
 544          if ($result!=true){ return (false); }
 545  
 546          return (true);
 547      }
 548  
 549      /**
 550      * Remove a group from a group
 551      *
 552      * @param string $parent The parent group name
 553      * @param string $child The child group name
 554      * @return bool
 555      */
 556      public function group_del_group($parent,$child){
 557  
 558          // Find the parent dn
 559          $parent_group=$this->group_info($parent,array("cn"));
 560          if ($parent_group[0]["dn"]===NULL){ return (false); }
 561          $parent_dn=$parent_group[0]["dn"];
 562  
 563          // Find the child dn
 564          $child_group=$this->group_info($child,array("cn"));
 565          if ($child_group[0]["dn"]===NULL){ return (false); }
 566          $child_dn=$child_group[0]["dn"];
 567  
 568          $del=array();
 569          $del["member"] = $child_dn;
 570  
 571          $result=@ldap_mod_del($this->_conn,$parent_dn,$del);
 572          if ($result==false){ return (false); }
 573          return (true);
 574      }
 575  
 576      /**
 577      * Remove a user from a group
 578      *
 579      * @param string $group The group to remove a user from
 580      * @param string $user The AD user to remove from the group
 581      * @param bool $isGUID Is the username passed a GUID or a samAccountName
 582      * @return bool
 583      */
 584      public function group_del_user($group,$user,$isGUID=false){
 585  
 586          // Find the parent dn
 587          $group_info=$this->group_info($group,array("cn"));
 588          if ($group_info[0]["dn"]===NULL){ return (false); }
 589          $group_dn=$group_info[0]["dn"];
 590  
 591          // Find the users dn
 592          $user_dn=$this->user_dn($user,$isGUID);
 593          if ($user_dn===false){ return (false); }
 594  
 595          $del=array();
 596          $del["member"] = $user_dn;
 597  
 598          $result=@ldap_mod_del($this->_conn,$group_dn,$del);
 599          if ($result==false){ return (false); }
 600          return (true);
 601      }
 602  
 603      /**
 604      * Remove a contact from a group
 605      *
 606      * @param string $group The group to remove a user from
 607      * @param string $contact_dn The DN of a contact to remove from the group
 608      * @return bool
 609      */
 610      public function group_del_contact($group,$contact_dn){
 611  
 612          // Find the parent dn
 613          $group_info=$this->group_info($group,array("cn"));
 614          if ($group_info[0]["dn"]===NULL){ return (false); }
 615          $group_dn=$group_info[0]["dn"];
 616  
 617          $del=array();
 618          $del["member"] = $contact_dn;
 619  
 620          $result=@ldap_mod_del($this->_conn,$group_dn,$del);
 621          if ($result==false){ return (false); }
 622          return (true);
 623      }
 624  
 625      /**
 626      * Return a list of groups in a group
 627      *
 628      * @param string $group The group to query
 629      * @param bool $recursive Recursively get groups
 630      * @return array
 631      */
 632      public function groups_in_group($group, $recursive = NULL){
 633          if (!$this->_bind){ return (false); }
 634          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } // Use the default option if they haven't set it
 635  
 636          // Search the directory for the members of a group
 637          $info=$this->group_info($group,array("member","cn"));
 638          $groups=$info[0]["member"];
 639          if (!is_array($groups)) {
 640              return (false);
 641          }
 642  
 643          $group_array=array();
 644  
 645          for ($i=0; $i<$groups["count"]; $i++){
 646               $filter="(&(objectCategory=group)(distinguishedName=".$this->ldap_slashes($groups[$i])."))";
 647               $fields = array("samaccountname", "distinguishedname", "objectClass");
 648               $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 649               $entries = ldap_get_entries($this->_conn, $sr);
 650  
 651               // not a person, look for a group
 652               if ($entries['count'] == 0 && $recursive == true) {
 653                  $filter="(&(objectCategory=group)(distinguishedName=".$this->ldap_slashes($groups[$i])."))";
 654                  $fields = array("distinguishedname");
 655                  $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 656                  $entries = ldap_get_entries($this->_conn, $sr);
 657                  if (!isset($entries[0]['distinguishedname'][0])) {
 658                      continue;
 659                  }
 660                  $sub_groups = $this->groups_in_group($entries[0]['distinguishedname'][0], $recursive);
 661                  if (is_array($sub_groups)) {
 662                      $group_array = array_merge($group_array, $sub_groups);
 663                      $group_array = array_unique($group_array);
 664                  }
 665                  continue;
 666               }
 667  
 668               $group_array[] = $entries[0]['distinguishedname'][0];
 669          }
 670          return ($group_array);
 671      }
 672  
 673      /**
 674      * Return a list of members in a group
 675      *
 676      * @param string $group The group to query
 677      * @param bool $recursive Recursively get group members
 678      * @return array
 679      */
 680      public function group_members($group, $recursive = NULL){
 681          if (!$this->_bind){ return (false); }
 682          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } // Use the default option if they haven't set it
 683          // Search the directory for the members of a group
 684          $info=$this->group_info($group,array("member","cn"));
 685          $users=$info[0]["member"];
 686          if (!is_array($users)) {
 687              return (false);
 688          }
 689  
 690          $user_array=array();
 691  
 692          for ($i=0; $i<$users["count"]; $i++){
 693               $filter="(&(objectCategory=person)(distinguishedName=".$this->ldap_slashes($users[$i])."))";
 694               $fields = array("samaccountname", "distinguishedname", "objectClass");
 695               $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 696               $entries = ldap_get_entries($this->_conn, $sr);
 697  
 698               // not a person, look for a group
 699               if ($entries['count'] == 0 && $recursive == true) {
 700                  $filter="(&(objectCategory=group)(distinguishedName=".$this->ldap_slashes($users[$i])."))";
 701                  $fields = array("samaccountname");
 702                  $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 703                  $entries = ldap_get_entries($this->_conn, $sr);
 704                  if (!isset($entries[0]['samaccountname'][0])) {
 705                      continue;
 706                  }
 707                  $sub_users = $this->group_members($entries[0]['samaccountname'][0], $recursive);
 708                  if (is_array($sub_users)) {
 709                      $user_array = array_merge($user_array, $sub_users);
 710                      $user_array = array_unique($user_array);
 711                  }
 712                  continue;
 713               }
 714  
 715               if ($entries[0]['samaccountname'][0] === NULL && $entries[0]['distinguishedname'][0] !== NULL) {
 716                   $user_array[] = $entries[0]['distinguishedname'][0];
 717               }
 718               elseif ($entries[0]['samaccountname'][0] !== NULL) {
 719                  $user_array[] = $entries[0]['samaccountname'][0];
 720               }
 721          }
 722          return ($user_array);
 723      }
 724  
 725      /**
 726      * Group Information.  Returns an array of information about a group.
 727      * The group name is case sensitive
 728      *
 729      * @param string $group_name The group name to retrieve info about
 730      * @param array $fields Fields to retrieve
 731      * @return array
 732      */
 733      public function group_info($group_name,$fields=NULL){
 734          if ($group_name===NULL){ return (false); }
 735          if (!$this->_bind){ return (false); }
 736  
 737          if (stristr($group_name, '+')) {
 738              $group_name=stripslashes($group_name);
 739          }
 740  
 741          $filter="(&(objectCategory=group)(name=".$this->ldap_slashes($group_name)."))";
 742          //echo ($filter."!!!<br>");
 743          if ($fields===NULL){ $fields=array("member","memberof","cn","description","distinguishedname","objectcategory","samaccountname"); }
 744          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 745          $entries = ldap_get_entries($this->_conn, $sr);
 746          //print_r($entries);
 747          return ($entries);
 748      }
 749  
 750      /**
 751      * Return a complete list of "groups in groups"
 752      *
 753      * @param string $group The group to get the list from
 754      * @return array
 755      */
 756      public function recursive_groups($group){
 757          if ($group===NULL){ return (false); }
 758  
 759          $ret_groups=array();
 760  
 761          $groups=$this->group_info($group,array("memberof"));
 762          if (isset($groups[0]["memberof"]) && is_array($groups[0]["memberof"])) {
 763              $groups=$groups[0]["memberof"];
 764  
 765              if ($groups){
 766                  $group_names=$this->nice_names($groups);
 767                  $ret_groups=array_merge($ret_groups,$group_names); //final groups to return
 768  
 769                  foreach ($group_names as $id => $group_name){
 770                      $child_groups=$this->recursive_groups($group_name);
 771                      $ret_groups=array_merge($ret_groups,$child_groups);
 772                  }
 773              }
 774          }
 775  
 776          return ($ret_groups);
 777      }
 778  
 779      /**
 780      * Returns a complete list of the groups in AD based on a SAM Account Type
 781      *
 782      * @param string $samaccounttype The account type to return
 783      * @param bool $include_desc Whether to return a description
 784      * @param string $search Search parameters
 785      * @param bool $sorted Whether to sort the results
 786      * @return array
 787      */
 788      public function search_groups($samaccounttype = ADLDAP_SECURITY_GLOBAL_GROUP, $include_desc = false, $search = "*", $sorted = true) {
 789          if (!$this->_bind){ return (false); }
 790  
 791          $filter = '(&(objectCategory=group)';
 792          if ($samaccounttype !== null) {
 793              $filter .= '(samaccounttype='. $samaccounttype .')';
 794          }
 795          $filter .= '(cn='.$search.'))';
 796          // Perform the search and grab all their details
 797          $fields=array("samaccountname","description");
 798          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 799          $entries = ldap_get_entries($this->_conn, $sr);
 800  
 801          $groups_array = array();
 802          for ($i=0; $i<$entries["count"]; $i++){
 803              if ($include_desc && strlen($entries[$i]["description"][0]) > 0 ){
 804                  $groups_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["description"][0];
 805              } elseif ($include_desc){
 806                  $groups_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["samaccountname"][0];
 807              } else {
 808                  array_push($groups_array, $entries[$i]["samaccountname"][0]);
 809              }
 810          }
 811          if( $sorted ){ asort($groups_array); }
 812          return ($groups_array);
 813      }
 814  
 815      /**
 816      * Returns a complete list of all groups in AD
 817      *
 818      * @param bool $include_desc Whether to return a description
 819      * @param string $search Search parameters
 820      * @param bool $sorted Whether to sort the results
 821      * @return array
 822      */
 823      public function all_groups($include_desc = false, $search = "*", $sorted = true){
 824          $groups_array = $this->search_groups(null, $include_desc, $search, $sorted);
 825          return ($groups_array);
 826      }
 827  
 828      /**
 829      * Returns a complete list of security groups in AD
 830      *
 831      * @param bool $include_desc Whether to return a description
 832      * @param string $search Search parameters
 833      * @param bool $sorted Whether to sort the results
 834      * @return array
 835      */
 836      public function all_security_groups($include_desc = false, $search = "*", $sorted = true){
 837          $groups_array = $this->search_groups(ADLDAP_SECURITY_GLOBAL_GROUP, $include_desc, $search, $sorted);
 838          return ($groups_array);
 839      }
 840  
 841      /**
 842      * Returns a complete list of distribution lists in AD
 843      *
 844      * @param bool $include_desc Whether to return a description
 845      * @param string $search Search parameters
 846      * @param bool $sorted Whether to sort the results
 847      * @return array
 848      */
 849      public function all_distribution_groups($include_desc = false, $search = "*", $sorted = true){
 850          $groups_array = $this->search_groups(ADLDAP_DISTRIBUTION_GROUP, $include_desc, $search, $sorted);
 851          return ($groups_array);
 852      }
 853  
 854      //*****************************************************************************************************************
 855      // USER FUNCTIONS
 856  
 857      /**
 858      * Create a user
 859      *
 860      * If you specify a password here, this can only be performed over SSL
 861      *
 862      * @param array $attributes The attributes to set to the user account
 863      * @return bool
 864      */
 865          public function user_create($attributes){
 866          // Check for compulsory fields
 867          if (!array_key_exists("username",$attributes)){ return ("Missing compulsory field [username]"); }
 868          if (!array_key_exists("firstname",$attributes)){ return ("Missing compulsory field [firstname]"); }
 869          if (!array_key_exists("surname",$attributes)){ return ("Missing compulsory field [surname]"); }
 870          if (!array_key_exists("email",$attributes)){ return ("Missing compulsory field [email]"); }
 871          if (!array_key_exists("container",$attributes)){ return ("Missing compulsory field [container]"); }
 872          if (!is_array($attributes["container"])){ return ("Container attribute must be an array."); }
 873  
 874          if (array_key_exists("password",$attributes) && (!$this->_use_ssl && !$this->_use_tls)){
 875              throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
 876          }
 877  
 878          if (!array_key_exists("display_name",$attributes)){ $attributes["display_name"]=$attributes["firstname"]." ".$attributes["surname"]; }
 879  
 880          // Translate the schema
 881          $add=$this->adldap_schema($attributes);
 882  
 883          // Additional stuff only used for adding accounts
 884          $add["cn"][0]=$attributes["display_name"];
 885          $add["samaccountname"][0]=$attributes["username"];
 886          $add["objectclass"][0]="top";
 887          $add["objectclass"][1]="person";
 888          $add["objectclass"][2]="organizationalPerson";
 889          $add["objectclass"][3]="user"; //person?
 890          //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
 891  
 892          // Set the account control attribute
 893          $control_options=array("NORMAL_ACCOUNT");
 894          if (!$attributes["enabled"]){ $control_options[]="ACCOUNTDISABLE"; }
 895          $add["userAccountControl"][0]=$this->account_control($control_options);
 896          //echo ("<pre>"); print_r($add);
 897  
 898          // Determine the container
 899          $attributes["container"]=array_reverse($attributes["container"]);
 900          $container="OU=".implode(",OU=",$attributes["container"]);
 901  
 902          // Add the entry
 903          $result=@ldap_add($this->_conn, "CN=".$add["cn"][0].", ".$container.",".$this->_base_dn, $add);
 904          if ($result!=true){ return (false); }
 905  
 906          return (true);
 907      }
 908  
 909      /**
 910      * Delete a user account
 911      *
 912      * @param string $username The username to delete (please be careful here!)
 913      * @param bool $isGUID Is the username a GUID or a samAccountName
 914      * @return array
 915      */
 916      public function user_delete($username,$isGUID=false) {
 917          $userinfo = $this->user_info($username, array("*"),$isGUID);
 918          $dn = $userinfo[0]['distinguishedname'][0];
 919          $result=$this->dn_delete($dn);
 920          if ($result!=true){ return (false); }
 921          return (true);
 922      }
 923  
 924      /**
 925      * Groups the user is a member of
 926      *
 927      * @param string $username The username to query
 928      * @param bool $recursive Recursive list of groups
 929      * @param bool $isGUID Is the username passed a GUID or a samAccountName
 930      * @return array
 931      */
 932      public function user_groups($username,$recursive=NULL,$isGUID=false){
 933          if ($username===NULL){ return (false); }
 934          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } // Use the default option if they haven't set it
 935          if (!$this->_bind){ return (false); }
 936  
 937          // Search the directory for their information
 938          $info=@$this->user_info($username,array("memberof","primarygroupid"),$isGUID);
 939          $groups=$this->nice_names($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames)
 940  
 941          if ($recursive === true){
 942              foreach ($groups as $id => $group_name){
 943                  $extra_groups=$this->recursive_groups($group_name);
 944                  $groups=array_merge($groups,$extra_groups);
 945              }
 946          }
 947  
 948          return ($groups);
 949      }
 950  
 951      /**
 952      * Find information about the users
 953      *
 954      * @param string $username The username to query
 955      * @param array $fields Array of parameters to query
 956      * @param bool $isGUID Is the username passed a GUID or a samAccountName
 957      * @return array
 958      */
 959      public function user_info($username,$fields=NULL,$isGUID=false){
 960          if ($username===NULL){ return (false); }
 961          if (!$this->_bind){ return (false); }
 962  
 963          if ($isGUID === true) {
 964              $username = $this->strguid2hex($username);
 965              $filter="objectguid=".$username;
 966          }
 967          else if (strstr($username, "@")) {
 968               $filter="userPrincipalName=".$username;
 969          }
 970          else {
 971               $filter="samaccountname=".$username;
 972          }
 973          $filter = "(&(objectCategory=person)({$filter}))";
 974          if ($fields===NULL){ $fields=array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid"); }
 975          if (!in_array("objectsid",$fields)){
 976              $fields[] = "objectsid";
 977          }
 978          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
 979          $entries = ldap_get_entries($this->_conn, $sr);
 980  
 981          if (isset($entries[0])) {
 982              if ($entries[0]['count'] >= 1) {
 983                  if (in_array("memberof", $fields)) {
 984                      // AD does not return the primary group in the ldap query, we may need to fudge it
 985                      if ($this->_real_primarygroup && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0])){
 986                          //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
 987                          $entries[0]["memberof"][]=$this->get_primary_group($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
 988                      } else {
 989                          $entries[0]["memberof"][]="CN=Domain Users,CN=Users,".$this->_base_dn;
 990                      }
 991                      $entries[0]["memberof"]["count"]++;
 992                  }
 993              }
 994              return $entries;
 995          }
 996          return false;
 997      }
 998  
 999      /**
1000      * Determine if a user is in a specific group
1001      *
1002      * @param string $username The username to query
1003      * @param string $group The name of the group to check against
1004      * @param bool $recursive Check groups recursively
1005      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1006      * @return bool
1007      */
1008      public function user_ingroup($username,$group,$recursive=NULL,$isGUID=false){
1009          if ($username===NULL){ return (false); }
1010          if ($group===NULL){ return (false); }
1011          if (!$this->_bind){ return (false); }
1012          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } // Use the default option if they haven't set it
1013  
1014          // Get a list of the groups
1015          $groups=$this->user_groups($username,$recursive,$isGUID);
1016  
1017          // Return true if the specified group is in the group list
1018          if (in_array($group,$groups)){ return (true); }
1019  
1020          return (false);
1021      }
1022  
1023      /**
1024       * Return info about the domain itself
1025       *
1026       * @authot Andreas Gohr <gohr@cosmocode.de>
1027       * @param array $fields The fields to query
1028       * @return array
1029       */
1030      public function domain_info($fields){
1031          if (!$this->_bind){ return (false); }
1032  
1033          $sr = ldap_read($this->_conn, $this->_base_dn, 'objectclass=*', $fields);
1034          if (!$sr) {
1035              return false;
1036          }
1037          $info = ldap_get_entries($this->_conn, $sr);
1038          if(count($info)) return $info[0];
1039  
1040          return false;
1041      }
1042  
1043      /**
1044      * Determine a user's password expiry date
1045      *
1046      * @param string $username The username to query
1047      * @param book $isGUID Is the username passed a GUID or a samAccountName
1048      * @requires bcmath http://www.php.net/manual/en/book.bc.php
1049      * @return array
1050      */
1051      public function user_password_expiry($username,$isGUID=false) {
1052          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1053          if (!$this->_bind){ return (false); }
1054          if (!function_exists('bcmod')) { return ("Missing function support [bcmod] http://www.php.net/manual/en/book.bc.php"); };
1055  
1056          $userinfo = $this->user_info($username, array("pwdlastset", "useraccountcontrol"), $isGUID);
1057          $pwdlastset = $userinfo[0]['pwdlastset'][0];
1058          $status = array();
1059  
1060          if ($userinfo[0]['useraccountcontrol'][0] == '66048') {
1061              // Password does not expire
1062              return "Does not expire";
1063          }
1064          if ($pwdlastset === '0') {
1065              // Password has already expired
1066              return "Password has expired";
1067          }
1068  
1069           // Password expiry in AD can be calculated from TWO values:
1070           //   - User's own pwdLastSet attribute: stores the last time the password was changed
1071           //   - Domain's maxPwdAge attribute: how long passwords last in the domain
1072           //
1073           // Although Microsoft chose to use a different base and unit for time measurements.
1074           // This function will convert them to Unix timestamps
1075           $sr = ldap_read($this->_conn, $this->_base_dn, 'objectclass=*', array('maxPwdAge'));
1076           if (!$sr) {
1077               return false;
1078           }
1079           $info = ldap_get_entries($this->_conn, $sr);
1080           $maxpwdage = $info[0]['maxpwdage'][0];
1081  
1082  
1083           // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx
1084           //
1085           // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC),
1086           // stored in a 64 bit integer.
1087           //
1088           // The number of seconds between this date and Unix epoch is 11644473600.
1089           //
1090           // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond
1091           // intervals from the time the password was set before the password expires.
1092           //
1093           // We also need to scale this to seconds but also this value is a _negative_ quantity!
1094           //
1095           // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire
1096           //
1097           // Unfortunately the maths involved are too big for PHP integers, so I've had to require
1098           // BCMath functions to work with arbitrary precision numbers.
1099           if (bcmod($maxpwdage, 4294967296) === '0') {
1100              return "Domain does not expire passwords";
1101          }
1102  
1103          // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's
1104          // time units.  Because maxpwd age is negative we need to subtract it.
1105          $pwdexpire = bcsub($pwdlastset, $maxpwdage);
1106  
1107          // Convert MS's time to Unix time
1108          $status['expiryts'] = bcsub(bcdiv($pwdexpire, '10000000'), '11644473600');
1109          $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdexpire, '10000000'), '11644473600'));
1110  
1111          return $status;
1112      }
1113  
1114      /**
1115      * Modify a user
1116      *
1117      * @param string $username The username to query
1118      * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
1119      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1120      * @return bool
1121      */
1122      public function user_modify($username,$attributes,$isGUID=false){
1123          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1124          if (array_key_exists("password",$attributes) && !$this->_use_ssl){
1125              throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
1126          }
1127  
1128          // Find the dn of the user
1129          $user_dn=$this->user_dn($username,$isGUID);
1130          if ($user_dn===false){ return (false); }
1131  
1132          // Translate the update to the LDAP schema
1133          $mod=$this->adldap_schema($attributes);
1134  
1135          // Check to see if this is an enabled status update
1136          if (!$mod && !array_key_exists("enabled", $attributes)){ return (false); }
1137  
1138          // Set the account control attribute (only if specified)
1139          if (array_key_exists("enabled",$attributes)){
1140              if ($attributes["enabled"]){ $control_options=array("NORMAL_ACCOUNT"); }
1141              else { $control_options=array("NORMAL_ACCOUNT","ACCOUNTDISABLE"); }
1142              $mod["userAccountControl"][0]=$this->account_control($control_options);
1143          }
1144  
1145          // Do the update
1146          $result=@ldap_modify($this->_conn,$user_dn,$mod);
1147          if ($result==false){ return (false); }
1148  
1149          return (true);
1150      }
1151  
1152      /**
1153      * Disable a user account
1154      *
1155      * @param string $username The username to disable
1156      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1157      * @return bool
1158      */
1159      public function user_disable($username,$isGUID=false){
1160          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1161          $attributes=array("enabled"=>0);
1162          $result = $this->user_modify($username, $attributes, $isGUID);
1163          if ($result==false){ return (false); }
1164  
1165          return (true);
1166      }
1167  
1168      /**
1169      * Enable a user account
1170      *
1171      * @param string $username The username to enable
1172      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1173      * @return bool
1174      */
1175      public function user_enable($username,$isGUID=false){
1176          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1177          $attributes=array("enabled"=>1);
1178          $result = $this->user_modify($username, $attributes, $isGUID);
1179          if ($result==false){ return (false); }
1180  
1181          return (true);
1182      }
1183  
1184      /**
1185      * Set the password of a user - This must be performed over SSL
1186      *
1187      * @param string $username The username to modify
1188      * @param string $password The new password
1189      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1190      * @return bool
1191      */
1192      public function user_password($username,$password,$isGUID=false){
1193          if ($username===NULL){ return (false); }
1194          if ($password===NULL){ return (false); }
1195          if (!$this->_bind){ return (false); }
1196          if (!$this->_use_ssl && !$this->_use_tls){
1197              throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
1198          }
1199  
1200          $user_dn=$this->user_dn($username,$isGUID);
1201          if ($user_dn===false){ return (false); }
1202  
1203          $add=array();
1204          $add["unicodePwd"][0]=$this->encode_password($password);
1205  
1206          $result=@ldap_mod_replace($this->_conn,$user_dn,$add);
1207          if ($result==false){
1208              $err = ldap_errno($this->_conn);
1209              if($err){
1210                  $msg = 'Error '.$err.': '.ldap_err2str($err).'.';
1211                  if($err == 53) $msg .= ' Your password might not match the password policy.';
1212                  throw new adLDAPException($msg);
1213              }else{
1214                  return false;
1215              }
1216          }
1217  
1218          return (true);
1219      }
1220  
1221      /**
1222      * Return a list of all users in AD
1223      *
1224      * @param bool $include_desc Return a description of the user
1225      * @param string $search Search parameter
1226      * @param bool $sorted Sort the user accounts
1227      * @return array
1228      */
1229      public function all_users($include_desc = false, $search = "*", $sorted = true){
1230          if (!$this->_bind){ return (false); }
1231  
1232          // Perform the search and grab all their details
1233          $filter = "(&(objectClass=user)(samaccounttype=". ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=".$search."))";
1234          $fields=array("samaccountname","displayname");
1235          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
1236          $entries = ldap_get_entries($this->_conn, $sr);
1237  
1238          $users_array = array();
1239          for ($i=0; $i<$entries["count"]; $i++){
1240              if ($include_desc && strlen($entries[$i]["displayname"][0])>0){
1241                  $users_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["displayname"][0];
1242              } elseif ($include_desc){
1243                  $users_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["samaccountname"][0];
1244              } else {
1245                  array_push($users_array, $entries[$i]["samaccountname"][0]);
1246              }
1247          }
1248          if ($sorted){ asort($users_array); }
1249          return ($users_array);
1250      }
1251  
1252      /**
1253      * Converts a username (samAccountName) to a GUID
1254      *
1255      * @param string $username The username to query
1256      * @return string
1257      */
1258      public function username2guid($username) {
1259          if (!$this->_bind){ return (false); }
1260          if ($username === null){ return ("Missing compulsory field [username]"); }
1261  
1262          $filter = "samaccountname=" . $username;
1263          $fields = array("objectGUID");
1264          $sr = @ldap_search($this->_conn, $this->_base_dn, $filter, $fields);
1265          if (ldap_count_entries($this->_conn, $sr) > 0) {
1266              $entry = @ldap_first_entry($this->_conn, $sr);
1267              $guid = @ldap_get_values_len($this->_conn, $entry, 'objectGUID');
1268              $strGUID = $this->binary2text($guid[0]);
1269              return ($strGUID);
1270          }
1271          else {
1272              return (false);
1273          }
1274      }
1275  
1276      /**
1277      * Move a user account to a different OU
1278      *
1279      * @param string $username The username to move (please be careful here!)
1280      * @param array $container The container or containers to move the user to (please be careful here!).
1281      * accepts containers in 1. parent 2. child order
1282      * @return array
1283      */
1284      public function user_move($username, $container) {
1285          if (!$this->_bind){ return (false); }
1286          if ($username === null){ return ("Missing compulsory field [username]"); }
1287          if ($container === null){ return ("Missing compulsory field [container]"); }
1288          if (!is_array($container)){ return ("Container must be an array"); }
1289  
1290          $userinfo = $this->user_info($username, array("*"));
1291          $dn = $userinfo[0]['distinguishedname'][0];
1292          $newrdn = "cn=" . $username;
1293          $container = array_reverse($container);
1294          $newcontainer = "ou=" . implode(",ou=",$container);
1295          $newbasedn = strtolower($newcontainer) . "," . $this->_base_dn;
1296          $result=@ldap_rename($this->_conn,$dn,$newrdn,$newbasedn,true);
1297          if ($result !== true) {
1298              return (false);
1299          }
1300          return (true);
1301      }
1302  
1303      //*****************************************************************************************************************
1304      // CONTACT FUNCTIONS
1305      // * Still work to do in this area, and new functions to write
1306  
1307      /**
1308      * Create a contact
1309      *
1310      * @param array $attributes The attributes to set to the contact
1311      * @return bool
1312      */
1313      public function contact_create($attributes){
1314          // Check for compulsory fields
1315          if (!array_key_exists("display_name",$attributes)){ return ("Missing compulsory field [display_name]"); }
1316          if (!array_key_exists("email",$attributes)){ return ("Missing compulsory field [email]"); }
1317          if (!array_key_exists("container",$attributes)){ return ("Missing compulsory field [container]"); }
1318          if (!is_array($attributes["container"])){ return ("Container attribute must be an array."); }
1319  
1320          // Translate the schema
1321          $add=$this->adldap_schema($attributes);
1322  
1323          // Additional stuff only used for adding contacts
1324          $add["cn"][0]=$attributes["display_name"];
1325          $add["objectclass"][0]="top";
1326          $add["objectclass"][1]="person";
1327          $add["objectclass"][2]="organizationalPerson";
1328          $add["objectclass"][3]="contact";
1329          if (!isset($attributes['exchange_hidefromlists'])) {
1330              $add["msExchHideFromAddressLists"][0]="TRUE";
1331          }
1332  
1333          // Determine the container
1334          $attributes["container"]=array_reverse($attributes["container"]);
1335          $container="OU=".implode(",OU=",$attributes["container"]);
1336  
1337          // Add the entry
1338          $result=@ldap_add($this->_conn, "CN=".$add["cn"][0].", ".$container.",".$this->_base_dn, $add);
1339          if ($result!=true){ return (false); }
1340  
1341          return (true);
1342      }
1343  
1344      /**
1345      * Determine the list of groups a contact is a member of
1346      *
1347      * @param string $distinguisedname The full DN of a contact
1348      * @param bool $recursive Recursively check groups
1349      * @return array
1350      */
1351      public function contact_groups($distinguishedname,$recursive=NULL){
1352          if ($distinguishedname===NULL){ return (false); }
1353          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } //use the default option if they haven't set it
1354          if (!$this->_bind){ return (false); }
1355  
1356          // Search the directory for their information
1357          $info=@$this->contact_info($distinguishedname,array("memberof","primarygroupid"));
1358          $groups=$this->nice_names($info[0]["memberof"]); //presuming the entry returned is our contact
1359  
1360          if ($recursive === true){
1361              foreach ($groups as $id => $group_name){
1362                  $extra_groups=$this->recursive_groups($group_name);
1363                  $groups=array_merge($groups,$extra_groups);
1364              }
1365          }
1366  
1367          return ($groups);
1368      }
1369  
1370      /**
1371      * Get contact information
1372      *
1373      * @param string $distinguisedname The full DN of a contact
1374      * @param array $fields Attributes to be returned
1375      * @return array
1376      */
1377      public function contact_info($distinguishedname,$fields=NULL){
1378          if ($distinguishedname===NULL){ return (false); }
1379          if (!$this->_bind){ return (false); }
1380  
1381          $filter="distinguishedName=".$distinguishedname;
1382          if ($fields===NULL){ $fields=array("distinguishedname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid"); }
1383          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
1384          $entries = ldap_get_entries($this->_conn, $sr);
1385  
1386          if ($entries[0]['count'] >= 1) {
1387              // AD does not return the primary group in the ldap query, we may need to fudge it
1388              if ($this->_real_primarygroup && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["primarygroupid"][0])){
1389                  //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
1390                  $entries[0]["memberof"][]=$this->get_primary_group($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
1391              } else {
1392                  $entries[0]["memberof"][]="CN=Domain Users,CN=Users,".$this->_base_dn;
1393              }
1394          }
1395  
1396          $entries[0]["memberof"]["count"]++;
1397          return ($entries);
1398      }
1399  
1400      /**
1401      * Determine if a contact is a member of a group
1402      *
1403      * @param string $distinguisedname The full DN of a contact
1404      * @param string $group The group name to query
1405      * @param bool $recursive Recursively check groups
1406      * @return bool
1407      */
1408      public function contact_ingroup($distinguisedname,$group,$recursive=NULL){
1409          if ($distinguisedname===NULL){ return (false); }
1410          if ($group===NULL){ return (false); }
1411          if (!$this->_bind){ return (false); }
1412          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } //use the default option if they haven't set it
1413  
1414          // Get a list of the groups
1415          $groups=$this->contact_groups($distinguisedname,array("memberof"),$recursive);
1416  
1417          // Return true if the specified group is in the group list
1418          if (in_array($group,$groups)){ return (true); }
1419  
1420          return (false);
1421      }
1422  
1423      /**
1424      * Modify a contact
1425      *
1426      * @param string $distinguishedname The contact to query
1427      * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
1428      * @return bool
1429      */
1430      public function contact_modify($distinguishedname,$attributes){
1431          if ($distinguishedname===NULL){ return ("Missing compulsory field [distinguishedname]"); }
1432  
1433          // Translate the update to the LDAP schema
1434          $mod=$this->adldap_schema($attributes);
1435  
1436          // Check to see if this is an enabled status update
1437          if (!$mod){ return (false); }
1438  
1439          // Do the update
1440          $result=ldap_modify($this->_conn,$distinguishedname,$mod);
1441          if ($result==false){ return (false); }
1442  
1443          return (true);
1444      }
1445  
1446      /**
1447      * Delete a contact
1448      *
1449      * @param string $distinguishedname The contact dn to delete (please be careful here!)
1450      * @return array
1451      */
1452      public function contact_delete($distinguishedname) {
1453          $result = $this->dn_delete($distinguishedname);
1454          if ($result!=true){ return (false); }
1455          return (true);
1456      }
1457  
1458      /**
1459      * Return a list of all contacts
1460      *
1461      * @param bool $include_desc Include a description of a contact
1462      * @param string $search The search parameters
1463      * @param bool $sorted Whether to sort the results
1464      * @return array
1465      */
1466      public function all_contacts($include_desc = false, $search = "*", $sorted = true){
1467          if (!$this->_bind){ return (false); }
1468  
1469          // Perform the search and grab all their details
1470          $filter = "(&(objectClass=contact)(cn=".$search."))";
1471          $fields=array("displayname","distinguishedname");
1472          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
1473          $entries = ldap_get_entries($this->_conn, $sr);
1474  
1475          $users_array = array();
1476          for ($i=0; $i<$entries["count"]; $i++){
1477              if ($include_desc && strlen($entries[$i]["displayname"][0])>0){
1478                  $users_array[ $entries[$i]["distinguishedname"][0] ] = $entries[$i]["displayname"][0];
1479              } elseif ($include_desc){
1480                  $users_array[ $entries[$i]["distinguishedname"][0] ] = $entries[$i]["distinguishedname"][0];
1481              } else {
1482                  array_push($users_array, $entries[$i]["distinguishedname"][0]);
1483              }
1484          }
1485          if ($sorted){ asort($users_array); }
1486          return ($users_array);
1487      }
1488  
1489      //*****************************************************************************************************************
1490      // FOLDER FUNCTIONS
1491  
1492      /**
1493      * Returns a folder listing for a specific OU
1494      * See http://adldap.sourceforge.net/wiki/doku.php?id=api_folder_functions
1495      *
1496      * @param array $folder_name An array to the OU you wish to list.
1497      *                           If set to NULL will list the root, strongly recommended to set
1498      *                           $recursive to false in that instance!
1499      * @param string $dn_type The type of record to list.  This can be ADLDAP_FOLDER or ADLDAP_CONTAINER.
1500      * @param bool $recursive Recursively search sub folders
1501      * @param bool $type Specify a type of object to search for
1502      * @return array
1503      */
1504      public function folder_list($folder_name = NULL, $dn_type = ADLDAP_FOLDER, $recursive = NULL, $type = NULL) {
1505          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } //use the default option if they haven't set it
1506          if (!$this->_bind){ return (false); }
1507  
1508          $filter = '(&';
1509          if ($type !== NULL) {
1510              switch ($type) {
1511                  case 'contact':
1512                      $filter .= '(objectClass=contact)';
1513                      break;
1514                  case 'computer':
1515                      $filter .= '(objectClass=computer)';
1516                      break;
1517                  case 'group':
1518                      $filter .= '(objectClass=group)';
1519                      break;
1520                  case 'folder':
1521                      $filter .= '(objectClass=organizationalUnit)';
1522                      break;
1523                  case 'container':
1524                      $filter .= '(objectClass=container)';
1525                      break;
1526                  case 'domain':
1527                      $filter .= '(objectClass=builtinDomain)';
1528                      break;
1529                  default:
1530                      $filter .= '(objectClass=user)';
1531                      break;
1532              }
1533          }
1534          else {
1535              $filter .= '(objectClass=*)';
1536          }
1537          // If the folder name is null then we will search the root level of AD
1538          // This requires us to not have an OU= part, just the base_dn
1539          $searchou = $this->_base_dn;
1540          if (is_array($folder_name)) {
1541              $ou = $dn_type . "=".implode("," . $dn_type . "=",$folder_name);
1542              $filter .= '(!(distinguishedname=' . $ou . ',' . $this->_base_dn . ')))';
1543              $searchou = $ou . ',' . $this->_base_dn;
1544          }
1545          else {
1546              $filter .= '(!(distinguishedname=' . $this->_base_dn . ')))';
1547          }
1548  
1549          if ($recursive === true) {
1550              $sr=ldap_search($this->_conn, $searchou, $filter, array('objectclass', 'distinguishedname', 'samaccountname'));
1551              $entries = @ldap_get_entries($this->_conn, $sr);
1552              if (is_array($entries)) {
1553                  return $entries;
1554              }
1555          }
1556          else {
1557              $sr=ldap_list($this->_conn, $searchou, $filter, array('objectclass', 'distinguishedname', 'samaccountname'));
1558              $entries = @ldap_get_entries($this->_conn, $sr);
1559              if (is_array($entries)) {
1560                  return $entries;
1561              }
1562          }
1563  
1564          return false;
1565      }
1566  
1567      //*****************************************************************************************************************
1568      // COMPUTER FUNCTIONS
1569  
1570      /**
1571      * Get information about a specific computer
1572      *
1573      * @param string $computer_name The name of the computer
1574      * @param array $fields Attributes to return
1575      * @return array
1576      */
1577      public function computer_info($computer_name,$fields=NULL){
1578          if ($computer_name===NULL){ return (false); }
1579          if (!$this->_bind){ return (false); }
1580  
1581          $filter="(&(objectClass=computer)(cn=".$computer_name."))";
1582          if ($fields===NULL){ $fields=array("memberof","cn","displayname","dnshostname","distinguishedname","objectcategory","operatingsystem","operatingsystemservicepack","operatingsystemversion"); }
1583          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
1584          $entries = ldap_get_entries($this->_conn, $sr);
1585  
1586          return ($entries);
1587      }
1588  
1589      /**
1590      * Check if a computer is in a group
1591      *
1592      * @param string $computer_name The name of the computer
1593      * @param string $group The group to check
1594      * @param bool $recursive Whether to check recursively
1595      * @return array
1596      */
1597      public function computer_ingroup($computer_name,$group,$recursive=NULL){
1598          if ($computer_name===NULL){ return (false); }
1599          if ($group===NULL){ return (false); }
1600          if (!$this->_bind){ return (false); }
1601          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } // use the default option if they haven't set it
1602  
1603          //get a list of the groups
1604          $groups=$this->computer_groups($computer_name,array("memberof"),$recursive);
1605  
1606          //return true if the specified group is in the group list
1607          if (in_array($group,$groups)){ return (true); }
1608  
1609          return (false);
1610      }
1611  
1612      /**
1613      * Get the groups a computer is in
1614      *
1615      * @param string $computer_name The name of the computer
1616      * @param bool $recursive Whether to check recursively
1617      * @return array
1618      */
1619      public function computer_groups($computer_name,$recursive=NULL){
1620          if ($computer_name===NULL){ return (false); }
1621          if ($recursive===NULL){ $recursive=$this->_recursive_groups; } //use the default option if they haven't set it
1622          if (!$this->_bind){ return (false); }
1623  
1624          //search the directory for their information
1625          $info=@$this->computer_info($computer_name,array("memberof","primarygroupid"));
1626          $groups=$this->nice_names($info[0]["memberof"]); //presuming the entry returned is our guy (unique usernames)
1627  
1628          if ($recursive === true){
1629              foreach ($groups as $id => $group_name){
1630                $extra_groups=$this->recursive_groups($group_name);
1631                $groups=array_merge($groups,$extra_groups);
1632              }
1633          }
1634  
1635          return ($groups);
1636      }
1637  
1638      //************************************************************************************************************
1639      //  ORGANIZATIONAL UNIT FUNCTIONS
1640  
1641       /**
1642      * Create an organizational unit
1643      *
1644      * @param array $attributes Default attributes of the ou
1645      * @return bool
1646      */
1647      public function ou_create($attributes){
1648          if (!is_array($attributes)){ return ("Attributes must be an array"); }
1649          if (!array_key_exists("ou_name",$attributes)){ return ("Missing compulsory field [ou_name]"); }
1650          if (!array_key_exists("container",$attributes)){ return ("Missing compulsory field [container]"); }
1651          if (!is_array($attributes["container"])){ return ("Container attribute must be an array."); }
1652          $attributes["container"]=array_reverse($attributes["container"]);
1653  
1654          $add=array();
1655          $add["objectClass"] = "organizationalUnit";
1656  
1657          $container="OU=".implode(",OU=",$attributes["container"]);
1658          $result=ldap_add($this->_conn,"CN=".$add["cn"].", ".$container.",".$this->_base_dn,$add);
1659          if ($result!=true){ return (false); }
1660  
1661          return (true);
1662      }
1663  
1664      //************************************************************************************************************
1665      // EXCHANGE FUNCTIONS
1666  
1667      /**
1668      * Create an Exchange account
1669      *
1670      * @param string $username The username of the user to add the Exchange account to
1671      * @param array $storagegroup The mailbox, Exchange Storage Group, for the user account, this must be a full CN
1672      *                            If the storage group has a different base_dn to the adLDAP configuration, set it using $base_dn
1673      * @param string $emailaddress The primary email address to add to this user
1674      * @param string $mailnickname The mail nick name.  If mail nickname is blank, the username will be used
1675      * @param bool $usedefaults Indicates whether the store should use the default quota, rather than the per-mailbox quota.
1676      * @param string $base_dn Specify an alternative base_dn for the Exchange storage group
1677      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1678      * @return bool
1679      */
1680      public function exchange_create_mailbox($username, $storagegroup, $emailaddress, $mailnickname=NULL, $usedefaults=TRUE, $base_dn=NULL, $isGUID=false){
1681          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1682          if ($storagegroup===NULL){ return ("Missing compulsory array [storagegroup]"); }
1683          if (!is_array($storagegroup)){ return ("[storagegroup] must be an array"); }
1684          if ($emailaddress===NULL){ return ("Missing compulsory field [emailaddress]"); }
1685  
1686          if ($base_dn===NULL) {
1687              $base_dn = $this->_base_dn;
1688          }
1689  
1690          $container="CN=".implode(",CN=",$storagegroup);
1691  
1692          if ($mailnickname===NULL) { $mailnickname=$username; }
1693          $mdbUseDefaults = $this->bool2str($usedefaults);
1694  
1695          $attributes = array(
1696              'exchange_homemdb'=>$container.",".$base_dn,
1697              'exchange_proxyaddress'=>'SMTP:' . $emailaddress,
1698              'exchange_mailnickname'=>$mailnickname,
1699              'exchange_usedefaults'=>$mdbUseDefaults
1700          );
1701          $result = $this->user_modify($username,$attributes,$isGUID);
1702          if ($result==false){ return (false); }
1703          return (true);
1704      }
1705  
1706      /**
1707      * Add an X400 address to Exchange
1708      * See http://tools.ietf.org/html/rfc1685 for more information.
1709      * An X400 Address looks similar to this X400:c=US;a= ;p=Domain;o=Organization;s=Doe;g=John;
1710      *
1711      * @param string $username The username of the user to add the X400 to to
1712      * @param string $country Country
1713      * @param string $admd Administration Management Domain
1714      * @param string $pdmd Private Management Domain (often your AD domain)
1715      * @param string $org Organization
1716      * @param string $surname Surname
1717      * @param string $givenName Given name
1718      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1719      * @return bool
1720      */
1721      public function exchange_add_X400($username, $country, $admd, $pdmd, $org, $surname, $givenname, $isGUID=false) {
1722          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1723  
1724          $proxyvalue = 'X400:';
1725  
1726          // Find the dn of the user
1727          $user=$this->user_info($username,array("cn","proxyaddresses"), $isGUID);
1728          if ($user[0]["dn"]===NULL){ return (false); }
1729          $user_dn=$user[0]["dn"];
1730  
1731          // We do not have to demote an email address from the default so we can just add the new proxy address
1732          $attributes['exchange_proxyaddress'] = $proxyvalue . 'c=' . $country . ';a=' . $admd . ';p=' . $pdmd . ';o=' . $org . ';s=' . $surname . ';g=' . $givenname . ';';
1733  
1734          // Translate the update to the LDAP schema
1735          $add=$this->adldap_schema($attributes);
1736  
1737          if (!$add){ return (false); }
1738  
1739          // Do the update
1740          // Take out the @ to see any errors, usually this error might occur because the address already
1741          // exists in the list of proxyAddresses
1742          $result=@ldap_mod_add($this->_conn,$user_dn,$add);
1743          if ($result==false){ return (false); }
1744  
1745          return (true);
1746      }
1747  
1748      /**
1749      * Add an address to Exchange
1750      *
1751      * @param string $username The username of the user to add the Exchange account to
1752      * @param string $emailaddress The email address to add to this user
1753      * @param bool $default Make this email address the default address, this is a bit more intensive as we have to demote any existing default addresses
1754      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1755      * @return bool
1756      */
1757      public function exchange_add_address($username, $emailaddress, $default=FALSE, $isGUID=false) {
1758          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1759          if ($emailaddress===NULL) { return ("Missing compulsory fields [emailaddress]"); }
1760  
1761          $proxyvalue = 'smtp:';
1762          if ($default === true) {
1763              $proxyvalue = 'SMTP:';
1764          }
1765  
1766          // Find the dn of the user
1767          $user=$this->user_info($username,array("cn","proxyaddresses"),$isGUID);
1768          if ($user[0]["dn"]===NULL){ return (false); }
1769          $user_dn=$user[0]["dn"];
1770  
1771          // We need to scan existing proxy addresses and demote the default one
1772          if (is_array($user[0]["proxyaddresses"]) && $default===true) {
1773              $modaddresses = array();
1774              for ($i=0;$i<sizeof($user[0]['proxyaddresses']);$i++) {
1775                  if (strstr($user[0]['proxyaddresses'][$i], 'SMTP:') !== false) {
1776                      $user[0]['proxyaddresses'][$i] = str_replace('SMTP:', 'smtp:', $user[0]['proxyaddresses'][$i]);
1777                  }
1778                  if ($user[0]['proxyaddresses'][$i] != '') {
1779                      $modaddresses['proxyAddresses'][$i] = $user[0]['proxyaddresses'][$i];
1780                  }
1781              }
1782              $modaddresses['proxyAddresses'][(sizeof($user[0]['proxyaddresses'])-1)] = 'SMTP:' . $emailaddress;
1783  
1784              $result=@ldap_mod_replace($this->_conn,$user_dn,$modaddresses);
1785              if ($result==false){ return (false); }
1786  
1787              return (true);
1788          }
1789          else {
1790              // We do not have to demote an email address from the default so we can just add the new proxy address
1791              $attributes['exchange_proxyaddress'] = $proxyvalue . $emailaddress;
1792  
1793              // Translate the update to the LDAP schema
1794              $add=$this->adldap_schema($attributes);
1795  
1796              if (!$add){ return (false); }
1797  
1798              // Do the update
1799              // Take out the @ to see any errors, usually this error might occur because the address already
1800              // exists in the list of proxyAddresses
1801              $result=@ldap_mod_add($this->_conn,$user_dn,$add);
1802              if ($result==false){ return (false); }
1803  
1804              return (true);
1805          }
1806      }
1807  
1808      /**
1809      * Remove an address to Exchange
1810      * If you remove a default address the account will no longer have a default,
1811      * we recommend changing the default address first
1812      *
1813      * @param string $username The username of the user to add the Exchange account to
1814      * @param string $emailaddress The email address to add to this user
1815      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1816      * @return bool
1817      */
1818      public function exchange_del_address($username, $emailaddress, $isGUID=false) {
1819          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1820          if ($emailaddress===NULL) { return ("Missing compulsory fields [emailaddress]"); }
1821  
1822          // Find the dn of the user
1823          $user=$this->user_info($username,array("cn","proxyaddresses"),$isGUID);
1824          if ($user[0]["dn"]===NULL){ return (false); }
1825          $user_dn=$user[0]["dn"];
1826  
1827          if (is_array($user[0]["proxyaddresses"])) {
1828              $mod = array();
1829              for ($i=0;$i<sizeof($user[0]['proxyaddresses']);$i++) {
1830                  if (strstr($user[0]['proxyaddresses'][$i], 'SMTP:') !== false && $user[0]['proxyaddresses'][$i] == 'SMTP:' . $emailaddress) {
1831                      $mod['proxyAddresses'][0] = 'SMTP:' . $emailaddress;
1832                  }
1833                  elseif (strstr($user[0]['proxyaddresses'][$i], 'smtp:') !== false && $user[0]['proxyaddresses'][$i] == 'smtp:' . $emailaddress) {
1834                      $mod['proxyAddresses'][0] = 'smtp:' . $emailaddress;
1835                  }
1836              }
1837  
1838              $result=@ldap_mod_del($this->_conn,$user_dn,$mod);
1839              if ($result==false){ return (false); }
1840  
1841              return (true);
1842          }
1843          else {
1844              return (false);
1845          }
1846      }
1847      /**
1848      * Change the default address
1849      *
1850      * @param string $username The username of the user to add the Exchange account to
1851      * @param string $emailaddress The email address to make default
1852      * @param bool $isGUID Is the username passed a GUID or a samAccountName
1853      * @return bool
1854      */
1855      public function exchange_primary_address($username, $emailaddress, $isGUID=false) {
1856          if ($username===NULL){ return ("Missing compulsory field [username]"); }
1857          if ($emailaddress===NULL) { return ("Missing compulsory fields [emailaddress]"); }
1858  
1859          // Find the dn of the user
1860          $user=$this->user_info($username,array("cn","proxyaddresses"), $isGUID);
1861          if ($user[0]["dn"]===NULL){ return (false); }
1862          $user_dn=$user[0]["dn"];
1863  
1864          if (is_array($user[0]["proxyaddresses"])) {
1865              $modaddresses = array();
1866              for ($i=0;$i<sizeof($user[0]['proxyaddresses']);$i++) {
1867                  if (strstr($user[0]['proxyaddresses'][$i], 'SMTP:') !== false) {
1868                      $user[0]['proxyaddresses'][$i] = str_replace('SMTP:', 'smtp:', $user[0]['proxyaddresses'][$i]);
1869                  }
1870                  if ($user[0]['proxyaddresses'][$i] == 'smtp:' . $emailaddress) {
1871                      $user[0]['proxyaddresses'][$i] = str_replace('smtp:', 'SMTP:', $user[0]['proxyaddresses'][$i]);
1872                  }
1873                  if ($user[0]['proxyaddresses'][$i] != '') {
1874                      $modaddresses['proxyAddresses'][$i] = $user[0]['proxyaddresses'][$i];
1875                  }
1876              }
1877  
1878              $result=@ldap_mod_replace($this->_conn,$user_dn,$modaddresses);
1879              if ($result==false){ return (false); }
1880  
1881              return (true);
1882          }
1883  
1884      }
1885  
1886      /**
1887      * Mail enable a contact
1888      * Allows email to be sent to them through Exchange
1889      *
1890      * @param string $distinguishedname The contact to mail enable
1891      * @param string $emailaddress The email address to allow emails to be sent through
1892      * @param string $mailnickname The mailnickname for the contact in Exchange.  If NULL this will be set to the display name
1893      * @return bool
1894      */
1895      public function exchange_contact_mailenable($distinguishedname, $emailaddress, $mailnickname=NULL){
1896          if ($distinguishedname===NULL){ return ("Missing compulsory field [distinguishedname]"); }
1897          if ($emailaddress===NULL){ return ("Missing compulsory field [emailaddress]"); }
1898  
1899          if ($mailnickname !== NULL) {
1900              // Find the dn of the user
1901              $user=$this->contact_info($distinguishedname,array("cn","displayname"));
1902              if ($user[0]["displayname"]===NULL){ return (false); }
1903              $mailnickname = $user[0]['displayname'][0];
1904          }
1905  
1906          $attributes = array("email"=>$emailaddress,"contact_email"=>"SMTP:" . $emailaddress,"exchange_proxyaddress"=>"SMTP:" . $emailaddress,"exchange_mailnickname"=>$mailnickname);
1907  
1908          // Translate the update to the LDAP schema
1909          $mod=$this->adldap_schema($attributes);
1910  
1911          // Check to see if this is an enabled status update
1912          if (!$mod){ return (false); }
1913  
1914          // Do the update
1915          $result=ldap_modify($this->_conn,$distinguishedname,$mod);
1916          if ($result==false){ return (false); }
1917  
1918          return (true);
1919      }
1920  
1921      /**
1922      * Returns a list of Exchange Servers in the ConfigurationNamingContext of the domain
1923      *
1924      * @param array $attributes An array of the AD attributes you wish to return
1925      * @return array
1926      */
1927      public function exchange_servers($attributes = array('cn','distinguishedname','serialnumber')) {
1928          if (!$this->_bind){ return (false); }
1929  
1930          $configurationNamingContext = $this->get_root_dse(array('configurationnamingcontext'));
1931          $sr = @ldap_search($this->_conn,$configurationNamingContext[0]['configurationnamingcontext'][0],'(&(objectCategory=msExchExchangeServer))',$attributes);
1932          $entries = @ldap_get_entries($this->_conn, $sr);
1933          return $entries;
1934      }
1935  
1936      /**
1937      * Returns a list of Storage Groups in Exchange for a given mail server
1938      *
1939      * @param string $exchangeServer The full DN of an Exchange server.  You can use exchange_servers() to find the DN for your server
1940      * @param array $attributes An array of the AD attributes you wish to return
1941      * @param bool $recursive If enabled this will automatically query the databases within a storage group
1942      * @return array
1943      */
1944      public function exchange_storage_groups($exchangeServer, $attributes = array('cn','distinguishedname'), $recursive = NULL) {
1945          if (!$this->_bind){ return (false); }
1946          if ($exchangeServer===NULL){ return ("Missing compulsory field [exchangeServer]"); }
1947          if ($recursive===NULL){ $recursive=$this->_recursive_groups; }
1948  
1949          $filter = '(&(objectCategory=msExchStorageGroup))';
1950          $sr=@ldap_search($this->_conn, $exchangeServer, $filter, $attributes);
1951          $entries = @ldap_get_entries($this->_conn, $sr);
1952  
1953          if ($recursive === true) {
1954              for ($i=0; $i<$entries['count']; $i++) {
1955                  $entries[$i]['msexchprivatemdb'] = $this->exchange_storage_databases($entries[$i]['distinguishedname'][0]);
1956              }
1957          }
1958  
1959          return $entries;
1960      }
1961  
1962      /**
1963      * Returns a list of Databases within any given storage group in Exchange for a given mail server
1964      *
1965      * @param string $storageGroup The full DN of an Storage Group.  You can use exchange_storage_groups() to find the DN
1966      * @param array $attributes An array of the AD attributes you wish to return
1967      * @return array
1968      */
1969      public function exchange_storage_databases($storageGroup, $attributes = array('cn','distinguishedname','displayname')) {
1970          if (!$this->_bind){ return (false); }
1971          if ($storageGroup===NULL){ return ("Missing compulsory field [storageGroup]"); }
1972  
1973          $filter = '(&(objectCategory=msExchPrivateMDB))';
1974          $sr=@ldap_search($this->_conn, $storageGroup, $filter, $attributes);
1975          $entries = @ldap_get_entries($this->_conn, $sr);
1976          return $entries;
1977      }
1978  
1979      //************************************************************************************************************
1980      // SERVER FUNCTIONS
1981  
1982      /**
1983      * Find the Base DN of your domain controller
1984      *
1985      * @return string
1986      */
1987      public function find_base_dn() {
1988          $namingContext = $this->get_root_dse(array('defaultnamingcontext'));
1989          return $namingContext[0]['defaultnamingcontext'][0];
1990      }
1991  
1992      /**
1993      * Get the RootDSE properties from a domain controller
1994      *
1995      * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext
1996      * @return array
1997      */
1998      public function get_root_dse($attributes = array("*", "+")) {
1999          if (!$this->_bind){ return (false); }
2000  
2001          $sr = @ldap_read($this->_conn, NULL, 'objectClass=*', $attributes);
2002          $entries = @ldap_get_entries($this->_conn, $sr);
2003          return $entries;
2004      }
2005  
2006      //************************************************************************************************************
2007      // UTILITY FUNCTIONS (Many of these functions are protected and can only be called from within the class)
2008  
2009      /**
2010      * Get last error from Active Directory
2011      *
2012      * This function gets the last message from Active Directory
2013      * This may indeed be a 'Success' message but if you get an unknown error
2014      * it might be worth calling this function to see what errors were raised
2015      *
2016      * return string
2017      */
2018      public function get_last_error() {
2019          return @ldap_error($this->_conn);
2020      }
2021  
2022      /**
2023      * Detect LDAP support in php
2024      *
2025      * @return bool
2026      */
2027      protected function ldap_supported() {
2028          if (!function_exists('ldap_connect')) {
2029              return (false);
2030          }
2031          return (true);
2032      }
2033  
2034      /**
2035      * Schema
2036      *
2037      * @param array $attributes Attributes to be queried
2038      * @return array
2039      */
2040      protected function adldap_schema($attributes){
2041  
2042          // LDAP doesn't like NULL attributes, only set them if they have values
2043          // If you wish to remove an attribute you should set it to a space
2044          // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute
2045          $mod=array();
2046  
2047          // Check every attribute to see if it contains 8bit characters and then UTF8 encode them
2048          array_walk($attributes, array($this, 'encode8bit'));
2049  
2050          if ($attributes["address_city"]){ $mod["l"][0]=$attributes["address_city"]; }
2051          if ($attributes["address_code"]){ $mod["postalCode"][0]=$attributes["address_code"]; }
2052          //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes?
2053          if ($attributes["address_country"]){ $mod["c"][0]=$attributes["address_country"]; }
2054          if ($attributes["address_pobox"]){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; }
2055          if ($attributes["address_state"]){ $mod["st"][0]=$attributes["address_state"]; }
2056          if ($attributes["address_street"]){ $mod["streetAddress"][0]=$attributes["address_street"]; }
2057          if ($attributes["company"]){ $mod["company"][0]=$attributes["company"]; }
2058          if ($attributes["change_password"]){ $mod["pwdLastSet"][0]=0; }
2059          if ($attributes["department"]){ $mod["department"][0]=$attributes["department"]; }
2060          if ($attributes["description"]){ $mod["description"][0]=$attributes["description"]; }
2061          if ($attributes["display_name"]){ $mod["displayName"][0]=$attributes["display_name"]; }
2062          if ($attributes["email"]){ $mod["mail"][0]=$attributes["email"]; }
2063          if ($attributes["expires"]){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format?
2064          if ($attributes["firstname"]){ $mod["givenName"][0]=$attributes["firstname"]; }
2065          if ($attributes["home_directory"]){ $mod["homeDirectory"][0]=$attributes["home_directory"]; }
2066          if ($attributes["home_drive"]){ $mod["homeDrive"][0]=$attributes["home_drive"]; }
2067          if ($attributes["initials"]){ $mod["initials"][0]=$attributes["initials"]; }
2068          if ($attributes["logon_name"]){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; }
2069          if ($attributes["manager"]){ $mod["manager"][0]=$attributes["manager"]; }  //UNTESTED ***Use DistinguishedName***
2070          if ($attributes["office"]){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; }
2071          if ($attributes["password"]){ $mod["unicodePwd"][0]=$this->encode_password($attributes["password"]); }
2072          if ($attributes["profile_path"]){ $mod["profilepath"][0]=$attributes["profile_path"]; }
2073          if ($attributes["script_path"]){ $mod["scriptPath"][0]=$attributes["script_path"]; }
2074          if ($attributes["surname"]){ $mod["sn"][0]=$attributes["surname"]; }
2075          if ($attributes["title"]){ $mod["title"][0]=$attributes["title"]; }
2076          if ($attributes["telephone"]){ $mod["telephoneNumber"][0]=$attributes["telephone"]; }
2077          if ($attributes["mobile"]){ $mod["mobile"][0]=$attributes["mobile"]; }
2078          if ($attributes["pager"]){ $mod["pager"][0]=$attributes["pager"]; }
2079          if ($attributes["ipphone"]){ $mod["ipphone"][0]=$attributes["ipphone"]; }
2080          if ($attributes["web_page"]){ $mod["wWWHomePage"][0]=$attributes["web_page"]; }
2081          if ($attributes["fax"]){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; }
2082          if ($attributes["enabled"]){ $mod["userAccountControl"][0]=$attributes["enabled"]; }
2083  
2084          // Distribution List specific schema
2085          if ($attributes["group_sendpermission"]){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; }
2086          if ($attributes["group_rejectpermission"]){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; }
2087  
2088          // Exchange Schema
2089          if ($attributes["exchange_homemdb"]){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; }
2090          if ($attributes["exchange_mailnickname"]){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; }
2091          if ($attributes["exchange_proxyaddress"]){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; }
2092          if ($attributes["exchange_usedefaults"]){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; }
2093          if ($attributes["exchange_policyexclude"]){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; }
2094          if ($attributes["exchange_policyinclude"]){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; }
2095          if ($attributes["exchange_addressbook"]){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; }
2096  
2097          // This schema is designed for contacts
2098          if ($attributes["exchange_hidefromlists"]){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; }
2099          if ($attributes["contact_email"]){ $mod["targetAddress"][0]=$attributes["contact_email"]; }
2100  
2101          //echo ("<pre>"); print_r($mod);
2102          /*
2103          // modifying a name is a bit fiddly
2104          if ($attributes["firstname"] && $attributes["surname"]){
2105              $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
2106              $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
2107              $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
2108          }
2109          */
2110  
2111          if (count($mod)==0){ return (false); }
2112          return ($mod);
2113      }
2114  
2115      /**
2116      * Coping with AD not returning the primary group
2117      * http://support.microsoft.com/?kbid=321360
2118      *
2119      * For some reason it's not possible to search on primarygrouptoken=XXX
2120      * If someone can show otherwise, I'd like to know about it :)
2121      * this way is resource intensive and generally a pain in the @#%^
2122      *
2123      * @deprecated deprecated since version 3.1, see get get_primary_group
2124      * @param string $gid Group ID
2125      * @return string
2126      */
2127      protected function group_cn($gid){
2128          if ($gid===NULL){ return (false); }
2129          $r=false;
2130  
2131          $filter="(&(objectCategory=group)(samaccounttype=". ADLDAP_SECURITY_GLOBAL_GROUP ."))";
2132          $fields=array("primarygrouptoken","samaccountname","distinguishedname");
2133          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
2134          $entries = ldap_get_entries($this->_conn, $sr);
2135  
2136          for ($i=0; $i<$entries["count"]; $i++){
2137              if ($entries[$i]["primarygrouptoken"][0]==$gid){
2138                  $r=$entries[$i]["distinguishedname"][0];
2139                  $i=$entries["count"];
2140              }
2141          }
2142  
2143          return ($r);
2144      }
2145  
2146      /**
2147      * Coping with AD not returning the primary group
2148      * http://support.microsoft.com/?kbid=321360
2149      *
2150      * This is a re-write based on code submitted by Bruce which prevents the
2151      * need to search each security group to find the true primary group
2152      *
2153      * @param string $gid Group ID
2154      * @param string $usersid User's Object SID
2155      * @return string
2156      */
2157      protected function get_primary_group($gid, $usersid){
2158          if ($gid===NULL || $usersid===NULL){ return (false); }
2159          $r=false;
2160  
2161          $gsid = substr_replace($usersid,pack('V',$gid),strlen($usersid)-4,4);
2162          $filter='(objectsid='.$this->getTextSID($gsid).')';
2163          $fields=array("samaccountname","distinguishedname");
2164          $sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);
2165          $entries = ldap_get_entries($this->_conn, $sr);
2166  
2167          return $entries[0]['distinguishedname'][0];
2168       }
2169  
2170      /**
2171      * Convert a binary SID to a text SID
2172      *
2173      * @param string $binsid A Binary SID
2174      * @return string
2175      */
2176       protected function getTextSID($binsid) {
2177          $hex_sid = bin2hex($binsid);
2178          $rev = hexdec(substr($hex_sid, 0, 2));
2179          $subcount = hexdec(substr($hex_sid, 2, 2));
2180          $auth = hexdec(substr($hex_sid, 4, 12));
2181          $result = "$rev-$auth";
2182  
2183          for ($x=0;$x < $subcount; $x++) {
2184              $subauth[$x] =
2185                  hexdec($this->little_endian(substr($hex_sid, 16 + ($x * 8), 8)));
2186                  $result .= "-" . $subauth[$x];
2187          }
2188  
2189          // Cheat by tacking on the S-
2190          return 'S-' . $result;
2191       }
2192  
2193      /**
2194      * Converts a little-endian hex number to one that hexdec() can convert
2195      *
2196      * @param string $hex A hex code
2197      * @return string
2198      */
2199       protected function little_endian($hex) {
2200          $result = '';
2201          for ($x = strlen($hex) - 2; $x >= 0; $x = $x - 2) {
2202              $result .= substr($hex, $x, 2);
2203          }
2204          return $result;
2205       }
2206  
2207      /**
2208      * Converts a binary attribute to a string
2209      *
2210      * @param string $bin A binary LDAP attribute
2211      * @return string
2212      */
2213      protected function binary2text($bin) {
2214          $hex_guid = bin2hex($bin);
2215          $hex_guid_to_guid_str = '';
2216          for($k = 1; $k <= 4; ++$k) {
2217              $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
2218          }
2219          $hex_guid_to_guid_str .= '-';
2220          for($k = 1; $k <= 2; ++$k) {
2221              $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
2222          }
2223          $hex_guid_to_guid_str .= '-';
2224          for($k = 1; $k <= 2; ++$k) {
2225              $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
2226          }
2227          $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
2228          $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
2229          return strtoupper($hex_guid_to_guid_str);
2230      }
2231  
2232      /**
2233      * Converts a binary GUID to a string GUID
2234      *
2235      * @param string $binaryGuid The binary GUID attribute to convert
2236      * @return string
2237      */
2238      public function decodeGuid($binaryGuid) {
2239          if ($binaryGuid === null){ return ("Missing compulsory field [binaryGuid]"); }
2240  
2241          $strGUID = $this->binary2text($binaryGuid);
2242          return ($strGUID);
2243      }
2244  
2245      /**
2246      * Converts a string GUID to a hexdecimal value so it can be queried
2247      *
2248      * @param string $strGUID A string representation of a GUID
2249      * @return string
2250      */
2251      protected function strguid2hex($strGUID) {
2252          $strGUID = str_replace('-', '', $strGUID);
2253  
2254          $octet_str = '\\' . substr($strGUID, 6, 2);
2255          $octet_str .= '\\' . substr($strGUID, 4, 2);
2256          $octet_str .= '\\' . substr($strGUID, 2, 2);
2257          $octet_str .= '\\' . substr($strGUID, 0, 2);
2258          $octet_str .= '\\' . substr($strGUID, 10, 2);
2259          $octet_str .= '\\' . substr($strGUID, 8, 2);
2260          $octet_str .= '\\' . substr($strGUID, 14, 2);
2261          $octet_str .= '\\' . substr($strGUID, 12, 2);
2262          //$octet_str .= '\\' . substr($strGUID, 16, strlen($strGUID));
2263          for ($i=16; $i<=(strlen($strGUID)-2); $i++) {
2264              if (($i % 2) == 0) {
2265                  $octet_str .= '\\' . substr($strGUID, $i, 2);
2266              }
2267          }
2268  
2269          return $octet_str;
2270      }
2271  
2272      /**
2273      * Obtain the user's distinguished name based on their userid
2274      *
2275      *
2276      * @param string $username The username
2277      * @param bool $isGUID Is the username passed a GUID or a samAccountName
2278      * @return string
2279      */
2280      protected function user_dn($username,$isGUID=false){
2281          $user=$this->user_info($username,array("cn"),$isGUID);
2282          if ($user[0]["dn"]===NULL){ return (false); }
2283          $user_dn=$user[0]["dn"];
2284          return ($user_dn);
2285      }
2286  
2287      /**
2288      * Encode a password for transmission over LDAP
2289      *
2290      * @param string $password The password to encode
2291      * @return string
2292      */
2293      protected function encode_password($password){
2294          $password="\"".$password."\"";
2295          $encoded="";
2296          for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password{$i}}\000"; }
2297          return ($encoded);
2298      }
2299  
2300      /**
2301      * Escape strings for the use in LDAP filters
2302      *
2303      * DEVELOPERS SHOULD BE DOING PROPER FILTERING IF THEY'RE ACCEPTING USER INPUT
2304      * Ported from Perl's Net::LDAP::Util escape_filter_value
2305      *
2306      * @param string $str The string the parse
2307      * @author Port by Andreas Gohr <andi@splitbrain.org>
2308      * @return string
2309      */
2310      protected function ldap_slashes($str){
2311          return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
2312                              '"\\\\\".join("",unpack("H2","$1"))',
2313                              $str);
2314      }
2315  
2316      /**
2317      * Select a random domain controller from your domain controller array
2318      *
2319      * @return string
2320      */
2321      protected function random_controller(){
2322          mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
2323          return ($this->_domain_controllers[array_rand($this->_domain_controllers)]);
2324      }
2325  
2326      /**
2327      * Account control options
2328      *
2329      * @param array $options The options to convert to int
2330      * @return int
2331      */
2332      protected function account_control($options){
2333          $val=0;
2334  
2335          if (is_array($options)){
2336              if (in_array("SCRIPT",$options)){ $val=$val+1; }
2337              if (in_array("ACCOUNTDISABLE",$options)){ $val=$val+2; }
2338              if (in_array("HOMEDIR_REQUIRED",$options)){ $val=$val+8; }
2339              if (in_array("LOCKOUT",$options)){ $val=$val+16; }
2340              if (in_array("PASSWD_NOTREQD",$options)){ $val=$val+32; }
2341              //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute.
2342              //For information about how to set the permission programmatically, see the "Property flag descriptions" section.
2343              if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)){ $val=$val+128; }
2344              if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)){ $val=$val+256; }
2345              if (in_array("NORMAL_ACCOUNT",$options)){ $val=$val+512; }
2346              if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)){ $val=$val+2048; }
2347              if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)){ $val=$val+4096; }
2348              if (in_array("SERVER_TRUST_ACCOUNT",$options)){ $val=$val+8192; }
2349              if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; }
2350              if (in_array("MNS_LOGON_ACCOUNT",$options)){ $val=$val+131072; }
2351              if (in_array("SMARTCARD_REQUIRED",$options)){ $val=$val+262144; }
2352              if (in_array("TRUSTED_FOR_DELEGATION",$options)){ $val=$val+524288; }
2353              if (in_array("NOT_DELEGATED",$options)){ $val=$val+1048576; }
2354              if (in_array("USE_DES_KEY_ONLY",$options)){ $val=$val+2097152; }
2355              if (in_array("DONT_REQ_PREAUTH",$options)){ $val=$val+4194304; }
2356              if (in_array("PASSWORD_EXPIRED",$options)){ $val=$val+8388608; }
2357              if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)){ $val=$val+16777216; }
2358          }
2359          return ($val);
2360      }
2361  
2362      /**
2363      * Take an LDAP query and return the nice names, without all the LDAP prefixes (eg. CN, DN)
2364      *
2365      * @param array $groups
2366      * @return array
2367      */
2368      protected function nice_names($groups){
2369  
2370          $group_array=array();
2371          for ($i=0; $i<$groups["count"]; $i++){ // For each group
2372              $line=$groups[$i];
2373  
2374              if (strlen($line)>0){
2375                  // More presumptions, they're all prefixed with CN=
2376                  // so we ditch the first three characters and the group
2377                  // name goes up to the first comma
2378                  $bits=explode(",",$line);
2379                  $group_array[]=substr($bits[0],3,(strlen($bits[0])-3));
2380              }
2381          }
2382          return ($group_array);
2383      }
2384  
2385      /**
2386      * Delete a distinguished name from Active Directory
2387      * You should never need to call this yourself, just use the wrapper functions user_delete and contact_delete
2388      *
2389      * @param string $dn The distinguished name to delete
2390      * @return bool
2391      */
2392      protected function dn_delete($dn){
2393          $result=ldap_delete($this->_conn, $dn);
2394          if ($result!=true){ return (false); }
2395          return (true);
2396      }
2397  
2398      /**
2399      * Convert a boolean value to a string
2400      * You should never need to call this yourself
2401      *
2402      * @param bool $bool Boolean value
2403      * @return string
2404      */
2405      protected function bool2str($bool) {
2406          return ($bool) ? 'TRUE' : 'FALSE';
2407      }
2408  
2409      /**
2410      * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
2411      */
2412      protected function encode8bit(&$item, $key) {
2413          $encode = false;
2414          if (is_string($item)) {
2415              for ($i=0; $i<strlen($item); $i++) {
2416                  if (ord($item[$i]) >> 7) {
2417                      $encode = true;
2418                  }
2419              }
2420          }
2421          if ($encode === true && $key != 'password') {
2422              $item = utf8_encode($item);
2423          }
2424      }
2425  }
2426  
2427  /**
2428  * adLDAP Exception Handler
2429  *
2430  * Exceptions of this type are thrown on bind failure or when SSL is required but not configured
2431  * Example:
2432  * try {
2433  *   $adldap = new adLDAP();
2434  * }
2435  * catch (adLDAPException $e) {
2436  *   echo $e;
2437  *   exit();
2438  * }
2439  */
2440  class adLDAPException extends Exception {}
2441  
2442  ?>


Generated: Sun Feb 3 03:00:06 2013 Cross-referenced by PHPXref 0.7
WikiForumIRCBugsGitXRefTranslate