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