Completed
Branch develop (2c126c)
by Laurent
24:08
created

Ldap::setVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2004		Rodolphe Quiedeville <[email protected]>
3
 * Copyright (C) 2004		Benoit Mortier       <[email protected]>
4
 * Copyright (C) 2005-2017	Regis Houssin        <[email protected]>
5
 * Copyright (C) 2006-2015	Laurent Destailleur  <[email protected]>
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19
 * or see http://www.gnu.org/
20
 */
21
22
/**
23
 *	\file 		htdocs/core/class/ldap.class.php
24
 *	\brief 		File of class to manage LDAP features
25
 */
26
27
/**
28
 *	Class to manage LDAP features
29
 */
30
class Ldap
31
{
32
	var $error;
33
34
	/**
35
	 * Tableau des serveurs (IP addresses ou nom d'hotes)
36
	 */
37
	var $server=array();
38
	/**
39
	 * Base DN (e.g. "dc=foo,dc=com")
40
	 */
41
	var $dn;
42
	/**
43
	 * type de serveur, actuellement OpenLdap et Active Directory
44
	 */
45
	var $serverType;
46
	/**
47
	 * Version du protocole ldap
48
	 */
49
	var $domain;
50
	/**
51
	 * User administrateur Ldap
52
	 * Active Directory ne supporte pas les connexions anonymes
53
	 */
54
	var $searchUser;
55
	/**
56
	 * Mot de passe de l'administrateur
57
	 * Active Directory ne supporte pas les connexions anonymes
58
	 */
59
	var $searchPassword;
60
	/**
61
	 *  DN des utilisateurs
62
	 */
63
	var $people;
64
	/**
65
	 * DN des groupes
66
	 */
67
	var $groups;
68
	/**
69
	 * Code erreur retourne par le serveur Ldap
70
	 */
71
	var $ldapErrorCode;
72
	/**
73
	 * Message texte de l'erreur
74
	 */
75
	var $ldapErrorText;
76
77
78
	//Fetch user
79
	var $name;
80
	var $firstname;
81
	var $login;
82
	var $phone;
83
	var $skype;
84
	var $fax;
85
	var $mail;
86
	var $mobile;
87
88
	var $uacf;
89
	var $pwdlastset;
90
91
	var $ldapcharset='UTF-8';	// LDAP should be UTF-8 encoded
92
93
94
	/**
95
	* The internal LDAP connection handle
96
	*/
97
	var $connection;
98
	/**
99
	 * Result of any connections etc.
100
	 */
101
	var $result;
102
103
104
	/**
105
	 *  Constructor
106
	 */
107
	function __construct()
108
	{
109
		global $conf;
110
111
		// Server
112
		if (! empty($conf->global->LDAP_SERVER_HOST))       $this->server[] = $conf->global->LDAP_SERVER_HOST;
113
		if (! empty($conf->global->LDAP_SERVER_HOST_SLAVE)) $this->server[] = $conf->global->LDAP_SERVER_HOST_SLAVE;
114
		$this->serverPort          = $conf->global->LDAP_SERVER_PORT;
115
		$this->ldapProtocolVersion = $conf->global->LDAP_SERVER_PROTOCOLVERSION;
116
		$this->dn                  = $conf->global->LDAP_SERVER_DN;
117
		$this->serverType          = $conf->global->LDAP_SERVER_TYPE;
118
		$this->domain              = $conf->global->LDAP_SERVER_DN;
119
		$this->searchUser          = $conf->global->LDAP_ADMIN_DN;
120
		$this->searchPassword      = $conf->global->LDAP_ADMIN_PASS;
121
		$this->people              = $conf->global->LDAP_USER_DN;
122
		$this->groups              = $conf->global->LDAP_GROUP_DN;
123
124
		$this->filter              = $conf->global->LDAP_FILTER_CONNECTION;	// Filter on user
125
		$this->filtermember        = $conf->global->LDAP_MEMBER_FILTER;		// Filter on member
126
127
		// Users
128
		$this->attr_login      = $conf->global->LDAP_FIELD_LOGIN; //unix
129
		$this->attr_sambalogin = $conf->global->LDAP_FIELD_LOGIN_SAMBA; //samba, activedirectory
130
		$this->attr_name       = $conf->global->LDAP_FIELD_NAME;
131
		$this->attr_firstname  = $conf->global->LDAP_FIELD_FIRSTNAME;
132
		$this->attr_mail       = $conf->global->LDAP_FIELD_MAIL;
133
		$this->attr_phone      = $conf->global->LDAP_FIELD_PHONE;
134
		$this->attr_skype      = $conf->global->LDAP_FIELD_SKYPE;
135
		$this->attr_fax        = $conf->global->LDAP_FIELD_FAX;
136
		$this->attr_mobile     = $conf->global->LDAP_FIELD_MOBILE;
137
	}
138
139
140
141
	// Connection handling methods -------------------------------------------
142
143
	/**
144
	 *	Connect and bind
145
	 * 	Use this->server, this->serverPort, this->ldapProtocolVersion, this->serverType, this->searchUser, this->searchPassword
146
	 * 	After return, this->connection and $this->bind are defined
147
	 *
148
	 *	@return		int		<0 if KO, 1 if bind anonymous, 2 if bind auth
149
	 */
150
	function connect_bind()
151
	{
152
		global $langs, $conf;
153
154
		$connected=0;
155
		$this->bind=0;
156
157
		// Check parameters
158
		if (count($this->server) == 0 || empty($this->server[0]))
159
		{
160
			$this->error='LDAP setup (file conf.php) is not complete';
161
			dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
162
			return -1;
163
		}
164
165
		if (! function_exists("ldap_connect"))
166
		{
167
			$this->error='LDAPFunctionsNotAvailableOnPHP';
168
			dol_syslog(get_class($this)."::connect_bind ".$this->error, LOG_WARNING);
169
			$return=-1;
170
		}
171
172
		if (empty($this->error))
173
		{
174
			// Loop on each ldap server
175
			foreach ($this->server as $key => $host)
176
			{
177
				if ($connected) break;
178
				if (empty($host)) continue;
179
180
				if (preg_match('/^ldap/',$host))
181
				{
182
					if ($this->serverPing($host) === true) {
183
						$this->connection = ldap_connect($host);
184
					}
185
					else continue;
186
				}
187
				else
188
				{
189
					if ($this->serverPing($host, $this->serverPort) === true) {
190
						$this->connection = ldap_connect($host,$this->serverPort);
191
					}
192
					else continue;
193
				}
194
195
				if (is_resource($this->connection))
196
				{
197
					// Begin TLS if requested by the configuration
198
					if (! empty($conf->global->LDAP_SERVER_USE_TLS))
199
					{
200
						if (! ldap_start_tls($this->connection))
201
						{
202
							dol_syslog(get_class($this)."::connect_bind failed to start tls", LOG_WARNING);
203
							$connected = 0;
204
							$this->close();
205
						}
206
					}
207
208
					// Execute the ldap_set_option here (after connect and before bind)
209
					$this->setVersion();
210
					ldap_set_option($this->connection, LDAP_OPT_SIZELIMIT, 0); // no limit here. should return true.
211
212
213
					if ($this->serverType == "activedirectory")
214
					{
215
						$result=$this->setReferrals();
216
						dol_syslog(get_class($this)."::connect_bind try bindauth for activedirectory on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
217
						$this->result=$this->bindauth($this->searchUser,$this->searchPassword);
218
						if ($this->result)
219
						{
220
							$this->bind=$this->result;
221
							$connected=2;
222
							break;
223
						}
224
						else
225
						{
226
							$this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
227
						}
228
					}
229
					else
230
					{
231
						// Try in auth mode
232
						if ($this->searchUser && $this->searchPassword)
233
						{
234
							dol_syslog(get_class($this)."::connect_bind try bindauth on ".$host." user=".$this->searchUser." password=".preg_replace('/./','*',$this->searchPassword),LOG_DEBUG);
235
							$this->result=$this->bindauth($this->searchUser,$this->searchPassword);
236
							if ($this->result)
237
							{
238
								$this->bind=$this->result;
239
								$connected=2;
240
								break;
241
							}
242
							else
243
							{
244
								$this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
245
							}
246
						}
247
						// Try in anonymous
248
						if (! $this->bind)
249
						{
250
							dol_syslog(get_class($this)."::connect_bind try bind on ".$host,LOG_DEBUG);
251
							$result=$this->bind();
252
							if ($result)
253
							{
254
								$this->bind=$this->result;
255
								$connected=1;
256
								break;
257
							}
258
							else
259
							{
260
								$this->error=ldap_errno($this->connection).' '.ldap_error($this->connection);
261
							}
262
						}
263
					}
264
				}
265
266
				if (! $connected) $this->close();
267
			}
268
		}
269
270
		if ($connected)
271
		{
272
			$return=$connected;
273
			dol_syslog(get_class($this)."::connect_bind return=".$return, LOG_DEBUG);
274
		}
275
		else
276
		{
277
			$this->error='Failed to connect to LDAP'.($this->error?': '.$this->error:'');
278
			$return=-1;
279
			dol_syslog(get_class($this)."::connect_bind return=".$return.' - '.$this->error, LOG_WARNING);
280
		}
281
		return $return;
282
	}
283
284
285
286
	/**
287
	 * Simply closes the connection set up earlier.
288
	 * Returns true if OK, false if there was an error.
289
	 *
290
	 * @return	boolean			true or false
291
	 */
292
	function close()
293
	{
294
		if ($this->connection && ! @ldap_close($this->connection))
295
		{
296
			return false;
297
		}
298
		else
299
		{
300
			return true;
301
		}
302
	}
303
304
	/**
305
	 * Anonymously binds to the connection. After this is done,
306
	 * queries and searches can be done - but read-only.
307
	 *
308
	 * @return	boolean			true or false
309
	 */
310
	function bind()
311
	{
312
		if (! $this->result=@ldap_bind($this->connection))
313
		{
314
			$this->ldapErrorCode = ldap_errno($this->connection);
315
			$this->ldapErrorText = ldap_error($this->connection);
316
			$this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
317
			return false;
318
		}
319
		else
320
		{
321
			return true;
322
		}
323
	}
324
325
	/**
326
	 * Binds as an authenticated user, which usually allows for write
327
	 * access. The FULL dn must be passed. For a directory manager, this is
328
	 * "cn=Directory Manager" under iPlanet. For a user, it will be something
329
	 * like "uid=jbloggs,ou=People,dc=foo,dc=com".
330
	 *
331
	 * @param	string	$bindDn			DN
332
	 * @param	string	$pass			Password
333
	 * @return	boolean					true or false
334
	 */
335
	function bindauth($bindDn,$pass)
336
	{
337
		if (! $this->result = @ldap_bind($this->connection, $bindDn, $pass))
338
		{
339
			$this->ldapErrorCode = ldap_errno($this->connection);
340
			$this->ldapErrorText = ldap_error($this->connection);
341
			$this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
342
			return false;
343
		}
344
		else
345
		{
346
			return true;
347
		}
348
	}
349
350
	/**
351
	 * Unbind du serveur ldap.
352
	 *
353
	 * @return	boolean					true or false
354
	 */
355
	function unbind()
356
	{
357
		if (!$this->result=@ldap_unbind($this->connection))
358
		{
359
			return false;
360
		} else {
361
			return true;
362
		}
363
	}
364
365
366
	/**
367
	 * Verification de la version du serveur ldap.
368
	 *
369
	 * @return	string					version
370
	 */
371
	function getVersion()
372
	{
373
		$version = 0;
374
		$version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
375
		return $version;
376
	}
377
378
	/**
379
	 * Change ldap protocol version to use.
380
	 *
381
	 * @return	boolean					version
382
	 */
383
	function setVersion() {
384
		// LDAP_OPT_PROTOCOL_VERSION est une constante qui vaut 17
385
		$ldapsetversion = ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $this->ldapProtocolVersion);
386
		return $ldapsetversion;
387
	}
388
389
	/**
390
	 * changement du referrals.
391
	 *
392
	 * @return	boolean					referrals
393
	 */
394
	function setReferrals() {
395
		// LDAP_OPT_REFERRALS est une constante qui vaut ?
396
		$ldapreferrals = ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
397
		return $ldapreferrals;
398
	}
399
400
401
	/**
402
	 * 	Add a LDAP entry
403
	 *	Ldap object connect and bind must have been done
404
	 *
405
	 *	@param	string	$dn			DN entry key
406
	 *	@param	array	$info		Attributes array
407
	 *	@param	User		$user		Objet user that create
408
	 *	@return	int					<0 if KO, >0 if OK
409
	 */
410
	function add($dn, $info, $user)
411
	{
412
		global $conf;
413
414
		dol_syslog(get_class($this)."::add dn=".$dn." info=".join(',',$info));
415
416
		// Check parameters
417
		if (! $this->connection)
418
		{
419
			$this->error="NotConnected";
420
			return -2;
421
		}
422
		if (! $this->bind)
423
		{
424
			$this->error="NotConnected";
425
			return -3;
426
		}
427
428
		// Encode to LDAP page code
429
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
430
		foreach($info as $key => $val)
431
		{
432
			if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
433
		}
434
435
		$this->dump($dn,$info);
436
437
		//print_r($info);
438
		$result=@ldap_add($this->connection, $dn, $info);
439
440
		if ($result)
441
		{
442
			dol_syslog(get_class($this)."::add successfull", LOG_DEBUG);
443
			return 1;
444
		}
445
		else
446
		{
447
			$this->ldapErrorCode = @ldap_errno($this->connection);
448
			$this->ldapErrorText = @ldap_error($this->connection);
449
			$this->error=$this->ldapErrorCode." ".$this->ldapErrorText;
450
			dol_syslog(get_class($this)."::add failed: ".$this->error, LOG_ERR);
451
			return -1;
452
		}
453
	}
454
455
	/**
456
	 * 	Modify a LDAP entry
457
	 *	Ldap object connect and bind must have been done
458
	 *
459
	 *	@param	string		$dn			DN entry key
460
	 *	@param	array		$info		Attributes array
461
	 *	@param	User			$user		Objet user that modify
462
	 *	@return	int						<0 if KO, >0 if OK
463
	 */
464
	function modify($dn, $info, $user)
465
	{
466
		global $conf;
467
468
		dol_syslog(get_class($this)."::modify dn=".$dn." info=".join(',',$info));
469
470
		// Check parameters
471
		if (! $this->connection)
472
		{
473
			$this->error="NotConnected";
474
			return -2;
475
		}
476
		if (! $this->bind)
477
		{
478
			$this->error="NotConnected";
479
			return -3;
480
		}
481
482
		// Encode to LDAP page code
483
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
484
		foreach($info as $key => $val)
485
		{
486
			if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
487
		}
488
489
		$this->dump($dn,$info);
490
491
		//print_r($info);
492
		$result=@ldap_modify($this->connection, $dn, $info);
493
494
		if ($result)
495
		{
496
			dol_syslog(get_class($this)."::modify successfull", LOG_DEBUG);
497
			return 1;
498
		}
499
		else
500
		{
501
			$this->error=@ldap_error($this->connection);
502
			dol_syslog(get_class($this)."::modify failed: ".$this->error, LOG_ERR);
503
			return -1;
504
		}
505
	}
506
507
	/**
508
	 * 	Rename a LDAP entry
509
	 *	Ldap object connect and bind must have been done
510
	 *
511
	 *	@param	string		$dn				Old DN entry key (uid=qqq,ou=xxx,dc=aaa,dc=bbb) (before update)
512
	 *	@param	string		$newrdn			New RDN entry key (uid=qqq)
513
	 *	@param	string		$newparent		New parent (ou=xxx,dc=aaa,dc=bbb)
514
	 *	@param	User			$user			Objet user that modify
515
	 *	@param	bool			$deleteoldrdn	If TRUE the old RDN value(s) is removed, else the old RDN value(s) is retained as non-distinguished values of the entry.
516
	 *	@return	int							<0 if KO, >0 if OK
517
	 */
518
	function rename($dn, $newrdn, $newparent, $user, $deleteoldrdn = true)
519
	{
520
		global $conf;
521
522
		dol_syslog(get_class($this)."::modify dn=".$dn." newrdn=".$newrdn." newparent=".$newparent." deleteoldrdn=".($deleteoldrdn?1:0));
523
524
		// Check parameters
525
		if (! $this->connection)
526
		{
527
			$this->error="NotConnected";
528
			return -2;
529
		}
530
		if (! $this->bind)
531
		{
532
			$this->error="NotConnected";
533
			return -3;
534
		}
535
536
		// Encode to LDAP page code
537
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
538
		$newrdn=$this->convFromOutputCharset($newrdn,$this->ldapcharset);
539
		$newparent=$this->convFromOutputCharset($newparent,$this->ldapcharset);
540
541
		//print_r($info);
542
		$result=@ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
543
544
		if ($result)
545
		{
546
			dol_syslog(get_class($this)."::rename successfull", LOG_DEBUG);
547
			return 1;
548
		}
549
		else
550
		{
551
			$this->error=@ldap_error($this->connection);
552
			dol_syslog(get_class($this)."::rename failed: ".$this->error, LOG_ERR);
553
			return -1;
554
		}
555
	}
556
557
	/**
558
	 *  Modify a LDAP entry (to use if dn != olddn)
559
	 *	Ldap object connect and bind must have been done
560
	 *
561
	 *  @param	string	$dn			DN entry key
562
	 *  @param	array	$info		Attributes array
563
	 *  @param	User		$user		Objet user that update
564
	 * 	@param	string	$olddn		Old DN entry key (before update)
565
	 * 	@param	string	$newrdn		New RDN entry key (uid=qqq) (for ldap_rename)
566
	 *	@param	string	$newparent	New parent (ou=xxx,dc=aaa,dc=bbb) (for ldap_rename)
567
	 *	@return	int					<0 if KO, >0 if OK
568
	 */
569
	function update($dn, $info, $user, $olddn, $newrdn=false, $newparent=false)
570
	{
571
		global $conf;
572
573
		dol_syslog(get_class($this)."::update dn=".$dn." olddn=".$olddn);
574
575
		// Check parameters
576
		if (! $this->connection)
577
		{
578
			$this->error="NotConnected";
579
			return -2;
580
		}
581
		if (! $this->bind)
582
		{
583
			$this->error="NotConnected";
584
			return -3;
585
		}
586
587
		if (! $olddn || $olddn != $dn)
588
		{
589
			if (! empty($olddn) && ! empty($newrdn) && ! empty($newparent) && $conf->global->LDAP_SERVER_PROTOCOLVERSION === '3')
590
			{
591
				// This function currently only works with LDAPv3
592
				$result = $this->rename($olddn, $newrdn, $newparent, $user, true);
593
			}
594
			else
595
			{
596
				// If change we make is rename the key of LDAP record, we create new one and if ok, we delete old one.
597
				$result = $this->add($dn, $info, $user);
598
				if ($result > 0 && $olddn && $olddn != $dn) $result = $this->delete($olddn);	// If add fails, we do not try to delete old one
599
			}
600
		}
601
		else
602
		{
603
			//$result = $this->delete($olddn);
604
			$result = $this->add($dn, $info, $user);	// If record has been deleted from LDAP, we recreate it. We ignore error if it already exists.
605
			$result = $this->modify($dn, $info, $user);	// We use add/modify instead of delete/add when olddn is received
606
		}
607
		if ($result <= 0)
608
		{
609
			$this->error = ldap_errno($this->connection)." ".ldap_error($this->connection)." ".$this->error;
610
			dol_syslog(get_class($this)."::update ".$this->error,LOG_ERR);
611
			//print_r($info);
612
			return -1;
613
		}
614
		else
615
		{
616
			dol_syslog(get_class($this)."::update done successfully");
617
			return 1;
618
		}
619
	}
620
621
622
	/**
623
	 * 	Delete a LDAP entry
624
	 *	Ldap object connect and bind must have been done
625
	 *
626
	 *	@param	string	$dn			DN entry key
627
	 *	@return	int					<0 if KO, >0 if OK
628
	 */
629
	function delete($dn)
630
	{
631
		global $conf;
632
633
		dol_syslog(get_class($this)."::delete Delete LDAP entry dn=".$dn);
634
635
		// Check parameters
636
		if (! $this->connection)
637
		{
638
			$this->error="NotConnected";
639
			return -2;
640
		}
641
		if (! $this->bind)
642
		{
643
			$this->error="NotConnected";
644
			return -3;
645
		}
646
647
		// Encode to LDAP page code
648
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
649
650
		$result=@ldap_delete($this->connection, $dn);
651
652
		if ($result) return 1;
653
		return -1;
654
	}
655
656
	/**
657
	 * 	Build a LDAP message
658
	 *
659
	 *	@param	string		$dn			DN entry key
660
	 *	@param	array		$info		Attributes array
661
	 *	@return	string					Content of file
662
	 */
663
	function dump_content($dn, $info)
664
	{
665
		$content='';
666
667
		// Create file content
668
		if (preg_match('/^ldap/',$this->server[0]))
669
		{
670
			$target="-H ".join(',',$this->server);
671
		}
672
		else
673
		{
674
			$target="-h ".join(',',$this->server)." -p ".$this->serverPort;
675
		}
676
		$content.="# ldapadd $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
677
		$content.="# ldapmodify $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
678
		$content.="# ldapdelete $target -c -v -D ".$this->searchUser." -W -f ldapinput.in\n";
679
		if (in_array('localhost',$this->server)) $content.="# If commands fails to connect, try without -h and -p\n";
680
		$content.="dn: ".$dn."\n";
681
		foreach($info as $key => $value)
682
		{
683
			if (! is_array($value))
684
			{
685
				$content.="$key: $value\n";
686
			}
687
			else
688
			{
689
				foreach($value as $valuekey => $valuevalue)
690
				{
691
					$content.="$key: $valuevalue\n";
692
				}
693
			}
694
		}
695
		return $content;
696
	}
697
698
	/**
699
	 * 	Dump a LDAP message to ldapinput.in file
700
	 *
701
	 *	@param	string		$dn			DN entry key
702
	 *	@param	array		$info		Attributes array
703
	 *	@return	int						<0 if KO, >0 if OK
704
	 */
705
	function dump($dn, $info)
706
	{
707
		global $conf;
708
709
		// Create content
710
		$content=$this->dump_content($dn, $info);
711
712
		//Create file
713
		$result=dol_mkdir($conf->ldap->dir_temp);
714
715
		$outputfile=$conf->ldap->dir_temp.'/ldapinput.in';
716
		$fp=fopen($outputfile,"w");
717
		if ($fp)
718
		{
719
			fputs($fp, $content);
720
			fclose($fp);
721
			if (! empty($conf->global->MAIN_UMASK))
722
			@chmod($outputfile, octdec($conf->global->MAIN_UMASK));
723
			return 1;
724
		}
725
		else
726
		{
727
			return -1;
728
		}
729
	}
730
731
	/**
732
	 * Ping a server before ldap_connect for avoid waiting
733
	 *
734
	 * @param string		$host		Server host or address
735
	 * @param int		$port		Server port (default 389)
736
	 * @param int		$timeout		Timeout in second (default 1s)
737
	 * @return boolean				true or false
738
	 */
739
	function serverPing($host, $port=389, $timeout=1)
740
	{
741
		$op = @fsockopen($host, $port, $errno, $errstr, $timeout);
742
		if (!$op) return false; //DC is N/A
743
		else {
744
			fclose($op); //explicitly close open socket connection
745
			return true; //DC is up & running, we can safely connect with ldap_connect
746
		}
747
	}
748
749
750
	// Attribute methods -----------------------------------------------------
751
752
    /**
753
	 * 	Add a LDAP attribute in entry
754
	 *	Ldap object connect and bind must have been done
755
	 *
756
	 *	@param	string		$dn			DN entry key
757
	 *	@param	array		$info		Attributes array
758
	 *	@param	User		$user		Objet user that create
759
	 *	@return	int						<0 if KO, >0 if OK
760
	 */
761
	function addAttribute($dn, $info, $user)
762
	{
763
		global $conf;
764
765
		dol_syslog(get_class($this)."::addAttribute dn=".$dn." info=".join(',',$info));
766
767
		// Check parameters
768
		if (! $this->connection)
769
		{
770
			$this->error="NotConnected";
771
			return -2;
772
		}
773
		if (! $this->bind)
774
		{
775
			$this->error="NotConnected";
776
			return -3;
777
		}
778
779
		// Encode to LDAP page code
780
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
781
		foreach($info as $key => $val)
782
		{
783
			if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
784
		}
785
786
		$this->dump($dn,$info);
787
788
		//print_r($info);
789
		$result=@ldap_mod_add($this->connection, $dn, $info);
790
791
		if ($result)
792
		{
793
			dol_syslog(get_class($this)."::add_attribute successfull", LOG_DEBUG);
794
			return 1;
795
		}
796
		else
797
		{
798
			$this->error=@ldap_error($this->connection);
799
			dol_syslog(get_class($this)."::add_attribute failed: ".$this->error, LOG_ERR);
800
			return -1;
801
		}
802
	}
803
804
    /**
805
	 * 	Update a LDAP attribute in entry
806
	 *	Ldap object connect and bind must have been done
807
	 *
808
	 *	@param	string		$dn			DN entry key
809
	 *	@param	array		$info		Attributes array
810
	 *	@param	User		$user		Objet user that create
811
	 *	@return	int						<0 if KO, >0 if OK
812
	 */
813
	function updateAttribute($dn, $info, $user)
814
	{
815
		global $conf;
816
817
		dol_syslog(get_class($this)."::updateAttribute dn=".$dn." info=".join(',',$info));
818
819
		// Check parameters
820
		if (! $this->connection)
821
		{
822
			$this->error="NotConnected";
823
			return -2;
824
		}
825
		if (! $this->bind)
826
		{
827
			$this->error="NotConnected";
828
			return -3;
829
		}
830
831
		// Encode to LDAP page code
832
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
833
		foreach($info as $key => $val)
834
		{
835
			if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
836
		}
837
838
		$this->dump($dn,$info);
839
840
		//print_r($info);
841
		$result=@ldap_mod_replace($this->connection, $dn, $info);
842
843
		if ($result)
844
		{
845
			dol_syslog(get_class($this)."::updateAttribute successfull", LOG_DEBUG);
846
			return 1;
847
		}
848
		else
849
		{
850
			$this->error=@ldap_error($this->connection);
851
			dol_syslog(get_class($this)."::updateAttribute failed: ".$this->error, LOG_ERR);
852
			return -1;
853
		}
854
	}
855
856
    /**
857
	 * 	Delete a LDAP attribute in entry
858
	 *	Ldap object connect and bind must have been done
859
	 *
860
	 *	@param	string		$dn			DN entry key
861
	 *	@param	array		$info		Attributes array
862
	 *	@param	User		$user		Objet user that create
863
	 *	@return	int						<0 if KO, >0 if OK
864
	 */
865
	function deleteAttribute($dn, $info, $user)
866
	{
867
		global $conf;
868
869
		dol_syslog(get_class($this)."::deleteAttribute dn=".$dn." info=".join(',',$info));
870
871
		// Check parameters
872
		if (! $this->connection)
873
		{
874
			$this->error="NotConnected";
875
			return -2;
876
		}
877
		if (! $this->bind)
878
		{
879
			$this->error="NotConnected";
880
			return -3;
881
		}
882
883
		// Encode to LDAP page code
884
		$dn=$this->convFromOutputCharset($dn,$this->ldapcharset);
885
		foreach($info as $key => $val)
886
		{
887
			if (! is_array($val)) $info[$key]=$this->convFromOutputCharset($val,$this->ldapcharset);
888
		}
889
890
		$this->dump($dn,$info);
891
892
		//print_r($info);
893
		$result=@ldap_mod_del($this->connection, $dn, $info);
894
895
		if ($result)
896
		{
897
			dol_syslog(get_class($this)."::deleteAttribute successfull", LOG_DEBUG);
898
			return 1;
899
		}
900
		else
901
		{
902
			$this->error=@ldap_error($this->connection);
903
			dol_syslog(get_class($this)."::deleteAttribute failed: ".$this->error, LOG_ERR);
904
			return -1;
905
		}
906
	}
907
908
    /**
909
	 *  Returns an array containing attributes and values for first record
910
	 *
911
	 *	@param	string	$dn			DN entry key
912
	 *	@param	string	$filter		Filter
913
	 *	@return	int|array			<0 or false if KO, array if OK
914
	 */
915
	function getAttribute($dn,$filter)
916
	{
917
		// Check parameters
918
		if (! $this->connection)
919
		{
920
			$this->error="NotConnected";
921
			return -2;
922
		}
923
		if (! $this->bind)
924
		{
925
			$this->error="NotConnected";
926
			return -3;
927
		}
928
929
		$search = ldap_search($this->connection,$dn,$filter);
930
931
		// Only one entry should ever be returned
932
		$entry = ldap_first_entry($this->connection, $search);
933
934
		if (!$entry)
935
		{
936
			$this->ldapErrorCode = -1;
937
			$this->ldapErrorText = "Couldn't find entry";
938
			return 0;  // Couldn't find entry...
939
		}
940
941
		// Get values
942
		if (! $values = ldap_get_attributes($this->connection, $entry))
943
		{
944
			$this->ldapErrorCode = ldap_errno($this->connection);
945
			$this->ldapErrorText = ldap_error($this->connection);
946
			return 0; // No matching attributes
947
		}
948
949
		// Return an array containing the attributes.
950
		return $values;
951
	}
952
953
	/**
954
	 *  Returns an array containing values for an attribute and for first record matching filterrecord
955
	 *
956
	 * 	@param	string	$filterrecord		Record
957
	 * 	@param	string	$attribute			Attributes
958
	 * 	@return void
959
	 */
960
	function getAttributeValues($filterrecord,$attribute)
961
	{
962
		$attributes=array();
963
		$attributes[0] = $attribute;
964
965
		// We need to search for this user in order to get their entry.
966
		$this->result = @ldap_search($this->connection,$this->people,$filterrecord,$attributes);
967
968
		// Pourquoi cette ligne ?
969
		//$info = ldap_get_entries($this->connection, $this->result);
970
971
		// Only one entry should ever be returned (no user will have the same uid)
972
		$entry = ldap_first_entry($this->connection, $this->result);
973
974
		if (!$entry)
975
		{
976
			$this->ldapErrorCode = -1;
977
			$this->ldapErrorText = "Couldn't find user";
978
			return false;  // Couldn't find the user...
979
		}
980
981
		// Get values
982
		if (! $values = @ldap_get_values($this->connection, $entry, $attribute))
983
		{
984
			$this->ldapErrorCode = ldap_errno($this->connection);
985
			$this->ldapErrorText = ldap_error($this->connection);
986
			return false; // No matching attributes
987
		}
988
989
		// Return an array containing the attributes.
990
		return $values;
991
	}
992
993
	/**
994
	 * 	Returns an array containing a details or list of LDAP record(s)
995
	 * 	ldapsearch -LLLx -hlocalhost -Dcn=admin,dc=parinux,dc=org -w password -b "ou=adherents,ou=people,dc=parinux,dc=org" userPassword
996
	 *
997
	 *	@param	string	$search			 	Value of fiel to search, '*' for all. Not used if $activefilter is set.
998
	 *	@param	string	$userDn			 	DN (Ex: ou=adherents,ou=people,dc=parinux,dc=org)
999
	 *	@param	string	$useridentifier 	Name of key field (Ex: uid)
1000
	 *	@param	array	$attributeArray 	Array of fields required. Note this array must also contains field $useridentifier (Ex: sn,userPassword)
1001
	 *	@param	int		$activefilter		'1' or 'user'=use field this->filter as filter instead of parameter $search, 'member'=use field this->filtermember as filter
1002
	 *	@param	array	$attributeAsArray 	Array of fields wanted as an array not a string
1003
	 *	@return	array						Array of [id_record][ldap_field]=value
1004
	 */
1005
	function getRecords($search, $userDn, $useridentifier, $attributeArray, $activefilter=0, $attributeAsArray=array())
1006
	{
1007
		$fulllist=array();
1008
1009
		dol_syslog(get_class($this)."::getRecords search=".$search." userDn=".$userDn." useridentifier=".$useridentifier." attributeArray=array(".join(',',$attributeArray).") activefilter=".$activefilter);
1010
1011
		// if the directory is AD, then bind first with the search user first
1012
		if ($this->serverType == "activedirectory")
1013
		{
1014
			$this->bindauth($this->searchUser, $this->searchPassword);
1015
			dol_syslog(get_class($this)."::bindauth serverType=activedirectory searchUser=".$this->searchUser);
1016
		}
1017
1018
		// Define filter
1019
		if (! empty($activefilter))
1020
		{
1021
			if (((string) $activefilter == '1' || (string) $activefilter == 'user') && $this->filter)
1022
			{
1023
				$filter = '('.$this->filter.')';
1024
			}
1025
			elseif (((string) $activefilter == 'member') && $this->filter)
1026
			{
1027
				$filter = '('.$this->filtermember.')';
1028
			}
1029
			else	// If this->filter is empty, make fiter on * (all)
1030
			{
1031
				$filter = '('.$useridentifier.'=*)';
1032
			}
1033
		}
1034
		else
1035
		{
1036
			$filter = '('.$useridentifier.'='.$search.')';
1037
		}
1038
1039
		if (is_array($attributeArray))
1040
		{
1041
			// Return list with required fields
1042
			$attributeArray=array_values($attributeArray);	// This is to force to have index reordered from 0 (not make ldap_search fails)
1043
			dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter. " attributeArray=(".join(',',$attributeArray).")");
1044
			//var_dump($attributeArray);
1045
			$this->result = @ldap_search($this->connection, $userDn, $filter, $attributeArray);
1046
		}
1047
		else
1048
		{
1049
			// Return list with fields selected by default
1050
			dol_syslog(get_class($this)."::getRecords connection=".$this->connection." userDn=".$userDn." filter=".$filter);
1051
			$this->result = @ldap_search($this->connection, $userDn, $filter);
1052
		}
1053
		if (!$this->result)
1054
		{
1055
			$this->error = 'LDAP search failed: '.ldap_errno($this->connection)." ".ldap_error($this->connection);
1056
			return -1;
1057
		}
1058
1059
		$info = @ldap_get_entries($this->connection, $this->result);
1060
1061
		// Warning: Dans info, les noms d'attributs sont en minuscule meme si passe
1062
		// a ldap_search en majuscule !!!
1063
		//print_r($info);
1064
1065
		for ($i = 0; $i < $info["count"]; $i++)
1066
		{
1067
			$recordid=$this->convToOutputCharset($info[$i][$useridentifier][0],$this->ldapcharset);
1068
			if ($recordid)
1069
			{
1070
				//print "Found record with key $useridentifier=".$recordid."<br>\n";
1071
				$fulllist[$recordid][$useridentifier]=$recordid;
1072
1073
				// Add to the array for each attribute in my list
1074
				$num = count($attributeArray);
1075
				for ($j = 0; $j < $num; $j++)
1076
				{
1077
					$keyattributelower=strtolower($attributeArray[$j]);
1078
					//print " Param ".$attributeArray[$j]."=".$info[$i][$keyattributelower][0]."<br>\n";
1079
1080
					//permet de recuperer le SID avec Active Directory
1081
					if ($this->serverType == "activedirectory" && $keyattributelower == "objectsid")
1082
					{
1083
						$objectsid = $this->getObjectSid($recordid);
1084
						$fulllist[$recordid][$attributeArray[$j]]    = $objectsid;
1085
					}
1086
					else
1087
					{
1088
						if(in_array($attributeArray[$j], $attributeAsArray) && is_array($info[$i][$keyattributelower])) {
1089
							$valueTab = array();
1090
							foreach($info[$i][$keyattributelower] as $key => $value) {
1091
								$valueTab[$key] = $this->convToOutputCharset($value,$this->ldapcharset);
1092
							}
1093
							$fulllist[$recordid][$attributeArray[$j]] = $valueTab;
1094
						} else {
1095
							$fulllist[$recordid][$attributeArray[$j]] = $this->convToOutputCharset($info[$i][$keyattributelower][0],$this->ldapcharset);
1096
						}
1097
					}
1098
				}
1099
			}
1100
		}
1101
1102
		asort($fulllist);
1103
		return $fulllist;
1104
	}
