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