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