1105
1106
	/**
1107
	 *  Converts a little-endian hex-number to one, that 'hexdec' can convert
1108
	 *	Required by Active Directory
1109
	 *
1110
	 *	@param	string		$hex			Hex value
1111
	 *	@return	string						Little endian
1112
	 */
1113
	function littleEndian($hex)
1114
	{
1115
		for ($x=dol_strlen($hex)-2; $x >= 0; $x=$x-2) {
1116
			$result .= substr($hex,$x,2);
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1117
		}
1118
		return $result;
1119
	}
1120
1121
1122
	/**
1123
	 *  Recupere le SID de l'utilisateur
1124
	 *	Required by Active Directory
1125
	 *
1126
	 * 	@param	string		$ldapUser		Login de l'utilisateur
1127
	 * 	@return	string						Sid
1128
	 */
1129
	function getObjectSid($ldapUser)
1130
	{
1131
		$criteria =  '('.$this->getUserIdentifier().'='.$ldapUser.')';
1132
		$justthese = array("objectsid");
1133
1134
		// if the directory is AD, then bind first with the search user first
1135
		if ($this->serverType == "activedirectory")
1136
		{
1137
			$this->bindauth($this->searchUser, $this->searchPassword);
1138
		}
1139
1140
		$i = 0;
1141
		$searchDN = $this->people;
1142
1143
		while ($i <= 2)
1144
		{
1145
			$ldapSearchResult = @ldap_search($this->connection, $searchDN, $criteria, $justthese);
1146
1147
			if (!$ldapSearchResult)
1148
			{
1149
				$this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1150
				return -1;
1151
			}
1152
1153
			$entry = ldap_first_entry($this->connection, $ldapSearchResult);
1154
1155
			if (!$entry)
1156
			{
1157
				// Si pas de resultat on cherche dans le domaine
1158
				$searchDN = $this->domain;
1159
				$i++;
1160
			}
1161
			else
1162
			{
1163
				$i++;
1164
				$i++;
1165
			}
1166
		}
1167
1168
		if ($entry)
1169
		{
1170
			$ldapBinary = ldap_get_values_len($this->connection, $entry, "objectsid");
0 ignored issues
show
Bug introduced by
The variable $entry does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1171
			$SIDText = $this->binSIDtoText($ldapBinary[0]);
1172
			return $SIDText;
1173
		}
1174
		else
1175
		{
1176
			$this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1177
			return '?';
1178
		}
1179
	}
