1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
156    /* LDAP options */
157    REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
158    REGISTER_LONG_CONSTANT("LDAP_OPT_SIZELIMIT", LDAP_OPT_SIZELIMIT, CONST_PERSISTENT | CONST_CS);
159    REGISTER_LONG_CONSTANT("LDAP_OPT_TIMELIMIT", LDAP_OPT_TIMELIMIT, CONST_PERSISTENT | CONST_CS);
160#ifdef LDAP_OPT_NETWORK_TIMEOUT
161    REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_OPT_NETWORK_TIMEOUT, CONST_PERSISTENT | CONST_CS);
162#elif defined (LDAP_X_OPT_CONNECT_TIMEOUT)
163    REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_X_OPT_CONNECT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
164#endif
165    REGISTER_LONG_CONSTANT("LDAP_OPT_PROTOCOL_VERSION", LDAP_OPT_PROTOCOL_VERSION, CONST_PERSISTENT | CONST_CS);
166    REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_NUMBER", LDAP_OPT_ERROR_NUMBER, CONST_PERSISTENT | CONST_CS);
167    REGISTER_LONG_CONSTANT("LDAP_OPT_REFERRALS", LDAP_OPT_REFERRALS, CONST_PERSISTENT | CONST_CS);
168#ifdef LDAP_OPT_RESTART
169    REGISTER_LONG_CONSTANT("LDAP_OPT_RESTART", LDAP_OPT_RESTART, CONST_PERSISTENT | CONST_CS);
170#endif
171#ifdef LDAP_OPT_HOST_NAME
172    REGISTER_LONG_CONSTANT("LDAP_OPT_HOST_NAME", LDAP_OPT_HOST_NAME, CONST_PERSISTENT | CONST_CS);
173#endif
174    REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_STRING", LDAP_OPT_ERROR_STRING, CONST_PERSISTENT | CONST_CS);
175#ifdef LDAP_OPT_MATCHED_DN
176    REGISTER_LONG_CONSTANT("LDAP_OPT_MATCHED_DN", LDAP_OPT_MATCHED_DN, CONST_PERSISTENT | CONST_CS);
177#endif
178    REGISTER_LONG_CONSTANT("LDAP_OPT_SERVER_CONTROLS", LDAP_OPT_SERVER_CONTROLS, CONST_PERSISTENT | CONST_CS);
179    REGISTER_LONG_CONSTANT("LDAP_OPT_CLIENT_CONTROLS", LDAP_OPT_CLIENT_CONTROLS, CONST_PERSISTENT | CONST_CS);
180#endif
181#ifdef LDAP_OPT_DEBUG_LEVEL
182    REGISTER_LONG_CONSTANT("LDAP_OPT_DEBUG_LEVEL", LDAP_OPT_DEBUG_LEVEL, CONST_PERSISTENT | CONST_CS);
183#endif
184
185#ifdef HAVE_LDAP_SASL
186    REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_MECH", LDAP_OPT_X_SASL_MECH, CONST_PERSISTENT | CONST_CS);
187    REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_REALM", LDAP_OPT_X_SASL_REALM, CONST_PERSISTENT | CONST_CS);
188    REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT | CONST_CS);
189    REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT | CONST_CS);
190#endif
191
192#ifdef ORALDAP
193    REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT | CONST_CS);
194    REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT | CONST_CS);
195    REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT | CONST_CS);
196#endif
197
198    le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number);
199    le_result = zend_register_list_destructors_ex(_free_ldap_result, NULL, "ldap result", module_number);
200    le_result_entry = zend_register_list_destructors_ex(_free_ldap_result_entry, NULL, "ldap result entry", module_number);
201
202    Z_TYPE(ldap_module_entry) = type;
203
204    return SUCCESS;
205}
206/* }}} */
207
208/* {{{ PHP_MSHUTDOWN_FUNCTION
209 */
210PHP_MSHUTDOWN_FUNCTION(ldap)
211{
212    UNREGISTER_INI_ENTRIES();
213    return SUCCESS;
214}
215/* }}} */
216
217/* {{{ PHP_MINFO_FUNCTION
218 */
219PHP_MINFO_FUNCTION(ldap)
220{
221    char tmp[32];
222#if HAVE_NSLDAP
223    LDAPVersion ver;
224    double SDKVersion;
225#endif
226
227    php_info_print_table_start();
228    php_info_print_table_row(2, "LDAP Support", "enabled");
229    php_info_print_table_row(2, "RCS Version", "$Id$");
230
231    if (LDAPG(max_links) == -1) {
232        snprintf(tmp, 31, "%ld/unlimited", LDAPG(num_links));
233    } else {
234        snprintf(tmp, 31, "%ld/%ld", LDAPG(num_links), LDAPG(max_links));
235    }
236    php_info_print_table_row(2, "Total Links", tmp);
237
238#ifdef LDAP_API_VERSION
239    snprintf(tmp, 31, "%d", LDAP_API_VERSION);
240    php_info_print_table_row(2, "API Version", tmp);
241#endif
242
243#ifdef LDAP_VENDOR_NAME
244    php_info_print_table_row(2, "Vendor Name", LDAP_VENDOR_NAME);
245#endif
246
247#ifdef LDAP_VENDOR_VERSION
248    snprintf(tmp, 31, "%d", LDAP_VENDOR_VERSION);
249    php_info_print_table_row(2, "Vendor Version", tmp);
250#endif
251
252#if HAVE_NSLDAP
253    SDKVersion = ldap_version(&ver);
254    snprintf(tmp, 31, "%F", SDKVersion/100.0);
255    php_info_print_table_row(2, "SDK Version", tmp);
256
257    snprintf(tmp, 31, "%F", ver.protocol_version/100.0);
258    php_info_print_table_row(2, "Highest LDAP Protocol Supported", tmp);
259
260    snprintf(tmp, 31, "%F", ver.SSL_version/100.0);
261    php_info_print_table_row(2, "SSL Level Supported", tmp);
262
263    if (ver.security_level != LDAP_SECURITY_NONE) {
264        snprintf(tmp, 31, "%d", ver.security_level);
265    } else {
266        strcpy(tmp, "SSL not enabled");
267    }
268    php_info_print_table_row(2, "Level of Encryption", tmp);
269#endif
270
271#ifdef HAVE_LDAP_SASL
272    php_info_print_table_row(2, "SASL Support", "Enabled");
273#endif
274
275    php_info_print_table_end();
276    DISPLAY_INI_ENTRIES();
277}
278/* }}} */
279
280/* {{{ proto resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])
281   Connect to an LDAP server */
282PHP_FUNCTION(ldap_connect)
283{
284    char *host = NULL;
285    int hostlen;
286    long port = 389; /* Default port */
287#ifdef HAVE_ORALDAP
288    char *wallet = NULL, *walletpasswd = NULL;
289    int walletlen = 0, walletpasswdlen = 0;
290    long authmode = GSLC_SSL_NO_AUTH;
291    int ssl=0;
292#endif
293    ldap_linkdata *ld;
294    LDAP *ldap;
295
296#ifdef HAVE_ORALDAP
297    if (ZEND_NUM_ARGS() == 3 || ZEND_NUM_ARGS() == 4) {
298        WRONG_PARAM_COUNT;
299    }
300
301    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|slssl", &host, &hostlen, &port, &wallet, &walletlen, &walletpasswd, &walletpasswdlen, &authmode) != SUCCESS) {
302        RETURN_FALSE;
303    }
304
305    if (ZEND_NUM_ARGS() == 5) {
306        ssl = 1;
307    }
308#else
309    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &host, &hostlen, &port) != SUCCESS) {
310        RETURN_FALSE;
311    }
312#endif
313
314    if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
315        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", LDAPG(num_links));
316        RETURN_FALSE;
317    }
318
319    ld = ecalloc(1, sizeof(ldap_linkdata));
320
321#ifdef LDAP_API_FEATURE_X_OPENLDAP
322    if (host != NULL && strchr(host, '/')) {
323        int rc;
324
325        rc = ldap_initialize(&ldap, host);
326        if (rc != LDAP_SUCCESS) {
327            efree(ld);
328            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc));
329            RETURN_FALSE;
330        }
331    } else {
332        ldap = ldap_init(host, port);
333    }
334#else
335    ldap = ldap_open(host, port);
336#endif
337
338    if (ldap == NULL) {
339        efree(ld);
340        RETURN_FALSE;
341    } else {
342#ifdef HAVE_ORALDAP
343        if (ssl) {
344            if (ldap_init_SSL(&ldap->ld_sb, wallet, walletpasswd, authmode)) {
345                efree(ld);
346                php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL init failed");
347                RETURN_FALSE;
348            }
349        }
350#endif
351        LDAPG(num_links)++;
352        ld->link = ldap;
353        ZEND_REGISTER_RESOURCE(return_value, ld, le_link);
354    }
355
356}
357/* }}} */
358
359/* {{{ _get_lderrno
360 */
361static int _get_lderrno(LDAP *ldap)
362{
363#if !HAVE_NSLDAP
364#if LDAP_API_VERSION > 2000 || HAVE_ORALDAP_10
365    int lderr;
366
367    /* New versions of OpenLDAP do it this way */
368    ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
369    return lderr;
370#else
371    return ldap->ld_errno;
372#endif
373#else
374    return ldap_get_lderrno(ldap, NULL, NULL);
375#endif
376}
377/* }}} */
378
379/* {{{ proto bool ldap_bind(resource link [, string dn [, string password]])
380   Bind to LDAP directory */
381PHP_FUNCTION(ldap_bind)
382{
383    zval *link;
384    char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
385    int ldap_bind_dnlen, ldap_bind_pwlen;
386    ldap_linkdata *ld;
387    int rc;
388
389    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) {
390        RETURN_FALSE;
391    }
392
393    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
394
395    if ((rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) {
396        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
397        RETURN_FALSE;
398    } else {
399        RETURN_TRUE;
400    }
401}
402/* }}} */
403
404#ifdef HAVE_LDAP_SASL
405typedef struct {
406    char *mech;
407    char *realm;
408    char *authcid;
409    char *passwd;
410    char *authzid;
411} php_ldap_bictx;
412
413/* {{{ _php_sasl_setdefs
414 */
415static php_ldap_bictx *_php_sasl_setdefs(LDAP *ld, char *sasl_mech, char *sasl_realm, char *sasl_authc_id, char *passwd, char *sasl_authz_id)
416{
417    php_ldap_bictx *ctx;
418
419    ctx = ber_memalloc(sizeof(php_ldap_bictx));
420    ctx->mech    = (sasl_mech) ? ber_strdup(sasl_mech) : NULL;
421    ctx->realm   = (sasl_realm) ? ber_strdup(sasl_realm) : NULL;
422    ctx->authcid = (sasl_authc_id) ? ber_strdup(sasl_authc_id) : NULL;
423    ctx->passwd  = (passwd) ? ber_strdup(passwd) : NULL;
424    ctx->authzid = (sasl_authz_id) ? ber_strdup(sasl_authz_id) : NULL;
425
426    if (ctx->mech == NULL) {
427        ldap_get_option(ld, LDAP_OPT_X_SASL_MECH, &ctx->mech);
428    }
429    if (ctx->realm == NULL) {
430        ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &ctx->realm);
431    }
432    if (ctx->authcid == NULL) {
433        ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHCID, &ctx->authcid);
434    }
435    if (ctx->authzid == NULL) {
436        ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHZID, &ctx->authzid);
437    }
438
439    return ctx;
440}
441/* }}} */
442
443/* {{{ _php_sasl_freedefs
444 */
445static void _php_sasl_freedefs(php_ldap_bictx *ctx)
446{
447    if (ctx->mech) ber_memfree(ctx->mech);
448    if (ctx->realm) ber_memfree(ctx->realm);
449    if (ctx->authcid) ber_memfree(ctx->authcid);
450    if (ctx->passwd) ber_memfree(ctx->passwd);
451    if (ctx->authzid) ber_memfree(ctx->authzid);
452    ber_memfree(ctx);
453}
454/* }}} */
455
456/* {{{ _php_sasl_interact
457   Internal interact function for SASL */
458static int _php_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
459{
460    sasl_interact_t *interact = in;
461    const char *p;
462    php_ldap_bictx *ctx = defaults;
463
464    for (;interact->id != SASL_CB_LIST_END;interact++) {
465        p = NULL;
466        switch(interact->id) {
467            case SASL_CB_GETREALM:
468                p = ctx->realm;
469                break;
470            case SASL_CB_AUTHNAME:
471                p = ctx->authcid;
472                break;
473            case SASL_CB_USER:
474                p = ctx->authzid;
475                break;
476            case SASL_CB_PASS:
477                p = ctx->passwd;
478                break;
479        }
480        if (p) {
481            interact->result = p;
482            interact->len = strlen(interact->result);
483        }
484    }
485    return LDAP_SUCCESS;
486}
487/* }}} */
488
489/* {{{ 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]]]]]]])
490   Bind to LDAP directory using SASL */
491PHP_FUNCTION(ldap_sasl_bind)
492{
493    zval *link;
494    ldap_linkdata *ld;
495    char *binddn = NULL;
496    char *passwd = NULL;
497    char *sasl_mech = NULL;
498    char *sasl_realm = NULL;
499    char *sasl_authz_id = NULL;
500    char *sasl_authc_id = NULL;
501    char *props = NULL;
502    int rc, dn_len, passwd_len, mech_len, realm_len, authc_id_len, authz_id_len, props_len;
503    php_ldap_bictx *ctx;
504
505    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) {
506        RETURN_FALSE;
507    }
508
509    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
510
511    ctx = _php_sasl_setdefs(ld->link, sasl_mech, sasl_realm, sasl_authc_id, passwd, sasl_authz_id);
512
513    if (props) {
514        ldap_set_option(ld->link, LDAP_OPT_X_SASL_SECPROPS, props);
515    }
516
517    rc = ldap_sasl_interactive_bind_s(ld->link, binddn, ctx->mech, NULL, NULL, LDAP_SASL_QUIET, _php_sasl_interact, ctx);
518    if (rc != LDAP_SUCCESS) {
519        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
520        RETVAL_FALSE;
521    } else {
522        RETVAL_TRUE;
523    }
524    _php_sasl_freedefs(ctx);
525}
526/* }}} */
527#endif /* HAVE_LDAP_SASL */
528
529/* {{{ proto bool ldap_unbind(resource link)
530   Unbind from LDAP directory */
531PHP_FUNCTION(ldap_unbind)
532{
533    zval *link;
534    ldap_linkdata *ld;
535
536    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
537        RETURN_FALSE;
538    }
539
540    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
541
542    zend_list_delete(Z_LVAL_P(link));
543    RETURN_TRUE;
544}
545/* }}} */
546
547/* {{{ php_set_opts
548 */
549static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref, int *old_sizelimit, int *old_timelimit, int *old_deref)
550{
551    /* sizelimit */
552    if (sizelimit > -1) {
553#if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP_10
554        ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
555        ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
556#else
557        *old_sizelimit = ldap->ld_sizelimit;
558        ldap->ld_sizelimit = sizelimit;
559#endif
560    }
561
562    /* timelimit */
563    if (timelimit > -1) {
564#if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP_10
565        ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_timelimit);
566        ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
567#else
568        *old_timelimit = ldap->ld_timelimit;
569        ldap->ld_timelimit = timelimit;
570#endif
571    }
572
573    /* deref */
574    if (deref > -1) {
575#if (LDAP_API_VERSION >= 2004) || HAVE_NSLDAP || HAVE_ORALDAP_10
576        ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_deref);
577        ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
578#else
579        *old_deref = ldap->ld_deref;
580        ldap->ld_deref = deref;
581#endif
582    }
583}
584/* }}} */
585
586/* {{{ php_ldap_do_search
587 */
588static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
589{
590    zval *link, *base_dn, **filter, *attrs, **attr;
591    long attrsonly, sizelimit, timelimit, deref;
592    char *ldap_base_dn = NULL, *ldap_filter = NULL, **ldap_attrs = NULL;
593    ldap_linkdata *ld = NULL;
594    LDAPMessage *ldap_res;
595    int ldap_attrsonly = 0, ldap_sizelimit = -1, ldap_timelimit = -1, ldap_deref = -1;
596    int old_ldap_sizelimit = -1, old_ldap_timelimit = -1, old_ldap_deref = -1;
597    int num_attribs = 0, ret = 1, i, errno, argcount = ZEND_NUM_ARGS();
598
599    if (zend_parse_parameters(argcount TSRMLS_CC, "zzZ|allll", &link, &base_dn, &filter, &attrs, &attrsonly,
600        &sizelimit, &timelimit, &deref) == FAILURE) {
601        return;
602    }
603
604    /* Reverse -> fall through */
605    switch (argcount) {
606        case 8:
607            ldap_deref = deref;
608        case 7:
609            ldap_timelimit = timelimit;
610        case 6:
611            ldap_sizelimit = sizelimit;
612        case 5:
613            ldap_attrsonly = attrsonly;
614        case 4:
615            num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs));
616            ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
617
618            for (i = 0; i<num_attribs; i++) {
619                if (zend_hash_index_find(Z_ARRVAL_P(attrs), i, (void **) &attr) != SUCCESS) {
620                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array initialization wrong");
621                    ret = 0;
622                    goto cleanup;
623                }
624
625                SEPARATE_ZVAL(attr);
626                convert_to_string_ex(attr);
627                ldap_attrs[i] = Z_STRVAL_PP(attr);
628            }
629            ldap_attrs[num_attribs] = NULL;
630        default:
631            break;
632    }
633
634    /* parallel search? */
635    if (Z_TYPE_P(link) == IS_ARRAY) {
636        int i, nlinks, nbases, nfilters, *rcs;
637        ldap_linkdata **lds;
638        zval **entry, *resource;
639
640        nlinks = zend_hash_num_elements(Z_ARRVAL_P(link));
641        if (nlinks == 0) {
642            php_error_docref(NULL TSRMLS_CC, E_WARNING, "No links in link array");
643            ret = 0;
644            goto cleanup;
645        }
646
647        if (Z_TYPE_P(base_dn) == IS_ARRAY) {
648            nbases = zend_hash_num_elements(Z_ARRVAL_P(base_dn));
649            if (nbases != nlinks) {
650                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");
651                ret = 0;
652                goto cleanup;
653            }
654            zend_hash_internal_pointer_reset(Z_ARRVAL_P(base_dn));
655        } else {
656            nbases = 0; /* this means string, not array */
657            /* If anything else than string is passed, ldap_base_dn = NULL */
658            if (Z_TYPE_P(base_dn) == IS_STRING) {
659                ldap_base_dn = Z_STRVAL_P(base_dn);
660            } else {
661                ldap_base_dn = NULL;
662            }
663        }
664
665        if (Z_TYPE_PP(filter) == IS_ARRAY) {
666            nfilters = zend_hash_num_elements(Z_ARRVAL_PP(filter));
667            if (nfilters != nlinks) {
668                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");
669                ret = 0;
670                goto cleanup;
671            }
672            zend_hash_internal_pointer_reset(Z_ARRVAL_PP(filter));
673        } else {
674            nfilters = 0; /* this means string, not array */
675            convert_to_string_ex(filter);
676            ldap_filter = Z_STRVAL_PP(filter);
677        }
678
679        lds = safe_emalloc(nlinks, sizeof(ldap_linkdata), 0);
680        rcs = safe_emalloc(nlinks, sizeof(*rcs), 0);
681
682        zend_hash_internal_pointer_reset(Z_ARRVAL_P(link));
683        for (i=0; i<nlinks; i++) {
684            zend_hash_get_current_data(Z_ARRVAL_P(link), (void **)&entry);
685
686            ld = (ldap_linkdata *) zend_fetch_resource(entry TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
687            if (ld == NULL) {
688                ret = 0;
689                goto cleanup_parallel;
690            }
691            if (nbases != 0) { /* base_dn an array? */
692                zend_hash_get_current_data(Z_ARRVAL_P(base_dn), (void **)&entry);
693                zend_hash_move_forward(Z_ARRVAL_P(base_dn));
694
695                /* If anything else than string is passed, ldap_base_dn = NULL */
696                if (Z_TYPE_PP(entry) == IS_STRING) {
697                    ldap_base_dn = Z_STRVAL_PP(entry);
698                } else {
699                    ldap_base_dn = NULL;
700                }
701            }
702            if (nfilters != 0) { /* filter an array? */
703                zend_hash_get_current_data(Z_ARRVAL_PP(filter), (void **)&entry);
704                zend_hash_move_forward(Z_ARRVAL_PP(filter));
705                convert_to_string_ex(entry);
706                ldap_filter = Z_STRVAL_PP(entry);
707            }
708
709            php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
710
711            /* Run the actual search */
712            rcs[i] = ldap_search(ld->link, ldap_base_dn, scope, ldap_filter, ldap_attrs, ldap_attrsonly);
713            lds[i] = ld;
714            zend_hash_move_forward(Z_ARRVAL_P(link));
715        }
716
717        array_init(return_value);
718
719        /* Collect results from the searches */
720        for (i=0; i<nlinks; i++) {
721            MAKE_STD_ZVAL(resource);
722            if (rcs[i] != -1) {
723                rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
724            }
725            if (rcs[i] != -1) {
726                ZEND_REGISTER_RESOURCE(resource, ldap_res, le_result);
727                add_next_index_zval(return_value, resource);
728            } else {
729                add_next_index_bool(return_value, 0);
730            }
731        }
732
733cleanup_parallel:
734        efree(lds);
735        efree(rcs);
736    } else {
737        convert_to_string_ex(filter);
738        ldap_filter = Z_STRVAL_PP(filter);
739
740        /* If anything else than string is passed, ldap_base_dn = NULL */
741        if (Z_TYPE_P(base_dn) == IS_STRING) {
742            ldap_base_dn = Z_STRVAL_P(base_dn);
743        }
744
745        ld = (ldap_linkdata *) zend_fetch_resource(&link TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
746        if (ld == NULL) {
747            ret = 0;
748            goto cleanup;
749        }
750
751        php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
752
753        /* Run the actual search */
754        errno = ldap_search_s(ld->link, ldap_base_dn, scope, ldap_filter, ldap_attrs, ldap_attrsonly, &ldap_res);
755
756        if (errno != LDAP_SUCCESS
757            && errno != LDAP_SIZELIMIT_EXCEEDED
758#ifdef LDAP_ADMINLIMIT_EXCEEDED
759            && errno != LDAP_ADMINLIMIT_EXCEEDED
760#endif
761#ifdef LDAP_REFERRAL
762            && errno != LDAP_REFERRAL
763#endif
764        ) {
765            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Search: %s", ldap_err2string(errno));
766            ret = 0;
767        } else {
768            if (errno == LDAP_SIZELIMIT_EXCEEDED) {
769                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Partial search results returned: Sizelimit exceeded");
770            }
771#ifdef LDAP_ADMINLIMIT_EXCEEDED
772            else if (errno == LDAP_ADMINLIMIT_EXCEEDED) {
773                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Partial search results returned: Adminlimit exceeded");
774            }
775#endif
776
777            ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result);
778        }
779    }
780
781cleanup:
782    if (ld) {
783        /* Restoring previous options */
784        php_set_opts(ld->link, old_ldap_sizelimit, old_ldap_timelimit, old_ldap_deref, &ldap_sizelimit, &ldap_timelimit, &ldap_deref);
785    }
786    if (ldap_attrs != NULL) {
787        efree(ldap_attrs);
788    }
789    if (!ret) {
790        RETVAL_BOOL(ret);
791    }
792}
793/* }}} */
794
795/* {{{ proto resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
796   Read an entry */
797PHP_FUNCTION(ldap_read)
798{
799    php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_BASE);
800}
801/* }}} */
802
803/* {{{ proto resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
804   Single-level search */
805PHP_FUNCTION(ldap_list)
806{
807    php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_ONELEVEL);
808}
809/* }}} */
810
811/* {{{ proto resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]])
812   Search LDAP tree under base_dn */
813PHP_FUNCTION(ldap_search)
814{
815    php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_SUBTREE);
816}
817/* }}} */
818
819/* {{{ proto bool ldap_free_result(resource result)
820   Free result memory */
821PHP_FUNCTION(ldap_free_result)
822{
823    zval *result;
824    LDAPMessage *ldap_result;
825
826    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) != SUCCESS) {
827        return;
828    }
829
830    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
831
832    zend_list_delete(Z_LVAL_P(result));  /* Delete list entry */
833    RETVAL_TRUE;
834}
835/* }}} */
836
837/* {{{ proto int ldap_count_entries(resource link, resource result)
838   Count the number of entries in a search result */
839PHP_FUNCTION(ldap_count_entries)
840{
841    zval *link, *result;
842    ldap_linkdata *ld;
843    LDAPMessage *ldap_result;
844
845    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
846        return;
847    }
848
849    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
850    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
851
852    RETURN_LONG(ldap_count_entries(ld->link, ldap_result));
853}
854/* }}} */
855
856/* {{{ proto resource ldap_first_entry(resource link, resource result)
857   Return first result id */
858PHP_FUNCTION(ldap_first_entry)
859{
860    zval *link, *result;
861    ldap_linkdata *ld;
862    ldap_resultentry *resultentry;
863    LDAPMessage *ldap_result, *entry;
864
865    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
866        return;
867    }
868
869    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
870    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
871
872    if ((entry = ldap_first_entry(ld->link, ldap_result)) == NULL) {
873        RETVAL_FALSE;
874    } else {
875        resultentry = emalloc(sizeof(ldap_resultentry));
876        ZEND_REGISTER_RESOURCE(return_value, resultentry, le_result_entry);
877        resultentry->id = Z_LVAL_P(result);
878        zend_list_addref(resultentry->id);
879        resultentry->data = entry;
880        resultentry->ber = NULL;
881    }
882}
883/* }}} */
884
885/* {{{ proto resource ldap_next_entry(resource link, resource result_entry)
886   Get next result entry */
887PHP_FUNCTION(ldap_next_entry)
888{
889    zval *link, *result_entry;
890    ldap_linkdata *ld;
891    ldap_resultentry *resultentry, *resultentry_next;
892    LDAPMessage *entry_next;
893
894    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
895        return;
896    }
897
898    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
899    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
900
901    if ((entry_next = ldap_next_entry(ld->link, resultentry->data)) == NULL) {
902        RETVAL_FALSE;
903    } else {
904        resultentry_next = emalloc(sizeof(ldap_resultentry));
905        ZEND_REGISTER_RESOURCE(return_value, resultentry_next, le_result_entry);
906        resultentry_next->id = resultentry->id;
907        zend_list_addref(resultentry->id);
908        resultentry_next->data = entry_next;
909        resultentry_next->ber = NULL;
910    }
911}
912/* }}} */
913
914/* {{{ proto array ldap_get_entries(resource link, resource result)
915   Get all result entries */
916PHP_FUNCTION(ldap_get_entries)
917{
918    zval *link, *result;
919    LDAPMessage *ldap_result, *ldap_result_entry;
920    zval *tmp1, *tmp2;
921    ldap_linkdata *ld;
922    LDAP *ldap;
923    int num_entries, num_attrib, num_values, i;
924    BerElement *ber;
925    char *attribute;
926    size_t attr_len;
927    struct berval **ldap_value;
928    char *dn;
929
930    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
931        return;
932    }
933
934    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
935    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
936
937    ldap = ld->link;
938    num_entries = ldap_count_entries(ldap, ldap_result);
939
940    array_init(return_value);
941    add_assoc_long(return_value, "count", num_entries);
942
943    if (num_entries == 0) {
944        return;
945    }
946
947    ldap_result_entry = ldap_first_entry(ldap, ldap_result);
948    if (ldap_result_entry == NULL) {
949        zval_dtor(return_value);
950        RETURN_FALSE;
951    }
952
953    num_entries = 0;
954    while (ldap_result_entry != NULL) {
955        MAKE_STD_ZVAL(tmp1);
956        array_init(tmp1);
957
958        num_attrib = 0;
959        attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
960
961        while (attribute != NULL) {
962            ldap_value = ldap_get_values_len(ldap, ldap_result_entry, attribute);
963            num_values = ldap_count_values_len(ldap_value);
964
965            MAKE_STD_ZVAL(tmp2);
966            array_init(tmp2);
967            add_assoc_long(tmp2, "count", num_values);
968            for (i = 0; i < num_values; i++) {
969                add_index_stringl(tmp2, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1);
970            }
971            ldap_value_free_len(ldap_value);
972
973            attr_len = strlen(attribute);
974            zend_hash_update(Z_ARRVAL_P(tmp1), php_strtolower(attribute, attr_len), attr_len+1, (void *) &tmp2, sizeof(zval *), NULL);
975            add_index_string(tmp1, num_attrib, attribute, 1);
976
977            num_attrib++;
978#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
979            ldap_memfree(attribute);
980#endif
981            attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
982        }
983#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
984        if (ber != NULL) {
985            ber_free(ber, 0);
986        }
987#endif
988
989        add_assoc_long(tmp1, "count", num_attrib);
990        dn = ldap_get_dn(ldap, ldap_result_entry);
991        add_assoc_string(tmp1, "dn", dn, 1);
992#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
993        ldap_memfree(dn);
994#else
995        free(dn);
996#endif
997
998        zend_hash_index_update(Z_ARRVAL_P(return_value), num_entries, (void *) &tmp1, sizeof(zval *), NULL);
999
1000        num_entries++;
1001        ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
1002    }
1003
1004    add_assoc_long(return_value, "count", num_entries);
1005
1006}
1007/* }}} */
1008
1009/* {{{ proto string ldap_first_attribute(resource link, resource result_entry)
1010   Return first attribute */
1011PHP_FUNCTION(ldap_first_attribute)
1012{
1013    zval *link, *result_entry;
1014    ldap_linkdata *ld;
1015    ldap_resultentry *resultentry;
1016    char *attribute;
1017    long dummy_ber;
1018
1019    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1020        return;
1021    }
1022
1023    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1024    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1025
1026    if ((attribute = ldap_first_attribute(ld->link, resultentry->data, &resultentry->ber)) == NULL) {
1027        RETURN_FALSE;
1028    } else {
1029        RETVAL_STRING(attribute, 1);
1030#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1031        ldap_memfree(attribute);
1032#endif
1033    }
1034}
1035/* }}} */
1036
1037/* {{{ proto string ldap_next_attribute(resource link, resource result_entry)
1038   Get the next attribute in result */
1039PHP_FUNCTION(ldap_next_attribute)
1040{
1041    zval *link, *result_entry;
1042    ldap_linkdata *ld;
1043    ldap_resultentry *resultentry;
1044    char *attribute;
1045    long dummy_ber;
1046
1047    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1048        return;
1049    }
1050
1051    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1052    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1053
1054    if (resultentry->ber == NULL) {
1055        php_error_docref(NULL TSRMLS_CC, E_WARNING, "called before calling ldap_first_attribute() or no attributes found in result entry");
1056        RETURN_FALSE;
1057    }
1058
1059    if ((attribute = ldap_next_attribute(ld->link, resultentry->data, resultentry->ber)) == NULL) {
1060#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1061        if (resultentry->ber != NULL) {
1062            ber_free(resultentry->ber, 0);
1063            resultentry->ber = NULL;
1064        }
1065#endif
1066        RETURN_FALSE;
1067    } else {
1068        RETVAL_STRING(attribute, 1);
1069#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1070        ldap_memfree(attribute);
1071#endif
1072    }
1073}
1074/* }}} */
1075
1076/* {{{ proto array ldap_get_attributes(resource link, resource result_entry)
1077   Get attributes from a search result entry */
1078PHP_FUNCTION(ldap_get_attributes)
1079{
1080    zval *link, *result_entry;
1081    zval *tmp;
1082    ldap_linkdata *ld;
1083    ldap_resultentry *resultentry;
1084    char *attribute;
1085    struct berval **ldap_value;
1086    int i, num_values, num_attrib;
1087    BerElement *ber;
1088
1089    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1090        return;
1091    }
1092
1093    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1094    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1095
1096    array_init(return_value);
1097    num_attrib = 0;
1098
1099    attribute = ldap_first_attribute(ld->link, resultentry->data, &ber);
1100    while (attribute != NULL) {
1101        ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute);
1102        num_values = ldap_count_values_len(ldap_value);
1103
1104        MAKE_STD_ZVAL(tmp);
1105        array_init(tmp);
1106        add_assoc_long(tmp, "count", num_values);
1107        for (i = 0; i < num_values; i++) {
1108            add_index_stringl(tmp, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1);
1109        }
1110        ldap_value_free_len(ldap_value);
1111
1112        zend_hash_update(Z_ARRVAL_P(return_value), attribute, strlen(attribute)+1, (void *) &tmp, sizeof(zval *), NULL);
1113        add_index_string(return_value, num_attrib, attribute, 1);
1114
1115        num_attrib++;
1116#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1117        ldap_memfree(attribute);
1118#endif
1119        attribute = ldap_next_attribute(ld->link, resultentry->data, ber);
1120    }
1121#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1122    if (ber != NULL) {
1123        ber_free(ber, 0);
1124    }
1125#endif
1126
1127    add_assoc_long(return_value, "count", num_attrib);
1128}
1129/* }}} */
1130
1131/* {{{ proto array ldap_get_values_len(resource link, resource result_entry, string attribute)
1132   Get all values with lengths from a result entry */
1133PHP_FUNCTION(ldap_get_values_len)
1134{
1135    zval *link, *result_entry;
1136    ldap_linkdata *ld;
1137    ldap_resultentry *resultentry;
1138    char *attr;
1139    struct berval **ldap_value_len;
1140    int i, num_values, attr_len;
1141
1142    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &link, &result_entry, &attr, &attr_len) != SUCCESS) {
1143        return;
1144    }
1145
1146    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1147    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1148
1149    if ((ldap_value_len = ldap_get_values_len(ld->link, resultentry->data, attr)) == NULL) {
1150        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot get the value(s) of attribute %s", ldap_err2string(_get_lderrno(ld->link)));
1151        RETURN_FALSE;
1152    }
1153
1154    num_values = ldap_count_values_len(ldap_value_len);
1155    array_init(return_value);
1156
1157    for (i=0; i<num_values; i++) {
1158        add_next_index_stringl(return_value, ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len, 1);
1159    }
1160
1161    add_assoc_long(return_value, "count", num_values);
1162    ldap_value_free_len(ldap_value_len);
1163
1164}
1165/* }}} */
1166
1167/* {{{ proto string ldap_get_dn(resource link, resource result_entry)
1168   Get the DN of a result entry */
1169PHP_FUNCTION(ldap_get_dn)
1170{
1171    zval *link, *result_entry;
1172    ldap_linkdata *ld;
1173    ldap_resultentry *resultentry;
1174    char *text;
1175
1176    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1177        return;
1178    }
1179
1180    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1181    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1182
1183    text = ldap_get_dn(ld->link, resultentry->data);
1184    if (text != NULL) {
1185        RETVAL_STRING(text, 1);
1186#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1187        ldap_memfree(text);
1188#else
1189        free(text);
1190#endif
1191    } else {
1192        RETURN_FALSE;
1193    }
1194}
1195/* }}} */
1196
1197/* {{{ proto array ldap_explode_dn(string dn, int with_attrib)
1198   Splits DN into its component parts */
1199PHP_FUNCTION(ldap_explode_dn)
1200{
1201    long with_attrib;
1202    char *dn, **ldap_value;
1203    int i, count, dn_len;
1204
1205    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &dn, &dn_len, &with_attrib) != SUCCESS) {
1206        return;
1207    }
1208
1209    if (!(ldap_value = ldap_explode_dn(dn, with_attrib))) {
1210        /* Invalid parameters were passed to ldap_explode_dn */
1211        RETURN_FALSE;
1212    }
1213
1214    i=0;
1215    while (ldap_value[i] != NULL) i++;
1216    count = i;
1217
1218    array_init(return_value);
1219
1220    add_assoc_long(return_value, "count", count);
1221    for (i = 0; i<count; i++) {
1222        add_index_string(return_value, i, ldap_value[i], 1);
1223    }
1224
1225    ldap_value_free(ldap_value);
1226}
1227/* }}} */
1228
1229/* {{{ proto string ldap_dn2ufn(string dn)
1230   Convert DN to User Friendly Naming format */
1231PHP_FUNCTION(ldap_dn2ufn)
1232{
1233    char *dn, *ufn;
1234    int dn_len;
1235
1236    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dn, &dn_len) != SUCCESS) {
1237        return;
1238    }
1239
1240    ufn = ldap_dn2ufn(dn);
1241
1242    if (ufn != NULL) {
1243        RETVAL_STRING(ufn, 1);
1244#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 || WINDOWS
1245        ldap_memfree(ufn);
1246#endif
1247    } else {
1248        RETURN_FALSE;
1249    }
1250}
1251/* }}} */
1252
1253
1254/* added to fix use of ldap_modify_add for doing an ldap_add, gerrit thomson. */
1255#define PHP_LD_FULL_ADD 0xff
1256/* {{{ php_ldap_do_modify
1257 */
1258static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper)
1259{
1260    zval *link, *entry, **value, **ivalue;
1261    ldap_linkdata *ld;
1262    char *dn;
1263    LDAPMod **ldap_mods;
1264    int i, j, num_attribs, num_values, dn_len;
1265    int *num_berval;
1266    char *attribute;
1267    ulong index;
1268    int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */
1269
1270    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &entry) != SUCCESS) {
1271        return;
1272    }
1273
1274    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1275
1276    num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry));
1277    ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0);
1278    num_berval = safe_emalloc(num_attribs, sizeof(int), 0);
1279    zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry));
1280
1281    /* added by gerrit thomson to fix ldap_add using ldap_mod_add */
1282    if (oper == PHP_LD_FULL_ADD) {
1283        oper = LDAP_MOD_ADD;
1284        is_full_add = 1;
1285    }
1286    /* end additional , gerrit thomson */
1287
1288    for (i = 0; i < num_attribs; i++) {
1289        ldap_mods[i] = emalloc(sizeof(LDAPMod));
1290        ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
1291        ldap_mods[i]->mod_type = NULL;
1292
1293        if (zend_hash_get_current_key(Z_ARRVAL_P(entry), &attribute, &index, 0) == HASH_KEY_IS_STRING) {
1294            ldap_mods[i]->mod_type = estrdup(attribute);
1295        } else {
1296            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown attribute in the data");
1297            /* Free allocated memory */
1298            while (i >= 0) {
1299                if (ldap_mods[i]->mod_type) {
1300                    efree(ldap_mods[i]->mod_type);
1301                }
1302                efree(ldap_mods[i]);
1303                i--;
1304            }
1305            efree(num_berval);
1306            efree(ldap_mods);
1307            RETURN_FALSE;
1308        }
1309
1310        zend_hash_get_current_data(Z_ARRVAL_P(entry), (void **)&value);
1311
1312        if (Z_TYPE_PP(value) != IS_ARRAY) {
1313            num_values = 1;
1314        } else {
1315            num_values = zend_hash_num_elements(Z_ARRVAL_PP(value));
1316        }
1317
1318        num_berval[i] = num_values;
1319        ldap_mods[i]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
1320
1321/* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
1322        if ((num_values == 1) && (Z_TYPE_PP(value) != IS_ARRAY)) {
1323            convert_to_string_ex(value);
1324            ldap_mods[i]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
1325            ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_PP(value);
1326            ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_PP(value);
1327        } else {
1328            for (j = 0; j < num_values; j++) {
1329                if (zend_hash_index_find(Z_ARRVAL_PP(value), j, (void **) &ivalue) != SUCCESS) {
1330                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value array must have consecutive indices 0, 1, ...");
1331                    num_berval[i] = j;
1332                    num_attribs = i + 1;
1333                    RETVAL_FALSE;
1334                    goto errexit;
1335                }
1336                convert_to_string_ex(ivalue);
1337                ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
1338                ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_PP(ivalue);
1339                ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_PP(ivalue);
1340            }
1341        }
1342        ldap_mods[i]->mod_bvalues[num_values] = NULL;
1343        zend_hash_move_forward(Z_ARRVAL_P(entry));
1344    }
1345    ldap_mods[num_attribs] = NULL;
1346
1347/* check flag to see if do_mod was called to perform full add , gerrit thomson */
1348    if (is_full_add == 1) {
1349        if ((i = ldap_add_s(ld->link, dn, ldap_mods)) != LDAP_SUCCESS) {
1350            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Add: %s", ldap_err2string(i));
1351            RETVAL_FALSE;
1352        } else RETVAL_TRUE;
1353    } else {
1354        if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
1355            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modify: %s", ldap_err2string(i));
1356            RETVAL_FALSE;
1357        } else RETVAL_TRUE;
1358    }
1359
1360errexit:
1361    for (i = 0; i < num_attribs; i++) {
1362        efree(ldap_mods[i]->mod_type);
1363        for (j = 0; j < num_berval[i]; j++) {
1364            efree(ldap_mods[i]->mod_bvalues[j]);
1365        }
1366        efree(ldap_mods[i]->mod_bvalues);
1367        efree(ldap_mods[i]);
1368    }
1369    efree(num_berval);
1370    efree(ldap_mods);
1371
1372    return;
1373}
1374/* }}} */
1375
1376/* {{{ proto bool ldap_add(resource link, string dn, array entry)
1377   Add entries to LDAP directory */
1378PHP_FUNCTION(ldap_add)
1379{
1380    /* 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 */
1381    php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD);
1382}
1383/* }}} */
1384
1385/* three functions for attribute base modifications, gerrit Thomson */
1386
1387/* {{{ proto bool ldap_mod_replace(resource link, string dn, array entry)
1388   Replace attribute values with new ones */
1389PHP_FUNCTION(ldap_mod_replace)
1390{
1391    php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE);
1392}
1393/* }}} */
1394
1395/* {{{ proto bool ldap_mod_add(resource link, string dn, array entry)
1396   Add attribute values to current */
1397PHP_FUNCTION(ldap_mod_add)
1398{
1399    php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD);
1400}
1401/* }}} */
1402
1403/* {{{ proto bool ldap_mod_del(resource link, string dn, array entry)
1404   Delete attribute values */
1405PHP_FUNCTION(ldap_mod_del)
1406{
1407    php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE);
1408}
1409/* }}} */
1410
1411/* {{{ proto bool ldap_delete(resource link, string dn)
1412   Delete an entry from a directory */
1413PHP_FUNCTION(ldap_delete)
1414{
1415    zval *link;
1416    ldap_linkdata *ld;
1417    char *dn;
1418    int rc, dn_len;
1419
1420    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &link, &dn, &dn_len) != SUCCESS) {
1421        return;
1422    }
1423
1424    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1425
1426    if ((rc = ldap_delete_s(ld->link, dn)) != LDAP_SUCCESS) {
1427        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delete: %s", ldap_err2string(rc));
1428        RETURN_FALSE;
1429    }
1430
1431    RETURN_TRUE;
1432}
1433/* }}} */
1434
1435/* {{{ proto int ldap_errno(resource link)
1436   Get the current ldap error number */
1437PHP_FUNCTION(ldap_errno)
1438{
1439    zval *link;
1440    ldap_linkdata *ld;
1441
1442    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
1443        return;
1444    }
1445
1446    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1447
1448    RETURN_LONG(_get_lderrno(ld->link));
1449}
1450/* }}} */
1451
1452/* {{{ proto string ldap_err2str(int errno)
1453   Convert error number to error string */
1454PHP_FUNCTION(ldap_err2str)
1455{
1456    long perrno;
1457
1458    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &perrno) != SUCCESS) {
1459        return;
1460    }
1461
1462    RETURN_STRING(ldap_err2string(perrno), 1);
1463}
1464/* }}} */
1465
1466/* {{{ proto string ldap_error(resource link)
1467   Get the current ldap error string */
1468PHP_FUNCTION(ldap_error)
1469{
1470    zval *link;
1471    ldap_linkdata *ld;
1472    int ld_errno;
1473
1474    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
1475        return;
1476    }
1477
1478    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1479
1480    ld_errno = _get_lderrno(ld->link);
1481
1482    RETURN_STRING(ldap_err2string(ld_errno), 1);
1483}
1484/* }}} */
1485
1486/* {{{ proto bool ldap_compare(resource link, string dn, string attr, string value)
1487   Determine if an entry has a specific value for one of its attributes */
1488PHP_FUNCTION(ldap_compare)
1489{
1490    zval *link;
1491    char *dn, *attr, *value;
1492    int dn_len, attr_len, value_len;
1493    ldap_linkdata *ld;
1494    int errno;
1495
1496    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len) != SUCCESS) {
1497        return;
1498    }
1499
1500    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1501
1502    errno = ldap_compare_s(ld->link, dn, attr, value);
1503
1504    switch (errno) {
1505        case LDAP_COMPARE_TRUE:
1506            RETURN_TRUE;
1507            break;
1508
1509        case LDAP_COMPARE_FALSE:
1510            RETURN_FALSE;
1511            break;
1512    }
1513
1514    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: %s", ldap_err2string(errno));
1515    RETURN_LONG(-1);
1516}
1517/* }}} */
1518
1519/* {{{ proto bool ldap_sort(resource link, resource result, string sortfilter)
1520   Sort LDAP result entries */
1521PHP_FUNCTION(ldap_sort)
1522{
1523    zval *link, *result;
1524    ldap_linkdata *ld;
1525    char *sortfilter;
1526    int sflen;
1527    zend_rsrc_list_entry *le;
1528
1529    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &link, &result, &sortfilter, &sflen) != SUCCESS) {
1530        RETURN_FALSE;
1531    }
1532
1533    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1534
1535    if (zend_hash_index_find(&EG(regular_list), Z_LVAL_P(result), (void **) &le) != SUCCESS || le->type != le_result) {
1536        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied resource is not a valid ldap result resource");
1537        RETURN_FALSE;
1538    }
1539
1540    if (ldap_sort_entries(ld->link, (LDAPMessage **) &le->ptr, sflen ? sortfilter : NULL, strcmp) != LDAP_SUCCESS) {
1541        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ldap_err2string(errno));
1542        RETURN_FALSE;
1543    }
1544
1545    RETURN_TRUE;
1546}
1547/* }}} */
1548
1549#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
1550/* {{{ proto bool ldap_get_option(resource link, int option, mixed retval)
1551   Get the current value of various session-wide parameters */
1552PHP_FUNCTION(ldap_get_option)
1553{
1554    zval *link, *retval;
1555    ldap_linkdata *ld;
1556    long option;
1557
1558    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &link, &option, &retval) != SUCCESS) {
1559        return;
1560    }
1561
1562    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1563
1564    switch (option) {
1565    /* options with int value */
1566    case LDAP_OPT_DEREF:
1567    case LDAP_OPT_SIZELIMIT:
1568    case LDAP_OPT_TIMELIMIT:
1569    case LDAP_OPT_PROTOCOL_VERSION:
1570    case LDAP_OPT_ERROR_NUMBER:
1571    case LDAP_OPT_REFERRALS:
1572#ifdef LDAP_OPT_RESTART
1573    case LDAP_OPT_RESTART:
1574#endif
1575        {
1576            int val;
1577
1578            if (ldap_get_option(ld->link, option, &val)) {
1579                RETURN_FALSE;
1580            }
1581            zval_dtor(retval);
1582            ZVAL_LONG(retval, val);
1583        } break;
1584#ifdef LDAP_OPT_NETWORK_TIMEOUT
1585    case LDAP_OPT_NETWORK_TIMEOUT:
1586        {
1587            struct timeval *timeout = NULL;
1588
1589            if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
1590                if (timeout) {
1591                    ldap_memfree(timeout);
1592                }
1593                RETURN_FALSE;
1594            }
1595            if (!timeout) {
1596                RETURN_FALSE;
1597            }
1598            zval_dtor(retval);
1599            ZVAL_LONG(retval, timeout->tv_sec);
1600            ldap_memfree(timeout);
1601        } break;
1602#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1603    case LDAP_X_OPT_CONNECT_TIMEOUT:
1604        {
1605            int timeout;
1606
1607            if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
1608                RETURN_FALSE;
1609            }
1610            zval_dtor(retval);
1611            ZVAL_LONG(retval, (timeout / 1000));
1612        } break;
1613#endif
1614    /* options with string value */
1615    case LDAP_OPT_ERROR_STRING:
1616#ifdef LDAP_OPT_HOST_NAME
1617    case LDAP_OPT_HOST_NAME:
1618#endif
1619#ifdef HAVE_LDAP_SASL
1620    case LDAP_OPT_X_SASL_MECH:
1621    case LDAP_OPT_X_SASL_REALM:
1622    case LDAP_OPT_X_SASL_AUTHCID:
1623    case LDAP_OPT_X_SASL_AUTHZID:
1624#endif
1625#ifdef LDAP_OPT_MATCHED_DN
1626    case LDAP_OPT_MATCHED_DN:
1627#endif
1628        {
1629            char *val = NULL;
1630
1631            if (ldap_get_option(ld->link, option, &val) || val == NULL || *val == '\0') {
1632                if (val) {
1633                    ldap_memfree(val);
1634                }
1635                RETURN_FALSE;
1636            }
1637            zval_dtor(retval);
1638            ZVAL_STRING(retval, val, 1);
1639            ldap_memfree(val);
1640        } break;
1641/* options not implemented
1642    case LDAP_OPT_SERVER_CONTROLS:
1643    case LDAP_OPT_CLIENT_CONTROLS:
1644    case LDAP_OPT_API_INFO:
1645    case LDAP_OPT_API_FEATURE_INFO:
1646*/
1647    default:
1648        RETURN_FALSE;
1649    }
1650    RETURN_TRUE;
1651}
1652/* }}} */
1653
1654/* {{{ proto bool ldap_set_option(resource link, int option, mixed newval)
1655   Set the value of various session-wide parameters */
1656PHP_FUNCTION(ldap_set_option)
1657{
1658    zval *link, **newval;
1659    ldap_linkdata *ld;
1660    LDAP *ldap;
1661    long option;
1662
1663    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zlZ", &link, &option, &newval) != SUCCESS) {
1664        return;
1665    }
1666
1667    if (Z_TYPE_P(link) == IS_NULL) {
1668        ldap = NULL;
1669    } else {
1670        ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1671        ldap = ld->link;
1672    }
1673
1674    switch (option) {
1675    /* options with int value */
1676    case LDAP_OPT_DEREF:
1677    case LDAP_OPT_SIZELIMIT:
1678    case LDAP_OPT_TIMELIMIT:
1679    case LDAP_OPT_PROTOCOL_VERSION:
1680    case LDAP_OPT_ERROR_NUMBER:
1681#ifdef LDAP_OPT_DEBUG_LEVEL
1682    case LDAP_OPT_DEBUG_LEVEL:
1683#endif
1684        {
1685            int val;
1686
1687            convert_to_long_ex(newval);
1688            val = Z_LVAL_PP(newval);
1689            if (ldap_set_option(ldap, option, &val)) {
1690                RETURN_FALSE;
1691            }
1692        } break;
1693#ifdef LDAP_OPT_NETWORK_TIMEOUT
1694    case LDAP_OPT_NETWORK_TIMEOUT:
1695        {
1696            struct timeval timeout;
1697
1698            convert_to_long_ex(newval);
1699            timeout.tv_sec = Z_LVAL_PP(newval);
1700            timeout.tv_usec = 0;
1701            if (ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
1702                RETURN_FALSE;
1703            }
1704        } break;
1705#elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1706    case LDAP_X_OPT_CONNECT_TIMEOUT:
1707        {
1708            int timeout;
1709
1710            convert_to_long_ex(newval);
1711            timeout = 1000 * Z_LVAL_PP(newval); /* Convert to milliseconds */
1712            if (ldap_set_option(ldap, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
1713                RETURN_FALSE;
1714            }
1715        } break;
1716#endif
1717        /* options with string value */
1718    case LDAP_OPT_ERROR_STRING:
1719#ifdef LDAP_OPT_HOST_NAME
1720    case LDAP_OPT_HOST_NAME:
1721#endif
1722#ifdef HAVE_LDAP_SASL
1723    case LDAP_OPT_X_SASL_MECH:
1724    case LDAP_OPT_X_SASL_REALM:
1725    case LDAP_OPT_X_SASL_AUTHCID:
1726    case LDAP_OPT_X_SASL_AUTHZID:
1727#endif
1728#ifdef LDAP_OPT_MATCHED_DN
1729    case LDAP_OPT_MATCHED_DN:
1730#endif
1731        {
1732            char *val;
1733            convert_to_string_ex(newval);
1734            val = Z_STRVAL_PP(newval);
1735            if (ldap_set_option(ldap, option, val)) {
1736                RETURN_FALSE;
1737            }
1738        } break;
1739        /* options with boolean value */
1740    case LDAP_OPT_REFERRALS:
1741#ifdef LDAP_OPT_RESTART
1742    case LDAP_OPT_RESTART:
1743#endif
1744        {
1745            void *val;
1746            convert_to_boolean_ex(newval);
1747            val = Z_LVAL_PP(newval)
1748                ? LDAP_OPT_ON : LDAP_OPT_OFF;
1749            if (ldap_set_option(ldap, option, val)) {
1750                RETURN_FALSE;
1751            }
1752        } break;
1753        /* options with control list value */
1754    case LDAP_OPT_SERVER_CONTROLS:
1755    case LDAP_OPT_CLIENT_CONTROLS:
1756        {
1757            LDAPControl *ctrl, **ctrls, **ctrlp;
1758            zval **ctrlval, **val;
1759            int ncontrols;
1760            char error=0;
1761
1762            if ((Z_TYPE_PP(newval) != IS_ARRAY) || !(ncontrols = zend_hash_num_elements(Z_ARRVAL_PP(newval)))) {
1763                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected non-empty array value for this option");
1764                RETURN_FALSE;
1765            }
1766            ctrls = safe_emalloc((1 + ncontrols), sizeof(*ctrls), 0);
1767            *ctrls = NULL;
1768            ctrlp = ctrls;
1769            zend_hash_internal_pointer_reset(Z_ARRVAL_PP(newval));
1770            while (zend_hash_get_current_data(Z_ARRVAL_PP(newval), (void**)&ctrlval) == SUCCESS) {
1771                if (Z_TYPE_PP(ctrlval) != IS_ARRAY) {
1772                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "The array value must contain only arrays, where each array is a control");
1773                    error = 1;
1774                    break;
1775                }
1776                if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "oid", sizeof("oid"), (void **) &val) != SUCCESS) {
1777                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Control must have an oid key");
1778                    error = 1;
1779                    break;
1780                }
1781                ctrl = *ctrlp = emalloc(sizeof(**ctrlp));
1782                convert_to_string_ex(val);
1783                ctrl->ldctl_oid = Z_STRVAL_PP(val);
1784                if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "value", sizeof("value"), (void **) &val) == SUCCESS) {
1785                    convert_to_string_ex(val);
1786                    ctrl->ldctl_value.bv_val = Z_STRVAL_PP(val);
1787                    ctrl->ldctl_value.bv_len = Z_STRLEN_PP(val);
1788                } else {
1789                    ctrl->ldctl_value.bv_val = NULL;
1790                    ctrl->ldctl_value.bv_len = 0;
1791                }
1792                if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "iscritical", sizeof("iscritical"), (void **) &val) == SUCCESS) {
1793                    convert_to_boolean_ex(val);
1794                    ctrl->ldctl_iscritical = Z_BVAL_PP(val);
1795                } else {
1796                    ctrl->ldctl_iscritical = 0;
1797                }
1798
1799                ++ctrlp;
1800                *ctrlp = NULL;
1801                zend_hash_move_forward(Z_ARRVAL_PP(newval));
1802            }
1803            if (!error) {
1804                error = ldap_set_option(ldap, option, ctrls);
1805            }
1806            ctrlp = ctrls;
1807            while (*ctrlp) {
1808                efree(*ctrlp);
1809                ctrlp++;
1810            }
1811            efree(ctrls);
1812            if (error) {
1813                RETURN_FALSE;
1814            }
1815        } break;
1816    default:
1817        RETURN_FALSE;
1818    }
1819    RETURN_TRUE;
1820}
1821/* }}} */
1822
1823#ifdef HAVE_LDAP_PARSE_RESULT
1824/* {{{ proto bool ldap_parse_result(resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals)
1825   Extract information from result */
1826PHP_FUNCTION(ldap_parse_result)
1827{
1828    zval *link, *result, *errcode, *matcheddn, *errmsg, *referrals;
1829    ldap_linkdata *ld;
1830    LDAPMessage *ldap_result;
1831    char **lreferrals, **refp;
1832    char *lmatcheddn, *lerrmsg;
1833    int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
1834
1835    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz|zzz", &link, &result, &errcode, &matcheddn, &errmsg, &referrals) != SUCCESS) {
1836        return;
1837    }
1838
1839    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1840    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
1841
1842    rc = ldap_parse_result(ld->link, ldap_result, &lerrcode,
1843                myargcount > 3 ? &lmatcheddn : NULL,
1844                myargcount > 4 ? &lerrmsg : NULL,
1845                myargcount > 5 ? &lreferrals : NULL,
1846                NULL /* &serverctrls */,
1847                0);
1848    if (rc != LDAP_SUCCESS) {
1849        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s", ldap_err2string(rc));
1850        RETURN_FALSE;
1851    }
1852
1853    zval_dtor(errcode);
1854    ZVAL_LONG(errcode, lerrcode);
1855
1856    /* Reverse -> fall through */
1857    switch (myargcount) {
1858        case 6:
1859            zval_dtor(referrals);
1860            array_init(referrals);
1861            if (lreferrals != NULL) {
1862                refp = lreferrals;
1863                while (*refp) {
1864                    add_next_index_string(referrals, *refp, 1);
1865                    refp++;
1866                }
1867                ldap_value_free(lreferrals);
1868            }
1869        case 5:
1870            zval_dtor(errmsg);
1871            if (lerrmsg == NULL) {
1872                ZVAL_EMPTY_STRING(errmsg);
1873            } else {
1874                ZVAL_STRING(errmsg, lerrmsg, 1);
1875                ldap_memfree(lerrmsg);
1876            }
1877        case 4:
1878            zval_dtor(matcheddn);
1879            if (lmatcheddn == NULL) {
1880                ZVAL_EMPTY_STRING(matcheddn);
1881            } else {
1882                ZVAL_STRING(matcheddn, lmatcheddn, 1);
1883                ldap_memfree(lmatcheddn);
1884            }
1885    }
1886    RETURN_TRUE;
1887}
1888/* }}} */
1889#endif
1890
1891/* {{{ proto resource ldap_first_reference(resource link, resource result)
1892   Return first reference */
1893PHP_FUNCTION(ldap_first_reference)
1894{
1895    zval *link, *result;
1896    ldap_linkdata *ld;
1897    ldap_resultentry *resultentry;
1898    LDAPMessage *ldap_result, *entry;
1899
1900    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result) != SUCCESS) {
1901        return;
1902    }
1903
1904    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1905    ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result);
1906
1907    if ((entry = ldap_first_reference(ld->link, ldap_result)) == NULL) {
1908        RETVAL_FALSE;
1909    } else {
1910        resultentry = emalloc(sizeof(ldap_resultentry));
1911        ZEND_REGISTER_RESOURCE(return_value, resultentry, le_result_entry);
1912        resultentry->id = Z_LVAL_P(result);
1913        zend_list_addref(resultentry->id);
1914        resultentry->data = entry;
1915        resultentry->ber = NULL;
1916    }
1917}
1918/* }}} */
1919
1920/* {{{ proto resource ldap_next_reference(resource link, resource reference_entry)
1921   Get next reference */
1922PHP_FUNCTION(ldap_next_reference)
1923{
1924    zval *link, *result_entry;
1925    ldap_linkdata *ld;
1926    ldap_resultentry *resultentry, *resultentry_next;
1927    LDAPMessage *entry_next;
1928
1929    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &link, &result_entry) != SUCCESS) {
1930        return;
1931    }
1932
1933    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1934    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1935
1936    if ((entry_next = ldap_next_reference(ld->link, resultentry->data)) == NULL) {
1937        RETVAL_FALSE;
1938    } else {
1939        resultentry_next = emalloc(sizeof(ldap_resultentry));
1940        ZEND_REGISTER_RESOURCE(return_value, resultentry_next, le_result_entry);
1941        resultentry_next->id = resultentry->id;
1942        zend_list_addref(resultentry->id);
1943        resultentry_next->data = entry_next;
1944        resultentry_next->ber = NULL;
1945    }
1946}
1947/* }}} */
1948
1949#ifdef HAVE_LDAP_PARSE_REFERENCE
1950/* {{{ proto bool ldap_parse_reference(resource link, resource reference_entry, array referrals)
1951   Extract information from reference entry */
1952PHP_FUNCTION(ldap_parse_reference)
1953{
1954    zval *link, *result_entry, *referrals;
1955    ldap_linkdata *ld;
1956    ldap_resultentry *resultentry;
1957    char **lreferrals, **refp;
1958
1959    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz", &link, &result_entry, &referrals) != SUCCESS) {
1960        return;
1961    }
1962
1963    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1964    ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, &result_entry, -1, "ldap result entry", le_result_entry);
1965
1966    if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, NULL /* &serverctrls */, 0) != LDAP_SUCCESS) {
1967        RETURN_FALSE;
1968    }
1969
1970    zval_dtor(referrals);
1971    array_init(referrals);
1972    if (lreferrals != NULL) {
1973        refp = lreferrals;
1974        while (*refp) {
1975            add_next_index_string(referrals, *refp, 1);
1976            refp++;
1977        }
1978        ldap_value_free(lreferrals);
1979    }
1980    RETURN_TRUE;
1981}
1982/* }}} */
1983#endif
1984
1985/* {{{ proto bool ldap_rename(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn);
1986   Modify the name of an entry */
1987PHP_FUNCTION(ldap_rename)
1988{
1989    zval *link;
1990    ldap_linkdata *ld;
1991    int rc;
1992    char *dn, *newrdn, *newparent;
1993    int dn_len, newrdn_len, newparent_len;
1994    zend_bool deleteoldrdn;
1995
1996    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsssb", &link, &dn, &dn_len, &newrdn, &newrdn_len, &newparent, &newparent_len, &deleteoldrdn) != SUCCESS) {
1997        return;
1998    }
1999
2000    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2001
2002    if (newparent_len == 0) {
2003        newparent = NULL;
2004    }
2005
2006#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
2007    rc = ldap_rename_s(ld->link, dn, newrdn, newparent, deleteoldrdn, NULL, NULL);
2008#else
2009    if (newparent_len != 0) {
2010        php_error_docref(NULL TSRMLS_CC, E_WARNING, "You are using old LDAP API, newparent must be the empty string, can only modify RDN");
2011        RETURN_FALSE;
2012    }
2013/* could support old APIs but need check for ldap_modrdn2()/ldap_modrdn() */
2014    rc = ldap_modrdn2_s(ld->link, dn, newrdn, deleteoldrdn);
2015#endif
2016
2017    if (rc == LDAP_SUCCESS) {
2018        RETURN_TRUE;
2019    }
2020    RETURN_FALSE;
2021}
2022/* }}} */
2023
2024#ifdef HAVE_LDAP_START_TLS_S
2025/* {{{ proto bool ldap_start_tls(resource link)
2026   Start TLS */
2027PHP_FUNCTION(ldap_start_tls)
2028{
2029    zval *link;
2030    ldap_linkdata *ld;
2031    int rc, protocol = LDAP_VERSION3;
2032
2033    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &link) != SUCCESS) {
2034        return;
2035    }
2036
2037    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2038
2039    if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) ||
2040        ((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS)
2041    ) {
2042        php_error_docref(NULL TSRMLS_CC, E_WARNING,"Unable to start TLS: %s", ldap_err2string(rc));
2043        RETURN_FALSE;
2044    } else {
2045        RETURN_TRUE;
2046    }
2047}
2048/* }}} */
2049#endif
2050#endif /* (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 */
2051
2052#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
2053/* {{{ _ldap_rebind_proc()
2054*/
2055int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req, ber_int_t msgid, void *params)
2056{
2057    ldap_linkdata *ld;
2058    int retval;
2059    zval *cb_url;
2060    zval **cb_args[2];
2061    zval *cb_retval;
2062    zval *cb_link = (zval *) params;
2063    TSRMLS_FETCH();
2064
2065    ld = (ldap_linkdata *) zend_fetch_resource(&cb_link TSRMLS_CC, -1, "ldap link", NULL, 1, le_link);
2066
2067    /* link exists and callback set? */
2068    if (ld == NULL || ld->rebindproc == NULL) {
2069        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link not found or no callback set");
2070        return LDAP_OTHER;
2071    }
2072
2073    /* callback */
2074    MAKE_STD_ZVAL(cb_url);
2075    ZVAL_STRING(cb_url, estrdup(url), 0);
2076    cb_args[0] = &cb_link;
2077    cb_args[1] = &cb_url;
2078    if (call_user_function_ex(EG(function_table), NULL, ld->rebindproc, &cb_retval, 2, cb_args, 0, NULL TSRMLS_CC) == SUCCESS && cb_retval) {
2079        convert_to_long_ex(&cb_retval);
2080        retval = Z_LVAL_P(cb_retval);
2081        zval_ptr_dtor(&cb_retval);
2082    } else {
2083        php_error_docref(NULL TSRMLS_CC, E_WARNING, "rebind_proc PHP callback failed");
2084        retval = LDAP_OTHER;
2085    }
2086    zval_dtor(cb_url);
2087    FREE_ZVAL(cb_url);
2088    return retval;
2089}
2090/* }}} */
2091
2092/* {{{ proto bool ldap_set_rebind_proc(resource link, string callback)
2093   Set a callback function to do re-binds on referral chasing. */
2094PHP_FUNCTION(ldap_set_rebind_proc)
2095{
2096    zval *link, *callback;
2097    ldap_linkdata *ld;
2098    char *callback_name;
2099
2100    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &link, &callback) != SUCCESS) {
2101        RETURN_FALSE;
2102    }
2103
2104    ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
2105
2106    if (Z_TYPE_P(callback) == IS_STRING && Z_STRLEN_P(callback) == 0) {
2107        /* unregister rebind procedure */
2108        if (ld->rebindproc != NULL) {
2109            zval_dtor(ld->rebindproc);
2110            ld->rebindproc = NULL;
2111            ldap_set_rebind_proc(ld->link, NULL, NULL);
2112        }
2113        RETURN_TRUE;
2114    }
2115
2116    /* callable? */
2117    if (!zend_is_callable(callback, 0, &callback_name TSRMLS_CC)) {
2118        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Two arguments expected for '%s' to be a valid callback", callback_name);
2119        efree(callback_name);
2120        RETURN_FALSE;
2121    }
2122    efree(callback_name);
2123
2124    /* register rebind procedure */
2125    if (ld->rebindproc == NULL) {
2126        ldap_set_rebind_proc(ld->link, _ldap_rebind_proc, (void *) link);
2127    } else {
2128        zval_dtor(ld->rebindproc);
2129    }
2130
2131    ALLOC_ZVAL(ld->rebindproc);
2132    *ld->rebindproc = *callback;
2133    zval_copy_ctor(ld->rebindproc);
2134    RETURN_TRUE;
2135}
2136/* }}} */
2137#endif
2138
2139#ifdef STR_TRANSLATION
2140/* {{{ php_ldap_do_translate
2141 */
2142static void php_ldap_do_translate(INTERNAL_FUNCTION_PARAMETERS, int way)
2143{
2144    char *value;
2145    int result, ldap_len;
2146
2147    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) != SUCCESS) {
2148        return;
2149    }
2150
2151    if (value_len == 0) {
2152        RETURN_FALSE;
2153    }
2154
2155    if (way == 1) {
2156        result = ldap_8859_to_t61(&value, &value_len, 0);
2157    } else {
2158        result = ldap_t61_to_8859(&value, &value_len, 0);
2159    }
2160
2161    if (result == LDAP_SUCCESS) {
2162        RETVAL_STRINGL(value, value_len, 1);
2163        free(value);
2164    } else {
2165        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Conversion from iso-8859-1 to t61 failed: %s", ldap_err2string(result));
2166        RETVAL_FALSE;
2167    }
2168}
2169/* }}} */
2170
2171/* {{{ proto string ldap_t61_to_8859(string value)
2172   Translate t61 characters to 8859 characters */
2173PHP_FUNCTION(ldap_t61_to_8859)
2174{
2175    php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2176}
2177/* }}} */
2178
2179/* {{{ proto string ldap_8859_to_t61(string value)
2180   Translate 8859 characters to t61 characters */
2181PHP_FUNCTION(ldap_8859_to_t61)
2182{
2183    php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2184}
2185/* }}} */
2186#endif
2187
2188/* {{{ arginfo */
2189ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0)
2190    ZEND_ARG_INFO(0, hostname)
2191    ZEND_ARG_INFO(0, port)
2192#ifdef HAVE_ORALDAP
2193    ZEND_ARG_INFO(0, wallet)
2194    ZEND_ARG_INFO(0, wallet_passwd)
2195    ZEND_ARG_INFO(0, authmode)
2196#endif
2197ZEND_END_ARG_INFO()
2198
2199ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_resource, 0, 0, 1)
2200    ZEND_ARG_INFO(0, link_identifier)
2201ZEND_END_ARG_INFO()
2202
2203ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind, 0, 0, 1)
2204    ZEND_ARG_INFO(0, link_identifier)
2205    ZEND_ARG_INFO(0, bind_rdn)
2206    ZEND_ARG_INFO(0, bind_password)
2207ZEND_END_ARG_INFO()
2208
2209#ifdef HAVE_LDAP_SASL
2210ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sasl_bind, 0, 0, 1)
2211    ZEND_ARG_INFO(0, link)
2212    ZEND_ARG_INFO(0, binddn)
2213    ZEND_ARG_INFO(0, password)
2214    ZEND_ARG_INFO(0, sasl_mech)
2215    ZEND_ARG_INFO(0, sasl_realm)
2216    ZEND_ARG_INFO(0, sasl_authz_id)
2217    ZEND_ARG_INFO(0, props)
2218ZEND_END_ARG_INFO()
2219#endif
2220
2221ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_read, 0, 0, 3)
2222    ZEND_ARG_INFO(0, link_identifier)
2223    ZEND_ARG_INFO(0, base_dn)
2224    ZEND_ARG_INFO(0, filter)
2225    ZEND_ARG_INFO(0, attributes)
2226    ZEND_ARG_INFO(0, attrsonly)
2227    ZEND_ARG_INFO(0, sizelimit)
2228    ZEND_ARG_INFO(0, timelimit)
2229    ZEND_ARG_INFO(0, deref)
2230ZEND_END_ARG_INFO()
2231
2232ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_list, 0, 0, 3)
2233    ZEND_ARG_INFO(0, link_identifier)
2234    ZEND_ARG_INFO(0, base_dn)
2235    ZEND_ARG_INFO(0, filter)
2236    ZEND_ARG_INFO(0, attributes)
2237    ZEND_ARG_INFO(0, attrsonly)
2238    ZEND_ARG_INFO(0, sizelimit)
2239    ZEND_ARG_INFO(0, timelimit)
2240    ZEND_ARG_INFO(0, deref)
2241ZEND_END_ARG_INFO()
2242
2243ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_search, 0, 0, 3)
2244    ZEND_ARG_INFO(0, link_identifier)
2245    ZEND_ARG_INFO(0, base_dn)
2246    ZEND_ARG_INFO(0, filter)
2247    ZEND_ARG_INFO(0, attributes)
2248    ZEND_ARG_INFO(0, attrsonly)
2249    ZEND_ARG_INFO(0, sizelimit)
2250    ZEND_ARG_INFO(0, timelimit)
2251    ZEND_ARG_INFO(0, deref)
2252ZEND_END_ARG_INFO()
2253
2254ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_count_entries, 0, 0, 2)
2255    ZEND_ARG_INFO(0, link_identifier)
2256    ZEND_ARG_INFO(0, result_identifier)
2257ZEND_END_ARG_INFO()
2258
2259ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_entry, 0, 0, 2)
2260    ZEND_ARG_INFO(0, link_identifier)
2261    ZEND_ARG_INFO(0, result_identifier)
2262ZEND_END_ARG_INFO()
2263
2264ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_entry, 0, 0, 2)
2265    ZEND_ARG_INFO(0, link_identifier)
2266    ZEND_ARG_INFO(0, result_identifier)
2267ZEND_END_ARG_INFO()
2268
2269ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_entries, 0, 0, 2)
2270    ZEND_ARG_INFO(0, link_identifier)
2271    ZEND_ARG_INFO(0, result_identifier)
2272ZEND_END_ARG_INFO()
2273
2274ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_attribute, 0, 0, 2)
2275    ZEND_ARG_INFO(0, link_identifier)
2276    ZEND_ARG_INFO(0, result_entry_identifier)
2277ZEND_END_ARG_INFO()
2278
2279ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_attribute, 0, 0, 2)
2280    ZEND_ARG_INFO(0, link_identifier)
2281    ZEND_ARG_INFO(0, result_entry_identifier)
2282ZEND_END_ARG_INFO()
2283
2284ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_attributes, 0, 0, 2)
2285    ZEND_ARG_INFO(0, link_identifier)
2286    ZEND_ARG_INFO(0, result_entry_identifier)
2287ZEND_END_ARG_INFO()
2288
2289ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values, 0, 0, 3)
2290    ZEND_ARG_INFO(0, link_identifier)
2291    ZEND_ARG_INFO(0, result_entry_identifier)
2292    ZEND_ARG_INFO(0, attribute)
2293ZEND_END_ARG_INFO()
2294
2295ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values_len, 0, 0, 3)
2296    ZEND_ARG_INFO(0, link_identifier)
2297    ZEND_ARG_INFO(0, result_entry_identifier)
2298    ZEND_ARG_INFO(0, attribute)
2299ZEND_END_ARG_INFO()
2300
2301ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_dn, 0, 0, 2)
2302    ZEND_ARG_INFO(0, link_identifier)
2303    ZEND_ARG_INFO(0, result_entry_identifier)
2304ZEND_END_ARG_INFO()
2305
2306ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_explode_dn, 0, 0, 2)
2307    ZEND_ARG_INFO(0, dn)
2308    ZEND_ARG_INFO(0, with_attrib)
2309ZEND_END_ARG_INFO()
2310
2311ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_dn2ufn, 0, 0, 1)
2312    ZEND_ARG_INFO(0, dn)
2313ZEND_END_ARG_INFO()
2314
2315ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_add, 0, 0, 3)
2316    ZEND_ARG_INFO(0, link_identifier)
2317    ZEND_ARG_INFO(0, dn)
2318    ZEND_ARG_INFO(0, entry)
2319ZEND_END_ARG_INFO()
2320
2321ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_delete, 0, 0, 2)
2322    ZEND_ARG_INFO(0, link_identifier)
2323    ZEND_ARG_INFO(0, dn)
2324ZEND_END_ARG_INFO()
2325
2326ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify, 0, 0, 3)
2327    ZEND_ARG_INFO(0, link_identifier)
2328    ZEND_ARG_INFO(0, dn)
2329    ZEND_ARG_INFO(0, entry)
2330ZEND_END_ARG_INFO()
2331
2332ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add, 0, 0, 3)
2333    ZEND_ARG_INFO(0, link_identifier)
2334    ZEND_ARG_INFO(0, dn)
2335    ZEND_ARG_INFO(0, entry)
2336ZEND_END_ARG_INFO()
2337
2338ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_replace, 0, 0, 3)
2339    ZEND_ARG_INFO(0, link_identifier)
2340    ZEND_ARG_INFO(0, dn)
2341    ZEND_ARG_INFO(0, entry)
2342ZEND_END_ARG_INFO()
2343
2344ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_del, 0, 0, 3)
2345    ZEND_ARG_INFO(0, link_identifier)
2346    ZEND_ARG_INFO(0, dn)
2347    ZEND_ARG_INFO(0, entry)
2348ZEND_END_ARG_INFO()
2349
2350ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_err2str, 0, 0, 1)
2351    ZEND_ARG_INFO(0, errno)
2352ZEND_END_ARG_INFO()
2353
2354ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_compare, 0, 0, 4)
2355    ZEND_ARG_INFO(0, link_identifier)
2356    ZEND_ARG_INFO(0, dn)
2357    ZEND_ARG_INFO(0, attribute)
2358    ZEND_ARG_INFO(0, value)
2359ZEND_END_ARG_INFO()
2360
2361ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sort, 0, 0, 3)
2362    ZEND_ARG_INFO(0, link)
2363    ZEND_ARG_INFO(0, result)
2364    ZEND_ARG_INFO(0, sortfilter)
2365ZEND_END_ARG_INFO()
2366
2367#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
2368ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_rename, 0, 0, 5)
2369    ZEND_ARG_INFO(0, link_identifier)
2370    ZEND_ARG_INFO(0, dn)
2371    ZEND_ARG_INFO(0, newrdn)
2372    ZEND_ARG_INFO(0, newparent)
2373    ZEND_ARG_INFO(0, deleteoldrdn)
2374ZEND_END_ARG_INFO()
2375
2376ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_option, 0, 0, 3)
2377    ZEND_ARG_INFO(0, link_identifier)
2378    ZEND_ARG_INFO(0, option)
2379    ZEND_ARG_INFO(1, retval)
2380ZEND_END_ARG_INFO()
2381
2382ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_option, 0, 0, 3)
2383    ZEND_ARG_INFO(0, link_identifier)
2384    ZEND_ARG_INFO(0, option)
2385    ZEND_ARG_INFO(0, newval)
2386ZEND_END_ARG_INFO()
2387
2388ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_reference, 0, 0, 2)
2389    ZEND_ARG_INFO(0, link)
2390    ZEND_ARG_INFO(0, result)
2391ZEND_END_ARG_INFO()
2392
2393ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_reference, 0, 0, 2)
2394    ZEND_ARG_INFO(0, link)
2395    ZEND_ARG_INFO(0, entry)
2396ZEND_END_ARG_INFO()
2397
2398#ifdef HAVE_LDAP_PARSE_REFERENCE
2399ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_reference, 0, 0, 3)
2400    ZEND_ARG_INFO(0, link)
2401    ZEND_ARG_INFO(0, entry)
2402    ZEND_ARG_INFO(1, referrals)
2403ZEND_END_ARG_INFO()
2404#endif
2405
2406
2407#ifdef HAVE_LDAP_PARSE_RESULT
2408ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_result, 0, 0, 3)
2409    ZEND_ARG_INFO(0, link)
2410    ZEND_ARG_INFO(0, result)
2411    ZEND_ARG_INFO(1, errcode)
2412    ZEND_ARG_INFO(1, matcheddn)
2413    ZEND_ARG_INFO(1, errmsg)
2414    ZEND_ARG_INFO(1, referrals)
2415ZEND_END_ARG_INFO()
2416#endif
2417#endif
2418
2419#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
2420ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_rebind_proc, 0, 0, 2)
2421    ZEND_ARG_INFO(0, link)
2422    ZEND_ARG_INFO(0, callback)
2423ZEND_END_ARG_INFO()
2424#endif
2425
2426#ifdef STR_TRANSLATION
2427ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_t61_to_8859, 0, 0, 1)
2428    ZEND_ARG_INFO(0, value)
2429ZEND_END_ARG_INFO()
2430
2431ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_8859_to_t61, 0, 0, 1)
2432    ZEND_ARG_INFO(0, value)
2433ZEND_END_ARG_INFO()
2434#endif
2435/* }}} */
2436
2437/*
2438    This is just a small subset of the functionality provided by the LDAP library. All the
2439    operations are synchronous. Referrals are not handled automatically.
2440*/
2441/* {{{ ldap_functions[]
2442 */
2443const zend_function_entry ldap_functions[] = {
2444    PHP_FE(ldap_connect,                                arginfo_ldap_connect)
2445    PHP_FALIAS(ldap_close,      ldap_unbind,            arginfo_ldap_resource)
2446    PHP_FE(ldap_bind,                                   arginfo_ldap_bind)
2447#ifdef HAVE_LDAP_SASL
2448    PHP_FE(ldap_sasl_bind,                              arginfo_ldap_sasl_bind)
2449#endif
2450    PHP_FE(ldap_unbind,                                 arginfo_ldap_resource)
2451    PHP_FE(ldap_read,                                   arginfo_ldap_read)
2452    PHP_FE(ldap_list,                                   arginfo_ldap_list)
2453    PHP_FE(ldap_search,                                 arginfo_ldap_search)
2454    PHP_FE(ldap_free_result,                            arginfo_ldap_resource)
2455    PHP_FE(ldap_count_entries,                          arginfo_ldap_count_entries)
2456    PHP_FE(ldap_first_entry,                            arginfo_ldap_first_entry)
2457    PHP_FE(ldap_next_entry,                             arginfo_ldap_next_entry)
2458    PHP_FE(ldap_get_entries,                            arginfo_ldap_get_entries)
2459    PHP_FE(ldap_first_attribute,                        arginfo_ldap_first_attribute)
2460    PHP_FE(ldap_next_attribute,                         arginfo_ldap_next_attribute)
2461    PHP_FE(ldap_get_attributes,                         arginfo_ldap_get_attributes)
2462    PHP_FALIAS(ldap_get_values, ldap_get_values_len,    arginfo_ldap_get_values)
2463    PHP_FE(ldap_get_values_len,                         arginfo_ldap_get_values_len)
2464    PHP_FE(ldap_get_dn,                                 arginfo_ldap_get_dn)
2465    PHP_FE(ldap_explode_dn,                             arginfo_ldap_explode_dn)
2466    PHP_FE(ldap_dn2ufn,                                 arginfo_ldap_dn2ufn)
2467    PHP_FE(ldap_add,                                    arginfo_ldap_add)
2468    PHP_FE(ldap_delete,                                 arginfo_ldap_delete)
2469    PHP_FALIAS(ldap_modify,     ldap_mod_replace,       arginfo_ldap_modify)
2470
2471/* additional functions for attribute based modifications, Gerrit Thomson */
2472    PHP_FE(ldap_mod_add,                                arginfo_ldap_mod_add)
2473    PHP_FE(ldap_mod_replace,                            arginfo_ldap_mod_replace)
2474    PHP_FE(ldap_mod_del,                                arginfo_ldap_mod_del)
2475/* end gjt mod */
2476
2477    PHP_FE(ldap_errno,                                  arginfo_ldap_resource)
2478    PHP_FE(ldap_err2str,                                arginfo_ldap_err2str)
2479    PHP_FE(ldap_error,                                  arginfo_ldap_resource)
2480    PHP_FE(ldap_compare,                                arginfo_ldap_compare)
2481    PHP_FE(ldap_sort,                                   arginfo_ldap_sort)
2482
2483#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
2484    PHP_FE(ldap_rename,                                 arginfo_ldap_rename)
2485    PHP_FE(ldap_get_option,                             arginfo_ldap_get_option)
2486    PHP_FE(ldap_set_option,                             arginfo_ldap_set_option)
2487    PHP_FE(ldap_first_reference,                        arginfo_ldap_first_reference)
2488    PHP_FE(ldap_next_reference,                         arginfo_ldap_next_reference)
2489#ifdef HAVE_LDAP_PARSE_REFERENCE
2490    PHP_FE(ldap_parse_reference,                        arginfo_ldap_parse_reference)
2491#endif
2492#ifdef HAVE_LDAP_PARSE_RESULT
2493    PHP_FE(ldap_parse_result,                           arginfo_ldap_parse_result)
2494#endif
2495#ifdef HAVE_LDAP_START_TLS_S
2496    PHP_FE(ldap_start_tls,                              arginfo_ldap_resource)
2497#endif
2498#endif
2499
2500#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
2501    PHP_FE(ldap_set_rebind_proc,                        arginfo_ldap_set_rebind_proc)
2502#endif
2503
2504#ifdef STR_TRANSLATION
2505    PHP_FE(ldap_t61_to_8859,                            arginfo_ldap_t61_to_8859)
2506    PHP_FE(ldap_8859_to_t61,                            arginfo_ldap_8859_to_t61)
2507#endif
2508
2509    PHP_FE_END
2510};
2511/* }}} */
2512
2513zend_module_entry ldap_module_entry = { /* {{{ */
2514    STANDARD_MODULE_HEADER,
2515    "ldap",
2516    ldap_functions,
2517    PHP_MINIT(ldap),
2518    PHP_MSHUTDOWN(ldap),
2519    NULL,
2520    NULL,
2521    PHP_MINFO(ldap),
2522    NO_VERSION_YET,
2523    PHP_MODULE_GLOBALS(ldap),
2524    PHP_GINIT(ldap),
2525    NULL,
2526    NULL,
2527    STANDARD_MODULE_PROPERTIES_EX
2528};
2529/* }}} */
2530
2531/*
2532 * Local variables:
2533 * tab-width: 4
2534 * c-basic-offset: 4
2535 * End:
2536 * vim600: sw=4 ts=4 fdm=marker
2537 * vim<600: sw=4 ts=4
2538 */
2539