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