1180
1181
	/**
1182
	 * Returns the textual SID
1183
	 * Indispensable pour Active Directory
1184
	 *
1185
	 * @param	string	$binsid		Binary SID
1186
	 * @return	string				Textual SID
1187
	 */
1188
	function binSIDtoText($binsid)
1189
	{
1190
		$hex_sid=bin2hex($binsid);
1191
		$rev = hexdec(substr($hex_sid,0,2));          // Get revision-part of SID
1192
		$subcount = hexdec(substr($hex_sid,2,2));    // Get count of sub-auth entries
1193
		$auth = hexdec(substr($hex_sid,4,12));      // SECURITY_NT_AUTHORITY
1194
		$result = "$rev-$auth";
1195
		for ($x=0;$x < $subcount; $x++)
1196
		{
1197
			$result .= "-".hexdec($this->littleEndian(substr($hex_sid,16+($x*8),8)));  // get all SECURITY_NT_AUTHORITY
1198
		}
1199
		return $result;
1200
	}
1201
1202
1203
	/**
1204
	 * 	Fonction de recherche avec filtre
1205
	 *	this->connection doit etre defini donc la methode bind ou bindauth doit avoir deja ete appelee
1206
	 *	Ne pas utiliser pour recherche d'une liste donnee de proprietes
1207
	 *	car conflit majuscule-minuscule. A n'utiliser que pour les pages
1208
	 *	'Fiche LDAP' qui affiche champ lisibles par defaut.
1209
	 *
1210
	 * 	@param	string		$checkDn		DN de recherche (Ex: ou=users,cn=my-domain,cn=com)
1211
	 * 	@param 	string		$filter			Search filter (ex: (sn=nom_personne) )
1212
	 *	@return	array|int					Array with answers (key lowercased - value)
1213
	 */
