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