Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  /*
   3    @version   v5.21.0  2021-02-27
   4    @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   5    @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   6     Released under both BSD license and Lesser GPL library license.
   7    Whenever there is any discrepancy between the two licenses,
   8    the BSD license will take precedence.
   9    Set tabs to 8.
  10  
  11    Revision 1: (02/25/2005) Updated codebase to include the _inject_bind_options function. This allows
  12    users to access the options in the ldap_set_option function appropriately. Most importantly
  13    LDAP Version 3 is now supported. See the examples for more information. Also fixed some minor
  14    bugs that surfaced when PHP error levels were set high.
  15  
  16    Joshua Eldridge (joshuae74#hotmail.com)
  17  */
  18  
  19  // security - hide paths
  20  if (!defined('ADODB_DIR')) die();
  21  
  22  if (!defined('LDAP_ASSOC')) {
  23  	 define('LDAP_ASSOC',ADODB_FETCH_ASSOC);
  24  	 define('LDAP_NUM',ADODB_FETCH_NUM);
  25  	 define('LDAP_BOTH',ADODB_FETCH_BOTH);
  26  }
  27  
  28  class ADODB_ldap extends ADOConnection {
  29  	 var $databaseType = 'ldap';
  30  	 var $dataProvider = 'ldap';
  31  
  32  	 # Connection information
  33  	 var $username = false;
  34  	 var $password = false;
  35  
  36  	 # Used during searches
  37  	 var $filter;
  38  	 var $dn;
  39  	 var $version;
  40  	 var $port = 389;
  41  
  42  	 # Options configuration information
  43  	 var $LDAP_CONNECT_OPTIONS;
  44  
  45  	 # error on binding, eg. "Binding: invalid credentials"
  46  	 var $_bind_errmsg = "Binding: %s";
  47  
  48  	 // returns true or false
  49  
  50  	function _connect( $host, $username, $password, $ldapbase)
  51  	 {
  52  	 	 global $LDAP_CONNECT_OPTIONS;
  53  
  54  	 	 if ( !function_exists( 'ldap_connect' ) ) return null;
  55  
  56  	 	 if (strpos($host,'ldap://') === 0 || strpos($host,'ldaps://') === 0) {
  57  	 	 	 $this->_connectionID = @ldap_connect($host);
  58  	 	 } else {
  59  	 	 	 $conn_info = array( $host,$this->port);
  60  
  61  	 	 	 if ( strstr( $host, ':' ) ) {
  62  	 	 	 	 $conn_info = explode( ':', $host );
  63  	 	 	 }
  64  
  65  	 	 	 $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] );
  66  	 	 }
  67  	 	 if (!$this->_connectionID) {
  68  	 	 	 $e = 'Could not connect to ' . $conn_info[0];
  69  	 	 	 $this->_errorMsg = $e;
  70  	 	 	 if ($this->debug) ADOConnection::outp($e);
  71  	 	 	 return false;
  72  	 	 }
  73  	 	 if( count( $LDAP_CONNECT_OPTIONS ) > 0 ) {
  74  	 	 	 $this->_inject_bind_options( $LDAP_CONNECT_OPTIONS );
  75  	 	 }
  76  
  77  	 	 if ($username) {
  78  	 	 	 $bind = @ldap_bind( $this->_connectionID, $username, $password );
  79  	 	 } else {
  80  	 	 	 $username = 'anonymous';
  81  	 	 	 $bind = @ldap_bind( $this->_connectionID );
  82  	 	 }
  83  
  84  	 	 if (!$bind) {
  85  	 	 	 $e = sprintf($this->_bind_errmsg,ldap_error($this->_connectionID));
  86  	 	 	 $this->_errorMsg = $e;
  87  	 	 	 if ($this->debug) ADOConnection::outp($e);
  88  	 	 	 return false;
  89  	 	 }
  90  	 	 $this->_errorMsg = '';
  91  	 	 $this->database = $ldapbase;
  92  	 	 return $this->_connectionID;
  93  	 }
  94  
  95  /*
  96  	 Valid Domain Values for LDAP Options:
  97  
  98  	 LDAP_OPT_DEREF (integer)
  99  	 LDAP_OPT_SIZELIMIT (integer)
 100  	 LDAP_OPT_TIMELIMIT (integer)
 101  	 LDAP_OPT_PROTOCOL_VERSION (integer)
 102  	 LDAP_OPT_ERROR_NUMBER (integer)
 103  	 LDAP_OPT_REFERRALS (boolean)
 104  	 LDAP_OPT_RESTART (boolean)
 105  	 LDAP_OPT_HOST_NAME (string)
 106  	 LDAP_OPT_ERROR_STRING (string)
 107  	 LDAP_OPT_MATCHED_DN (string)
 108  	 LDAP_OPT_SERVER_CONTROLS (array)
 109  	 LDAP_OPT_CLIENT_CONTROLS (array)
 110  
 111  	 Make sure to set this BEFORE calling Connect()
 112  
 113  	 Example:
 114  
 115  	 $LDAP_CONNECT_OPTIONS = Array(
 116  	 	 Array (
 117  	 	 	 "OPTION_NAME"=>LDAP_OPT_DEREF,
 118  	 	 	 "OPTION_VALUE"=>2
 119  	 	 ),
 120  	 	 Array (
 121  	 	 	 "OPTION_NAME"=>LDAP_OPT_SIZELIMIT,
 122  	 	 	 "OPTION_VALUE"=>100
 123  	 	 ),
 124  	 	 Array (
 125  	 	 	 "OPTION_NAME"=>LDAP_OPT_TIMELIMIT,
 126  	 	 	 "OPTION_VALUE"=>30
 127  	 	 ),
 128  	 	 Array (
 129  	 	 	 "OPTION_NAME"=>LDAP_OPT_PROTOCOL_VERSION,
 130  	 	 	 "OPTION_VALUE"=>3
 131  	 	 ),
 132  	 	 Array (
 133  	 	 	 "OPTION_NAME"=>LDAP_OPT_ERROR_NUMBER,
 134  	 	 	 "OPTION_VALUE"=>13
 135  	 	 ),
 136  	 	 Array (
 137  	 	 	 "OPTION_NAME"=>LDAP_OPT_REFERRALS,
 138  	 	 	 "OPTION_VALUE"=>FALSE
 139  	 	 ),
 140  	 	 Array (
 141  	 	 	 "OPTION_NAME"=>LDAP_OPT_RESTART,
 142  	 	 	 "OPTION_VALUE"=>FALSE
 143  	 	 )
 144  	 );
 145  */
 146  
 147  	function _inject_bind_options( $options ) {
 148  	 	 foreach( $options as $option ) {
 149  	 	 	 ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] )
 150  	 	 	 	 or die( "Unable to set server option: " . $option["OPTION_NAME"] );
 151  	 	 }
 152  	 }
 153  
 154  	 /* returns _queryID or false */
 155  	function _query($sql,$inputarr=false)
 156  	 {
 157  	 	 $rs = @ldap_search( $this->_connectionID, $this->database, $sql );
 158  	 	 $this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql.': '.ldap_error($this->_connectionID);
 159  	 	 return $rs;
 160  	 }
 161  
 162  	function ErrorMsg()
 163  	 {
 164  	 	 return $this->_errorMsg;
 165  	 }
 166  
 167  	function ErrorNo()
 168  	 {
 169  	 	 return @ldap_errno($this->_connectionID);
 170  	 }
 171  
 172  	 /* closes the LDAP connection */
 173  	function _close()
 174  	 {
 175  	 	 @ldap_close( $this->_connectionID );
 176  	 	 $this->_connectionID = false;
 177  	 }
 178  
 179  	function SelectDB($db) {
 180  	 	 $this->database = $db;
 181  	 	 return true;
 182  	 } // SelectDB
 183  
 184  	function ServerInfo()
 185  	 {
 186  	 	 if( !empty( $this->version ) ) {
 187  	 	 	 return $this->version;
 188  	 	 }
 189  
 190  	 	 $version = array();
 191  	 	 /*
 192  	 	 Determines how aliases are handled during search.
 193  	 	 LDAP_DEREF_NEVER (0x00)
 194  	 	 LDAP_DEREF_SEARCHING (0x01)
 195  	 	 LDAP_DEREF_FINDING (0x02)
 196  	 	 LDAP_DEREF_ALWAYS (0x03)
 197  	 	 The LDAP_DEREF_SEARCHING value means aliases are dereferenced during the search but
 198  	 	 not when locating the base object of the search. The LDAP_DEREF_FINDING value means
 199  	 	 aliases are dereferenced when locating the base object but not during the search.
 200  	 	 Default: LDAP_DEREF_NEVER
 201  	 	 */
 202  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF'] ) ;
 203  	 	 switch ( $version['LDAP_OPT_DEREF'] ) {
 204  	 	 	 case 0:
 205  	 	 	 	 $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_NEVER';
 206  	 	 	 case 1:
 207  	 	 	 	 $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_SEARCHING';
 208  	 	 	 case 2:
 209  	 	 	 	 $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_FINDING';
 210  	 	 	 case 3:
 211  	 	 	 	 $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_ALWAYS';
 212  	 	 }
 213  
 214  	 	 /*
 215  	 	 A limit on the number of entries to return from a search.
 216  	 	 LDAP_NO_LIMIT (0) means no limit.
 217  	 	 Default: LDAP_NO_LIMIT
 218  	 	 */
 219  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT'] );
 220  	 	 if ( $version['LDAP_OPT_SIZELIMIT'] == 0 ) {
 221  	 	 	 $version['LDAP_OPT_SIZELIMIT'] = 'LDAP_NO_LIMIT';
 222  	 	 }
 223  
 224  	 	 /*
 225  	 	 A limit on the number of seconds to spend on a search.
 226  	 	 LDAP_NO_LIMIT (0) means no limit.
 227  	 	 Default: LDAP_NO_LIMIT
 228  	 	 */
 229  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT'] );
 230  	 	 if ( $version['LDAP_OPT_TIMELIMIT'] == 0 ) {
 231  	 	 	 $version['LDAP_OPT_TIMELIMIT'] = 'LDAP_NO_LIMIT';
 232  	 	 }
 233  
 234  	 	 /*
 235  	 	 Determines whether the LDAP library automatically follows referrals returned by LDAP servers or not.
 236  	 	 LDAP_OPT_ON
 237  	 	 LDAP_OPT_OFF
 238  	 	 Default: ON
 239  	 	 */
 240  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS'] );
 241  	 	 if ( $version['LDAP_OPT_REFERRALS'] == 0 ) {
 242  	 	 	 $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_OFF';
 243  	 	 } else {
 244  	 	 	 $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_ON';
 245  	 	 }
 246  
 247  	 	 /*
 248  	 	 Determines whether LDAP I/O operations are automatically restarted if they abort prematurely.
 249  	 	 LDAP_OPT_ON
 250  	 	 LDAP_OPT_OFF
 251  	 	 Default: OFF
 252  	 	 */
 253  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART'] );
 254  	 	 if ( $version['LDAP_OPT_RESTART'] == 0 ) {
 255  	 	 	 $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_OFF';
 256  	 	 } else {
 257  	 	 	 $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_ON';
 258  	 	 }
 259  
 260  	 	 /*
 261  	 	 This option indicates the version of the LDAP protocol used when communicating with the primary LDAP server.
 262  	 	 LDAP_VERSION2 (2)
 263  	 	 LDAP_VERSION3 (3)
 264  	 	 Default: LDAP_VERSION2 (2)
 265  	 	 */
 266  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION'] );
 267  	 	 if ( $version['LDAP_OPT_PROTOCOL_VERSION'] == 2 ) {
 268  	 	 	 $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION2';
 269  	 	 } else {
 270  	 	 	 $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION3';
 271  	 	 }
 272  
 273  	 	 /* The host name (or list of hosts) for the primary LDAP server. */
 274  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME'] );
 275  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER'] );
 276  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING'] );
 277  	 	 ldap_get_option( $this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN'] );
 278  
 279  	 	 return $this->version = $version;
 280  	 }
 281  }
 282  
 283  /*--------------------------------------------------------------------------------------
 284  	 Class Name: Recordset
 285  --------------------------------------------------------------------------------------*/
 286  
 287  class ADORecordSet_ldap extends ADORecordSet{
 288  
 289  	 var $databaseType = "ldap";
 290  	 var $canSeek = false;
 291  	 var $_entryID; /* keeps track of the entry resource identifier */
 292  
 293  	function __construct($queryID,$mode=false)
 294  	 {
 295  	 	 if ($mode === false) {
 296  	 	 	 global $ADODB_FETCH_MODE;
 297  	 	 	 $mode = $ADODB_FETCH_MODE;
 298  	 	 }
 299  	 	 switch ($mode)
 300  	 	 {
 301  	 	 case ADODB_FETCH_NUM:
 302  	 	 	 $this->fetchMode = LDAP_NUM;
 303  	 	 	 break;
 304  	 	 case ADODB_FETCH_ASSOC:
 305  	 	 	 $this->fetchMode = LDAP_ASSOC;
 306  	 	 	 break;
 307  	 	 case ADODB_FETCH_DEFAULT:
 308  	 	 case ADODB_FETCH_BOTH:
 309  	 	 default:
 310  	 	 	 $this->fetchMode = LDAP_BOTH;
 311  	 	 	 break;
 312  	 	 }
 313  
 314  	 	 parent::__construct($queryID);
 315  	 }
 316  
 317  	function _initrs()
 318  	 {
 319  	 	 /*
 320  	 	 This could be teaked to respect the $COUNTRECS directive from ADODB
 321  	 	 It's currently being used in the _fetch() function and the
 322  	 	 GetAssoc() function
 323  	 	 */
 324  	 	 $this->_numOfRows = ldap_count_entries( $this->connection->_connectionID, $this->_queryID );
 325  	 }
 326  
 327  	 /*
 328  	 Return whole recordset as a multi-dimensional associative array
 329  	 */
 330  	function GetAssoc($force_array = false, $first2cols = false, $fetchMode = -1)
 331  	 {
 332  	 	 $records = $this->_numOfRows;
 333  	 	 $results = array();
 334  	 	 for ( $i=0; $i < $records; $i++ ) {
 335  	 	 	 foreach ( $this->fields as $k=>$v ) {
 336  	 	 	 	 if ( is_array( $v ) ) {
 337  	 	 	 	 	 if ( $v['count'] == 1 ) {
 338  	 	 	 	 	 	 $results[$i][$k] = $v[0];
 339  	 	 	 	 	 } else {
 340  	 	 	 	 	 	 array_shift( $v );
 341  	 	 	 	 	 	 $results[$i][$k] = $v;
 342  	 	 	 	 	 }
 343  	 	 	 	 }
 344  	 	 	 }
 345  	 	 }
 346  
 347  	 	 return $results;
 348  	 }
 349  
 350  	function GetRowAssoc($upper = ADODB_ASSOC_CASE)
 351  	 {
 352  	 	 $results = array();
 353  	 	 foreach ( $this->fields as $k=>$v ) {
 354  	 	 	 if ( is_array( $v ) ) {
 355  	 	 	 	 if ( $v['count'] == 1 ) {
 356  	 	 	 	 	 $results[$k] = $v[0];
 357  	 	 	 	 } else {
 358  	 	 	 	 	 array_shift( $v );
 359  	 	 	 	 	 $results[$k] = $v;
 360  	 	 	 	 }
 361  	 	 	 }
 362  	 	 }
 363  
 364  	 	 return $results;
 365  	 }
 366  
 367  	function GetRowNums()
 368  	 {
 369  	 	 $results = array();
 370  	 	 foreach ( $this->fields as $k=>$v ) {
 371  	 	 	 static $i = 0;
 372  	 	 	 if (is_array( $v )) {
 373  	 	 	 	 if ( $v['count'] == 1 ) {
 374  	 	 	 	 	 $results[$i] = $v[0];
 375  	 	 	 	 } else {
 376  	 	 	 	 	 array_shift( $v );
 377  	 	 	 	 	 $results[$i] = $v;
 378  	 	 	 	 }
 379  	 	 	 	 $i++;
 380  	 	 	 }
 381  	 	 }
 382  	 	 return $results;
 383  	 }
 384  
 385  	function _fetch()
 386  	 {
 387  	 	 if ( $this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0 ) {
 388  	 	 	 return false;
 389  	 	 }
 390  
 391  	 	 if ( $this->_currentRow == 0 ) {
 392  	 	 	 $this->_entryID = ldap_first_entry( $this->connection->_connectionID, $this->_queryID );
 393  	 	 } else {
 394  	 	 	 $this->_entryID = ldap_next_entry( $this->connection->_connectionID, $this->_entryID );
 395  	 	 }
 396  
 397  	 	 $this->fields = ldap_get_attributes( $this->connection->_connectionID, $this->_entryID );
 398  	 	 $this->_numOfFields = $this->fields['count'];
 399  
 400  	 	 switch ( $this->fetchMode ) {
 401  
 402  	 	 	 case LDAP_ASSOC:
 403  	 	 	 	 $this->fields = $this->GetRowAssoc();
 404  	 	 	 	 break;
 405  
 406  	 	 	 case LDAP_NUM:
 407  	 	 	 	 $this->fields = array_merge($this->GetRowNums(),$this->GetRowAssoc());
 408  	 	 	 	 break;
 409  
 410  	 	 	 case LDAP_BOTH:
 411  	 	 	 default:
 412  	 	 	 	 $this->fields = $this->GetRowNums();
 413  	 	 	 	 break;
 414  	 	 }
 415  
 416  	 	 return is_array( $this->fields );
 417  	 }
 418  
 419  	function _close() {
 420  	 	 @ldap_free_result( $this->_queryID );
 421  	 	 $this->_queryID = false;
 422  	 }
 423  
 424  }