1214
	function search($checkDn, $filter)
1215
	{
1216
		dol_syslog(get_class($this)."::search checkDn=".$checkDn." filter=".$filter);
1217
1218
		$checkDn=$this->convFromOutputCharset($checkDn,$this->ldapcharset);
1219
		$filter=$this->convFromOutputCharset($filter,$this->ldapcharset);
1220
1221
		// if the directory is AD, then bind first with the search user first
1222
		if ($this->serverType == "activedirectory") {
1223
			$this->bindauth($this->searchUser, $this->searchPassword);
1224
		}
1225
1226
		$this->result = @ldap_search($this->connection, $checkDn, $filter);
1227
1228
		$result = @ldap_get_entries($this->connection, $this->result);
1229
		if (! $result)
1230
		{
1231
			$this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1232
			return -1;
1233
		}
1234
		else
1235
		{
1236
			ldap_free_result($this->result);
1237
			return $result;
1238
		}
1239
	}
1240
1241
1242
	/**
1243
	 * 		Load all attribute of a LDAP user
1244
	 *
1245
	 * 		@param	User	$user		User to search for. Not used if a filter is provided.
1246
	 *      @param  string	$filter		Filter for search. Must start with &.
1247
	 *                       	       	Examples: &(objectClass=inetOrgPerson) &(objectClass=user)(objectCategory=person) &(isMemberOf=cn=Sales,ou=Groups,dc=opencsi,dc=com)
1248
	 *		@return	int					>0 if OK, <0 if KO
1249
	 */
