Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

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