xref: /PHP-5.6/ext/ldap/ldap.c (revision 49782c54)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Amitay Isaacs  <amitay@w-o-i.com>                           |
16    |          Eric Warnke    <ericw@albany.edu>                           |
17    |          Rasmus Lerdorf <rasmus@php.net>                             |
18    |          Gerrit Thomson <334647@swin.edu.au>                         |
19    |          Jani Taskinen  <sniper@iki.fi>                              |
20    |          Stig Venaas    <venaas@uninett.no>                          |
21    |          Doug Goldstein <cardoe@cardoe.com>                          |
22    | PHP 4.0 updates:  Zeev Suraski <zeev@zend.com>                       |
23    +----------------------------------------------------------------------+
24  */
25 
26 /* $Id: 03ca03d3adee59787d58cd342d8f864c5d96252a $ */
27 #define IS_EXT_MODULE
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 /* Additional headers for NetWare */
34 #if defined(NETWARE) && (NEW_LIBC)
35 #include <sys/select.h>
36 #include <sys/timeval.h>
37 #endif
38 
39 #include "php.h"
40 #include "php_ini.h"
41 
42 #include <stddef.h>
43 
44 #include "ext/standard/dl.h"
45 #include "php_ldap.h"
46 
47 #ifdef PHP_WIN32
48 #include <string.h>
49 #include "config.w32.h"
50 #if HAVE_NSLDAP
51 #include <winsock2.h>
52 #endif
53 #define strdup _strdup
54 #undef WINDOWS
55 #undef strcasecmp
56 #undef strncasecmp
57 #define WINSOCK 1
58 #define __STDC__ 1
59 #endif
60 
61 #include "ext/standard/php_string.h"
62 #include "ext/standard/info.h"
63 
64 #ifdef HAVE_LDAP_SASL_H
65 #include <sasl.h>
66 #elif defined(HAVE_LDAP_SASL_SASL_H)
67 #include <sasl/sasl.h>
68 #endif
69 
70 #define PHP_LDAP_ESCAPE_FILTER 0x01
71 #define PHP_LDAP_ESCAPE_DN     0x02
72 
73 #if defined(LDAP_CONTROL_PAGEDRESULTS) && !defined(HAVE_LDAP_CONTROL_FIND)
ldap_control_find(const char * oid,LDAPControl ** ctrls,LDAPControl *** nextctrlp)74 LDAPControl *ldap_control_find( const char *oid, LDAPControl **ctrls, LDAPControl ***nextctrlp)
75 {
76 	assert(nextctrlp == NULL);
77 	return ldap_find_control(oid, ctrls);
78 }
79 #endif
80 
81 #if !defined(LDAP_API_FEATURE_X_OPENLDAP)
ldap_memvfree(void ** v)82 void ldap_memvfree(void **v)
83 {
84 	ldap_value_free((char **)v);
85 }
86 #endif
87 
88 typedef struct {
89 	LDAP *link;
90 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
91 	zval *rebindproc;
92 #endif
93 } ldap_linkdata;
94 
95 typedef struct {
96 	LDAPMessage *data;
97 	BerElement *ber;
98 	int id;
99 } ldap_resultentry;
100 
101 ZEND_DECLARE_MODULE_GLOBALS(ldap)
102 static PHP_GINIT_FUNCTION(ldap);
103 
104 static int le_link, le_result, le_result_entry;
105 
106 #ifdef COMPILE_DL_LDAP
ZEND_GET_MODULE(ldap)107 ZEND_GET_MODULE(ldap)
108 #endif
109 
110 static void _close_ldap_link(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
111 {
112 	ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr;
113 
114 	ldap_unbind_ext(ld->link, NULL, NULL);
115 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
116 
117 	if (ld->rebindproc != NULL) {
118 		zval_dtor(ld->rebindproc);
119 		FREE_ZVAL(ld->rebindproc);
120 	}
121 #endif
122 
123 	efree(ld);
124 	LDAPG(num_links)--;
125 }
126 /* }}} */
127 
_free_ldap_result(zend_rsrc_list_entry * rsrc TSRMLS_DC)128 static void _free_ldap_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
129 {
130 	LDAPMessage *result = (LDAPMessage *)rsrc->ptr;
131 	ldap_msgfree(result);
132 }
133 /* }}} */
134 
_free_ldap_result_entry(zend_rsrc_list_entry * rsrc TSRMLS_DC)135 static void _free_ldap_result_entry(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
136 {
137 	ldap_resultentry *entry = (ldap_resultentry *)rsrc->ptr;
138 
139 	if (entry->ber != NULL) {
140 		ber_free(entry->ber, 0);
141 		entry->ber = NULL;
142 	}
143 	zend_list_delete(entry->id);
144 	efree(entry);
145 }
146 /* }}} */
147 
148 /* {{{ PHP_INI_BEGIN
149  */
150 PHP_INI_BEGIN()
151 	STD_PHP_INI_ENTRY_EX("ldap.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_ldap_globals, ldap_globals, display_link_numbers)
PHP_INI_END()152 PHP_INI_END()
153 /* }}} */
154 
155 /* {{{ PHP_GINIT_FUNCTION
156  */
157 static PHP_GINIT_FUNCTION(ldap)
158 {
159 	ldap_globals->num_links = 0;
160 }
161 /* }}} */
162 
163 /* {{{ PHP_MINIT_FUNCTION
164  */
PHP_MINIT_FUNCTION(ldap)165 PHP_MINIT_FUNCTION(ldap)
166 {
167 	REGISTER_INI_ENTRIES();
168 
169 	/* Constants to be used with deref-parameter in php_ldap_do_search() */
170 	REGISTER_LONG_CONSTANT("LDAP_DEREF_NEVER", LDAP_DEREF_NEVER, CONST_PERSISTENT | CONST_CS);
171 	REGISTER_LONG_CONSTANT("LDAP_DEREF_SEARCHING", LDAP_DEREF_SEARCHING, CONST_PERSISTENT | CONST_CS);
172 	REGISTER_LONG_CONSTANT("LDAP_DEREF_FINDING", LDAP_DEREF_FINDING, CONST_PERSISTENT | CONST_CS);
173 	REGISTER_LONG_CONSTANT("LDAP_DEREF_ALWAYS", LDAP_DEREF_ALWAYS, CONST_PERSISTENT | CONST_CS);
174 
175 	/* Constants to be used with ldap_modify_batch() */
176 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_ADD", LDAP_MODIFY_BATCH_ADD, CONST_PERSISTENT | CONST_CS);
177 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE", LDAP_MODIFY_BATCH_REMOVE, CONST_PERSISTENT | CONST_CS);
178 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE_ALL", LDAP_MODIFY_BATCH_REMOVE_ALL, CONST_PERSISTENT | CONST_CS);
179 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REPLACE", LDAP_MODIFY_BATCH_REPLACE, CONST_PERSISTENT | CONST_CS);
180 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_ATTRIB", LDAP_MODIFY_BATCH_ATTRIB, CONST_PERSISTENT | CONST_CS);
181 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_MODTYPE", LDAP_MODIFY_BATCH_MODTYPE, CONST_PERSISTENT | CONST_CS);
182 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT | CONST_CS);
183 
184 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
185 	/* LDAP options */
186 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
187 	REGISTER_LONG_CONSTANT("LDAP_OPT_SIZELIMIT", LDAP_OPT_SIZELIMIT, CONST_PERSISTENT | CONST_CS);
188 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMELIMIT", LDAP_OPT_TIMELIMIT, CONST_PERSISTENT | CONST_CS);
189 #ifdef LDAP_OPT_NETWORK_TIMEOUT
190 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_OPT_NETWORK_TIMEOUT, CONST_PERSISTENT | CONST_CS);
191 #elif defined (LDAP_X_OPT_CONNECT_TIMEOUT)
192 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_X_OPT_CONNECT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
193 #endif
194 #ifdef LDAP_OPT_TIMEOUT
195 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMEOUT", LDAP_OPT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
196 #endif
197 	REGISTER_LONG_CONSTANT("LDAP_OPT_PROTOCOL_VERSION", LDAP_OPT_PROTOCOL_VERSION, CONST_PERSISTENT | CONST_CS);
198 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_NUMBER", LDAP_OPT_ERROR_NUMBER, CONST_PERSISTENT | CONST_CS);
199 	REGISTER_LONG_CONSTANT("LDAP_OPT_REFERRALS", LDAP_OPT_REFERRALS, CONST_PERSISTENT | CONST_CS);
200 #ifdef LDAP_OPT_RESTART
201 	REGISTER_LONG_CONSTANT("LDAP_OPT_RESTART", LDAP_OPT_RESTART, CONST_PERSISTENT | CONST_CS);
202 #endif
203 #ifdef LDAP_OPT_HOST_NAME
204 	REGISTER_LONG_CONSTANT("LDAP_OPT_HOST_NAME", LDAP_OPT_HOST_NAME, CONST_PERSISTENT | CONST_CS);
205 #endif
206 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_STRING", LDAP_OPT_ERROR_STRING, CONST_PERSISTENT | CONST_CS);
207 #ifdef LDAP_OPT_MATCHED_DN
208 	REGISTER_LONG_CONSTANT("LDAP_OPT_MATCHED_DN", LDAP_OPT_MATCHED_DN, CONST_PERSISTENT | CONST_CS);
209 #endif
210 	REGISTER_LONG_CONSTANT("LDAP_OPT_SERVER_CONTROLS", LDAP_OPT_SERVER_CONTROLS, CONST_PERSISTENT | CONST_CS);
211 	REGISTER_LONG_CONSTANT("LDAP_OPT_CLIENT_CONTROLS", LDAP_OPT_CLIENT_CONTROLS, CONST_PERSISTENT | CONST_CS);
212 #endif
213 #ifdef LDAP_OPT_DEBUG_LEVEL
214 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEBUG_LEVEL", LDAP_OPT_DEBUG_LEVEL, CONST_PERSISTENT | CONST_CS);
215 #endif
216 
217 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
218 	REGISTER_LONG_CONSTANT("LDAP_OPT_DIAGNOSTIC_MESSAGE", LDAP_OPT_DIAGNOSTIC_MESSAGE, CONST_PERSISTENT | CONST_CS);
219 #endif
220 
221 #ifdef HAVE_LDAP_SASL
222 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_MECH", LDAP_OPT_X_SASL_MECH, CONST_PERSISTENT | CONST_CS);
223 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_REALM", LDAP_OPT_X_SASL_REALM, CONST_PERSISTENT | CONST_CS);
224 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT | CONST_CS);
225 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT | CONST_CS);
226 #endif
227 
228 #ifdef ORALDAP
229 	REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT | CONST_CS);
230 	REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT | CONST_CS);
231 	REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT | CONST_CS);
232 #endif
233 
234 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
235 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT | CONST_CS);
236 
237 	le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number);
238 	le_result = zend_register_list_destructors_ex(_free_ldap_result, NULL, "ldap result", module_number);
239 	le_result_entry = zend_register_list_destructors_ex(_free_ldap_result_entry, NULL, "ldap result entry", module_number);
240 
241 	Z_TYPE(ldap_module_entry) = type;
242 
243 	return SUCCESS;
244 }
245 /* }}} */
246 
247 /* {{{ PHP_MSHUTDOWN_FUNCTION
248  */
PHP_MSHUTDOWN_FUNCTION(ldap)249 PHP_MSHUTDOWN_FUNCTION(ldap)
250 {
251 	UNREGISTER_INI_ENTRIES();
252 	return SUCCESS;
253 }
254 /* }}} */
255 
256 /* {{{ PHP_MINFO_FUNCTION
257  */
PHP_MINFO_FUNCTION(ldap)258 PHP_MINFO_FUNCTION(ldap)
259 {
260 	char tmp[32];
261 #if HAVE_NSLDAP
262 	LDAPVersion ver;
263 	double SDKVersion;
264 #endif
265 
266 	php_info_print_table_start();
267 	php_info_print_table_row(2, "LDAP Support", "enabled");
268 	php_info_print_table_row(2, "RCS Version", "$Id: 03ca03d3adee59787d58cd342d8f864c5d96252a $");
269 
270 	if (LDAPG(max_links) == -1) {
271 		snprintf(tmp, 31, "%ld/unlimited", LDAPG(num_links));
272 	} else {
273 		snprintf(tmp, 31, "%ld/%ld", LDAPG(num_links), LDAPG(max_links));
274 	}
275 	php_info_print_table_row(2, "Total Links", tmp);
276 
277 #ifdef LDAP_API_VERSION
278 	snprintf(tmp, 31, "%d", LDAP_API_VERSION);
279 	php_info_print_table_row(2, "API Version", tmp);
280 #endif
281 
282 #ifdef LDAP_VENDOR_NAME
283 	php_info_print_table_row(2, "Vendor Name", LDAP_VENDOR_NAME);
284 #endif
285 
286 #ifdef LDAP_VENDOR_VERSION
287 	snprintf(tmp, 31, "%d", LDAP_VENDOR_VERSION);
288 	php_info_print_table_row(2, "Vendor Version", tmp);
289 #endif
290 
291 #if HAVE_NSLDAP
292 	SDKVersion = ldap_version(&ver);
293 	snprintf(tmp, 31, "%F", SDKVersion/100.0);
294 	php_info_print_table_row(2, "SDK Version", tmp);
295 
296 	snprintf(tmp, 31, "%F", ver.protocol_version/100.0);
297 	php_info_print_table_row(2, "Highest LDAP Protocol Supported", tmp);
298 
299 	snprintf(tmp, 31, "%F", ver.SSL_version/100.0);
300 	php_info_print_table_row(2, "SSL Level Supported", tmp);
301 
302 	if (ver.security_level != LDAP_SECURITY_NONE) {
303 		snprintf(tmp, 31, "%d", ver.security_level);
304 	} else {
305 		strcpy(tmp, "SSL not enabled");
306 	}
307 	php_info_print_table_row(2, "Level of Encryption", tmp);
308 #endif
309 
310 #ifdef HAVE_LDAP_SASL
311 	php_info_print_table_row(2, "SASL Support", "Enabled");
312 #endif
313 
314 	php_info_print_table_end();
315 	DISPLAY_INI_ENTRIES();
316 }
317 /* }}} */
318 
319 /* {{{ proto resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])
320    Connect to an LDAP server */
PHP_FUNCTION(ldap_connect)321 PHP_FUNCTION(ldap_connect)
322 {
323 	char *host = NULL;
324 	int hostlen;
325 	long port = LDAP_PORT;
326 #ifdef HAVE_ORALDAP
327 	char *wallet = NULL, *walletpasswd = NULL;
328 	int walletlen = 0, walletpasswdlen = 0;
329 	long authmode = GSLC_SSL_NO_AUTH;
330 	int ssl=0;
331 #endif
332 	ldap_linkdata *ld;
333 	LDAP *ldap = NULL;
334 
335 #ifdef HAVE_ORALDAP
336 	if (ZEND_NUM_ARGS() == 3 || ZEND_NUM_ARGS() == 4) {
337 		WRONG_PARAM_COUNT;
338 	}
339 
340 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|slssl", &host, &hostlen, &port, &wallet, &walletlen, &walletpasswd, &walletpasswdlen, &authmode) != SUCCESS) {
341 		RETURN_FALSE;
342 	}
343 
344 	if (ZEND_NUM_ARGS() == 5) {
345 		ssl = 1;
346 	}
347 #else
348 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &host, &hostlen, &port) != SUCCESS) {
349 		RETURN_FALSE;
350 	}
351 #endif
352 	if (!port) {
353 		port = LDAP_PORT;
354 	}
355 
356 	if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
357 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", LDAPG(num_links));
358 		RETURN_FALSE;
359 	}
360 
361 	ld = ecalloc(1, sizeof(ldap_linkdata));
362 
363 	{
364 		int rc = LDAP_SUCCESS;
365 		char	*url = host;
366 		if (!ldap_is_ldap_url(url)) {
367 			int	urllen = hostlen + sizeof( "ldap://:65535" );
368 
369 			if (port <= 0 || port > 65535) {
370 				efree(ld);
371 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid port number: %ld", port);
372 				RETURN_FALSE;
373 			}
374 
375 			url = emalloc(urllen);
376 			if (host && (strchr(host, ':') != NULL)) {
377 				/* Legacy support for host:port */
378 				snprintf( url, urllen, "ldap://%s", host );
379 			} else {
380 				snprintf( url, urllen, "ldap://%s:%ld", host ? host : "", port );
381 			}
382 		}
383 
384 #ifdef LDAP_API_FEATURE_X_OPENLDAP
385 		/* ldap_init() is deprecated, use ldap_initialize() instead.
386 		 */
387 		rc = ldap_initialize(&ldap, url);
388 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
389 		/* ldap_init does not support URLs.
390 		 * We must try the original host and port information.
391 		 */
392 		ldap = ldap_init(host, port);
393 		if (ldap == NULL) {
394 			efree(ld);
395 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create session handle");
396 			RETURN_FALSE;
397 		}
398 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
399 		if (url != host) {
400 			efree(url);
401 		}
402 		if (rc != LDAP_SUCCESS) {
403 			efree(ld);
404 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc));
405 			RETURN_FALSE;
406 		}
407 	}
408 
409 	if (ldap == NULL) {
410 		efree(ld);
411 		RETURN_FALSE;
412 	} else {
413 #ifdef HAVE_ORALDAP
414 		if (ssl) {
415 			if (ldap_init_SSL(&ldap->ld_sb, wallet, walletpasswd, authmode)) {
416 				efree(ld);
417 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL init failed");
418 				RETURN_FALSE;
419 			}
420 		}
421 #endif
422 		LDAPG(num_links)++;
423 		ld->link = ldap;
424 		ZEND_REGISTER_RESOURCE(return_value, ld, le_link);
425 	}
426 
427 }
428 /* }}} */
429 
430 /* {{{ _get_lderrno
431  */
_get_lderrno(LDAP * ldap)432 static int _get_lderrno(LDAP *ldap)
433 {
434 #if !HAVE_NSLDAP
435 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
436 	int lderr;
437 
438 	/* New versions of OpenLDAP do it this way */
439 	ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
440 	return lderr;
441 #else
442 	return ldap->ld_errno;
443 #endif
444 #else
445 	return ldap_get_lderrno(ldap, NULL, NULL);
446 #endif
447 }
448 /* }}} */
449 
450 /* {{{ _set_lderrno
451  */
_set_lderrno(LDAP * ldap,int lderr)452 static void _set_lderrno(LDAP *ldap, int lderr)
453 {
454 #if !HAVE_NSLDAP
455 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
456 	/* New versions of OpenLDAP do it this way */
457 	ldap_set_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
458 #else
459 	ldap->ld_errno = lderr;
460 #endif
461 #else
462 	ldap_set_lderrno(ldap, lderr, NULL, NULL);
463 #endif
464 }
465 /* }}} */
466 
467 /* {{{ proto bool ldap_bind(resource link [, string dn [, string password]])
468    Bind to LDAP directory */
PHP_FUNCTION(ldap_bind)469 PHP_FUNCTION(ldap_bind)
470 {
471 	zval *link;
472 	char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
473 	int ldap_bind_dnlen, ldap_bind_pwlen;
474 	ldap_linkdata *ld;
475 	int rc;
476 
477 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ss", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen) != SUCCESS) {
478 		RETURN_FALSE;
479 	}
480 
481 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
482 
483 	if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) {
484 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
485 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "DN contains a null byte");
486 		RETURN_FALSE;
487 	}
488 
489 	if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) {
490 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
491 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Password contains a null byte");
492 		RETURN_FALSE;
493 	}
494 
495 	{
496 #ifdef LDAP_API_FEATURE_X_OPENLDAP
497 		/* ldap_simple_bind_s() is deprecated, use ldap_sasl_bind_s() instead.
498 		 */
499 		struct berval   cred;
500 
501 		cred.bv_val = ldap_bind_pw;
502 		cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0;
503 		rc = ldap_sasl_bind_s(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
504 				NULL, NULL,     /* no controls right now */
505 				NULL);	  /* we don't care about the server's credentials */
506 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
507 		rc = ldap_simple_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw);
508 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
509 	}
510 	if ( rc != LDAP_SUCCESS) {
511 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
512 		RETURN_FALSE;
513 	} else {
514 		RETURN_TRUE;
515 	}
516 }
517 /* }}} */
518 
519 #ifdef HAVE_LDAP_SASL
520 typedef struct {
521 	char *mech;
522 	char *realm;
523 	char *authcid;
524 	char *passwd;
525 	char *authzid;
526 } php_ldap_bictx;
527 
528 /* {{{ _php_sasl_setdefs
529  */
_php_sasl_setdefs(LDAP * ld,char * sasl_mech,char * sasl_realm,char * sasl_authc_id,char * passwd,char * sasl_authz_id)530 static php_ldap_bictx *_php_sasl_setdefs(LDAP *ld, char *sasl_mech, char *sasl_realm, char *sasl_authc_id, char *passwd, char *sasl_authz_id)
531 {
532 	php_ldap_bictx *ctx;
533 
534 	ctx = ber_memalloc(sizeof(php_ldap_bictx));
535 	ctx->mech    = (sasl_mech) ? ber_strdup(sasl_mech) : NULL;
536 	ctx->realm   = (sasl_realm) ? ber_strdup(sasl_realm) : NULL;
537 	ctx->authcid = (sasl_authc_id) ? ber_strdup(sasl_authc_id) : NULL;
538 	ctx->passwd  = (passwd) ? ber_strdup(passwd) : NULL;
539 	ctx->authzid = (sasl_authz_id) ? ber_strdup(sasl_authz_id) : NULL;
540 
541 	if (ctx->mech == NULL) {
542 		ldap_get_option(ld, LDAP_OPT_X_SASL_MECH, &ctx->mech);
543 	}
544 	if (ctx->realm == NULL) {
545 		ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &ctx->realm);
546 	}
547 	if (ctx->authcid == NULL) {
548 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHCID, &ctx->authcid);
549 	}
550 	if (ctx->authzid == NULL) {
551 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHZID, &ctx->authzid);
552 	}
553 
554 	return ctx;
555 }
556 /* }}} */
557 
558 /* {{{ _php_sasl_freedefs
559  */
_php_sasl_freedefs(php_ldap_bictx * ctx)560 static void _php_sasl_freedefs(php_ldap_bictx *ctx)
561 {
562 	if (ctx->mech) ber_memfree(ctx->mech);
563 	if (ctx->realm) ber_memfree(ctx->realm);
564 	if (ctx->authcid) ber_memfree(ctx->authcid);
565 	if (ctx->passwd) ber_memfree(ctx->passwd);
566 	if (ctx->authzid) ber_memfree(ctx->authzid);
567 	ber_memfree(ctx);
568 }
569 /* }}} */
570 
571 /* {{{ _php_sasl_interact
572    Internal interact function for SASL */
_php_sasl_interact(LDAP * ld,unsigned flags,void * defaults,void * in)573 static int _php_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
574 {
575 	sasl_interact_t *interact = in;
576 	const char *p;
577 	php_ldap_bictx *ctx = defaults;
578 
579 	for (;interact->id != SASL_CB_LIST_END;interact++) {
580 		p = NULL;
581 		switch(interact->id) {
582 			case SASL_CB_GETREALM:
583 				p = ctx->realm;
584 				break;
585 			case SASL_CB_AUTHNAME:
586 				p = ctx->authcid;
587 				break;
588 			case SASL_CB_USER:
589 				p = ctx->authzid;
590 				break;
591 			case SASL_CB_PASS:
592 				p = ctx->passwd;
593 				break;
594 		}
595 		if (p) {
596 			interact->result = p;
597 			interact->len = strlen(interact->result);
598 		}
599 	}
600 	return LDAP_SUCCESS;
601 }
602 /* }}} */
603 
604 /* {{{ proto bool ldap_sasl_bind(resource link [, string binddn [, string password [, string sasl_mech [, string sasl_realm [, string sasl_authc_id [, string sasl_authz_id [, string props]]]]]]])
605    Bind to LDAP directory using SASL */
PHP_FUNCTION(ldap_sasl_bind)606 PHP_FUNCTION(ldap_sasl_bind)
607 {
608 	zval *link;
609 	ldap_linkdata *ld;
610 	char *binddn = NULL;
611 	char *passwd = NULL;
612 	char *sasl_mech = NULL;
613 	char *sasl_realm = NULL;
614 	char *sasl_authz_id = NULL;
615 	char *sasl_authc_id = NULL;
616 	char *props = NULL;
617 	int rc, dn_len, passwd_len, mech_len, realm_len, authc_id_len, authz_id_len, props_len;
618 	php_ldap_bictx *ctx;
619 
620 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|sssssss", &link, &binddn, &dn_len, &passwd, &passwd_len, &sasl_mech, &mech_len, &sasl_realm, &realm_len, &sasl_authc_id, &authc_id_len, &sasl_authz_id, &authz_id_len, &props, &props_len) != SUCCESS) {
621 		RETURN_FALSE;
622 	}
623 
624 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
625 
626 	ctx = _php_sasl_setdefs(ld->link, sasl_mech, sasl_realm, sasl_authc_id, passwd, sasl_authz_id);
627 
628 	if (props) {
629 		ldap_set_option(ld->link, LDAP_OPT_X_SASL_SECPROPS, props);
630 	}
631 
632 	rc = ldap_sasl_interactive_bind_s(ld->link, binddn, ctx->mech, NULL, NULL, LDAP_SASL_QUIET, _php_sasl_interact, ctx);
633 	if (rc != LDAP_SUCCESS) {
634 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
635 		RETVAL_FALSE;
636 	} else {
637 		RETVAL_TRUE;
638 	}
639 	_php_sasl_freedefs(ctx);
640 }
641 /* }}} */
642 #endif /* HAVE_LDAP_SASL */
643 
644 /* {{{ proto bool ldap_unbind(resource link)
645    Unbind from LDAP directory */
PHP_FUNCTION(ldap_unbind)646 PHP_FUNCTION(ldap_unbind)
647 {
648 	zval *link;
649 	ldap_linkdata *ld;
650 
651 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
652 		RETURN_FALSE;
653 	}
654 
655 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
656 
657 	zend_list_delete(Z_LVAL_P(link));
658 	RETURN_TRUE;
659 }
660 /* }}} */
661 
662 /* {{{ php_set_opts
663  */
php_set_opts(LDAP * ldap,int sizelimit,int timelimit,int deref,int * old_sizelimit,int * old_timelimit,int * old_deref)664 static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref, int *old_sizelimit, int *old_timelimit, int *old_deref)
665 {
666 	/* sizelimit */
667 	if (sizelimit > -1) {
668 #if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP
669 		ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
670 		ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
671 #else
672 		*old_sizelimit = ldap->ld_sizelimit;
673 		ldap->ld_sizelimit = sizelimit;
674 #endif
675 	}
676 
677 	/* timelimit */
678 	if (timelimit > -1) {
679 #if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP
680 		ldap_get_option(ldap, LDAP_OPT_TIMELIMIT, old_timelimit);
681 		ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
682 #else
683 		*old_timelimit = ldap->ld_timelimit;
684 		ldap->ld_timelimit = timelimit;
685 #endif
686 	}
687 
688 	/* deref */
689 	if (deref > -1) {
690 #if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP
691 		ldap_get_option(ldap, LDAP_OPT_DEREF, old_deref);
692 		ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
693 #else
694 		*old_deref = ldap->ld_deref;
695 		ldap->ld_deref = deref;
696 #endif
697 	}
698 }
699 /* }}} */
700 
701 /* {{{ php_ldap_do_search
702  */
php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS,int scope)703 static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
704 {
705 	zval *link, *base_dn, **filter, *attrs = NULL, **attr;
706 	long attrsonly, sizelimit, timelimit, deref;
707 	char *ldap_base_dn = NULL, *ldap_filter = NULL, **ldap_attrs = NULL;
708 	ldap_linkdata *ld = NULL;
709 	LDAPMessage *ldap_res;
710 	int ldap_attrsonly = 0, ldap_sizelimit = -1, ldap_timelimit = -1, ldap_deref = -1;
711 	int old_ldap_sizelimit = -1, old_ldap_timelimit = -1, old_ldap_deref = -1;
712 	int num_attribs = 0, ret = 1, i, errno, argcount = ZEND_NUM_ARGS();
713 
714 	if (zend_parse_parameters(argcount TSRMLS_CC, "zzZ|allll", &link, &base_dn, &filter, &attrs, &attrsonly,
715 		&sizelimit, &timelimit, &deref) == FAILURE) {
716 		return;
717 	}
718 
719 	/* Reverse -> fall through */
720 	switch (argcount) {
721 		case 8:
722 			ldap_deref = deref;
723 		case 7:
724 			ldap_timelimit = timelimit;
725 		case 6:
726 			ldap_sizelimit = sizelimit;
727 		case 5:
728 			ldap_attrsonly = attrsonly;
729 		case 4:
730 			num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs));
731 			ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
732 
733 			for (i = 0; i<num_attribs; i++) {
734 				if (zend_hash_index_find(Z_ARRVAL_P(attrs), i, (void **) &attr) != SUCCESS) {
735 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array initialization wrong");
736 					ret = 0;
737 					goto cleanup;
738 				}
739 
740 				SEPARATE_ZVAL(attr);
741 				convert_to_string_ex(attr);
742 				ldap_attrs[i] = Z_STRVAL_PP(attr);
743 			}
744 			ldap_attrs[num_attribs] = NULL;
745 		default:
746 			break;
747 	}
748 
749 	/* parallel search? */
750 	if (Z_TYPE_P(link) == IS_ARRAY) {
751 		int i, nlinks, nbases, nfilters, *rcs;
752 		ldap_linkdata **lds;
753 		zval **entry, *resource;
754 
755 		nlinks = zend_hash_num_elements(Z_ARRVAL_P(link));
756 		if (nlinks == 0) {
757 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "No links in link array");
758 			ret = 0;
759 			goto cleanup;
760 		}
761 
762 		if (Z_TYPE_P(base_dn) == IS_ARRAY) {
763 			nbases = zend_hash_num_elements(Z_ARRVAL_P(base_dn));
764 			if (nbases != nlinks) {
765 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Base must either be a string, or an array with the same number of elements as the links array");
766 				ret = 0;
767 				goto cleanup;
768 			}
769 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(base_dn));
770 		} else {
771 			nbases = 0; /* this means string, not array */
772 			/* If anything else than string is passed, ldap_base_dn = NULL */
773 			if (Z_TYPE_P(base_dn) == IS_STRING) {
774 				ldap_base_dn = Z_STRVAL_P(base_dn);
775 			} else {
776 				ldap_base_dn = NULL;
777 			}
778 		}
779 
780 		if (Z_TYPE_PP(filter) == IS_ARRAY) {
781 			nfilters = zend_hash_num_elements(Z_ARRVAL_PP(filter));
782 			if (nfilters != nlinks) {
783 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter must either be a string, or an array with the same number of elements as the links array");
784 				ret = 0;
785 				goto cleanup;
786 			}
787 			zend_hash_internal_pointer_reset(Z_ARRVAL_PP(filter));
788 		} else {
789 			nfilters = 0; /* this means string, not array */
790 			convert_to_string_ex(filter);
791 			ldap_filter = Z_STRVAL_PP(filter);
792 		}
793 
794 		lds = safe_emalloc(nlinks, sizeof(ldap_linkdata), 0);
795 		rcs = safe_emalloc(nlinks, sizeof(*rcs), 0);
796 
797 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(link));
798 		for (i=0; i<nlinks; i++) {
799 			zend_hash_get_current_data(Z_ARRVAL_P(link), (void **)&entry);
800 
801 			ld = (ldap_linkdata *) zend_fetch_resource(entry TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
802 			if (ld == NULL) {
803 				ret = 0;
804 				goto cleanup_parallel;
805 			}
806 			if (nbases != 0) { /* base_dn an array? */
807 				zend_hash_get_current_data(Z_ARRVAL_P(base_dn), (void **)&entry);
808 				zend_hash_move_forward(Z_ARRVAL_P(base_dn));
809 
810 				/* If anything else than string is passed, ldap_base_dn = NULL */
811 				if (Z_TYPE_PP(entry) == IS_STRING) {
812 					ldap_base_dn = Z_STRVAL_PP(entry);
813 				} else {
814 					ldap_base_dn = NULL;
815 				}
816 			}
817 			if (nfilters != 0) { /* filter an array? */
818 				zend_hash_get_current_data(Z_ARRVAL_PP(filter), (void **)&entry);
819 				zend_hash_move_forward(Z_ARRVAL_PP(filter));
820 				convert_to_string_ex(entry);
821 				ldap_filter = Z_STRVAL_PP(entry);
822 			}
823 
824 			php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
825 
826 			/* Run the actual search */
827 			ldap_search_ext(ld->link, ldap_base_dn, scope, ldap_filter, ldap_attrs, ldap_attrsonly, NULL, NULL, NULL, ldap_sizelimit, &rcs[i]);
828 			lds[i] = ld;
829 			zend_hash_move_forward(Z_ARRVAL_P(link));
830 		}
831 
832 		array_init(return_value);
833 
834 		/* Collect results from the searches */
835 		for (i=0; i<nlinks; i++) {
836 			MAKE_STD_ZVAL(resource);
837 			if (rcs[i] != -1) {
838 				rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
839 			}
840 			if (rcs[i] != -1) {
841 				ZEND_REGISTER_RESOURCE(resource, ldap_res, le_result);
842 				add_next_index_zval(return_value, resource);
843 			} else {
844 				add_next_index_bool(return_value, 0);
845 			}
846 		}
847 
848 cleanup_parallel:
849 		efree(lds);
850 		efree(rcs);
851 	} else {
852 		convert_to_string_ex(filter);
853 		ldap_filter = Z_STRVAL_PP(filter);
854 
855 		/* If anything else than string is passed, ldap_base_dn = NULL */
856 		if (Z_TYPE_P(base_dn) == IS_STRING) {
857 			ldap_base_dn = Z_STRVAL_P(base_dn);
858 		}
859 
860 		ld = (ldap_linkdata *) zend_fetch_resource(&link TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
861 		if (ld == NULL) {
862 			ret = 0;
863 			goto cleanup;
864 		}
865 
866 		php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
867 
868 		/* Run the actual search */
869 		errno = ldap_search_ext_s(ld->link, ldap_base_dn, scope, ldap_filter, ldap_attrs, ldap_attrsonly, NULL, NULL, NULL, ldap_sizelimit, &ldap_res);
870 
871 		if (errno != LDAP_SUCCESS
872 			&& errno != LDAP_SIZELIMIT_EXCEEDED
873 #ifdef LDAP_ADMINLIMIT_EXCEEDED
874 			&& errno != LDAP_ADMINLIMIT_EXCEEDED
875 #endif
876 #ifdef LDAP_REFERRAL
877 			&& errno != LDAP_REFERRAL
878 #endif
879 		) {
880 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Search: %s", ldap_err2string(errno));
881 			ret = 0;
882 		} else {
883 			if (errno == LDAP_SIZELIMIT_EXCEEDED) {
884 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Partial search results returned: Sizelimit exceeded");
885 			}
886 #ifdef LDAP_ADMINLIMIT_EXCEEDED
887 			else if (errno == LDAP_ADMINLIMIT_EXCEEDED) {
888 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Partial search results returned: Adminlimit exceeded");
889 			}
890 #endif
891 
892 			ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result);
893 		}
894 	}
895 
896 cleanup:
897 	if (ld) {
898 		/* Restoring previous options */
899 		php_set_opts(ld->link, old_ldap_sizelimit, old_ldap_timelimit, old_ldap_deref, &ldap_sizelimit, &ldap_timelimit, &ldap_deref);
900 	}
901 	if (ldap_attrs != NULL) {
902 		efree(ldap_attrs);
903 	}
904 	if (!ret) {
905 		RETVAL_BOOL(ret);
906 	}
907 }
908 /* }}} */
909 
910 /* {{{ proto resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
911    Read an entry */
PHP_FUNCTION(ldap_read)912 PHP_FUNCTION(ldap_read)
913 {
914 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_BASE);
915 }
916 /* }}} */
917 
918 /* {{{ proto resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
919    Single-level search */
PHP_FUNCTION(ldap_list)920 PHP_FUNCTION(ldap_list)
921 {
922 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_ONELEVEL);
923 }
924 /* }}} */
925 
926 /* {{{ proto resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
927    Search LDAP tree under base_dn */
PHP_FUNCTION(ldap_search)928 PHP_FUNCTION(ldap_search)
929 {
930 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_SUBTREE);
931 }
932 /* }}} */
933 
934 /* {{{ proto bool ldap_free_result(resource result)
935    Free result memory */
PHP_FUNCTION(ldap_free_result)936 PHP_FUNCTION(ldap_free_result)
937 {
938 	zval *result;
939 	LDAPMessage *ldap_result;
940 
941 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) != SUCCESS) {
942 		return;
943 	}
944 
945 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
946 
947 	zend_list_delete(Z_LVAL_P(result));  /* Delete list entry */
948 	RETVAL_TRUE;
949 }
950 /* }}} */
951 
952 /* {{{ proto int ldap_count_entries(resource link, resource result)
953    Count the number of entries in a search result */
PHP_FUNCTION(ldap_count_entries)954 PHP_FUNCTION(ldap_count_entries)
955 {
956 	zval *link, *result;
957 	ldap_linkdata *ld;
958 	LDAPMessage *ldap_result;
959 
960 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
961 		return;
962 	}
963 
964 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
965 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
966 
967 	RETURN_LONG(ldap_count_entries(ld->link, ldap_result));
968 }
969 /* }}} */
970 
971 /* {{{ proto resource ldap_first_entry(resource link, resource result)
972    Return first result id */
PHP_FUNCTION(ldap_first_entry)973 PHP_FUNCTION(ldap_first_entry)
974 {
975 	zval *link, *result;
976 	ldap_linkdata *ld;
977 	ldap_resultentry *resultentry;
978 	LDAPMessage *ldap_result, *entry;
979 
980 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
981 		return;
982 	}
983 
984 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
985 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
986 
987 	if ((entry = ldap_first_entry(ld->link, ldap_result)) == NULL) {
988 		RETVAL_FALSE;
989 	} else {
990 		resultentry = emalloc(sizeof(ldap_resultentry));
991 		ZEND_REGISTER_RESOURCE(return_value, resultentry, le_result_entry);
992 		resultentry->id = Z_LVAL_P(result);
993 		zend_list_addref(resultentry->id);
994 		resultentry->data = entry;
995 		resultentry->ber = NULL;
996 	}
997 }
998 /* }}} */
999 
1000 /* {{{ proto resource ldap_next_entry(resource link, resource result_entry)
1001    Get next result entry */
PHP_FUNCTION(ldap_next_entry)1002 PHP_FUNCTION(ldap_next_entry)
1003 {
1004 	zval *link, *result_entry;
1005 	ldap_linkdata *ld;
1006 	ldap_resultentry *resultentry, *resultentry_next;
1007 	LDAPMessage *entry_next;
1008 
1009 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1010 		return;
1011 	}
1012 
1013 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1014 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1015 
1016 	if ((entry_next = ldap_next_entry(ld->link, resultentry->data)) == NULL) {
1017 		RETVAL_FALSE;
1018 	} else {
1019 		resultentry_next = emalloc(sizeof(ldap_resultentry));
1020 		ZEND_REGISTER_RESOURCE(return_value, resultentry_next, le_result_entry);
1021 		resultentry_next->id = resultentry->id;
1022 		zend_list_addref(resultentry->id);
1023 		resultentry_next->data = entry_next;
1024 		resultentry_next->ber = NULL;
1025 	}
1026 }
1027 /* }}} */
1028 
1029 /* {{{ proto array ldap_get_entries(resource link, resource result)
1030    Get all result entries */
PHP_FUNCTION(ldap_get_entries)1031 PHP_FUNCTION(ldap_get_entries)
1032 {
1033 	zval *link, *result;
1034 	LDAPMessage *ldap_result, *ldap_result_entry;
1035 	zval *tmp1, *tmp2;
1036 	ldap_linkdata *ld;
1037 	LDAP *ldap;
1038 	int num_entries, num_attrib, num_values, i;
1039 	BerElement *ber;
1040 	char *attribute;
1041 	size_t attr_len;
1042 	struct berval **ldap_value;
1043 	char *dn;
1044 
1045 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
1046 		return;
1047 	}
1048 
1049 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1050 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
1051 
1052 	ldap = ld->link;
1053 	num_entries = ldap_count_entries(ldap, ldap_result);
1054 
1055 	array_init(return_value);
1056 	add_assoc_long(return_value, "count", num_entries);
1057 
1058 	if (num_entries == 0) {
1059 		return;
1060 	}
1061 
1062 	ldap_result_entry = ldap_first_entry(ldap, ldap_result);
1063 	if (ldap_result_entry == NULL) {
1064 		zval_dtor(return_value);
1065 		RETURN_FALSE;
1066 	}
1067 
1068 	num_entries = 0;
1069 	while (ldap_result_entry != NULL) {
1070 		MAKE_STD_ZVAL(tmp1);
1071 		array_init(tmp1);
1072 
1073 		num_attrib = 0;
1074 		attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
1075 
1076 		while (attribute != NULL) {
1077 			ldap_value = ldap_get_values_len(ldap, ldap_result_entry, attribute);
1078 			num_values = ldap_count_values_len(ldap_value);
1079 
1080 			MAKE_STD_ZVAL(tmp2);
1081 			array_init(tmp2);
1082 			add_assoc_long(tmp2, "count", num_values);
1083 			for (i = 0; i < num_values; i++) {
1084 				add_index_stringl(tmp2, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1);
1085 			}
1086 			ldap_value_free_len(ldap_value);
1087 
1088 			attr_len = strlen(attribute);
1089 			zend_hash_update(Z_ARRVAL_P(tmp1), php_strtolower(attribute, attr_len), attr_len+1, (void *) &tmp2, sizeof(zval *), NULL);
1090 			add_index_string(tmp1, num_attrib, attribute, 1);
1091 
1092 			num_attrib++;
1093 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1094 			ldap_memfree(attribute);
1095 #endif
1096 			attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
1097 		}
1098 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1099 		if (ber != NULL) {
1100 			ber_free(ber, 0);
1101 		}
1102 #endif
1103 
1104 		add_assoc_long(tmp1, "count", num_attrib);
1105 		dn = ldap_get_dn(ldap, ldap_result_entry);
1106 		if (dn) {
1107 			add_assoc_string(tmp1, "dn", dn, 1);
1108 		} else {
1109 			add_assoc_null(tmp1, "dn");
1110 		}
1111 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1112 		ldap_memfree(dn);
1113 #else
1114 		free(dn);
1115 #endif
1116 
1117 		zend_hash_index_update(Z_ARRVAL_P(return_value), num_entries, (void *) &tmp1, sizeof(zval *), NULL);
1118 
1119 		num_entries++;
1120 		ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
1121 	}
1122 
1123 	add_assoc_long(return_value, "count", num_entries);
1124 
1125 }
1126 /* }}} */
1127 
1128 /* {{{ proto string ldap_first_attribute(resource link, resource result_entry)
1129    Return first attribute */
PHP_FUNCTION(ldap_first_attribute)1130 PHP_FUNCTION(ldap_first_attribute)
1131 {
1132 	zval *link, *result_entry;
1133 	ldap_linkdata *ld;
1134 	ldap_resultentry *resultentry;
1135 	char *attribute;
1136 	long dummy_ber;
1137 
1138 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1139 		return;
1140 	}
1141 
1142 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1143 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1144 
1145 	if ((attribute = ldap_first_attribute(ld->link, resultentry->data, &resultentry->ber)) == NULL) {
1146 		RETURN_FALSE;
1147 	} else {
1148 		RETVAL_STRING(attribute, 1);
1149 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1150 		ldap_memfree(attribute);
1151 #endif
1152 	}
1153 }
1154 /* }}} */
1155 
1156 /* {{{ proto string ldap_next_attribute(resource link, resource result_entry)
1157    Get the next attribute in result */
PHP_FUNCTION(ldap_next_attribute)1158 PHP_FUNCTION(ldap_next_attribute)
1159 {
1160 	zval *link, *result_entry;
1161 	ldap_linkdata *ld;
1162 	ldap_resultentry *resultentry;
1163 	char *attribute;
1164 	long dummy_ber;
1165 
1166 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1167 		return;
1168 	}
1169 
1170 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1171 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1172 
1173 	if (resultentry->ber == NULL) {
1174 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "called before calling ldap_first_attribute() or no attributes found in result entry");
1175 		RETURN_FALSE;
1176 	}
1177 
1178 	if ((attribute = ldap_next_attribute(ld->link, resultentry->data, resultentry->ber)) == NULL) {
1179 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1180 		if (resultentry->ber != NULL) {
1181 			ber_free(resultentry->ber, 0);
1182 			resultentry->ber = NULL;
1183 		}
1184 #endif
1185 		RETURN_FALSE;
1186 	} else {
1187 		RETVAL_STRING(attribute, 1);
1188 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1189 		ldap_memfree(attribute);
1190 #endif
1191 	}
1192 }
1193 /* }}} */
1194 
1195 /* {{{ proto array ldap_get_attributes(resource link, resource result_entry)
1196    Get attributes from a search result entry */
PHP_FUNCTION(ldap_get_attributes)1197 PHP_FUNCTION(ldap_get_attributes)
1198 {
1199 	zval *link, *result_entry;
1200 	zval *tmp;
1201 	ldap_linkdata *ld;
1202 	ldap_resultentry *resultentry;
1203 	char *attribute;
1204 	struct berval **ldap_value;
1205 	int i, num_values, num_attrib;
1206 	BerElement *ber;
1207 
1208 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1209 		return;
1210 	}
1211 
1212 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1213 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1214 
1215 	array_init(return_value);
1216 	num_attrib = 0;
1217 
1218 	attribute = ldap_first_attribute(ld->link, resultentry->data, &ber);
1219 	while (attribute != NULL) {
1220 		ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute);
1221 		num_values = ldap_count_values_len(ldap_value);
1222 
1223 		MAKE_STD_ZVAL(tmp);
1224 		array_init(tmp);
1225 		add_assoc_long(tmp, "count", num_values);
1226 		for (i = 0; i < num_values; i++) {
1227 			add_index_stringl(tmp, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1);
1228 		}
1229 		ldap_value_free_len(ldap_value);
1230 
1231 		zend_hash_update(Z_ARRVAL_P(return_value), attribute, strlen(attribute)+1, (void *) &tmp, sizeof(zval *), NULL);
1232 		add_index_string(return_value, num_attrib, attribute, 1);
1233 
1234 		num_attrib++;
1235 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1236 		ldap_memfree(attribute);
1237 #endif
1238 		attribute = ldap_next_attribute(ld->link, resultentry->data, ber);
1239 	}
1240 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1241 	if (ber != NULL) {
1242 		ber_free(ber, 0);
1243 	}
1244 #endif
1245 
1246 	add_assoc_long(return_value, "count", num_attrib);
1247 }
1248 /* }}} */
1249 
1250 /* {{{ proto array ldap_get_values_len(resource link, resource result_entry, string attribute)
1251    Get all values with lengths from a result entry */
PHP_FUNCTION(ldap_get_values_len)1252 PHP_FUNCTION(ldap_get_values_len)
1253 {
1254 	zval *link, *result_entry;
1255 	ldap_linkdata *ld;
1256 	ldap_resultentry *resultentry;
1257 	char *attr;
1258 	struct berval **ldap_value_len;
1259 	int i, num_values, attr_len;
1260 
1261 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &link, &result_entry, &attr, &attr_len) != SUCCESS) {
1262 		return;
1263 	}
1264 
1265 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1266 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1267 
1268 	if ((ldap_value_len = ldap_get_values_len(ld->link, resultentry->data, attr)) == NULL) {
1269 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot get the value(s) of attribute %s", ldap_err2string(_get_lderrno(ld->link)));
1270 		RETURN_FALSE;
1271 	}
1272 
1273 	num_values = ldap_count_values_len(ldap_value_len);
1274 	array_init(return_value);
1275 
1276 	for (i=0; i<num_values; i++) {
1277 		add_next_index_stringl(return_value, ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len, 1);
1278 	}
1279 
1280 	add_assoc_long(return_value, "count", num_values);
1281 	ldap_value_free_len(ldap_value_len);
1282 
1283 }
1284 /* }}} */
1285 
1286 /* {{{ proto string ldap_get_dn(resource link, resource result_entry)
1287    Get the DN of a result entry */
PHP_FUNCTION(ldap_get_dn)1288 PHP_FUNCTION(ldap_get_dn)
1289 {
1290 	zval *link, *result_entry;
1291 	ldap_linkdata *ld;
1292 	ldap_resultentry *resultentry;
1293 	char *text;
1294 
1295 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1296 		return;
1297 	}
1298 
1299 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1300 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1301 
1302 	text = ldap_get_dn(ld->link, resultentry->data);
1303 	if (text != NULL) {
1304 		RETVAL_STRING(text, 1);
1305 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1306 		ldap_memfree(text);
1307 #else
1308 		free(text);
1309 #endif
1310 	} else {
1311 		RETURN_FALSE;
1312 	}
1313 }
1314 /* }}} */
1315 
1316 /* {{{ proto array ldap_explode_dn(string dn, int with_attrib)
1317    Splits DN into its component parts */
PHP_FUNCTION(ldap_explode_dn)1318 PHP_FUNCTION(ldap_explode_dn)
1319 {
1320 	long with_attrib;
1321 	char *dn, **ldap_value;
1322 	int i, count, dn_len;
1323 
1324 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &dn, &dn_len, &with_attrib) != SUCCESS) {
1325 		return;
1326 	}
1327 
1328 	if (!(ldap_value = ldap_explode_dn(dn, with_attrib))) {
1329 		/* Invalid parameters were passed to ldap_explode_dn */
1330 		RETURN_FALSE;
1331 	}
1332 
1333 	i=0;
1334 	while (ldap_value[i] != NULL) i++;
1335 	count = i;
1336 
1337 	array_init(return_value);
1338 
1339 	add_assoc_long(return_value, "count", count);
1340 	for (i = 0; i<count; i++) {
1341 		add_index_string(return_value, i, ldap_value[i], 1);
1342 	}
1343 
1344 	ldap_memvfree((void **)ldap_value);
1345 }
1346 /* }}} */
1347 
1348 /* {{{ proto string ldap_dn2ufn(string dn)
1349    Convert DN to User Friendly Naming format */
PHP_FUNCTION(ldap_dn2ufn)1350 PHP_FUNCTION(ldap_dn2ufn)
1351 {
1352 	char *dn, *ufn;
1353 	int dn_len;
1354 
1355 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dn, &dn_len) != SUCCESS) {
1356 		return;
1357 	}
1358 
1359 	ufn = ldap_dn2ufn(dn);
1360 
1361 	if (ufn != NULL) {
1362 		RETVAL_STRING(ufn, 1);
1363 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP || WINDOWS
1364 		ldap_memfree(ufn);
1365 #endif
1366 	} else {
1367 		RETURN_FALSE;
1368 	}
1369 }
1370 /* }}} */
1371 
1372 
1373 /* added to fix use of ldap_modify_add for doing an ldap_add, gerrit thomson. */
1374 #define PHP_LD_FULL_ADD 0xff
1375 /* {{{ php_ldap_do_modify
1376  */
php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS,int oper)1377 static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper)
1378 {
1379 	zval *link, *entry, **value, **ivalue;
1380 	ldap_linkdata *ld;
1381 	char *dn;
1382 	LDAPMod **ldap_mods;
1383 	int i, j, num_attribs, num_values, dn_len;
1384 	int *num_berval;
1385 	char *attribute;
1386 	ulong index;
1387 	int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */
1388 
1389 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &entry) != SUCCESS) {
1390 		return;
1391 	}
1392 
1393 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1394 
1395 	num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry));
1396 	ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0);
1397 	num_berval = safe_emalloc(num_attribs, sizeof(int), 0);
1398 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry));
1399 
1400 	/* added by gerrit thomson to fix ldap_add using ldap_mod_add */
1401 	if (oper == PHP_LD_FULL_ADD) {
1402 		oper = LDAP_MOD_ADD;
1403 		is_full_add = 1;
1404 	}
1405 	/* end additional , gerrit thomson */
1406 
1407 	for (i = 0; i < num_attribs; i++) {
1408 		ldap_mods[i] = emalloc(sizeof(LDAPMod));
1409 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
1410 		ldap_mods[i]->mod_type = NULL;
1411 
1412 		if (zend_hash_get_current_key(Z_ARRVAL_P(entry), &attribute, &index, 0) == HASH_KEY_IS_STRING) {
1413 			ldap_mods[i]->mod_type = estrdup(attribute);
1414 		} else {
1415 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown attribute in the data");
1416 			/* Free allocated memory */
1417 			while (i >= 0) {
1418 				if (ldap_mods[i]->mod_type) {
1419 					efree(ldap_mods[i]->mod_type);
1420 				}
1421 				efree(ldap_mods[i]);
1422 				i--;
1423 			}
1424 			efree(num_berval);
1425 			efree(ldap_mods);
1426 			RETURN_FALSE;
1427 		}
1428 
1429 		zend_hash_get_current_data(Z_ARRVAL_P(entry), (void **)&value);
1430 
1431 		if (Z_TYPE_PP(value) != IS_ARRAY) {
1432 			num_values = 1;
1433 		} else {
1434 			num_values = zend_hash_num_elements(Z_ARRVAL_PP(value));
1435 		}
1436 
1437 		num_berval[i] = num_values;
1438 		ldap_mods[i]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
1439 
1440 /* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
1441 		if ((num_values == 1) && (Z_TYPE_PP(value) != IS_ARRAY)) {
1442 			convert_to_string_ex(value);
1443 			ldap_mods[i]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
1444 			ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_PP(value);
1445 			ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_PP(value);
1446 		} else {
1447 			for (j = 0; j < num_values; j++) {
1448 				if (zend_hash_index_find(Z_ARRVAL_PP(value), j, (void **) &ivalue) != SUCCESS) {
1449 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value array must have consecutive indices 0, 1, ...");
1450 					num_berval[i] = j;
1451 					num_attribs = i + 1;
1452 					RETVAL_FALSE;
1453 					goto errexit;
1454 				}
1455 				convert_to_string_ex(ivalue);
1456 				ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
1457 				ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_PP(ivalue);
1458 				ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_PP(ivalue);
1459 			}
1460 		}
1461 		ldap_mods[i]->mod_bvalues[num_values] = NULL;
1462 		zend_hash_move_forward(Z_ARRVAL_P(entry));
1463 	}
1464 	ldap_mods[num_attribs] = NULL;
1465 
1466 /* check flag to see if do_mod was called to perform full add , gerrit thomson */
1467 	if (is_full_add == 1) {
1468 		if ((i = ldap_add_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
1469 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Add: %s", ldap_err2string(i));
1470 			RETVAL_FALSE;
1471 		} else RETVAL_TRUE;
1472 	} else {
1473 		if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
1474 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modify: %s", ldap_err2string(i));
1475 			RETVAL_FALSE;
1476 		} else RETVAL_TRUE;
1477 	}
1478 
1479 errexit:
1480 	for (i = 0; i < num_attribs; i++) {
1481 		efree(ldap_mods[i]->mod_type);
1482 		for (j = 0; j < num_berval[i]; j++) {
1483 			efree(ldap_mods[i]->mod_bvalues[j]);
1484 		}
1485 		efree(ldap_mods[i]->mod_bvalues);
1486 		efree(ldap_mods[i]);
1487 	}
1488 	efree(num_berval);
1489 	efree(ldap_mods);
1490 
1491 	return;
1492 }
1493 /* }}} */
1494 
1495 /* {{{ proto bool ldap_add(resource link, string dn, array entry)
1496    Add entries to LDAP directory */
PHP_FUNCTION(ldap_add)1497 PHP_FUNCTION(ldap_add)
1498 {
1499 	/* use a newly define parameter into the do_modify so ldap_mod_add can be used the way it is supposed to be used , Gerrit THomson */
1500 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD);
1501 }
1502 /* }}} */
1503 
1504 /* three functions for attribute base modifications, gerrit Thomson */
1505 
1506 /* {{{ proto bool ldap_mod_replace(resource link, string dn, array entry)
1507    Replace attribute values with new ones */
PHP_FUNCTION(ldap_mod_replace)1508 PHP_FUNCTION(ldap_mod_replace)
1509 {
1510 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE);
1511 }
1512 /* }}} */
1513 
1514 /* {{{ proto bool ldap_mod_add(resource link, string dn, array entry)
1515    Add attribute values to current */
PHP_FUNCTION(ldap_mod_add)1516 PHP_FUNCTION(ldap_mod_add)
1517 {
1518 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD);
1519 }
1520 /* }}} */
1521 
1522 /* {{{ proto bool ldap_mod_del(resource link, string dn, array entry)
1523    Delete attribute values */
PHP_FUNCTION(ldap_mod_del)1524 PHP_FUNCTION(ldap_mod_del)
1525 {
1526 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE);
1527 }
1528 /* }}} */
1529 
1530 /* {{{ proto bool ldap_delete(resource link, string dn)
1531    Delete an entry from a directory */
PHP_FUNCTION(ldap_delete)1532 PHP_FUNCTION(ldap_delete)
1533 {
1534 	zval *link;
1535 	ldap_linkdata *ld;
1536 	char *dn;
1537 	int rc, dn_len;
1538 
1539 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &link, &dn, &dn_len) != SUCCESS) {
1540 		return;
1541 	}
1542 
1543 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1544 
1545 	if ((rc = ldap_delete_ext_s(ld->link, dn, NULL, NULL)) != LDAP_SUCCESS) {
1546 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delete: %s", ldap_err2string(rc));
1547 		RETURN_FALSE;
1548 	}
1549 
1550 	RETURN_TRUE;
1551 }
1552 /* }}} */
1553 
1554 /* {{{ _ldap_str_equal_to_const
1555  */
_ldap_str_equal_to_const(const char * str,uint str_len,const char * cstr)1556 static int _ldap_str_equal_to_const(const char *str, uint str_len, const char *cstr)
1557 {
1558 	int i;
1559 
1560 	if (strlen(cstr) != str_len)
1561 		return 0;
1562 
1563 	for (i = 0; i < str_len; ++i) {
1564 		if (str[i] != cstr[i]) {
1565 			return 0;
1566 		}
1567 	}
1568 
1569 	return 1;
1570 }
1571 /* }}} */
1572 
1573 /* {{{ _ldap_strlen_max
1574  */
_ldap_strlen_max(const char * str,uint max_len)1575 static int _ldap_strlen_max(const char *str, uint max_len)
1576 {
1577 	int i;
1578 
1579 	for (i = 0; i < max_len; ++i) {
1580 		if (str[i] == '\0') {
1581 			return i;
1582 		}
1583 	}
1584 
1585 	return max_len;
1586 }
1587 /* }}} */
1588 
1589 /* {{{ _ldap_hash_fetch
1590  */
_ldap_hash_fetch(zval * hashTbl,const char * key,zval ** out)1591 static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)
1592 {
1593 	zval **fetched;
1594 	if (zend_hash_find(Z_ARRVAL_P(hashTbl), key, strlen(key)+1, (void **) &fetched) == SUCCESS) {
1595 		*out = *fetched;
1596 	}
1597 	else {
1598 		*out = NULL;
1599 	}
1600 }
1601 /* }}} */
1602 
1603 /* {{{ proto bool ldap_modify_batch(resource link, string dn, array modifs)
1604    Perform multiple modifications as part of one operation */
PHP_FUNCTION(ldap_modify_batch)1605 PHP_FUNCTION(ldap_modify_batch)
1606 {
1607 	ldap_linkdata *ld;
1608 	zval *link, *mods, *mod, *modinfo, *modval;
1609 	zval *attrib, *modtype, *vals;
1610 	zval **fetched;
1611 	char *dn;
1612 	int dn_len;
1613 	int i, j, k;
1614 	int num_mods, num_modprops, num_modvals;
1615 	LDAPMod **ldap_mods;
1616 	uint oper;
1617 
1618 	/*
1619 	$mods = array(
1620 		array(
1621 			"attrib" => "unicodePwd",
1622 			"modtype" => LDAP_MODIFY_BATCH_REMOVE,
1623 			"values" => array($oldpw)
1624 		),
1625 		array(
1626 			"attrib" => "unicodePwd",
1627 			"modtype" => LDAP_MODIFY_BATCH_ADD,
1628 			"values" => array($newpw)
1629 		),
1630 		array(
1631 			"attrib" => "userPrincipalName",
1632 			"modtype" => LDAP_MODIFY_BATCH_REPLACE,
1633 			"values" => array("janitor@corp.contoso.com")
1634 		),
1635 		array(
1636 			"attrib" => "userCert",
1637 			"modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
1638 		)
1639 	);
1640 	*/
1641 
1642 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &mods) != SUCCESS) {
1643 		return;
1644 	}
1645 
1646 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1647 
1648 	/* perform validation */
1649 	{
1650 		char *modkey;
1651 		uint modkeylen;
1652 		long modtype;
1653 
1654 		/* to store the wrongly-typed keys */
1655 		ulong tmpUlong;
1656 
1657 		/* make sure the DN contains no NUL bytes */
1658 		if (_ldap_strlen_max(dn, dn_len) != dn_len) {
1659 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "DN must not contain NUL bytes");
1660 			RETURN_FALSE;
1661 		}
1662 
1663 		/* make sure the top level is a normal array */
1664 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods));
1665 		if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) {
1666 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must not be string-indexed");
1667 			RETURN_FALSE;
1668 		}
1669 
1670 		num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
1671 
1672 		for (i = 0; i < num_mods; i++) {
1673 			/* is the numbering consecutive? */
1674 			if (zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched) != SUCCESS) {
1675 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must have consecutive indices 0, 1, ...");
1676 				RETURN_FALSE;
1677 			}
1678 			mod = *fetched;
1679 
1680 			/* is it an array? */
1681 			if (Z_TYPE_P(mod) != IS_ARRAY) {
1682 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be an array itself");
1683 				RETURN_FALSE;
1684 			}
1685 
1686 			/* for the modification hashtable... */
1687 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod));
1688 			num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod));
1689 
1690 			for (j = 0; j < num_modprops; j++) {
1691 				/* are the keys strings? */
1692 				if (zend_hash_get_current_key_ex(Z_ARRVAL_P(mod), &modkey, &modkeylen, &tmpUlong, 0, NULL) != HASH_KEY_IS_STRING) {
1693 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be string-indexed");
1694 					RETURN_FALSE;
1695 				}
1696 
1697 				/* modkeylen includes the terminating NUL byte; remove that */
1698 				--modkeylen;
1699 
1700 				/* is this a valid entry? */
1701 				if (
1702 					!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB) &&
1703 					!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE) &&
1704 					!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)
1705 				) {
1706 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "The only allowed keys in entries of the modifications array are '" LDAP_MODIFY_BATCH_ATTRIB "', '" LDAP_MODIFY_BATCH_MODTYPE "' and '" LDAP_MODIFY_BATCH_VALUES "'");
1707 					RETURN_FALSE;
1708 				}
1709 
1710 				zend_hash_get_current_data(Z_ARRVAL_P(mod), (void **) &fetched);
1711 				modinfo = *fetched;
1712 
1713 				/* does the value type match the key? */
1714 				if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB)) {
1715 					if (Z_TYPE_P(modinfo) != IS_STRING) {
1716 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be a string");
1717 						RETURN_FALSE;
1718 					}
1719 
1720 					if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
1721 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
1722 						RETURN_FALSE;
1723 					}
1724 				}
1725 				else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE)) {
1726 					if (Z_TYPE_P(modinfo) != IS_LONG) {
1727 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be a long");
1728 						RETURN_FALSE;
1729 					}
1730 
1731 					/* is the value in range? */
1732 					modtype = Z_LVAL_P(modinfo);
1733 					if (
1734 						modtype != LDAP_MODIFY_BATCH_ADD &&
1735 						modtype != LDAP_MODIFY_BATCH_REMOVE &&
1736 						modtype != LDAP_MODIFY_BATCH_REPLACE &&
1737 						modtype != LDAP_MODIFY_BATCH_REMOVE_ALL
1738 					) {
1739 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "The '" LDAP_MODIFY_BATCH_MODTYPE "' value must match one of the LDAP_MODIFY_BATCH_* constants");
1740 						RETURN_FALSE;
1741 					}
1742 
1743 					/* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */
1744 					if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
1745 						if (zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
1746 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must not be provided");
1747 							RETURN_FALSE;
1748 						}
1749 					}
1750 					else {
1751 						if (!zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
1752 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is not LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must be provided");
1753 							RETURN_FALSE;
1754 						}
1755 					}
1756 				}
1757 				else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)) {
1758 					if (Z_TYPE_P(modinfo) != IS_ARRAY) {
1759 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' value must be an array");
1760 						RETURN_FALSE;
1761 					}
1762 
1763 					/* is the array not empty? */
1764 					zend_hash_internal_pointer_reset(Z_ARRVAL_P(modinfo));
1765 					num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo));
1766 					if (num_modvals == 0) {
1767 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have at least one element");
1768 						RETURN_FALSE;
1769 					}
1770 
1771 					/* are its keys integers? */
1772 					if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
1773 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must not be string-indexed");
1774 						RETURN_FALSE;
1775 					}
1776 
1777 					/* are the keys consecutive? */
1778 					for (k = 0; k < num_modvals; k++) {
1779 						if (zend_hash_index_find(Z_ARRVAL_P(modinfo), k, (void **) &fetched) != SUCCESS) {
1780 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have consecutive indices 0, 1, ...");
1781 							RETURN_FALSE;
1782 						}
1783 						modval = *fetched;
1784 
1785 						/* is the data element a string? */
1786 						if (Z_TYPE_P(modval) != IS_STRING) {
1787 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each element of a '" LDAP_MODIFY_BATCH_VALUES "' array must be a string");
1788 							RETURN_FALSE;
1789 						}
1790 					}
1791 				}
1792 
1793 				zend_hash_move_forward(Z_ARRVAL_P(mod));
1794 			}
1795 		}
1796 	}
1797 	/* validation was successful */
1798 
1799 	/* allocate array of modifications */
1800 	ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0);
1801 
1802 	/* for each modification */
1803 	for (i = 0; i < num_mods; i++) {
1804 		/* allocate the modification struct */
1805 		ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0);
1806 
1807 		/* fetch the relevant data */
1808 		zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched);
1809 		mod = *fetched;
1810 
1811 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib);
1812 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype);
1813 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals);
1814 
1815 		/* map the modification type */
1816 		switch (Z_LVAL_P(modtype)) {
1817 			case LDAP_MODIFY_BATCH_ADD:
1818 				oper = LDAP_MOD_ADD;
1819 				break;
1820 			case LDAP_MODIFY_BATCH_REMOVE:
1821 			case LDAP_MODIFY_BATCH_REMOVE_ALL:
1822 				oper = LDAP_MOD_DELETE;
1823 				break;
1824 			case LDAP_MODIFY_BATCH_REPLACE:
1825 				oper = LDAP_MOD_REPLACE;
1826 				break;
1827 			default:
1828 				php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown and uncaught modification type.");
1829 				RETURN_FALSE;
1830 		}
1831 
1832 		/* fill in the basic info */
1833 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
1834 		ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib));
1835 
1836 		if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) {
1837 			/* no values */
1838 			ldap_mods[i]->mod_bvalues = NULL;
1839 		}
1840 		else {
1841 			/* allocate space for the values as part of this modification */
1842 			num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals));
1843 			ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0);
1844 
1845 			/* for each value */
1846 			for (j = 0; j < num_modvals; j++) {
1847 				/* fetch it */
1848 				zend_hash_index_find(Z_ARRVAL_P(vals), j, (void **) &fetched);
1849 				modval = *fetched;
1850 
1851 				/* allocate the data struct */
1852 				ldap_mods[i]->mod_bvalues[j] = safe_emalloc(1, sizeof(struct berval), 0);
1853 
1854 				/* fill it */
1855 				ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(modval);
1856 				ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(Z_STRVAL_P(modval), Z_STRLEN_P(modval));
1857 			}
1858 
1859 			/* NULL-terminate values */
1860 			ldap_mods[i]->mod_bvalues[num_modvals] = NULL;
1861 		}
1862 	}
1863 
1864 	/* NULL-terminate modifications */
1865 	ldap_mods[num_mods] = NULL;
1866 
1867 	/* perform (finally) */
1868 	if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
1869 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Batch Modify: %s", ldap_err2string(i));
1870 		RETVAL_FALSE;
1871 	} else RETVAL_TRUE;
1872 
1873 	/* clean up */
1874 	{
1875 		for (i = 0; i < num_mods; i++) {
1876 			/* attribute */
1877 			efree(ldap_mods[i]->mod_type);
1878 
1879 			if (ldap_mods[i]->mod_bvalues != NULL) {
1880 				/* each BER value */
1881 				for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) {
1882 					/* free the data bytes */
1883 					efree(ldap_mods[i]->mod_bvalues[j]->bv_val);
1884 
1885 					/* free the bvalue struct */
1886 					efree(ldap_mods[i]->mod_bvalues[j]);
1887 				}
1888 
1889 				/* the BER value array */
1890 				efree(ldap_mods[i]->mod_bvalues);
1891 			}
1892 
1893 			/* the modification */
1894 			efree(ldap_mods[i]);
1895 		}
1896 
1897 		/* the modifications array */
1898 		efree(ldap_mods);
1899 	}
1900 }
1901 /* }}} */
1902 
1903 /* {{{ proto int ldap_errno(resource link)
1904    Get the current ldap error number */
PHP_FUNCTION(ldap_errno)1905 PHP_FUNCTION(ldap_errno)
1906 {
1907 	zval *link;
1908 	ldap_linkdata *ld;
1909 
1910 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
1911 		return;
1912 	}
1913 
1914 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1915 
1916 	RETURN_LONG(_get_lderrno(ld->link));
1917 }
1918 /* }}} */
1919 
1920 /* {{{ proto string ldap_err2str(int errno)
1921    Convert error number to error string */
PHP_FUNCTION(ldap_err2str)1922 PHP_FUNCTION(ldap_err2str)
1923 {
1924 	long perrno;
1925 
1926 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perrno) != SUCCESS) {
1927 		return;
1928 	}
1929 
1930 	RETURN_STRING(ldap_err2string(perrno), 1);
1931 }
1932 /* }}} */
1933 
1934 /* {{{ proto string ldap_error(resource link)
1935    Get the current ldap error string */
PHP_FUNCTION(ldap_error)1936 PHP_FUNCTION(ldap_error)
1937 {
1938 	zval *link;
1939 	ldap_linkdata *ld;
1940 	int ld_errno;
1941 
1942 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
1943 		return;
1944 	}
1945 
1946 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1947 
1948 	ld_errno = _get_lderrno(ld->link);
1949 
1950 	RETURN_STRING(ldap_err2string(ld_errno), 1);
1951 }
1952 /* }}} */
1953 
1954 /* {{{ proto bool ldap_compare(resource link, string dn, string attr, string value)
1955    Determine if an entry has a specific value for one of its attributes */
PHP_FUNCTION(ldap_compare)1956 PHP_FUNCTION(ldap_compare)
1957 {
1958 	zval *link;
1959 	char *dn, *attr, *value;
1960 	int dn_len, attr_len, value_len;
1961 	ldap_linkdata *ld;
1962 	int errno;
1963 	struct berval lvalue;
1964 
1965 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len) != SUCCESS) {
1966 		return;
1967 	}
1968 
1969 	lvalue.bv_val = value;
1970 	lvalue.bv_len = value_len;
1971 
1972 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1973 
1974 	errno = ldap_compare_ext_s(ld->link, dn, attr, &lvalue, NULL, NULL);
1975 
1976 	switch (errno) {
1977 		case LDAP_COMPARE_TRUE:
1978 			RETURN_TRUE;
1979 			break;
1980 
1981 		case LDAP_COMPARE_FALSE:
1982 			RETURN_FALSE;
1983 			break;
1984 	}
1985 
1986 	php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: %s", ldap_err2string(errno));
1987 	RETURN_LONG(-1);
1988 }
1989 /* }}} */
1990 
1991 /* {{{ proto bool ldap_sort(resource link, resource result, string sortfilter)
1992    Sort LDAP result entries */
PHP_FUNCTION(ldap_sort)1993 PHP_FUNCTION(ldap_sort)
1994 {
1995 	zval *link, *result;
1996 	ldap_linkdata *ld;
1997 	char *sortfilter;
1998 	int sflen;
1999 	zend_rsrc_list_entry *le;
2000 
2001 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &link, &result, &sortfilter, &sflen) != SUCCESS) {
2002 		RETURN_FALSE;
2003 	}
2004 
2005 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2006 
2007 	if (zend_hash_index_find(&EG(regular_list), Z_LVAL_P(result), (void **) &le) != SUCCESS || le->type != le_result) {
2008 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied resource is not a valid ldap result resource");
2009 		RETURN_FALSE;
2010 	}
2011 
2012 	if (ldap_sort_entries(ld->link, (LDAPMessage **) &le->ptr, sflen ? sortfilter : NULL, strcmp) != LDAP_SUCCESS) {
2013 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ldap_err2string(errno));
2014 		RETURN_FALSE;
2015 	}
2016 
2017 	RETURN_TRUE;
2018 }
2019 /* }}} */
2020 
2021 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
2022 /* {{{ proto bool ldap_get_option(resource link, int option, mixed retval)
2023    Get the current value of various session-wide parameters */
PHP_FUNCTION(ldap_get_option)2024 PHP_FUNCTION(ldap_get_option)
2025 {
2026 	zval *link, *retval;
2027 	ldap_linkdata *ld;
2028 	long option;
2029 
2030 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &link, &option, &retval) != SUCCESS) {
2031 		return;
2032 	}
2033 
2034 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2035 
2036 	switch (option) {
2037 	/* options with int value */
2038 	case LDAP_OPT_DEREF:
2039 	case LDAP_OPT_SIZELIMIT:
2040 	case LDAP_OPT_TIMELIMIT:
2041 	case LDAP_OPT_PROTOCOL_VERSION:
2042 	case LDAP_OPT_ERROR_NUMBER:
2043 	case LDAP_OPT_REFERRALS:
2044 #ifdef LDAP_OPT_RESTART
2045 	case LDAP_OPT_RESTART:
2046 #endif
2047 		{
2048 			int val;
2049 
2050 			if (ldap_get_option(ld->link, option, &val)) {
2051 				RETURN_FALSE;
2052 			}
2053 			zval_dtor(retval);
2054 			ZVAL_LONG(retval, val);
2055 		} break;
2056 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2057 	case LDAP_OPT_NETWORK_TIMEOUT:
2058 		{
2059 			struct timeval *timeout = NULL;
2060 
2061 			if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
2062 				if (timeout) {
2063 					ldap_memfree(timeout);
2064 				}
2065 				RETURN_FALSE;
2066 			}
2067 			if (!timeout) {
2068 				RETURN_FALSE;
2069 			}
2070 			zval_dtor(retval);
2071 			ZVAL_LONG(retval, timeout->tv_sec);
2072 			ldap_memfree(timeout);
2073 		} break;
2074 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
2075 	case LDAP_X_OPT_CONNECT_TIMEOUT:
2076 		{
2077 			int timeout;
2078 
2079 			if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
2080 				RETURN_FALSE;
2081 			}
2082 			zval_dtor(retval);
2083 			ZVAL_LONG(retval, (timeout / 1000));
2084 		} break;
2085 #endif
2086 #ifdef LDAP_OPT_TIMEOUT
2087 	case LDAP_OPT_TIMEOUT:
2088 		{
2089 			struct timeval *timeout = NULL;
2090 
2091 			if (ldap_get_option(ld->link, LDAP_OPT_TIMEOUT, (void *) &timeout)) {
2092 				if (timeout) {
2093 					ldap_memfree(timeout);
2094 				}
2095 				RETURN_FALSE;
2096 			}
2097 			if (!timeout) {
2098 				RETURN_FALSE;
2099 			}
2100 			zval_dtor(retval);
2101 			ZVAL_LONG(retval, timeout->tv_sec);
2102 			ldap_memfree(timeout);
2103 		} break;
2104 #endif
2105 	/* options with string value */
2106 	case LDAP_OPT_ERROR_STRING:
2107 #ifdef LDAP_OPT_HOST_NAME
2108 	case LDAP_OPT_HOST_NAME:
2109 #endif
2110 #ifdef HAVE_LDAP_SASL
2111 	case LDAP_OPT_X_SASL_MECH:
2112 	case LDAP_OPT_X_SASL_REALM:
2113 	case LDAP_OPT_X_SASL_AUTHCID:
2114 	case LDAP_OPT_X_SASL_AUTHZID:
2115 #endif
2116 #ifdef LDAP_OPT_MATCHED_DN
2117 	case LDAP_OPT_MATCHED_DN:
2118 #endif
2119 		{
2120 			char *val = NULL;
2121 
2122 			if (ldap_get_option(ld->link, option, &val) || val == NULL || *val == '\0') {
2123 				if (val) {
2124 					ldap_memfree(val);
2125 				}
2126 				RETURN_FALSE;
2127 			}
2128 			zval_dtor(retval);
2129 			ZVAL_STRING(retval, val, 1);
2130 			ldap_memfree(val);
2131 		} break;
2132 /* options not implemented
2133 	case LDAP_OPT_SERVER_CONTROLS:
2134 	case LDAP_OPT_CLIENT_CONTROLS:
2135 	case LDAP_OPT_API_INFO:
2136 	case LDAP_OPT_API_FEATURE_INFO:
2137 */
2138 	default:
2139 		RETURN_FALSE;
2140 	}
2141 	RETURN_TRUE;
2142 }
2143 /* }}} */
2144 
2145 /* {{{ proto bool ldap_set_option(resource link, int option, mixed newval)
2146    Set the value of various session-wide parameters */
PHP_FUNCTION(ldap_set_option)2147 PHP_FUNCTION(ldap_set_option)
2148 {
2149 	zval *link, **newval;
2150 	ldap_linkdata *ld;
2151 	LDAP *ldap;
2152 	long option;
2153 
2154 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zlZ", &link, &option, &newval) != SUCCESS) {
2155 		return;
2156 	}
2157 
2158 	if (Z_TYPE_P(link) == IS_NULL) {
2159 		ldap = NULL;
2160 	} else {
2161 		ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2162 		ldap = ld->link;
2163 	}
2164 
2165 	switch (option) {
2166 	/* options with int value */
2167 	case LDAP_OPT_DEREF:
2168 	case LDAP_OPT_SIZELIMIT:
2169 	case LDAP_OPT_TIMELIMIT:
2170 	case LDAP_OPT_PROTOCOL_VERSION:
2171 	case LDAP_OPT_ERROR_NUMBER:
2172 #ifdef LDAP_OPT_DEBUG_LEVEL
2173 	case LDAP_OPT_DEBUG_LEVEL:
2174 #endif
2175 		{
2176 			int val;
2177 
2178 			convert_to_long_ex(newval);
2179 			val = Z_LVAL_PP(newval);
2180 			if (ldap_set_option(ldap, option, &val)) {
2181 				RETURN_FALSE;
2182 			}
2183 		} break;
2184 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2185 	case LDAP_OPT_NETWORK_TIMEOUT:
2186 		{
2187 			struct timeval timeout;
2188 
2189 			convert_to_long_ex(newval);
2190 			timeout.tv_sec = Z_LVAL_PP(newval);
2191 			timeout.tv_usec = 0;
2192 			if (ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
2193 				RETURN_FALSE;
2194 			}
2195 		} break;
2196 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
2197 	case LDAP_X_OPT_CONNECT_TIMEOUT:
2198 		{
2199 			int timeout;
2200 
2201 			convert_to_long_ex(newval);
2202 			timeout = 1000 * Z_LVAL_PP(newval); /* Convert to milliseconds */
2203 			if (ldap_set_option(ldap, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
2204 				RETURN_FALSE;
2205 			}
2206 		} break;
2207 #endif
2208 #ifdef LDAP_OPT_TIMEOUT
2209 	case LDAP_OPT_TIMEOUT:
2210 		{
2211 			struct timeval timeout;
2212 
2213 			convert_to_long_ex(newval);
2214 			timeout.tv_sec = Z_LVAL_PP(newval);
2215 			timeout.tv_usec = 0;
2216 			if (ldap_set_option(ldap, LDAP_OPT_TIMEOUT, (void *) &timeout)) {
2217 				RETURN_FALSE;
2218 			}
2219 		} break;
2220 #endif
2221 		/* options with string value */
2222 	case LDAP_OPT_ERROR_STRING:
2223 #ifdef LDAP_OPT_HOST_NAME
2224 	case LDAP_OPT_HOST_NAME:
2225 #endif
2226 #ifdef HAVE_LDAP_SASL
2227 	case LDAP_OPT_X_SASL_MECH:
2228 	case LDAP_OPT_X_SASL_REALM:
2229 	case LDAP_OPT_X_SASL_AUTHCID:
2230 	case LDAP_OPT_X_SASL_AUTHZID:
2231 #endif
2232 #ifdef LDAP_OPT_MATCHED_DN
2233 	case LDAP_OPT_MATCHED_DN:
2234 #endif
2235 		{
2236 			char *val;
2237 			convert_to_string_ex(newval);
2238 			val = Z_STRVAL_PP(newval);
2239 			if (ldap_set_option(ldap, option, val)) {
2240 				RETURN_FALSE;
2241 			}
2242 		} break;
2243 		/* options with boolean value */
2244 	case LDAP_OPT_REFERRALS:
2245 #ifdef LDAP_OPT_RESTART
2246 	case LDAP_OPT_RESTART:
2247 #endif
2248 		{
2249 			void *val;
2250 			convert_to_boolean_ex(newval);
2251 			val = Z_LVAL_PP(newval)
2252 				? LDAP_OPT_ON : LDAP_OPT_OFF;
2253 			if (ldap_set_option(ldap, option, val)) {
2254 				RETURN_FALSE;
2255 			}
2256 		} break;
2257 		/* options with control list value */
2258 	case LDAP_OPT_SERVER_CONTROLS:
2259 	case LDAP_OPT_CLIENT_CONTROLS:
2260 		{
2261 			LDAPControl *ctrl, **ctrls, **ctrlp;
2262 			zval **ctrlval, **val;
2263 			int ncontrols;
2264 			char error=0;
2265 
2266 			if ((Z_TYPE_PP(newval) != IS_ARRAY) || !(ncontrols = zend_hash_num_elements(Z_ARRVAL_PP(newval)))) {
2267 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected non-empty array value for this option");
2268 				RETURN_FALSE;
2269 			}
2270 			ctrls = safe_emalloc((1 + ncontrols), sizeof(*ctrls), 0);
2271 			*ctrls = NULL;
2272 			ctrlp = ctrls;
2273 			zend_hash_internal_pointer_reset(Z_ARRVAL_PP(newval));
2274 			while (zend_hash_get_current_data(Z_ARRVAL_PP(newval), (void**)&ctrlval) == SUCCESS) {
2275 				if (Z_TYPE_PP(ctrlval) != IS_ARRAY) {
2276 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "The array value must contain only arrays, where each array is a control");
2277 					error = 1;
2278 					break;
2279 				}
2280 				if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "oid", sizeof("oid"), (void **) &val) != SUCCESS) {
2281 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Control must have an oid key");
2282 					error = 1;
2283 					break;
2284 				}
2285 				ctrl = *ctrlp = emalloc(sizeof(**ctrlp));
2286 				convert_to_string_ex(val);
2287 				ctrl->ldctl_oid = Z_STRVAL_PP(val);
2288 				if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "value", sizeof("value"), (void **) &val) == SUCCESS) {
2289 					convert_to_string_ex(val);
2290 					ctrl->ldctl_value.bv_val = Z_STRVAL_PP(val);
2291 					ctrl->ldctl_value.bv_len = Z_STRLEN_PP(val);
2292 				} else {
2293 					ctrl->ldctl_value.bv_val = NULL;
2294 					ctrl->ldctl_value.bv_len = 0;
2295 				}
2296 				if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "iscritical", sizeof("iscritical"), (void **) &val) == SUCCESS) {
2297 					convert_to_boolean_ex(val);
2298 					ctrl->ldctl_iscritical = Z_BVAL_PP(val);
2299 				} else {
2300 					ctrl->ldctl_iscritical = 0;
2301 				}
2302 
2303 				++ctrlp;
2304 				*ctrlp = NULL;
2305 				zend_hash_move_forward(Z_ARRVAL_PP(newval));
2306 			}
2307 			if (!error) {
2308 				error = ldap_set_option(ldap, option, ctrls);
2309 			}
2310 			ctrlp = ctrls;
2311 			while (*ctrlp) {
2312 				efree(*ctrlp);
2313 				ctrlp++;
2314 			}
2315 			efree(ctrls);
2316 			if (error) {
2317 				RETURN_FALSE;
2318 			}
2319 		} break;
2320 	default:
2321 		RETURN_FALSE;
2322 	}
2323 	RETURN_TRUE;
2324 }
2325 /* }}} */
2326 
2327 #ifdef HAVE_LDAP_PARSE_RESULT
2328 /* {{{ proto bool ldap_parse_result(resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals)
2329    Extract information from result */
PHP_FUNCTION(ldap_parse_result)2330 PHP_FUNCTION(ldap_parse_result)
2331 {
2332 	zval *link, *result, *errcode, *matcheddn, *errmsg, *referrals;
2333 	ldap_linkdata *ld;
2334 	LDAPMessage *ldap_result;
2335 	char **lreferrals, **refp;
2336 	char *lmatcheddn, *lerrmsg;
2337 	int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
2338 
2339 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz|zzz", &link, &result, &errcode, &matcheddn, &errmsg, &referrals) != SUCCESS) {
2340 		return;
2341 	}
2342 
2343 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2344 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
2345 
2346 	rc = ldap_parse_result(ld->link, ldap_result, &lerrcode,
2347 				myargcount > 3 ? &lmatcheddn : NULL,
2348 				myargcount > 4 ? &lerrmsg : NULL,
2349 				myargcount > 5 ? &lreferrals : NULL,
2350 				NULL /* &serverctrls */,
2351 				0);
2352 	if (rc != LDAP_SUCCESS) {
2353 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s", ldap_err2string(rc));
2354 		RETURN_FALSE;
2355 	}
2356 
2357 	zval_dtor(errcode);
2358 	ZVAL_LONG(errcode, lerrcode);
2359 
2360 	/* Reverse -> fall through */
2361 	switch (myargcount) {
2362 		case 6:
2363 			zval_dtor(referrals);
2364 			array_init(referrals);
2365 			if (lreferrals != NULL) {
2366 				refp = lreferrals;
2367 				while (*refp) {
2368 					add_next_index_string(referrals, *refp, 1);
2369 					refp++;
2370 				}
2371 				ldap_memvfree((void**)lreferrals);
2372 			}
2373 		case 5:
2374 			zval_dtor(errmsg);
2375 			if (lerrmsg == NULL) {
2376 				ZVAL_EMPTY_STRING(errmsg);
2377 			} else {
2378 				ZVAL_STRING(errmsg, lerrmsg, 1);
2379 				ldap_memfree(lerrmsg);
2380 			}
2381 		case 4:
2382 			zval_dtor(matcheddn);
2383 			if (lmatcheddn == NULL) {
2384 				ZVAL_EMPTY_STRING(matcheddn);
2385 			} else {
2386 				ZVAL_STRING(matcheddn, lmatcheddn, 1);
2387 				ldap_memfree(lmatcheddn);
2388 			}
2389 	}
2390 	RETURN_TRUE;
2391 }
2392 /* }}} */
2393 #endif
2394 
2395 /* {{{ proto resource ldap_first_reference(resource link, resource result)
2396    Return first reference */
PHP_FUNCTION(ldap_first_reference)2397 PHP_FUNCTION(ldap_first_reference)
2398 {
2399 	zval *link, *result;
2400 	ldap_linkdata *ld;
2401 	ldap_resultentry *resultentry;
2402 	LDAPMessage *ldap_result, *entry;
2403 
2404 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
2405 		return;
2406 	}
2407 
2408 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2409 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
2410 
2411 	if ((entry = ldap_first_reference(ld->link, ldap_result)) == NULL) {
2412 		RETVAL_FALSE;
2413 	} else {
2414 		resultentry = emalloc(sizeof(ldap_resultentry));
2415 		ZEND_REGISTER_RESOURCE(return_value, resultentry, le_result_entry);
2416 		resultentry->id = Z_LVAL_P(result);
2417 		zend_list_addref(resultentry->id);
2418 		resultentry->data = entry;
2419 		resultentry->ber = NULL;
2420 	}
2421 }
2422 /* }}} */
2423 
2424 /* {{{ proto resource ldap_next_reference(resource link, resource reference_entry)
2425    Get next reference */
PHP_FUNCTION(ldap_next_reference)2426 PHP_FUNCTION(ldap_next_reference)
2427 {
2428 	zval *link, *result_entry;
2429 	ldap_linkdata *ld;
2430 	ldap_resultentry *resultentry, *resultentry_next;
2431 	LDAPMessage *entry_next;
2432 
2433 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
2434 		return;
2435 	}
2436 
2437 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2438 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
2439 
2440 	if ((entry_next = ldap_next_reference(ld->link, resultentry->data)) == NULL) {
2441 		RETVAL_FALSE;
2442 	} else {
2443 		resultentry_next = emalloc(sizeof(ldap_resultentry));
2444 		ZEND_REGISTER_RESOURCE(return_value, resultentry_next, le_result_entry);
2445 		resultentry_next->id = resultentry->id;
2446 		zend_list_addref(resultentry->id);
2447 		resultentry_next->data = entry_next;
2448 		resultentry_next->ber = NULL;
2449 	}
2450 }
2451 /* }}} */
2452 
2453 #ifdef HAVE_LDAP_PARSE_REFERENCE
2454 /* {{{ proto bool ldap_parse_reference(resource link, resource reference_entry, array referrals)
2455    Extract information from reference entry */
PHP_FUNCTION(ldap_parse_reference)2456 PHP_FUNCTION(ldap_parse_reference)
2457 {
2458 	zval *link, *result_entry, *referrals;
2459 	ldap_linkdata *ld;
2460 	ldap_resultentry *resultentry;
2461 	char **lreferrals, **refp;
2462 
2463 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz", &link, &result_entry, &referrals) != SUCCESS) {
2464 		return;
2465 	}
2466 
2467 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2468 	ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
2469 
2470 	if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, NULL /* &serverctrls */, 0) != LDAP_SUCCESS) {
2471 		RETURN_FALSE;
2472 	}
2473 
2474 	zval_dtor(referrals);
2475 	array_init(referrals);
2476 	if (lreferrals != NULL) {
2477 		refp = lreferrals;
2478 		while (*refp) {
2479 			add_next_index_string(referrals, *refp, 1);
2480 			refp++;
2481 		}
2482 		ldap_memvfree((void**)lreferrals);
2483 	}
2484 	RETURN_TRUE;
2485 }
2486 /* }}} */
2487 #endif
2488 
2489 /* {{{ proto bool ldap_rename(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn);
2490    Modify the name of an entry */
PHP_FUNCTION(ldap_rename)2491 PHP_FUNCTION(ldap_rename)
2492 {
2493 	zval *link;
2494 	ldap_linkdata *ld;
2495 	int rc;
2496 	char *dn, *newrdn, *newparent;
2497 	int dn_len, newrdn_len, newparent_len;
2498 	zend_bool deleteoldrdn;
2499 
2500 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsssb", &link, &dn, &dn_len, &newrdn, &newrdn_len, &newparent, &newparent_len, &deleteoldrdn) != SUCCESS) {
2501 		return;
2502 	}
2503 
2504 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2505 
2506 	if (newparent_len == 0) {
2507 		newparent = NULL;
2508 	}
2509 
2510 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
2511 	rc = ldap_rename_s(ld->link, dn, newrdn, newparent, deleteoldrdn, NULL, NULL);
2512 #else
2513 	if (newparent_len != 0) {
2514 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "You are using old LDAP API, newparent must be the empty string, can only modify RDN");
2515 		RETURN_FALSE;
2516 	}
2517 /* could support old APIs but need check for ldap_modrdn2()/ldap_modrdn() */
2518 	rc = ldap_modrdn2_s(ld->link, dn, newrdn, deleteoldrdn);
2519 #endif
2520 
2521 	if (rc == LDAP_SUCCESS) {
2522 		RETURN_TRUE;
2523 	}
2524 	RETURN_FALSE;
2525 }
2526 /* }}} */
2527 
2528 #ifdef HAVE_LDAP_START_TLS_S
2529 /* {{{ proto bool ldap_start_tls(resource link)
2530    Start TLS */
PHP_FUNCTION(ldap_start_tls)2531 PHP_FUNCTION(ldap_start_tls)
2532 {
2533 	zval *link;
2534 	ldap_linkdata *ld;
2535 	int rc, protocol = LDAP_VERSION3;
2536 
2537 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
2538 		return;
2539 	}
2540 
2541 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2542 
2543 	if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) ||
2544 		((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS)
2545 	) {
2546 		php_error_docref(NULL TSRMLS_CC, E_WARNING,"Unable to start TLS: %s", ldap_err2string(rc));
2547 		RETURN_FALSE;
2548 	} else {
2549 		RETURN_TRUE;
2550 	}
2551 }
2552 /* }}} */
2553 #endif
2554 #endif /* (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP */
2555 
2556 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
2557 /* {{{ _ldap_rebind_proc()
2558 */
_ldap_rebind_proc(LDAP * ldap,const char * url,ber_tag_t req,ber_int_t msgid,void * params)2559 int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req, ber_int_t msgid, void *params)
2560 {
2561 	ldap_linkdata *ld;
2562 	int retval;
2563 	zval *cb_url;
2564 	zval **cb_args[2];
2565 	zval *cb_retval;
2566 	zval *cb_link = (zval *) params;
2567 	TSRMLS_FETCH();
2568 
2569 	ld = (ldap_linkdata *) zend_fetch_resource(&cb_link TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
2570 
2571 	/* link exists and callback set? */
2572 	if (ld == NULL || ld->rebindproc == NULL) {
2573 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link not found or no callback set");
2574 		return LDAP_OTHER;
2575 	}
2576 
2577 	/* callback */
2578 	MAKE_STD_ZVAL(cb_url);
2579 	ZVAL_STRING(cb_url, estrdup(url), 0);
2580 	cb_args[0] = &cb_link;
2581 	cb_args[1] = &cb_url;
2582 	if (call_user_function_ex(EG(function_table), NULL, ld->rebindproc, &cb_retval, 2, cb_args, 0, NULL TSRMLS_CC) == SUCCESS && cb_retval) {
2583 		convert_to_long_ex(&cb_retval);
2584 		retval = Z_LVAL_P(cb_retval);
2585 		zval_ptr_dtor(&cb_retval);
2586 	} else {
2587 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "rebind_proc PHP callback failed");
2588 		retval = LDAP_OTHER;
2589 	}
2590 	zval_dtor(cb_url);
2591 	FREE_ZVAL(cb_url);
2592 	return retval;
2593 }
2594 /* }}} */
2595 
2596 /* {{{ proto bool ldap_set_rebind_proc(resource link, string callback)
2597    Set a callback function to do re-binds on referral chasing. */
PHP_FUNCTION(ldap_set_rebind_proc)2598 PHP_FUNCTION(ldap_set_rebind_proc)
2599 {
2600 	zval *link, *callback;
2601 	ldap_linkdata *ld;
2602 	char *callback_name;
2603 
2604 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &link, &callback) != SUCCESS) {
2605 		RETURN_FALSE;
2606 	}
2607 
2608 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2609 
2610 	if (Z_TYPE_P(callback) == IS_STRING && Z_STRLEN_P(callback) == 0) {
2611 		/* unregister rebind procedure */
2612 		if (ld->rebindproc != NULL) {
2613 			zval_dtor(ld->rebindproc);
2614 			FREE_ZVAL(ld->rebindproc);
2615 			ld->rebindproc = NULL;
2616 			ldap_set_rebind_proc(ld->link, NULL, NULL);
2617 		}
2618 		RETURN_TRUE;
2619 	}
2620 
2621 	/* callable? */
2622 	if (!zend_is_callable(callback, 0, &callback_name TSRMLS_CC)) {
2623 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Two arguments expected for '%s' to be a valid callback", callback_name);
2624 		efree(callback_name);
2625 		RETURN_FALSE;
2626 	}
2627 	efree(callback_name);
2628 
2629 	/* register rebind procedure */
2630 	if (ld->rebindproc == NULL) {
2631 		ldap_set_rebind_proc(ld->link, _ldap_rebind_proc, (void *) link);
2632 	} else {
2633 		zval_dtor(ld->rebindproc);
2634 	}
2635 
2636 	ALLOC_ZVAL(ld->rebindproc);
2637 	*ld->rebindproc = *callback;
2638 	zval_copy_ctor(ld->rebindproc);
2639 	RETURN_TRUE;
2640 }
2641 /* }}} */
2642 #endif
2643 
php_ldap_do_escape(const zend_bool * map,const char * value,size_t valuelen,char ** result,size_t * resultlen)2644 static void php_ldap_do_escape(const zend_bool *map, const char *value, size_t valuelen, char **result, size_t *resultlen)
2645 {
2646 	char hex[] = "0123456789abcdef";
2647 	int i, p = 0;
2648 	size_t len = 0;
2649 
2650 	for (i = 0; i < valuelen; i++) {
2651 		len += (map[(unsigned char) value[i]]) ? 3 : 1;
2652 	}
2653 
2654 	(*result) = (char *) safe_emalloc_string(1, len, 1);
2655 	(*resultlen) = len;
2656 
2657 	for (i = 0; i < valuelen; i++) {
2658 		unsigned char v = (unsigned char) value[i];
2659 
2660 		if (map[v]) {
2661 			(*result)[p++] = '\\';
2662 			(*result)[p++] = hex[v >> 4];
2663 			(*result)[p++] = hex[v & 0x0f];
2664 		} else {
2665 			(*result)[p++] = v;
2666 		}
2667 	}
2668 
2669 	(*result)[p++] = '\0';
2670 }
2671 
php_ldap_escape_map_set_chars(zend_bool * map,const char * chars,const int charslen,char escape)2672 static void php_ldap_escape_map_set_chars(zend_bool *map, const char *chars, const int charslen, char escape)
2673 {
2674 	int i = 0;
2675 	while (i < charslen) {
2676 		map[(unsigned char) chars[i++]] = escape;
2677 	}
2678 }
2679 
PHP_FUNCTION(ldap_escape)2680 PHP_FUNCTION(ldap_escape)
2681 {
2682 	char *value, *ignores, *result;
2683 	int valuelen = 0, ignoreslen = 0, i;
2684 	size_t resultlen;
2685 	long flags = 0;
2686 	zend_bool map[256] = {0}, havecharlist = 0;
2687 
2688 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sl", &value, &valuelen, &ignores, &ignoreslen, &flags) != SUCCESS) {
2689 		return;
2690 	}
2691 
2692 	if (!valuelen) {
2693 		RETURN_EMPTY_STRING();
2694 	}
2695 
2696 	if (flags & PHP_LDAP_ESCAPE_FILTER) {
2697 		havecharlist = 1;
2698 		php_ldap_escape_map_set_chars(map, "\\*()\0", sizeof("\\*()\0") - 1, 1);
2699 	}
2700 
2701 	if (flags & PHP_LDAP_ESCAPE_DN) {
2702 		havecharlist = 1;
2703 		php_ldap_escape_map_set_chars(map, "\\,=+<>;\"#", sizeof("\\,=+<>;\"#") - 1, 1);
2704 	}
2705 
2706 	if (!havecharlist) {
2707 		for (i = 0; i < 256; i++) {
2708 			map[i] = 1;
2709 		}
2710 	}
2711 
2712 	if (ignoreslen) {
2713 		php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0);
2714 	}
2715 
2716 	php_ldap_do_escape(map, value, valuelen, &result, &resultlen);
2717 
2718 	RETURN_STRINGL(result, resultlen, 0);
2719 }
2720 
2721 #ifdef STR_TRANSLATION
2722 /* {{{ php_ldap_do_translate
2723  */
php_ldap_do_translate(INTERNAL_FUNCTION_PARAMETERS,int way)2724 static void php_ldap_do_translate(INTERNAL_FUNCTION_PARAMETERS, int way)
2725 {
2726 	char *value;
2727 	int result, ldap_len;
2728 
2729 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) != SUCCESS) {
2730 		return;
2731 	}
2732 
2733 	if (value_len == 0) {
2734 		RETURN_FALSE;
2735 	}
2736 
2737 	if (way == 1) {
2738 		result = ldap_8859_to_t61(&value, &value_len, 0);
2739 	} else {
2740 		result = ldap_t61_to_8859(&value, &value_len, 0);
2741 	}
2742 
2743 	if (result == LDAP_SUCCESS) {
2744 		RETVAL_STRINGL(value, value_len, 1);
2745 		free(value);
2746 	} else {
2747 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Conversion from iso-8859-1 to t61 failed: %s", ldap_err2string(result));
2748 		RETVAL_FALSE;
2749 	}
2750 }
2751 /* }}} */
2752 
2753 /* {{{ proto string ldap_t61_to_8859(string value)
2754    Translate t61 characters to 8859 characters */
PHP_FUNCTION(ldap_t61_to_8859)2755 PHP_FUNCTION(ldap_t61_to_8859)
2756 {
2757 	php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2758 }
2759 /* }}} */
2760 
2761 /* {{{ proto string ldap_8859_to_t61(string value)
2762    Translate 8859 characters to t61 characters */
PHP_FUNCTION(ldap_8859_to_t61)2763 PHP_FUNCTION(ldap_8859_to_t61)
2764 {
2765 	php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2766 }
2767 /* }}} */
2768 #endif
2769 
2770 #ifdef LDAP_CONTROL_PAGEDRESULTS
2771 /* {{{ proto mixed ldap_control_paged_result(resource link, int pagesize [, bool iscritical [, string cookie]])
2772    Inject paged results control*/
PHP_FUNCTION(ldap_control_paged_result)2773 PHP_FUNCTION(ldap_control_paged_result)
2774 {
2775 	long pagesize;
2776 	zend_bool iscritical;
2777 	zval *link;
2778 	char *cookie = NULL;
2779 	int cookie_len = 0;
2780 	struct berval lcookie = { 0, NULL };
2781 	ldap_linkdata *ld;
2782 	LDAP *ldap;
2783 	BerElement *ber = NULL;
2784 	LDAPControl	ctrl, *ctrlsp[2];
2785 	int rc, myargcount = ZEND_NUM_ARGS();
2786 
2787 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|bs", &link, &pagesize, &iscritical, &cookie, &cookie_len) != SUCCESS) {
2788 		return;
2789 	}
2790 
2791 	if (Z_TYPE_P(link) == IS_NULL) {
2792 		ldap = NULL;
2793 	} else {
2794 		ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2795 		ldap = ld->link;
2796 	}
2797 
2798 	ber = ber_alloc_t(LBER_USE_DER);
2799 	if (ber == NULL) {
2800 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER encoding resources for paged results control");
2801 		RETURN_FALSE;
2802 	}
2803 
2804 	ctrl.ldctl_iscritical = 0;
2805 
2806 	switch (myargcount) {
2807 		case 4:
2808 			lcookie.bv_val = cookie;
2809 			lcookie.bv_len = cookie_len;
2810 			/* fallthru */
2811 		case 3:
2812 			ctrl.ldctl_iscritical = (int)iscritical;
2813 			/* fallthru */
2814 	}
2815 
2816 	if (ber_printf(ber, "{iO}", (int)pagesize, &lcookie) == LBER_ERROR) {
2817 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to BER printf paged results control");
2818 		RETVAL_FALSE;
2819 		goto lcpr_error_out;
2820 	}
2821 	rc = ber_flatten2(ber, &ctrl.ldctl_value, 0);
2822 	if (rc == LBER_ERROR) {
2823 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to BER encode paged results control");
2824 		RETVAL_FALSE;
2825 		goto lcpr_error_out;
2826 	}
2827 
2828 	ctrl.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2829 
2830 	if (ldap) {
2831 		/* directly set the option */
2832 		ctrlsp[0] = &ctrl;
2833 		ctrlsp[1] = NULL;
2834 
2835 		rc = ldap_set_option(ldap, LDAP_OPT_SERVER_CONTROLS, ctrlsp);
2836 		if (rc != LDAP_SUCCESS) {
2837 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set paged results control: %s (%d)", ldap_err2string(rc), rc);
2838 			RETVAL_FALSE;
2839 			goto lcpr_error_out;
2840 		}
2841 		RETVAL_TRUE;
2842 	} else {
2843 		/* return a PHP control object */
2844 		array_init(return_value);
2845 
2846 		add_assoc_string(return_value, "oid", ctrl.ldctl_oid, 1);
2847 		if (ctrl.ldctl_value.bv_len) {
2848 			add_assoc_stringl(return_value, "value", ctrl.ldctl_value.bv_val, ctrl.ldctl_value.bv_len, 1);
2849 		}
2850 		if (ctrl.ldctl_iscritical) {
2851 			add_assoc_bool(return_value, "iscritical", ctrl.ldctl_iscritical);
2852 		}
2853 	}
2854 
2855 lcpr_error_out:
2856 	if (ber != NULL) {
2857 		ber_free(ber, 1);
2858 	}
2859 	return;
2860 }
2861 /* }}} */
2862 
2863 /* {{{ proto bool ldap_control_paged_result_response(resource link, resource result [, string &cookie [, int &estimated]])
2864    Extract paged results control response */
PHP_FUNCTION(ldap_control_paged_result_response)2865 PHP_FUNCTION(ldap_control_paged_result_response)
2866 {
2867 	zval *link, *result, *cookie, *estimated;
2868 	struct berval lcookie;
2869 	int lestimated;
2870 	ldap_linkdata *ld;
2871 	LDAPMessage *ldap_result;
2872 	LDAPControl **lserverctrls, *lctrl;
2873 	BerElement *ber;
2874 	ber_tag_t tag;
2875 	int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
2876 
2877 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|zz", &link, &result, &cookie, &estimated) != SUCCESS) {
2878 		return;
2879 	}
2880 
2881 	ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2882 	ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
2883 
2884 	rc = ldap_parse_result(ld->link,
2885 				ldap_result,
2886 				&lerrcode,
2887 				NULL,		/* matcheddn */
2888 				NULL,		/* errmsg */
2889 				NULL,		/* referrals */
2890 				&lserverctrls,
2891 				0);
2892 
2893 	if (rc != LDAP_SUCCESS) {
2894 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s (%d)", ldap_err2string(rc), rc);
2895 		RETURN_FALSE;
2896 	}
2897 
2898 	if (lerrcode != LDAP_SUCCESS) {
2899 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is: %s (%d)", ldap_err2string(lerrcode), lerrcode);
2900 		RETURN_FALSE;
2901 	}
2902 
2903 	if (lserverctrls == NULL) {
2904 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No server controls in result");
2905 		RETURN_FALSE;
2906 	}
2907 
2908 	lctrl = ldap_control_find(LDAP_CONTROL_PAGEDRESULTS, lserverctrls, NULL);
2909 	if (lctrl == NULL) {
2910 		ldap_controls_free(lserverctrls);
2911 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No paged results control response in result");
2912 		RETURN_FALSE;
2913 	}
2914 
2915 	ber = ber_init(&lctrl->ldctl_value);
2916 	if (ber == NULL) {
2917 		ldap_controls_free(lserverctrls);
2918 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER decoding resources for paged results control response");
2919 		RETURN_FALSE;
2920 	}
2921 
2922 	tag = ber_scanf(ber, "{io}", &lestimated, &lcookie);
2923 	(void)ber_free(ber, 1);
2924 
2925 	if (tag == LBER_ERROR) {
2926 		ldap_controls_free(lserverctrls);
2927 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode paged results control response");
2928 		RETURN_FALSE;
2929 	}
2930 
2931 	if (lestimated < 0) {
2932 		ldap_controls_free(lserverctrls);
2933 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid paged results control response value");
2934 		RETURN_FALSE;
2935 	}
2936 
2937 	ldap_controls_free(lserverctrls);
2938 	if (myargcount == 4) {
2939 		zval_dtor(estimated);
2940 		ZVAL_LONG(estimated, lestimated);
2941 	}
2942 
2943 	zval_dtor(cookie);
2944  	if (lcookie.bv_len == 0) {
2945 		ZVAL_EMPTY_STRING(cookie);
2946  	} else {
2947 		ZVAL_STRINGL(cookie, lcookie.bv_val, lcookie.bv_len, 1);
2948  	}
2949  	ldap_memfree(lcookie.bv_val);
2950 
2951 	RETURN_TRUE;
2952 }
2953 /* }}} */
2954 #endif
2955 
2956 /* {{{ arginfo */
2957 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0)
2958 	ZEND_ARG_INFO(0, hostname)
2959 	ZEND_ARG_INFO(0, port)
2960 #ifdef HAVE_ORALDAP
2961 	ZEND_ARG_INFO(0, wallet)
2962 	ZEND_ARG_INFO(0, wallet_passwd)
2963 	ZEND_ARG_INFO(0, authmode)
2964 #endif
2965 ZEND_END_ARG_INFO()
2966 
2967 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_resource, 0, 0, 1)
2968 	ZEND_ARG_INFO(0, link_identifier)
2969 ZEND_END_ARG_INFO()
2970 
2971 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind, 0, 0, 1)
2972 	ZEND_ARG_INFO(0, link_identifier)
2973 	ZEND_ARG_INFO(0, bind_rdn)
2974 	ZEND_ARG_INFO(0, bind_password)
2975 ZEND_END_ARG_INFO()
2976 
2977 #ifdef HAVE_LDAP_SASL
2978 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sasl_bind, 0, 0, 1)
2979 	ZEND_ARG_INFO(0, link)
2980 	ZEND_ARG_INFO(0, binddn)
2981 	ZEND_ARG_INFO(0, password)
2982 	ZEND_ARG_INFO(0, sasl_mech)
2983 	ZEND_ARG_INFO(0, sasl_realm)
2984 	ZEND_ARG_INFO(0, sasl_authz_id)
2985 	ZEND_ARG_INFO(0, props)
2986 ZEND_END_ARG_INFO()
2987 #endif
2988 
2989 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_read, 0, 0, 3)
2990 	ZEND_ARG_INFO(0, link_identifier)
2991 	ZEND_ARG_INFO(0, base_dn)
2992 	ZEND_ARG_INFO(0, filter)
2993 	ZEND_ARG_INFO(0, attributes)
2994 	ZEND_ARG_INFO(0, attrsonly)
2995 	ZEND_ARG_INFO(0, sizelimit)
2996 	ZEND_ARG_INFO(0, timelimit)
2997 	ZEND_ARG_INFO(0, deref)
2998 ZEND_END_ARG_INFO()
2999 
3000 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_list, 0, 0, 3)
3001 	ZEND_ARG_INFO(0, link_identifier)
3002 	ZEND_ARG_INFO(0, base_dn)
3003 	ZEND_ARG_INFO(0, filter)
3004 	ZEND_ARG_INFO(0, attributes)
3005 	ZEND_ARG_INFO(0, attrsonly)
3006 	ZEND_ARG_INFO(0, sizelimit)
3007 	ZEND_ARG_INFO(0, timelimit)
3008 	ZEND_ARG_INFO(0, deref)
3009 ZEND_END_ARG_INFO()
3010 
3011 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_search, 0, 0, 3)
3012 	ZEND_ARG_INFO(0, link_identifier)
3013 	ZEND_ARG_INFO(0, base_dn)
3014 	ZEND_ARG_INFO(0, filter)
3015 	ZEND_ARG_INFO(0, attributes)
3016 	ZEND_ARG_INFO(0, attrsonly)
3017 	ZEND_ARG_INFO(0, sizelimit)
3018 	ZEND_ARG_INFO(0, timelimit)
3019 	ZEND_ARG_INFO(0, deref)
3020 ZEND_END_ARG_INFO()
3021 
3022 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_count_entries, 0, 0, 2)
3023 	ZEND_ARG_INFO(0, link_identifier)
3024 	ZEND_ARG_INFO(0, result_identifier)
3025 ZEND_END_ARG_INFO()
3026 
3027 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_entry, 0, 0, 2)
3028 	ZEND_ARG_INFO(0, link_identifier)
3029 	ZEND_ARG_INFO(0, result_identifier)
3030 ZEND_END_ARG_INFO()
3031 
3032 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_entry, 0, 0, 2)
3033 	ZEND_ARG_INFO(0, link_identifier)
3034 	ZEND_ARG_INFO(0, result_identifier)
3035 ZEND_END_ARG_INFO()
3036 
3037 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_entries, 0, 0, 2)
3038 	ZEND_ARG_INFO(0, link_identifier)
3039 	ZEND_ARG_INFO(0, result_identifier)
3040 ZEND_END_ARG_INFO()
3041 
3042 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_attribute, 0, 0, 2)
3043 	ZEND_ARG_INFO(0, link_identifier)
3044 	ZEND_ARG_INFO(0, result_entry_identifier)
3045 ZEND_END_ARG_INFO()
3046 
3047 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_attribute, 0, 0, 2)
3048 	ZEND_ARG_INFO(0, link_identifier)
3049 	ZEND_ARG_INFO(0, result_entry_identifier)
3050 ZEND_END_ARG_INFO()
3051 
3052 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_attributes, 0, 0, 2)
3053 	ZEND_ARG_INFO(0, link_identifier)
3054 	ZEND_ARG_INFO(0, result_entry_identifier)
3055 ZEND_END_ARG_INFO()
3056 
3057 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values, 0, 0, 3)
3058 	ZEND_ARG_INFO(0, link_identifier)
3059 	ZEND_ARG_INFO(0, result_entry_identifier)
3060 	ZEND_ARG_INFO(0, attribute)
3061 ZEND_END_ARG_INFO()
3062 
3063 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values_len, 0, 0, 3)
3064 	ZEND_ARG_INFO(0, link_identifier)
3065 	ZEND_ARG_INFO(0, result_entry_identifier)
3066 	ZEND_ARG_INFO(0, attribute)
3067 ZEND_END_ARG_INFO()
3068 
3069 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_dn, 0, 0, 2)
3070 	ZEND_ARG_INFO(0, link_identifier)
3071 	ZEND_ARG_INFO(0, result_entry_identifier)
3072 ZEND_END_ARG_INFO()
3073 
3074 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_explode_dn, 0, 0, 2)
3075 	ZEND_ARG_INFO(0, dn)
3076 	ZEND_ARG_INFO(0, with_attrib)
3077 ZEND_END_ARG_INFO()
3078 
3079 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_dn2ufn, 0, 0, 1)
3080 	ZEND_ARG_INFO(0, dn)
3081 ZEND_END_ARG_INFO()
3082 
3083 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_add, 0, 0, 3)
3084 	ZEND_ARG_INFO(0, link_identifier)
3085 	ZEND_ARG_INFO(0, dn)
3086 	ZEND_ARG_INFO(0, entry)
3087 ZEND_END_ARG_INFO()
3088 
3089 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_delete, 0, 0, 2)
3090 	ZEND_ARG_INFO(0, link_identifier)
3091 	ZEND_ARG_INFO(0, dn)
3092 ZEND_END_ARG_INFO()
3093 
3094 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify, 0, 0, 3)
3095 	ZEND_ARG_INFO(0, link_identifier)
3096 	ZEND_ARG_INFO(0, dn)
3097 	ZEND_ARG_INFO(0, entry)
3098 ZEND_END_ARG_INFO()
3099 
3100 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify_batch, 0, 0, 3)
3101 	ZEND_ARG_INFO(0, link_identifier)
3102 	ZEND_ARG_INFO(0, dn)
3103 	ZEND_ARG_ARRAY_INFO(0, modifications_info, 0)
3104 ZEND_END_ARG_INFO()
3105 
3106 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add, 0, 0, 3)
3107 	ZEND_ARG_INFO(0, link_identifier)
3108 	ZEND_ARG_INFO(0, dn)
3109 	ZEND_ARG_INFO(0, entry)
3110 ZEND_END_ARG_INFO()
3111 
3112 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_replace, 0, 0, 3)
3113 	ZEND_ARG_INFO(0, link_identifier)
3114 	ZEND_ARG_INFO(0, dn)
3115 	ZEND_ARG_INFO(0, entry)
3116 ZEND_END_ARG_INFO()
3117 
3118 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_del, 0, 0, 3)
3119 	ZEND_ARG_INFO(0, link_identifier)
3120 	ZEND_ARG_INFO(0, dn)
3121 	ZEND_ARG_INFO(0, entry)
3122 ZEND_END_ARG_INFO()
3123 
3124 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_err2str, 0, 0, 1)
3125 	ZEND_ARG_INFO(0, errno)
3126 ZEND_END_ARG_INFO()
3127 
3128 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_compare, 0, 0, 4)
3129 	ZEND_ARG_INFO(0, link_identifier)
3130 	ZEND_ARG_INFO(0, dn)
3131 	ZEND_ARG_INFO(0, attribute)
3132 	ZEND_ARG_INFO(0, value)
3133 ZEND_END_ARG_INFO()
3134 
3135 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sort, 0, 0, 3)
3136 	ZEND_ARG_INFO(0, link)
3137 	ZEND_ARG_INFO(0, result)
3138 	ZEND_ARG_INFO(0, sortfilter)
3139 ZEND_END_ARG_INFO()
3140 
3141 #ifdef LDAP_CONTROL_PAGEDRESULTS
3142 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_control_paged_result, 0, 0, 2)
3143 	ZEND_ARG_INFO(0, link)
3144 	ZEND_ARG_INFO(0, pagesize)
3145 	ZEND_ARG_INFO(0, iscritical)
3146 	ZEND_ARG_INFO(0, cookie)
3147 ZEND_END_ARG_INFO();
3148 
3149 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_control_paged_result_response, 0, 0, 2)
3150 	ZEND_ARG_INFO(0, link)
3151 	ZEND_ARG_INFO(0, result)
3152 	ZEND_ARG_INFO(1, cookie)
3153 	ZEND_ARG_INFO(1, estimated)
3154 ZEND_END_ARG_INFO();
3155 #endif
3156 
3157 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
3158 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_rename, 0, 0, 5)
3159 	ZEND_ARG_INFO(0, link_identifier)
3160 	ZEND_ARG_INFO(0, dn)
3161 	ZEND_ARG_INFO(0, newrdn)
3162 	ZEND_ARG_INFO(0, newparent)
3163 	ZEND_ARG_INFO(0, deleteoldrdn)
3164 ZEND_END_ARG_INFO()
3165 
3166 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_option, 0, 0, 3)
3167 	ZEND_ARG_INFO(0, link_identifier)
3168 	ZEND_ARG_INFO(0, option)
3169 	ZEND_ARG_INFO(1, retval)
3170 ZEND_END_ARG_INFO()
3171 
3172 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_option, 0, 0, 3)
3173 	ZEND_ARG_INFO(0, link_identifier)
3174 	ZEND_ARG_INFO(0, option)
3175 	ZEND_ARG_INFO(0, newval)
3176 ZEND_END_ARG_INFO()
3177 
3178 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_reference, 0, 0, 2)
3179 	ZEND_ARG_INFO(0, link)
3180 	ZEND_ARG_INFO(0, result)
3181 ZEND_END_ARG_INFO()
3182 
3183 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_reference, 0, 0, 2)
3184 	ZEND_ARG_INFO(0, link)
3185 	ZEND_ARG_INFO(0, entry)
3186 ZEND_END_ARG_INFO()
3187 
3188 #ifdef HAVE_LDAP_PARSE_REFERENCE
3189 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_reference, 0, 0, 3)
3190 	ZEND_ARG_INFO(0, link)
3191 	ZEND_ARG_INFO(0, entry)
3192 	ZEND_ARG_INFO(1, referrals)
3193 ZEND_END_ARG_INFO()
3194 #endif
3195 
3196 
3197 #ifdef HAVE_LDAP_PARSE_RESULT
3198 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_result, 0, 0, 3)
3199 	ZEND_ARG_INFO(0, link)
3200 	ZEND_ARG_INFO(0, result)
3201 	ZEND_ARG_INFO(1, errcode)
3202 	ZEND_ARG_INFO(1, matcheddn)
3203 	ZEND_ARG_INFO(1, errmsg)
3204 	ZEND_ARG_INFO(1, referrals)
3205 ZEND_END_ARG_INFO()
3206 #endif
3207 #endif
3208 
3209 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
3210 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_rebind_proc, 0, 0, 2)
3211 	ZEND_ARG_INFO(0, link)
3212 	ZEND_ARG_INFO(0, callback)
3213 ZEND_END_ARG_INFO()
3214 #endif
3215 
3216 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_escape, 0, 0, 1)
3217 	ZEND_ARG_INFO(0, value)
3218 	ZEND_ARG_INFO(0, ignore)
3219 	ZEND_ARG_INFO(0, flags)
3220 ZEND_END_ARG_INFO()
3221 
3222 #ifdef STR_TRANSLATION
3223 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_t61_to_8859, 0, 0, 1)
3224 	ZEND_ARG_INFO(0, value)
3225 ZEND_END_ARG_INFO()
3226 
3227 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_8859_to_t61, 0, 0, 1)
3228 	ZEND_ARG_INFO(0, value)
3229 ZEND_END_ARG_INFO()
3230 #endif
3231 /* }}} */
3232 
3233 /*
3234 	This is just a small subset of the functionality provided by the LDAP library. All the
3235 	operations are synchronous. Referrals are not handled automatically.
3236 */
3237 /* {{{ ldap_functions[]
3238  */
3239 const zend_function_entry ldap_functions[] = {
3240 	PHP_FE(ldap_connect,								arginfo_ldap_connect)
3241 	PHP_FALIAS(ldap_close,		ldap_unbind,			arginfo_ldap_resource)
3242 	PHP_FE(ldap_bind,									arginfo_ldap_bind)
3243 #ifdef HAVE_LDAP_SASL
3244 	PHP_FE(ldap_sasl_bind,								arginfo_ldap_sasl_bind)
3245 #endif
3246 	PHP_FE(ldap_unbind,									arginfo_ldap_resource)
3247 	PHP_FE(ldap_read,									arginfo_ldap_read)
3248 	PHP_FE(ldap_list,									arginfo_ldap_list)
3249 	PHP_FE(ldap_search,									arginfo_ldap_search)
3250 	PHP_FE(ldap_free_result,							arginfo_ldap_resource)
3251 	PHP_FE(ldap_count_entries,							arginfo_ldap_count_entries)
3252 	PHP_FE(ldap_first_entry,							arginfo_ldap_first_entry)
3253 	PHP_FE(ldap_next_entry,								arginfo_ldap_next_entry)
3254 	PHP_FE(ldap_get_entries,							arginfo_ldap_get_entries)
3255 	PHP_FE(ldap_first_attribute,						arginfo_ldap_first_attribute)
3256 	PHP_FE(ldap_next_attribute,							arginfo_ldap_next_attribute)
3257 	PHP_FE(ldap_get_attributes,							arginfo_ldap_get_attributes)
3258 	PHP_FALIAS(ldap_get_values,	ldap_get_values_len,	arginfo_ldap_get_values)
3259 	PHP_FE(ldap_get_values_len,							arginfo_ldap_get_values_len)
3260 	PHP_FE(ldap_get_dn,									arginfo_ldap_get_dn)
3261 	PHP_FE(ldap_explode_dn,								arginfo_ldap_explode_dn)
3262 	PHP_FE(ldap_dn2ufn,									arginfo_ldap_dn2ufn)
3263 	PHP_FE(ldap_add,									arginfo_ldap_add)
3264 	PHP_FE(ldap_delete,									arginfo_ldap_delete)
3265 	PHP_FE(ldap_modify_batch,							arginfo_ldap_modify_batch)
3266 	PHP_FALIAS(ldap_modify,		ldap_mod_replace,		arginfo_ldap_modify)
3267 
3268 /* additional functions for attribute based modifications, Gerrit Thomson */
3269 	PHP_FE(ldap_mod_add,								arginfo_ldap_mod_add)
3270 	PHP_FE(ldap_mod_replace,							arginfo_ldap_mod_replace)
3271 	PHP_FE(ldap_mod_del,								arginfo_ldap_mod_del)
3272 /* end gjt mod */
3273 
3274 	PHP_FE(ldap_errno,									arginfo_ldap_resource)
3275 	PHP_FE(ldap_err2str,								arginfo_ldap_err2str)
3276 	PHP_FE(ldap_error,									arginfo_ldap_resource)
3277 	PHP_FE(ldap_compare,								arginfo_ldap_compare)
3278 	PHP_FE(ldap_sort,									arginfo_ldap_sort)
3279 
3280 #if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
3281 	PHP_FE(ldap_rename,									arginfo_ldap_rename)
3282 	PHP_FE(ldap_get_option,								arginfo_ldap_get_option)
3283 	PHP_FE(ldap_set_option,								arginfo_ldap_set_option)
3284 	PHP_FE(ldap_first_reference,						arginfo_ldap_first_reference)
3285 	PHP_FE(ldap_next_reference,							arginfo_ldap_next_reference)
3286 #ifdef HAVE_LDAP_PARSE_REFERENCE
3287 	PHP_FE(ldap_parse_reference,						arginfo_ldap_parse_reference)
3288 #endif
3289 #ifdef HAVE_LDAP_PARSE_RESULT
3290 	PHP_FE(ldap_parse_result,							arginfo_ldap_parse_result)
3291 #endif
3292 #ifdef HAVE_LDAP_START_TLS_S
3293 	PHP_FE(ldap_start_tls,								arginfo_ldap_resource)
3294 #endif
3295 #endif
3296 
3297 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
3298 	PHP_FE(ldap_set_rebind_proc,						arginfo_ldap_set_rebind_proc)
3299 #endif
3300 
3301 	PHP_FE(ldap_escape,									arginfo_ldap_escape)
3302 
3303 #ifdef STR_TRANSLATION
3304 	PHP_FE(ldap_t61_to_8859,							arginfo_ldap_t61_to_8859)
3305 	PHP_FE(ldap_8859_to_t61,							arginfo_ldap_8859_to_t61)
3306 #endif
3307 
3308 #ifdef LDAP_CONTROL_PAGEDRESULTS
3309 	PHP_FE(ldap_control_paged_result,							arginfo_ldap_control_paged_result)
3310 	PHP_FE(ldap_control_paged_result_response,		arginfo_ldap_control_paged_result_response)
3311 #endif
3312 	PHP_FE_END
3313 };
3314 /* }}} */
3315 
3316 zend_module_entry ldap_module_entry = { /* {{{ */
3317 	STANDARD_MODULE_HEADER,
3318 	"ldap",
3319 	ldap_functions,
3320 	PHP_MINIT(ldap),
3321 	PHP_MSHUTDOWN(ldap),
3322 	NULL,
3323 	NULL,
3324 	PHP_MINFO(ldap),
3325 	NO_VERSION_YET,
3326 	PHP_MODULE_GLOBALS(ldap),
3327 	PHP_GINIT(ldap),
3328 	NULL,
3329 	NULL,
3330 	STANDARD_MODULE_PROPERTIES_EX
3331 };
3332 /* }}} */
3333 
3334 /*
3335  * Local variables:
3336  * tab-width: 4
3337  * c-basic-offset: 4
3338  * End:
3339  * vim600: sw=4 ts=4 fdm=marker
3340  * vim<600: sw=4 ts=4
3341  */
3342