1250
	function fetch($user,$filter)
1251
	{
1252
		// Perform the search and get the entry handles
1253
1254
		// if the directory is AD, then bind first with the search user first
1255
		if ($this->serverType == "activedirectory") {
1256
			$this->bindauth($this->searchUser, $this->searchPassword);
1257
		}
1258
1259
		$searchDN = $this->people;    // TODO Why searching in people then domain ?
1260
1261
		$result = '';
1262
		$i=0;
1263
		while ($i <= 2)
1264
		{
1265
		    dol_syslog(get_class($this)."::fetch search with searchDN=".$searchDN." filter=".$filter);
1266
			$this->result = @ldap_search($this->connection, $searchDN, $filter);
1267
			if ($this->result)
1268
			{
1269
				$result = @ldap_get_entries($this->connection, $this->result);
1270
				if ($result['count'] > 0) dol_syslog('Ldap::fetch search found '.$result['count'].' records');
1271
				else dol_syslog('Ldap::fetch search returns but found no records');
1272
				//var_dump($result);exit;
1273
			}
1274
			else
1275
			{
1276
			    $this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1277
                dol_syslog(get_class($this)."::fetch search fails");
1278
			    return -1;
1279
			}
1280
1281
			if (! $result)
1282
			{
1283
				// Si pas de resultat on cherche dans le domaine
1284
				$searchDN = $this->domain;
1285
				$i++;
1286
			}
1287
			else
1288
			{
1289
				break;
1290
			}
1291
		}
1292
1293
		if (! $result)
1294
		{
1295
			$this->error = ldap_errno($this->connection)." ".ldap_error($this->connection);
1296
			return -1;
1297
		}
1298
		else
1299
		{
1300
			$this->name       = $this->convToOutputCharset($result[0][$this->attr_name][0],$this->ldapcharset);
1301
			$this->firstname  = $this->convToOutputCharset($result[0][$this->attr_firstname][0],$this->ldapcharset);
1302
			$this->login      = $this->convToOutputCharset($result[0][$this->attr_login][0],$this->ldapcharset);
1303
			$this->phone      = $this->convToOutputCharset($result[0][$this->attr_phone][0],$this->ldapcharset);
1304
			$this->skype      = $this->convToOutputCharset($result[0][$this->attr_skype][0],$this->ldapcharset);
1305
			$this->fax        = $this->convToOutputCharset($result[0][$this->attr_fax][0],$this->ldapcharset);
1306
			$this->mail       = $this->convToOutputCharset($result[0][$this->attr_mail][0],$this->ldapcharset);
1307
			$this->mobile     = $this->convToOutputCharset($result[0][$this->attr_mobile][0],$this->ldapcharset);
1308
1309
			$this->uacf       = $this->parseUACF($this->convToOutputCharset($result[0]["useraccountcontrol"][0],$this->ldapcharset));
1310
			if (isset($result[0]["pwdlastset"][0]))	// If expiration on password exists
1311
			{
1312
				$this->pwdlastset = ($result[0]["pwdlastset"][0] != 0)?$this->convert_time($this->convToOutputCharset($result[0]["pwdlastset"][0],$this->ldapcharset)):0;
1313
			}
1314
			else
1315
			{
1316
				$this->pwdlastset = -1;
1317
			}
1318
			if (!$this->name && !$this->login) $this->pwdlastset = -1;
1319
			$this->badpwdtime = $this->convert_time($this->convToOutputCharset($result[0]["badpasswordtime"][0],$this->ldapcharset));
1320
1321
			// FQDN domain
1322
			$domain = str_replace('dc=','',$this->domain);
1323
			$domain = str_replace(',','.',$domain);
1324
			$this->domainFQDN = $domain;
1325
1326
			// Set ldapUserDn (each user can have a different dn)
1327
            //var_dump($result[0]);exit;
1328
			$this->ldapUserDN=$result[0]['dn'];
1329
1330
			ldap_free_result($this->result);
1331
			return 1;
1332
		}
1333
	}
1334
1335
1336
	// helper methods
1337
1338
	/**
1339
	 * 	Returns the correct user identifier to use, based on the ldap server type
1340
	 *
1341
	 *	@return	string 				Login
1342
	 */
1343
	function getUserIdentifier()
1344
	{
1345
		if ($this->serverType == "activedirectory") {
1346
			return $this->attr_sambalogin;
1347
		} else {
1348
			return $this->attr_login;
1349
		}
1350
	}
1351
1352
   /**
1353
	* 	UserAccountControl Flgs to more human understandable form...
1354
	*
1355
	*	@param	string		$uacf		UACF
1356
	*	@return	void
1357
	*/
1358
	function parseUACF($uacf)
1359
	{
1360
		//All flags array
1361
		$flags = array(
1362
			"TRUSTED_TO_AUTH_FOR_DELEGATION"  =>    16777216,
1363
			"PASSWORD_EXPIRED"                =>    8388608,
1364
			"DONT_REQ_PREAUTH"                =>    4194304,
1365
			"USE_DES_KEY_ONLY"                =>    2097152,
1366
			"NOT_DELEGATED"                   =>    1048576,
1367
			"TRUSTED_FOR_DELEGATION"          =>    524288,
1368
			"SMARTCARD_REQUIRED"              =>    262144,
1369
			"MNS_LOGON_ACCOUNT"               =>    131072,
1370
			"DONT_EXPIRE_PASSWORD"            =>    65536,
1371
			"SERVER_TRUST_ACCOUNT"            =>    8192,
1372
			"WORKSTATION_TRUST_ACCOUNT"       =>    4096,
1373
			"INTERDOMAIN_TRUST_ACCOUNT"       =>    2048,
1374
			"NORMAL_ACCOUNT"                  =>    512,
1375
			"TEMP_DUPLICATE_ACCOUNT"          =>    256,
1376
			"ENCRYPTED_TEXT_PWD_ALLOWED"      =>    128,
1377
			"PASSWD_CANT_CHANGE"              =>    64,
1378
			"PASSWD_NOTREQD"                  =>    32,
1379
			"LOCKOUT"                         =>    16,
1380
			"HOMEDIR_REQUIRED"                =>    8,
1381
			"ACCOUNTDISABLE"                  =>    2,
1382
			"SCRIPT"                          =>    1
1383
		);
1384
1385
		//Parse flags to text
1386
		$retval = array();
1387
		while (list($flag, $val) = each($flags)) {
1388
			if ($uacf >= $val) {
1389
				$uacf -= $val;
1390
				$retval[$val] = $flag;
1391
			}
1392
		}
1393
1394
		//Return human friendly flags
1395
		return($retval);
1396
	}
1397
1398
   /**
1399
	* 	SamAccountType value to text
1400
	*
1401
	*	@param	string	$samtype	SamType
1402
	*	@return	string				Sam string
1403
	*/
1404
	function parseSAT($samtype)
1405
	{
1406
		$stypes = array(
1407
			805306368    =>    "NORMAL_ACCOUNT",
1408
			805306369    =>    "WORKSTATION_TRUST",
1409
			805306370    =>    "INTERDOMAIN_TRUST",
1410
			268435456    =>    "SECURITY_GLOBAL_GROUP",
1411
			268435457    =>    "DISTRIBUTION_GROUP",
1412
			536870912    =>    "SECURITY_LOCAL_GROUP",
1413
			536870913    =>    "DISTRIBUTION_LOCAL_GROUP"
1414
		);
1415
1416
		$retval = "";
1417
		while (list($sat, $val) = each($stypes)) {
1418
			if ($samtype == $sat) {
1419
				$retval = $val;
1420
				break;
1421
			}
1422
		}
1423
		if (empty($retval)) $retval = "UNKNOWN_TYPE_" . $samtype;
1424
1425
		return($retval);
1426
	}
1427
1428
	/**
1429
	 *	Convertit le temps ActiveDirectory en Unix timestamp
1430
	 *
1431
	 *	@param	string	$value		AD time to convert
1432
	 *	@return	integer				Unix timestamp
1433
	 */
1434
	function convert_time($value)
1435
	{
1436
		$dateLargeInt=$value; // nano secondes depuis 1601 !!!!
1437
		$secsAfterADEpoch = $dateLargeInt / (10000000); // secondes depuis le 1 jan 1601
1438
		$ADToUnixConvertor=((1970-1601) * 365.242190) * 86400; // UNIX start date - AD start date * jours * secondes
1439
		$unixTimeStamp=intval($secsAfterADEpoch-$ADToUnixConvertor); // Unix time stamp
1440
		return $unixTimeStamp;
1441
	}
1442
1443
1444
	/**
1445
	 *  Convert a string into output/memory charset
1446
     *
1447
	 *  @param	string	$str            String to convert
1448
	 *  @param	string	$pagecodefrom	Page code of src string
1449
	 *  @return string         			Converted string
1450
	 */
1451
	private function convToOutputCharset($str,$pagecodefrom='UTF-8')
1452
	{
1453
		global $conf;
1454
		if ($pagecodefrom == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8')  $str=utf8_encode($str);
1455
		if ($pagecodefrom == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1')  $str=utf8_decode($str);
1456
		return $str;
1457
	}
1458
1459
	/**
1460
	 *  Convert a string from output/memory charset
1461
     *
1462
	 *  @param	string	$str            String to convert
1463
	 *  @param	string	$pagecodeto		Page code for result string
1464
	 *  @return string         			Converted string
1465
	 */
1466
	function convFromOutputCharset($str,$pagecodeto='UTF-8')
1467
	{
1468
		global $conf;
1469
		if ($pagecodeto == 'ISO-8859-1' && $conf->file->character_set_client == 'UTF-8') $str=utf8_decode($str);
1470
		if ($pagecodeto == 'UTF-8' && $conf->file->character_set_client == 'ISO-8859-1') $str=utf8_encode($str);
1471
		return $str;
1472
	}
1473
1474
1475
	/**
1476
	 *	Return available value of group GID
1477
	 *
1478
	 *	@param	string	$keygroup	Key of group
1479
	 *	@return	int					gid number
1480
	 */
1481
	function getNextGroupGid($keygroup='LDAP_KEY_GROUPS')
1482
	{
1483
		global $conf;
1484
1485
		if (empty($keygroup)) $keygroup='LDAP_KEY_GROUPS';
1486
1487
		$search='('.$conf->global->$keygroup.'=*)';
1488
		$result = $this->search($this->groups,$search);
1489
		if ($result)
1490
		{
1491
			$c = $result['count'];
1492
			$gids = array();
1493
			for($i=0;$i<$c;$i++)
1494
			{
1495
				$gids[] = $result[$i]['gidnumber'][0];
1496
			}
1497
			rsort($gids);
1498
1499
			return $gids[0]+1;
1500
		}
1501
1502
		return 0;
1503
	}
1504
}
1505