$NetBSD: ldap-ctrl-exop.patch,v 1.3 2012/10/29 08:50:29 manu Exp $ /* * Copyright (c) 2007-2009 Pierangelo Masarati * Copyright (c) 2009,2012 Emmanuel Dreyfus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ --- ext/ldap/config.m4.orig 2012-10-25 10:15:34.000000000 +0200 +++ ext/ldap/config.m4 2012-10-25 10:15:34.000000000 +0200 @@ -172,9 +172,11 @@ fi dnl Solaris 2.8 claims to be 2004 API, but doesn't have dnl ldap_parse_reference() nor ldap_start_tls_s() - AC_CHECK_FUNCS([ldap_parse_result ldap_parse_reference ldap_start_tls_s]) + AC_CHECK_FUNCS([ldap_parse_result ldap_parse_extended_result ldap_parse_whoami ldap_parse_passwd ldap_parse_reference]) + AC_CHECK_FUNCS([ldap_extended_operation_s ldap_start_tls_s ldap_whoami_s ldap_passwd_s]) + AC_CHECK_FUNCS([ldap_search_ext_s ldap_add_ext_s ldap_modify_ext_s ldap_delete_ext_s ldap_compare_ext_s]) dnl dnl SASL check dnl --- ext/ldap/ldap.c.orig 2012-10-25 10:15:34.000000000 +0200 +++ ext/ldap/ldap.c 2012-10-25 18:05:38.000000000 +0200 @@ -66,8 +66,13 @@ #elif defined(HAVE_LDAP_SASL_SASL_H) #include #endif +/* XXX Not detected by configure... */ +#ifdef LDAP_EXOP_REFRESH +#define HAVE_LDAP_REFRESH 1 +#endif + typedef struct { LDAP *link; #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC) zval *rebindproc; @@ -88,31 +93,46 @@ #ifdef COMPILE_DL_LDAP ZEND_GET_MODULE(ldap) #endif + +/* {{{ proto void _close_ldap_link() + close a connection and free LDAP resources */ static void _close_ldap_link(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr; - ldap_unbind_s(ld->link); -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC) + /* ldap_unbind_s() is deprecated; + * the distinction between ldap_unbind() and ldap_unbind_s() is moot */ +#ifdef LDAP_API_FEATURE_X_OPENLDAP + ldap_unbind_ext(ld->link, NULL, NULL); +#ifdef HAVE_3ARG_SETREBINDPROC + if (ld->rebindproc != NULL) { zval_dtor(ld->rebindproc); FREE_ZVAL(ld->rebindproc); } #endif +#else /* ! LDAP_API_FEATURE_X_OPENLDAP */ + ldap_unbind_s(ld->link); +#endif /* ! LDAP_API_FEATURE_X_OPENLDAP */ + efree(ld); LDAPG(num_links)--; } /* }}} */ +/* {{{ proto void _free_ldap_result() + free the result of an LDAP operation */ static void _free_ldap_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { LDAPMessage *result = (LDAPMessage *)rsrc->ptr; ldap_msgfree(result); } /* }}} */ +/* {{{ proto void _free_ldap_result_entry() + free an entry resulting from an LDAP search operation */ static void _free_ldap_result_entry(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { ldap_resultentry *entry = (ldap_resultentry *)rsrc->ptr; @@ -194,8 +214,21 @@ REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT | CONST_CS); #endif +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + REGISTER_LONG_CONSTANT("PP_passwordExpired", PP_passwordExpired, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_accountLocked", PP_accountLocked, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_changeAfterReset", PP_changeAfterReset, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_passwordModNotAllowed", PP_passwordModNotAllowed, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_mustSupplyOldPassword", PP_mustSupplyOldPassword, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_insufficientPasswordQuality", PP_insufficientPasswordQuality, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_passwordTooShort", PP_passwordTooShort, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_passwordTooYoung", PP_passwordTooYoung, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_passwordInHistory", PP_passwordInHistory, CONST_PERSISTENT | CONST_CS); + REGISTER_LONG_CONSTANT("PP_noError", PP_noError, CONST_PERSISTENT | CONST_CS); +#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ + le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number); le_result = zend_register_list_destructors_ex(_free_ldap_result, NULL, "ldap result", module_number); le_result_entry = zend_register_list_destructors_ex(_free_ldap_result_entry, NULL, "ldap result entry", module_number); @@ -276,15 +309,176 @@ DISPLAY_INI_ENTRIES(); } /* }}} */ + +/* {{{ proto int _php_parse_referrals_resp() + parse an array of LDAP referrals into a zval array */ +static int _php_parse_referrals_resp(char ***lreferralsp, zval **referrals) +{ + int num_referrals = 0; + + if (*lreferralsp != NULL) { + char **refp = *lreferralsp; + + while (*refp) { + add_next_index_string(*referrals, *refp, 1); + refp++; + num_referrals++; + } +#ifdef LDAP_API_FEATURE_X_OPENLDAP + ber_memvfree((void **)*lreferralsp); +#else + ldap_value_free(*lreferralsp); +#endif + *lreferralsp = NULL; + } + + return num_referrals; +} +/* }}} */ + +/* {{{ proto int _php_parse_controls() + parse an array of zvals into an array of LDAP controls */ +static int _php_parse_controls(zval **ctrls, LDAPControl ***lctrlsp) +{ + LDAPControl *lctrl, **lctrls, **lctrlp; + zval **ctrlval, **val; + int ncontrols; + char error = 0; + + + if ((Z_TYPE_PP(ctrls) != IS_ARRAY) || !(ncontrols = zend_hash_num_elements(Z_ARRVAL_PP(ctrls)))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected non-empty array value"); + return 0; + } + + lctrls = safe_emalloc((1 + ncontrols), sizeof(*lctrls), 0); + *lctrls = NULL; + lctrlp = lctrls; + zend_hash_internal_pointer_reset(Z_ARRVAL_PP(ctrls)); + while (zend_hash_get_current_data(Z_ARRVAL_PP(ctrls), (void**)&ctrlval) == SUCCESS) { + if (Z_TYPE_PP(ctrlval) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The array value must contain only arrays, where each array is a control"); + error = 1; + break; + } + if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "oid", sizeof("oid"), (void **) &val) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Control must have an oid key"); + error = 1; + break; + } + lctrl = *lctrlp = emalloc(sizeof(**lctrlp)); + convert_to_string_ex(val); + lctrl->ldctl_oid = Z_STRVAL_PP(val); + if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "value", sizeof("value"), (void **) &val) == SUCCESS) { + convert_to_string_ex(val); + lctrl->ldctl_value.bv_val = Z_STRVAL_PP(val); + lctrl->ldctl_value.bv_len = Z_STRLEN_PP(val); + } else { + lctrl->ldctl_value.bv_val = NULL; + lctrl->ldctl_value.bv_len = 0; + } + if (zend_hash_find(Z_ARRVAL_PP(ctrlval), "iscritical", sizeof("iscritical"), (void **) &val) == SUCCESS) { + convert_to_boolean_ex(val); + lctrl->ldctl_iscritical = Z_BVAL_PP(val); + } else { + lctrl->ldctl_iscritical = 0; + } + + ++lctrlp; + *lctrlp = NULL; + zend_hash_move_forward(Z_ARRVAL_PP(ctrls)); + } + if (!error) { + *lctrlsp = lctrls; + } + return ncontrols; +} +/* }}} */ + +/* {{{ proto void _php_free_controls() + frees an array of LDAP controls as parsed (and malloc'ed) by _php_parse_controls */ +static void _php_free_controls(LDAPControl ***lctrlsp) +{ + LDAPControl **lctrlp; + + for (lctrlp = *lctrlsp; *lctrlp; lctrlp++) { + efree(*lctrlp); + } + efree(*lctrlsp); + *lctrlsp = NULL; +} +/* }}} */ + +/* {{{ proto void _php_parse_controls_resp() + parse an array of LDAP controls into a zval array */ +static int _php_parse_controls_resp(LDAPControl ***lctrlsp, zval **ctrls) +{ + int num_ctrls = 0; + + if (*lctrlsp != NULL) { + int error = 0; + LDAPControl **ctrlp = *lctrlsp; + + while (*ctrlp) { + zval *ctrlval = NULL; + + if ( (*ctrlp)->ldctl_oid == NULL ) { + error = 1; + break; + } + + MAKE_STD_ZVAL(ctrlval); + array_init(ctrlval); + + add_assoc_string(ctrlval, "oid", (*ctrlp)->ldctl_oid, 1); + if ( (*ctrlp)->ldctl_value.bv_len ) { + add_assoc_stringl(ctrlval, "value", (*ctrlp)->ldctl_value.bv_val, (*ctrlp)->ldctl_value.bv_len, 1); + } + + /* As per : + * + * 4.1.11 + + The criticality field only has meaning in controls attached to + request messages (except UnbindRequest). For controls attached to + response messages and the UnbindRequest, the criticality field SHOULD + be FALSE, and MUST be ignored by the receiving protocol peer. + + */ + + add_next_index_zval(*ctrls, ctrlval); + + num_ctrls++; + ctrlp++; + } + ldap_controls_free(*lctrlsp); + *lctrlsp = NULL; + + if (error) { + /* ... */ + return -1; + } + } + + return num_ctrls; +} +/* }}} */ + /* {{{ proto resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]]) Connect to an LDAP server */ PHP_FUNCTION(ldap_connect) { char *host = NULL; int hostlen; - long port = 389; /* Default port */ + int port = +#ifdef LDAP_API_FEATURE_X_OPENLDAP + LDAP_PORT +#else /* ! LDAP_API_FEATURE_X_OPENLDAP */ + 389 /* Default port */ +#endif /* ! LDAP_API_FEATURE_X_OPENLDAP */ + ; #ifdef HAVE_ORALDAP char *wallet = NULL, *walletpasswd = NULL; int walletlen = 0, walletpasswdlen = 0; long authmode = GSLC_SSL_NO_AUTH; @@ -318,23 +512,41 @@ ld = ecalloc(1, sizeof(ldap_linkdata)); #ifdef LDAP_API_FEATURE_X_OPENLDAP - if (host != NULL && strchr(host, '/')) { - int rc; + /* OpenLDAP provides a specific call to detect valid LDAP URIs; + * ldap_init()/ldap_open() is deprecated, use ldap_initialize() instead. + */ + { + int rc; + char *url = host; + + if (!ldap_is_ldap_url(url)) { + int urllen = hostlen + sizeof( "ldap://:65535" ); + + if (port <= 0 || port > 65535) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid port number: %ld", port); + RETURN_FALSE; + } + + url = emalloc(urllen); + snprintf( url, urllen, "ldap://%s:%d", host ? host : "", port ); + } - rc = ldap_initialize(&ldap, host); + rc = ldap_initialize(&ldap, url); if (rc != LDAP_SUCCESS) { efree(ld); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc)); RETURN_FALSE; } - } else { - ldap = ldap_init(host, port); + + if (url != host) { + efree(url); + } } -#else +#else /* ! LDAP_API_FEATURE_X_OPENLDAP */ ldap = ldap_open(host, port); -#endif +#endif /* ! LDAP_API_FEATURE_X_OPENLDAP */ if (ldap == NULL) { efree(ld); RETURN_FALSE; @@ -391,17 +603,32 @@ } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); - if ((rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) { +#ifdef LDAP_API_FEATURE_X_OPENLDAP + { + struct berval cred; + + /* ldap_bind_s() is deprecated; use ldap_sasl_bind_s() instead */ + cred.bv_val = ldap_bind_pw; + cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0; + rc = ldap_sasl_bind_s(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, /* no controls right now */ + NULL); /* we don't care about the server's credentials */ + } +#else + rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE); +#endif + if ( rc != LDAP_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc)); RETURN_FALSE; } else { RETURN_TRUE; } } /* }}} */ +/* {{{ SASL bind stuff */ #ifdef HAVE_LDAP_SASL typedef struct { char *mech; char *realm; @@ -524,8 +751,9 @@ _php_sasl_freedefs(ctx); } /* }}} */ #endif /* HAVE_LDAP_SASL */ +/* }}} */ /* {{{ proto bool ldap_unbind(resource link) Unbind from LDAP directory */ PHP_FUNCTION(ldap_unbind) @@ -1221,9 +1449,14 @@ for (i = 0; i 3) { + if (is_full_add) { +#ifndef HAVE_LDAP_ADD_EXT_S + php_error_docref(NULL TSRMLS_CC, E_WARNING, "ldap_add_ext not available"); + RETURN_FALSE; +#endif /* ! HAVE_LDAP_ADD_EXT_S */ + + } else { +#ifndef HAVE_LDAP_MODIFY_EXT_S + php_error_docref(NULL TSRMLS_CC, E_WARNING, "ldap_modify_ext not available"); + RETURN_FALSE; +#endif /* ! HAVE_LDAP_MODIFY_EXT_S */ + } + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); + + num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry)); + ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0); + num_berval = safe_emalloc(num_attribs, sizeof(int), 0); + zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry)); + + for (i = 0; i < num_attribs; i++) { ldap_mods[i] = emalloc(sizeof(LDAPMod)); ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES; ldap_mods[i]->mod_type = NULL; @@ -1343,19 +1605,78 @@ zend_hash_move_forward(Z_ARRVAL_P(entry)); } ldap_mods[num_attribs] = NULL; + if (ext) { + switch (myargcount) { + case 5: + if (_php_parse_controls(cctrls, &lcctrls) == 0) { + RETVAL_FALSE; + goto errexit; + } + + case 4: + if (_php_parse_controls(sctrls, &lsctrls) == 0) { + RETVAL_FALSE; + goto errexit; + } + } + } + /* check flag to see if do_mod was called to perform full add , gerrit thomson */ if (is_full_add == 1) { - if ((i = ldap_add_s(ld->link, dn, ldap_mods)) != LDAP_SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Add: %s", ldap_err2string(i)); - RETVAL_FALSE; - } else RETVAL_TRUE; - } else { - if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modify: %s", ldap_err2string(i)); - RETVAL_FALSE; - } else RETVAL_TRUE; +#ifdef HAVE_LDAP_ADD_EXT_S + if (ext) { + rc = ldap_add_ext(ld->link, dn, ldap_mods, lsctrls, lcctrls, &msgid); + + } else { + rc = ldap_add_ext_s(ld->link, dn, ldap_mods, NULL, NULL); + } +#else /* ! HAVE_LDAP_ADD_EXT_S */ + rc = ldap_add_s(ld->link, dn, ldap_mods); +#endif /* ! HAVE_LDAP_ADD_EXT_S */ + + } else { +#ifdef HAVE_LDAP_MODIFY_EXT_S + if (ext) { + rc = ldap_modify_ext(ld->link, dn, ldap_mods, lsctrls, lcctrls, &msgid); + + } else { + rc = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL); + } +#else /* ! HAVE_LDAP_MODIFY_EXT_S */ + rc = ldap_modify_s(ld->link, dn, ldap_mods); +#endif /* ! HAVE_LDAP_MODIFY_EXT_S */ + } + + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: %s", is_full_add ? "Add" : "Modify", ldap_err2string(i)); + RETVAL_FALSE; + + } else { + if (ext) { + rc = ldap_result(ld->link, msgid, LDAP_MSG_ALL, NULL, &ldap_res); + if ((is_full_add && rc != LDAP_RES_ADD) || (!is_full_add && rc != LDAP_RES_MODIFY)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: unable to collect result", is_full_add ? "Add" : "Modify"); + RETVAL_FALSE; + + } else { + int lerr; + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); + rc = ldap_parse_result(ld->link, ldap_res, &lerr, NULL, NULL, NULL, NULL, 0); + if (rc == LDAP_SUCCESS) { + rc = lerr; + } + + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s failed: %s", is_full_add ? "Add" : "Modify", ldap_err2string(rc)); + } + } + + } else { + RETVAL_TRUE; + } } errexit: for (i = 0; i < num_attribs; i++) { @@ -1368,46 +1689,57 @@ } efree(num_berval); efree(ldap_mods); + if (ext) { + if (lsctrls) { + _php_free_controls(&lsctrls); + } + if (lcctrls) { + _php_free_controls(&lcctrls); + } + } + return; } /* }}} */ /* {{{ proto bool ldap_add(resource link, string dn, array entry) Add entries to LDAP directory */ PHP_FUNCTION(ldap_add) { - /* 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 */ - php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD); + /* 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 */ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 0); } /* }}} */ -/* three functions for attribute base modifications, gerrit Thomson */ +/* {{{ Three functions for attribute base modifications, gerrit Thomson */ /* {{{ proto bool ldap_mod_replace(resource link, string dn, array entry) Replace attribute values with new ones */ PHP_FUNCTION(ldap_mod_replace) { - php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE); + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 0); } /* }}} */ /* {{{ proto bool ldap_mod_add(resource link, string dn, array entry) Add attribute values to current */ PHP_FUNCTION(ldap_mod_add) { - php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD); + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 0); } /* }}} */ /* {{{ proto bool ldap_mod_del(resource link, string dn, array entry) + Delete attribute values */ PHP_FUNCTION(ldap_mod_del) { - php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE); + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 0); } /* }}} */ +/* }}} */ /* {{{ proto bool ldap_delete(resource link, string dn) Delete an entry from a directory */ PHP_FUNCTION(ldap_delete) @@ -1482,38 +1814,109 @@ RETURN_STRING(ldap_err2string(ld_errno), 1); } /* }}} */ -/* {{{ proto bool ldap_compare(resource link, string dn, string attr, string value) - Determine if an entry has a specific value for one of its attributes */ -PHP_FUNCTION(ldap_compare) +/* {{{ proto void php_ldap_do_compare */ +void php_ldap_do_compare(INTERNAL_FUNCTION_PARAMETERS, int ext) { - zval *link; - char *dn, *attr, *value; + zval *link, *dn, *attr, *value, **sctrls, **cctrls; + char *ldap_dn, *ldap_attr; int dn_len, attr_len, value_len; ldap_linkdata *ld; - int errno; + int rc, msgid, lerr, myargcount = ZEND_NUM_ARGS(); + LDAPMessage *ldap_res; + LDAPControl **lsctrls = NULL, **lcctrls = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len) != SUCCESS) { - return; + if (ext) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss|ZZ", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len, &sctrls, &cctrls) != SUCCESS) { + WRONG_PARAM_COUNT; + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len) != SUCCESS) { + WRONG_PARAM_COUNT; + } } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); - errno = ldap_compare_s(ld->link, dn, attr, value); - - switch (errno) { - case LDAP_COMPARE_TRUE: - RETURN_TRUE; - break; + if (ext) { + struct berval ldap_bvalue; + switch (myargcount) { + case 6: + _php_parse_controls(cctrls, &lcctrls); + case 5: + _php_parse_controls(sctrls, &lsctrls); + } - case LDAP_COMPARE_FALSE: + ldap_bvalue.bv_val = Z_STRVAL_PP(&value); + ldap_bvalue.bv_len = Z_STRLEN_PP(&value); + rc = ldap_compare_ext(ld->link, ldap_dn, ldap_attr, &ldap_bvalue, lsctrls, lcctrls, &msgid); + if (lsctrls) { + _php_free_controls(&lsctrls); + } + if (lcctrls) { + _php_free_controls(&lcctrls); + } + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: %s", ldap_err2string(rc)); RETURN_FALSE; - break; + } + + rc = ldap_result(ld->link, msgid, LDAP_MSG_ALL, NULL, &ldap_res); + if (rc != LDAP_RES_COMPARE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: unable to get result"); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); + rc = ldap_parse_result(ld->link, ldap_res, &lerr, NULL, NULL, NULL, NULL, 0); + if (rc == LDAP_SUCCESS) { + rc = lerr; + } + + switch (rc) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare failed: %s", ldap_err2string(rc)); + break; + } + + } else { +#ifdef HAVE_LDAP_COMPARE_EXT_S + struct berval ldap_bvalue; + + ldap_bvalue.bv_val = Z_STRVAL_PP(&value); + ldap_bvalue.bv_len = Z_STRLEN_PP(&value); + rc = ldap_compare_ext_s(ld->link, ldap_dn, ldap_attr, &ldap_bvalue, NULL, NULL); +#else /* ! HAVE_LDAP_COMPARE_EXT_S */ + char *ldap_value; + + ldap_value = Z_STRVAL_PP(value); + rc = ldap_compare_s(ld->link, ldap_dn, ldap_attr, ldap_value); +#endif /* ! HAVE_LDAP_COMPARE_EXT_S */ + + switch (rc) { + case LDAP_COMPARE_TRUE: + RETURN_TRUE; + break; + + case LDAP_COMPARE_FALSE: + RETURN_FALSE; + break; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: %s", ldap_err2string(rc)); + RETURN_LONG(-1); } - - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Compare: %s", ldap_err2string(errno)); - RETURN_LONG(-1); +} +/* {{{ proto bool ldap_compare(resource link, string dn, string attr, string valu) + Determine if an entry has a specific value for one of its attributes */ +PHP_FUNCTION(ldap_compare) +{ + php_ldap_do_compare(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ proto bool ldap_sort(resource link, resource result, string sortfilter) @@ -1545,59 +1948,230 @@ RETURN_TRUE; } /* }}} */ -#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 -/* {{{ proto bool ldap_get_option(resource link, int option, mixed retval) - Get the current value of various session-wide parameters */ -PHP_FUNCTION(ldap_get_option) +/* {{{ Extended API that returns result instead of just bool + * to allow further manipulation by the ldap_parse_*() funcs, + * Pierangelo Masarati */ + +/* {{{ proto result ldap_bind_ext(resource link [, string dn, string password]) + Bind to LDAP directory */ +PHP_FUNCTION(ldap_bind_ext) { - zval *link, *retval; + zval *link; + char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL; + int ldap_bind_dnlen, ldap_bind_pwlen; ldap_linkdata *ld; - long option; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &link, &option, &retval) != SUCCESS) { - return; + int rc, msgid, lerr; + LDAPMessage *ldap_res; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ss", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen) == FAILURE) { + RETURN_FALSE; } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); - switch (option) { - /* options with int value */ - case LDAP_OPT_DEREF: - case LDAP_OPT_SIZELIMIT: - case LDAP_OPT_TIMELIMIT: - case LDAP_OPT_PROTOCOL_VERSION: - case LDAP_OPT_ERROR_NUMBER: - case LDAP_OPT_REFERRALS: -#ifdef LDAP_OPT_RESTART - case LDAP_OPT_RESTART: +#ifdef LDAP_API_FEATURE_X_OPENLDAP + { + struct berval cred; + + /* ldap_bind() is deprecated; use ldap_sasl_bind() instead */ + cred.bv_val = ldap_bind_pw; + cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0; + + rc = ldap_sasl_bind(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, /* no controls right now */ + &msgid); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + } +#else + msgid = ldap_bind(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE); + if (msgid == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind"); + RETURN_FALSE; + } #endif - { - int val; + + rc = ldap_result(ld->link, msgid, LDAP_MSG_ALL, NULL, &ldap_res); + if (rc != LDAP_RES_BIND) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to get bind result: %s", ldap_err2string(rc)); + RETURN_FALSE; + } - if (ldap_get_option(ld->link, option, &val)) { - RETURN_FALSE; - } - zval_dtor(retval); - ZVAL_LONG(retval, val); - } break; -#ifdef LDAP_OPT_NETWORK_TIMEOUT - case LDAP_OPT_NETWORK_TIMEOUT: - { - struct timeval *timeout = NULL; + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); + rc = ldap_parse_result(ld->link, ldap_res, &lerr, NULL, NULL, NULL, NULL, 0); + if (rc == LDAP_SUCCESS) { + rc = lerr; + } - if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) { - if (timeout) { - ldap_memfree(timeout); - } - RETURN_FALSE; - } - if (!timeout) { - RETURN_FALSE; - } - zval_dtor(retval); - ZVAL_LONG(retval, timeout->tv_sec); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc)); + } +} +/* }}} */ + +/* {{{ proto result ldap_add_ext(resource link, string dn, array entry) + Add entries to LDAP directory; returns result */ +PHP_FUNCTION(ldap_add_ext) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 1); +} +/* }}} */ + +/* {{{ proto result ldap_mod_replace_ext(resource link, string dn, array entry) + Replace attribute values with new ones */ +PHP_FUNCTION(ldap_mod_replace_ext) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 1); +} +/* }}} */ + +/* {{{ proto result ldap_mod_add_ext(resource link, string dn, array entry) + Add attribute values to current */ +PHP_FUNCTION(ldap_mod_add_ext) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 1); +} +/* }}} */ + +/* {{{ proto result ldap_mod_del_ext(resource link, string dn, array entry) + Delete attribute values */ +PHP_FUNCTION(ldap_mod_del_ext) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 1); +} +/* }}} */ + +/* {{{ proto result ldap_delete_ext(resource link, string dn) + Delete an entry from a directory */ +PHP_FUNCTION(ldap_delete_ext) +{ + zval **link, **dn, **sctrls, **cctrls; + ldap_linkdata *ld; + char *ldap_dn; + int rc, dn_len, msgid, lerr, myargcount = ZEND_NUM_ARGS(); + LDAPMessage *ldap_res; + LDAPControl **lsctrls = NULL, **lcctrls = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|ZZ", &link, &dn, &dn_len, &sctrls, &cctrls) == FAILURE) { + WRONG_PARAM_COUNT; + } + + switch (myargcount) { + case 4: + _php_parse_controls(cctrls, &lcctrls); + + case 3: + _php_parse_controls(sctrls, &lsctrls); + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + + convert_to_string_ex(dn); + ldap_dn = Z_STRVAL_PP(dn); + +#ifdef HAVE_LDAP_DELETE_EXT_S + rc = ldap_delete_ext_s(ld->link, ldap_dn, NULL, NULL); +#else /* ! HAVE_LDAP_DELETE_EXT_S */ + rc = ldap_delete_s(ld->link, ldap_dn); +#endif /* ! HAVE_LDAP_DELETE_EXT_S */ + if (lsctrls) { + _php_free_controls(&lsctrls); + } + if (lcctrls) { + _php_free_controls(&lcctrls); + } + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delete: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + + rc = ldap_result(ld->link, msgid, LDAP_MSG_ALL, NULL, &ldap_res); + if (rc != LDAP_RES_DELETE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delete: unable to get result"); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); + rc = ldap_parse_result(ld->link, ldap_res, &lerr, NULL, NULL, NULL, NULL, 0); + if (rc == LDAP_SUCCESS) { + rc = lerr; + } + + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delete failed: %s", ldap_err2string(rc)); + } +} +/* }}} */ + +/* }}} End of extended API, Pierangelo Masarati */ + + +/* {{{ proto result ldap_compare_ext(resource link, string dn, string attr, string value) + Determine if an entry has a specific value for one of its attributes */ +PHP_FUNCTION(ldap_compare_ext) +{ + php_ldap_do_compare(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* }}} End of extended API, Pierangelo Masarati */ + + +#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10 +/* {{{ proto bool ldap_get_option(resource link, int option, mixed retval) + Get the current value of various session-wide parameters */ +PHP_FUNCTION(ldap_get_option) +{ + zval *link, *retval; + ldap_linkdata *ld; + long option; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &link, &option, &retval) != SUCCESS) { + return; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); + + switch (option) { + /* options with int value */ + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_ERROR_NUMBER: + case LDAP_OPT_REFERRALS: +#ifdef LDAP_OPT_RESTART + case LDAP_OPT_RESTART: +#endif + { + int val; + + if (ldap_get_option(ld->link, option, &val)) { + RETURN_FALSE; + } + zval_dtor(retval); + ZVAL_LONG(retval, val); + } break; +#ifdef LDAP_OPT_NETWORK_TIMEOUT + case LDAP_OPT_NETWORK_TIMEOUT: + { + struct timeval *timeout = NULL; + + if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) { + if (timeout) { + ldap_memfree(timeout); + } + RETURN_FALSE; + } + if (!timeout) { + RETURN_FALSE; + } + zval_dtor(retval); + ZVAL_LONG(retval, timeout->tv_sec); ldap_memfree(timeout); } break; #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT) case LDAP_X_OPT_CONNECT_TIMEOUT: @@ -1820,21 +2394,23 @@ } /* }}} */ #ifdef HAVE_LDAP_PARSE_RESULT -/* {{{ proto bool ldap_parse_result(resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals) +/* {{{ proto bool ldap_parse_result(resource link, resource result, int errcode [, string matcheddn [, string errmsg [, array referrals [, array serverctrls]]]]) Extract information from result */ PHP_FUNCTION(ldap_parse_result) { - zval *link, *result, *errcode, *matcheddn, *errmsg, *referrals; + zval *link, *result, *errcode, *matcheddn, *errmsg, *referrals, *serverctrls; ldap_linkdata *ld; LDAPMessage *ldap_result; - char **lreferrals, **refp; + LDAPControl **lserverctrls; + char **lreferrals; char *lmatcheddn, *lerrmsg; int rc, lerrcode, myargcount = ZEND_NUM_ARGS(); + /* int matcheddn_len, errmsg_len; */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz|zzz", &link, &result, &errcode, &matcheddn, &errmsg, &referrals) != SUCCESS) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrz|zzzz", &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) != SUCCESS) { + WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result); @@ -1842,9 +2418,9 @@ rc = ldap_parse_result(ld->link, ldap_result, &lerrcode, myargcount > 3 ? &lmatcheddn : NULL, myargcount > 4 ? &lerrmsg : NULL, myargcount > 5 ? &lreferrals : NULL, - NULL /* &serverctrls */, + myargcount > 6 ? &lserverctrls : NULL, 0); if (rc != LDAP_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s", ldap_err2string(rc)); RETURN_FALSE; @@ -1854,19 +2430,15 @@ ZVAL_LONG(errcode, lerrcode); /* Reverse -> fall through */ switch (myargcount) { + case 7: + /* use arg #7 as the array of controls returned by the server */ + zval_dtor(serverctrls); + array_init(serverctrls); + _php_parse_controls_resp(&lserverctrls, &serverctrls); case 6: - zval_dtor(referrals); - array_init(referrals); - if (lreferrals != NULL) { - refp = lreferrals; - while (*refp) { - add_next_index_string(referrals, *refp, 1); - refp++; - } - ldap_value_free(lreferrals); - } + _php_parse_referrals_resp(&lreferrals, &referrals); case 5: zval_dtor(errmsg); if (lerrmsg == NULL) { ZVAL_EMPTY_STRING(errmsg); @@ -1887,8 +2459,142 @@ } /* }}} */ #endif +/* {{{ Extended operation response parsing, Pierangelo Masarati */ +#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT +/* {{{ proto bool ldap_parse_exop(resource link, resource result [, string retoid [, string retdata]]) + Extract information from extended operation result */ +PHP_FUNCTION(ldap_parse_exop) +{ + zval *link, *result, *retoid, *retdata; + ldap_linkdata *ld; + LDAPMessage *ldap_result; + char *lretoid; + struct berval *lretdata; + int rc, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|zz", &link, &result, &retoid, &retdata) == SUCCESS) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); + ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, &result, -1, "ldap result", le_result); + + rc = ldap_parse_extended_result(ld->link, ldap_result, + myargcount > 2 ? &lretoid: NULL, + myargcount > 3 ? &lretdata: NULL, + 0); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse extended operation result: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + + /* Reverse -> fall through */ + switch (myargcount) { + case 4: + /* use arg #4 as the data returned by the server */ + zval_dtor(retdata); + if (lretdata == NULL) { + ZVAL_EMPTY_STRING(retdata); + } else { + ZVAL_STRINGL(retdata, lretdata->bv_val, lretdata->bv_len, 1); + ldap_memfree(lretdata->bv_val); + ldap_memfree(lretdata); + } + case 3: + zval_dtor(retoid); + if (lretoid == NULL) { + ZVAL_EMPTY_STRING(retoid); + } else { + ZVAL_STRING(retoid, lretoid, 1); + ldap_memfree(lretoid); + } + } + RETURN_TRUE; +} +/* }}} */ + +#ifdef HAVE_LDAP_PARSE_PASSWD +/* {{{ proto bool ldap_parse_exop_passwd(resource link, resource result, string newpasswd) + Extract information from RFC 3062 password modify extended operation result */ +PHP_FUNCTION(ldap_parse_exop_passwd) +{ + zval **link, **result, **newpasswd; + ldap_linkdata *ld; + LDAPMessage *ldap_result; + struct berval lnewpasswd; + int rc, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ", &link, &result, &newpasswd) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, result, -1, "ldap result", le_result); + + rc = ldap_parse_passwd(ld->link, ldap_result, &lnewpasswd); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse passwd modify extended operation result: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + + zval_dtor(*newpasswd); + if (lnewpasswd.bv_len == 0) { + ZVAL_EMPTY_STRING(*newpasswd); + } else { + ZVAL_STRINGL(*newpasswd, lnewpasswd.bv_val, lnewpasswd.bv_len, 1); + ldap_memfree(lnewpasswd.bv_val); + } + + RETURN_TRUE; +} +#else +/* TODO: implement based on ldap_parse_exop() */ +/* }}} */ +#endif + +#ifdef HAVE_LDAP_PARSE_WHOAMI +/* {{{ proto bool ldap_parse_exop_whoami(resource link, resource result, string authzid) + Extract information from whoami extended operation result (a Work in Progress) */ +PHP_FUNCTION(ldap_parse_exop_whoami) +{ + zval **link, **result, **authzid; + ldap_linkdata *ld; + LDAPMessage *ldap_result; + struct berval *lauthzid; + int rc, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ", &link, &result, &authzid) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, result, -1, "ldap result", le_result); + + rc = ldap_parse_whoami(ld->link, ldap_result, &lauthzid ); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse whoami extended operation result: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + + zval_dtor(*authzid); + if (lauthzid == NULL) { + ZVAL_EMPTY_STRING(*authzid); + } else { + ZVAL_STRINGL(*authzid, lauthzid->bv_val, lauthzid->bv_len, 1); + ldap_memfree(lauthzid->bv_val); + ldap_memfree(lauthzid); + } + RETURN_TRUE; +} +#else +/* TODO: implement based on ldap_parse_extended_result() */ +/* }}} */ +#endif +/* }}} */ +#endif + /* {{{ proto resource ldap_first_reference(resource link, resource result) Return first reference */ PHP_FUNCTION(ldap_first_reference) { @@ -2184,60 +2890,742 @@ } /* }}} */ #endif -/* {{{ arginfo */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0) - ZEND_ARG_INFO(0, hostname) - ZEND_ARG_INFO(0, port) -#ifdef HAVE_ORALDAP - ZEND_ARG_INFO(0, wallet) - ZEND_ARG_INFO(0, wallet_passwd) - ZEND_ARG_INFO(0, authmode) -#endif -ZEND_END_ARG_INFO() +/* {{{ Extended operations, Pierangelo Masarati */ +#ifdef HAVE_LDAP_EXTENDED_OPERATION_S +/* {{{ proto ? ldap_exop(resource link, string reqoid [, string reqdata [, string retoid [, string retdata]]]) + Extended operation */ +PHP_FUNCTION(ldap_exop) +{ + zval **link, **reqoid, **reqdata, **retoid, **retdata; + char *lreqoid, *lretoid = NULL; + struct berval lreqdata, *lretdata = NULL; + ldap_linkdata *ld; + LDAP *ldap; + LDAPMessage *ldap_res; + int rc, msgid, myargcount = ZEND_NUM_ARGS(); + /* int reqoid_len, reqdata_len, retdata_len, retoid_len, retdat_len; */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_resource, 0, 0, 1) - ZEND_ARG_INFO(0, link_identifier) -ZEND_END_ARG_INFO() + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ|ZZZ", &link, &reqoid, &reqdata, &retoid, &retdata) != SUCCESS) { + WRONG_PARAM_COUNT; + } -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind, 0, 0, 1) - ZEND_ARG_INFO(0, link_identifier) - ZEND_ARG_INFO(0, bind_rdn) - ZEND_ARG_INFO(0, bind_password) -ZEND_END_ARG_INFO() + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ldap = ld->link; + } -#ifdef HAVE_LDAP_SASL -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sasl_bind, 0, 0, 1) - ZEND_ARG_INFO(0, link) - ZEND_ARG_INFO(0, binddn) - ZEND_ARG_INFO(0, password) - ZEND_ARG_INFO(0, sasl_mech) - ZEND_ARG_INFO(0, sasl_realm) - ZEND_ARG_INFO(0, sasl_authz_id) - ZEND_ARG_INFO(0, props) -ZEND_END_ARG_INFO() -#endif + switch (myargcount) { + case 5: + case 4: + case 3: + convert_to_string_ex(reqdata); + lreqdata.bv_val = Z_STRVAL_PP(reqdata); + lreqdata.bv_len = Z_STRLEN_PP(reqdata); + /* fallthru */ + case 2: + convert_to_string_ex(reqoid); + lreqoid = Z_STRVAL_PP(reqoid); + } + + if (myargcount > 3) { + /* synchronous call */ + rc = ldap_extended_operation_s(ld->link, lreqoid, + lreqdata.bv_len > 0 ? &lreqdata: NULL, + NULL, + NULL, + &lretoid, + myargcount > 4 ? &lretdata : NULL ); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Extended operation %s failed: %s (%d)", lreqoid, ldap_err2string(rc), rc); + RETURN_FALSE; + } -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_read, 0, 0, 3) - ZEND_ARG_INFO(0, link_identifier) - ZEND_ARG_INFO(0, base_dn) - ZEND_ARG_INFO(0, filter) - ZEND_ARG_INFO(0, attributes) - ZEND_ARG_INFO(0, attrsonly) - ZEND_ARG_INFO(0, sizelimit) - ZEND_ARG_INFO(0, timelimit) - ZEND_ARG_INFO(0, deref) -ZEND_END_ARG_INFO() + /* Reverse -> fall through */ + switch (myargcount) { + case 5: + /* use arg #4 as the data returned by the server */ + zval_dtor(*retdata); + if (lretdata == NULL) { + ZVAL_EMPTY_STRING(*retdata); + } else { + ZVAL_STRINGL(*retdata, lretdata->bv_val, lretdata->bv_len, 1); + ldap_memfree(lretdata->bv_val); + ldap_memfree(lretdata); + } + case 4: + zval_dtor(*retoid); + if (lretoid == NULL) { + ZVAL_EMPTY_STRING(*retoid); + } else { + ZVAL_STRING(*retoid, lretoid, 1); + ldap_memfree(lretoid); + } + } -ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_list, 0, 0, 3) - ZEND_ARG_INFO(0, link_identifier) - ZEND_ARG_INFO(0, base_dn) - ZEND_ARG_INFO(0, filter) - ZEND_ARG_INFO(0, attributes) - ZEND_ARG_INFO(0, attrsonly) - ZEND_ARG_INFO(0, sizelimit) - ZEND_ARG_INFO(0, timelimit) + RETURN_TRUE; + } + + /* asynchronous call */ + rc = ldap_extended_operation(ld->link, lreqoid, + lreqdata.bv_len > 0 ? &lreqdata: NULL, + NULL, NULL, &msgid); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Extended operation %s failed: %s (%d)", lreqoid, ldap_err2string(rc), rc); + RETURN_FALSE; + } + + rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); + if (rc == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Extended operation %s failed", lreqoid); + RETURN_FALSE; + } + + /* return a PHP control object */ + array_init(return_value); + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); +} +/* }}} */ + +#ifdef HAVE_LDAP_PASSWD_S +/* {{{ proto ? ldap_exop_passwd(resource link [, string user [, string oldpw [, string newpw [, string newpasswd ]]]]) + Passwd modify extended operation */ +PHP_FUNCTION(ldap_exop_passwd) +{ + zval **link, **user, **newpw, **oldpw, **newpasswd; + struct berval luser, loldpw, lnewpw, lnewpasswd; + ldap_linkdata *ld; + LDAP *ldap; + LDAPMessage *ldap_res; + int rc, msgid, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ZZZZ", &link, &user, &oldpw, &newpw, &newpasswd) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ldap = ld->link; + } + + luser.bv_len = 0; + loldpw.bv_len = 0; + lnewpw.bv_len = 0; + + switch (myargcount) { + case 5: + case 4: + convert_to_string_ex(newpw); + lnewpw.bv_val = Z_STRVAL_PP(newpw); + lnewpw.bv_len = Z_STRLEN_PP(newpw); + + case 3: + convert_to_string_ex(oldpw); + loldpw.bv_val = Z_STRVAL_PP(oldpw); + loldpw.bv_len = Z_STRLEN_PP(oldpw); + + case 2: + convert_to_string_ex(user); + luser.bv_val = Z_STRVAL_PP(user); + luser.bv_len = Z_STRLEN_PP(user); + } + + if (myargcount > 4 || lnewpw.bv_len > 0) { + /* synchronous call */ + rc = ldap_passwd_s(ld->link, &luser, + loldpw.bv_len > 0 ? &loldpw : NULL, + /* loldpw.bv_len > 0 ? &loldpw : NULL, */ + lnewpw.bv_len > 0 ? &lnewpw : NULL, + &lnewpasswd, NULL, NULL); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + if (myargcount > 4) { + zval_dtor(*newpasswd); + if (lnewpasswd.bv_len == 0) { + ZVAL_EMPTY_STRING(*newpasswd); + } else { + ZVAL_STRINGL(*newpasswd, lnewpasswd.bv_val, lnewpasswd.bv_len, 1); + } + } + + ldap_memfree(lnewpasswd.bv_val); + + RETURN_TRUE; + } + + /* asynchronous call */ + rc = ldap_passwd(ld->link, &luser, + loldpw.bv_len > 0 ? &loldpw : NULL, + lnewpw.bv_len > 0 ? &lnewpw : NULL, + NULL, NULL, &msgid); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); + if (rc == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passwd modify extended operation failed"); + RETURN_FALSE; + } + + /* return a PHP control object */ + array_init(return_value); + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); +} +#else +/* TODO: implement based on ldap_extended_operation_s() */ +/* }}} */ +#endif + +#ifdef HAVE_LDAP_WHOAMI_S +/* {{{ proto bool ldap_exop_whoami(resource link [, string authzid]) + Whoami extended operation */ +PHP_FUNCTION(ldap_exop_whoami) +{ + zval **link, **authzid; + struct berval *lauthzid; + ldap_linkdata *ld; + LDAP *ldap; + LDAPMessage *ldap_res; + int rc, msgid, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z", &link, &authzid) == FAILURE) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ldap = ld->link; + } + + if (myargcount == 2) { + /* synchronous call */ + rc = ldap_whoami_s(ld->link, &lauthzid, NULL, NULL); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Whoami extended operation failed: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + zval_dtor(*authzid); + if (lauthzid == NULL) { + ZVAL_EMPTY_STRING(*authzid); + } else { + ZVAL_STRINGL(*authzid, lauthzid->bv_val, lauthzid->bv_len, 1); + ldap_memfree(lauthzid->bv_val); + ldap_memfree(lauthzid); + } + + RETURN_TRUE; + } + + /* asynchronous call */ + rc = ldap_whoami(ld->link, NULL, NULL, &msgid); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Whoami extended operation failed: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); + if (rc == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Whoami extended operation failed"); + RETURN_FALSE; + } + + /* return a PHP control object */ + array_init(return_value); + + ZEND_REGISTER_RESOURCE(return_value, ldap_res, le_result); +} +#else +/* TODO: implement based on ldap_extended_operation_s() */ +#endif +/* }}} */ +#endif +/* }}} */ + +/* {{{ LDAP controls encoding/decoding, Pierangelo Masarati */ +/* {{{ php_set_no_value_server_ctrl + */ +void php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAMETERS, const char *oid, const char *msg) +{ + zval **link, **iscritical; + ldap_linkdata *ld; + LDAP *ldap; + LDAPControl ctrl = { 0 }, *ctrlsp[2]; + int rc, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z", &link, &iscritical) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ldap = ld->link; + } + + if (myargcount == 2) { + convert_to_boolean_ex(iscritical); + ctrl.ldctl_iscritical = Z_BVAL_PP(iscritical); + } + + ctrl.ldctl_oid = (char *)oid; + + if (ldap) { + /* directly set the option */ + ctrlsp[0] = &ctrl; + ctrlsp[1] = NULL; + + rc = ldap_set_option(ldap, LDAP_OPT_SERVER_CONTROLS, ctrlsp); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set %s control: %s (%d)", msg, ldap_err2string(rc), rc); + RETURN_FALSE; + } + + } else { + /* return a PHP control object */ + array_init(return_value); + + add_assoc_string(return_value, "oid", ctrl.ldctl_oid, 1); + if (ctrl.ldctl_iscritical) { + add_assoc_bool(return_value, "iscritical", ctrl.ldctl_iscritical); + } + } + + RETURN_TRUE; +} +/* }}} */ + +#ifdef LDAP_CONTROL_MANAGEDSAIT +/* {{{ proto bool ldap_ctrl_manageDSAit(resource link [, bool iscritical]) + Inject manageDSAit control */ +PHP_FUNCTION(ldap_ctrl_manageDSAit) +{ + php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_CONTROL_MANAGEDSAIT, "manageDSAit"); +} +/* }}} */ +#endif + +#ifdef LDAP_CONTROL_PAGEDRESULTS +/* {{{ proto bool ldap_ctrl_paged_results(resource link, int pagesize [, bool iscritical [, string cookie]]) + Inject paged results control*/ +PHP_FUNCTION(ldap_ctrl_paged_results) +{ + zval **link, **pagesize, **iscritical, **cookie; + int lpagesize = 0; + struct berval lcookie = { 0, NULL }; + ldap_linkdata *ld; + LDAP *ldap; + BerElement *ber = NULL; + LDAPControl ctrl, *ctrlsp[2]; + int rc, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ|ZZ", &link, &pagesize, &iscritical, &cookie) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ldap = ld->link; + } + + ber = ber_alloc_t(LBER_USE_DER); + if (ber == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER encoding resources for paged results control"); + RETURN_FALSE; + } + + ctrl.ldctl_iscritical = 0; + + switch (myargcount) { + case 4: + convert_to_string_ex(cookie); + lcookie.bv_val = Z_STRVAL_PP(cookie); + lcookie.bv_len = Z_STRLEN_PP(cookie); + /* fallthru */ + case 3: + convert_to_boolean_ex(iscritical); + ctrl.ldctl_iscritical = Z_BVAL_PP(iscritical); + /* fallthru */ + } + convert_to_long_ex(pagesize); + lpagesize = Z_LVAL_PP(pagesize); + + ber_printf(ber, "{iO}", lpagesize, &lcookie ); + rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 ); + if ( rc == -1 ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to BER encode paged results control"); + RETURN_FALSE; + } + + ctrl.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; + + if (ldap) { + /* directly set the option */ + ctrlsp[0] = &ctrl; + ctrlsp[1] = NULL; + + rc = ldap_set_option(ldap, LDAP_OPT_SERVER_CONTROLS, ctrlsp); + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set paged results control: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + } else { + /* return a PHP control object */ + array_init(return_value); + + add_assoc_string(return_value, "oid", ctrl.ldctl_oid, 1); + if ( ctrl.ldctl_value.bv_len ) { + add_assoc_stringl(return_value, "value", ctrl.ldctl_value.bv_val, ctrl.ldctl_value.bv_len, 1); + } + if (ctrl.ldctl_iscritical) { + add_assoc_bool(return_value, "iscritical", ctrl.ldctl_iscritical); + } + } + + if (ber != NULL) { + ber_free(ber, 1); + } +} +/* }}} */ + +/* {{{ proto bool ldap_ctrl_paged_results_resp(resource link, resource result [, string cookie [, int estimated]]) + Extract paged results control response */ +PHP_FUNCTION(ldap_ctrl_paged_results_resp) +{ + zval **link, **result, **cookie, **estimated; + struct berval lcookie; + int lestimated; + ldap_linkdata *ld; + LDAPMessage *ldap_result; + LDAPControl **lserverctrls, *lctrl; + BerElement *ber; + ber_tag_t tag; + int rc, lerrcode, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ|ZZ", &link, &result, &cookie, &estimated) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, result, -1, "ldap result", le_result); + + rc = ldap_parse_result(ld->link, + ldap_result, + &lerrcode, + NULL, /* matcheddn */ + NULL, /* errmsg */ + NULL, /* referrals */ + &lserverctrls, + 0); + + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + if (lerrcode != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is: %s (%d)", ldap_err2string(lerrcode), lerrcode); + RETURN_FALSE; + } + + if (lserverctrls == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No server controls in result"); + RETURN_FALSE; + } + + lctrl = ldap_find_control(LDAP_CONTROL_PAGEDRESULTS, lserverctrls); + if (lctrl == NULL) { + ldap_controls_free(lserverctrls); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No paged results control response in result"); + RETURN_FALSE; + } + + ber = ber_init(&lctrl->ldctl_value); + if (ber == NULL) { + ldap_controls_free(lserverctrls); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER decoding resources for paged results control response"); + RETURN_FALSE; + } + + tag = ber_scanf(ber, "{io}", &lestimated, &lcookie ); + (void)ber_free(ber, 1); + + if (tag == LBER_ERROR) { + ldap_controls_free(lserverctrls); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode paged results control response"); + RETURN_FALSE; + } + + if (lestimated < 0) { + ldap_controls_free(lserverctrls); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid paged results control response value"); + RETURN_FALSE; + } + + ldap_controls_free(lserverctrls); + + if (myargcount == 4) { + zval_dtor(*estimated); + ZVAL_LONG(*estimated, lestimated); + } + + zval_dtor(*cookie); + if (lcookie.bv_len == 0) { + ZVAL_EMPTY_STRING(*cookie); + } else { + ZVAL_STRINGL(*cookie, lcookie.bv_val, lcookie.bv_len, 1); + } + ldap_memfree(lcookie.bv_val); + + RETURN_TRUE; +} +/* }}} */ +#endif + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +/* {{{ proto bool ldap_ctrl_ppolicy(resource link [, bool iscritical]) + Inject password policy control */ +PHP_FUNCTION(ldap_ctrl_ppolicy) +{ + php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_CONTROL_PASSWORDPOLICYREQUEST, "passwordPolicy"); +} +/* }}} */ + +/* {{{ proto bool ldap_ctrl_ppolicy_resp(resource link, resource result [, expire [, grace [, error[, errmsg]]]]) + Extract password policy control response */ +PHP_FUNCTION(ldap_ctrl_ppolicy_resp) +{ + zval **link, **result, **ppexpire, **ppgrace, **pperror, **pperrmsg; + int lexpire, lgrace; + LDAPPasswordPolicyError lerror; + ldap_linkdata *ld; + LDAPMessage *ldap_result; + LDAPControl **lserverctrls, *lctrl; + int rc, pperrmsg_len, lerrcode, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ|ZZZZ", &link, &result, &ppexpire, &ppgrace, &pperror, &pperrmsg) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); + ZEND_FETCH_RESOURCE(ldap_result, LDAPMessage *, result, -1, "ldap result", le_result); + + rc = ldap_parse_result(ld->link, + ldap_result, + &lerrcode, + NULL, /* matcheddn */ + NULL, /* errmsg */ + NULL, /* referrals */ + &lserverctrls, + 0); + + if (rc != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s (%d)", ldap_err2string(rc), rc); + RETURN_FALSE; + } + + if (lerrcode != LDAP_SUCCESS && lerrcode != LDAP_INVALID_CREDENTIALS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is: %s (%d)", ldap_err2string(lerrcode), lerrcode); + RETURN_FALSE; + } + + if (lserverctrls == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No server controls in result"); + RETURN_FALSE; + } + + lctrl = ldap_find_control(LDAP_CONTROL_PASSWORDPOLICYRESPONSE, lserverctrls); + if (lctrl == NULL) { + ldap_controls_free(lserverctrls); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No password policy control response in result"); + RETURN_FALSE; + } + + lerrcode = ldap_parse_passwordpolicy_control(ld->link, lctrl, &lexpire, &lgrace, &lerror); + ldap_controls_free(lserverctrls); + if (lerrcode != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse password policy control response: %s (%d)", ldap_err2string(lerrcode), lerrcode); + RETURN_FALSE; + } + + switch (myargcount) { + case 6: + zval_dtor(*pperrmsg); + ZVAL_STRING(*pperrmsg, (char *)ldap_passwordpolicy_err2txt(lerror), 1); + + case 5: + zval_dtor(*pperror); + ZVAL_LONG(*pperror, (long)lerror); + + case 4: + zval_dtor(*ppgrace); + ZVAL_LONG(*ppgrace, (long)lgrace); + } + + zval_dtor(*ppexpire); + ZVAL_LONG(*ppexpire, (long)lexpire); + + RETURN_TRUE; +} +/* }}} */ +#endif + +#ifdef LDAP_CONTROL_NOOP +/* {{{ proto bool ldap_ctrl_noop(resource link, bool iscritical) + Inject control*/ +PHP_FUNCTION(ldap_ctrl_noop) +{ + php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_CONTROL_NOOP, "no-op"); +} +/* }}} */ +#endif + +#ifdef LDAP_CONTROL_MANAGEDIT +/* {{{ proto bool ldap_ctrl_manageDIT(resource link [, bool iscritical]) + Inject control*/ +PHP_FUNCTION(ldap_ctrl_manageDIT) +{ + php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_CONTROL_MANAGEDIT, "manageDIT"); +} +/* }}} */ +#endif + +#ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY +/* {{{ proto bool ldap_ctrl_permissive_modify(resource link [, bool iscritical]) + Inject control*/ +PHP_FUNCTION(ldap_ctrl_permissive_modify) +{ + php_set_no_value_server_ctrl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_CONTROL_X_PERMISSIVE_MODIFY, "permissive modify"); +} +/* }}} */ +#endif +/* }}} */ + +#ifdef HAVE_LDAP_REFRESH +/* {{{ proto ? ldap_refresh(resource link , string dn , int ttl, [int *newttl]) + DDS refresh extended operation */ +PHP_FUNCTION(ldap_refresh) +{ + zval **link, **dn, **ttl, **newttl; + struct berval ldn; + ber_int_t lttl; + ber_int_t lnewttl; + ldap_linkdata *ld; + LDAP *ldap; + LDAPMessage *ldap_res; + int rc, msgid, myargcount = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &link, &dn, &ttl, &newttl) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + if (Z_TYPE_PP(link) == IS_NULL) { + ldap = NULL; + } else { + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, + link, -1, "ldap link", le_link); + ldap = ld->link; + } + + ldn.bv_len = 0; + convert_to_string_ex(dn); + ldn.bv_val = Z_STRVAL_PP(dn); + ldn.bv_len = Z_STRLEN_PP(dn); + + convert_to_long_ex(ttl); + lttl = (ber_int_t)Z_LVAL_PP(ttl); + + /* asynchronous call */ + rc = ldap_refresh_s(ld->link, &ldn, lttl, &lnewttl, NULL, NULL); + if (rc != LDAP_SUCCESS ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Refresh extended operation failed: %s (%d)", + ldap_err2string(rc), rc); + RETURN_FALSE; + } + + if (myargcount == 4) { + zval_dtor(*newttl); + ZVAL_LONG(*newttl, (long)lnewttl); + } + RETURN_TRUE; +} +#else +/* TODO: implement based on ldap_extended_operation_s() */ +/* }}} */ +#endif + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0) + ZEND_ARG_INFO(0, hostname) + ZEND_ARG_INFO(0, port) +#ifdef HAVE_ORALDAP + ZEND_ARG_INFO(0, wallet) + ZEND_ARG_INFO(0, wallet_passwd) + ZEND_ARG_INFO(0, authmode) +#endif +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_resource, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, bind_rdn) + ZEND_ARG_INFO(0, bind_password) +ZEND_END_ARG_INFO() + +#ifdef HAVE_LDAP_SASL +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sasl_bind, 0, 0, 1) + ZEND_ARG_INFO(0, link) + ZEND_ARG_INFO(0, binddn) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, sasl_mech) + ZEND_ARG_INFO(0, sasl_realm) + ZEND_ARG_INFO(0, sasl_authz_id) + ZEND_ARG_INFO(0, props) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_read, 0, 0, 3) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, base_dn) + ZEND_ARG_INFO(0, filter) + ZEND_ARG_INFO(0, attributes) + ZEND_ARG_INFO(0, attrsonly) + ZEND_ARG_INFO(0, sizelimit) + ZEND_ARG_INFO(0, timelimit) + ZEND_ARG_INFO(0, deref) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_list, 0, 0, 3) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, base_dn) + ZEND_ARG_INFO(0, filter) + ZEND_ARG_INFO(0, attributes) + ZEND_ARG_INFO(0, attrsonly) + ZEND_ARG_INFO(0, sizelimit) + ZEND_ARG_INFO(0, timelimit) ZEND_ARG_INFO(0, deref) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_search, 0, 0, 3) @@ -2411,8 +3799,9 @@ ZEND_ARG_INFO(1, errcode) ZEND_ARG_INFO(1, matcheddn) ZEND_ARG_INFO(1, errmsg) ZEND_ARG_INFO(1, referrals) + ZEND_ARG_INFO(1, serverctrls) ZEND_END_ARG_INFO() #endif #endif @@ -2431,8 +3820,40 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_8859_to_t61, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() #endif + +#ifdef HAVE_LDAP_EXTENDED_OPERATION_S +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop, 0, 0, 5) + ZEND_ARG_INFO(0, link) + ZEND_ARG_INFO(0, reqoid) + ZEND_ARG_INFO(1, reqdata) + ZEND_ARG_INFO(1, repoid) + ZEND_ARG_INFO(1, repdata) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_passwd, 0, 0, 5) + ZEND_ARG_INFO(0, link) + ZEND_ARG_INFO(1, user) + ZEND_ARG_INFO(1, oldpw) + ZEND_ARG_INFO(1, newpw) + ZEND_ARG_INFO(1, newpasswd) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_whoami, 0, 0, 2) + ZEND_ARG_INFO(0, link) + ZEND_ARG_INFO(1, authzid) +ZEND_END_ARG_INFO() +#endif + +#ifdef HAVE_LDAP_REFRESH +ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_refresh, 0, 0, 4) + ZEND_ARG_INFO(0, link) + ZEND_ARG_INFO(0, dn) + ZEND_ARG_INFO(1, ttl) + ZEND_ARG_INFO(0, newttl) +ZEND_END_ARG_INFO() +#endif /* }}} */ /* This is just a small subset of the functionality provided by the LDAP library. All the @@ -2494,10 +3915,23 @@ #endif #ifdef HAVE_LDAP_START_TLS_S PHP_FE(ldap_start_tls, arginfo_ldap_resource) #endif +#ifdef HAVE_LDAP_EXTENDED_OPERATION_S + PHP_FE(ldap_exop, + arginfo_ldap_exop) + PHP_FE(ldap_exop_passwd, + arginfo_ldap_exop_passwd) + PHP_FE(ldap_exop_whoami, + arginfo_ldap_exop_whoami) +#endif +#ifdef HAVE_LDAP_REFRESH + PHP_FE(ldap_refresh, + arginfo_ldap_refresh) +#endif #endif + #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC) PHP_FE(ldap_set_rebind_proc, arginfo_ldap_set_rebind_proc) #endif @@ -2505,8 +3939,33 @@ PHP_FE(ldap_t61_to_8859, arginfo_ldap_t61_to_8859) PHP_FE(ldap_8859_to_t61, arginfo_ldap_8859_to_t61) #endif +/* routines to handle standard track controls, Pierangelo Masarati */ +#ifdef LDAP_CONTROL_MANAGEDSAIT + PHP_FE(ldap_ctrl_manageDSAit, NULL) +#endif +#ifdef LDAP_CONTROL_PAGEDRESULTS + PHP_FE(ldap_ctrl_paged_results, NULL) /* fourth_arg_force_ref */ + PHP_FE(ldap_ctrl_paged_results_resp, NULL) /* arg3to4of4_force_ref */ +#endif +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + PHP_FE(ldap_ctrl_ppolicy, NULL) + PHP_FE(ldap_ctrl_ppolicy_resp, NULL) /* arg3to6of6_force_ref */ +#endif +#ifdef LDAP_CONTROL_NOOP + PHP_FE(ldap_ctrl_noop, NULL) +#endif +#ifdef LDAP_CONTROL_MANAGEDIT + PHP_FE(ldap_ctrl_manageDIT, NULL) +#endif +#ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY + PHP_FE(ldap_ctrl_permissive_modify, NULL) +#endif +/* end of ando mod */ + + + PHP_FE_END }; /* }}} */ @@ -2527,8 +3986,10 @@ STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ + + /* * Local variables: * tab-width: 4 * c-basic-offset: 4 --- ext/ldap/php_ldap.h.orig 2012-10-25 10:15:34.000000000 +0200 +++ ext/ldap/php_ldap.h 2012-10-25 10:15:34.000000000 +0200 @@ -36,8 +36,121 @@ PHP_MINIT_FUNCTION(ldap); PHP_MSHUTDOWN_FUNCTION(ldap); PHP_MINFO_FUNCTION(ldap); +#ifdef HAVE_LDAP_EXTENDED_OPERATION + +#endif + +#ifdef LDAP_CONTROL_MANAGEDSAIT +/* RFC 3296 */ +PHP_FUNCTION(ldap_ctrl_manageDSAit); +#endif +#ifdef LDAP_CONTROL_PROXY_AUTHZ +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_proxy_authz); +#endif +#ifdef LDAP_CONTROL_SUBENTRIES +/* RFC 3672 */ +PHP_FUNCTION(ldap_ctrl_subentries); +#endif +#ifdef LDAP_CONTROL_VALUESRETURNFILTER +/* RFC 3876 */ +PHP_FUNCTION(ldap_ctrl_vlv); +#endif +#ifdef LDAP_CONTROL_ASSERT +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_assert); +#endif +#ifdef LDAP_CONTROL_PRE_READ +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_preread); +PHP_FUNCTION(ldap_ctrl_postread); +#endif +#ifdef LDAP_CONTROL_SORTREQUEST +/* RFC 2891 */ +PHP_FUNCTION(ldap_ctrl_sort); +PHP_FUNCTION(ldap_ctrl_sort_resp); +#endif +#ifdef LDAP_CONTROL_PAGEDRESULTS +/* RFC 2696 */ +PHP_FUNCTION(ldap_ctrl_paged_results); +PHP_FUNCTION(ldap_ctrl_paged_results_resp); +#endif +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_ppolicy); +PHP_FUNCTION(ldap_ctrl_ppolicy_resp); +#endif +#ifdef LDAP_CONTROL_NOOP +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_noop); +#endif +#ifdef LDAP_CONTROL_NO_SUBORDINATES +/* don't know anything about it */ +#endif +#ifdef LDAP_CONTROL_MANAGEDIT +/* no spec; partially implemented in OpenLDAP 2.3 */ +PHP_FUNCTION(ldap_ctrl_manageDIT); +#endif +#ifdef LDAP_CONTROL_SLURP +/* don't know anything about it */ +#endif +#ifdef LDAP_CONTROL_VALSORT +/* <> */ +PHP_FUNCTION(ldap_ctrl_valsort); +#endif +#ifdef LDAP_CONTROL_SYNC +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_sync); +PHP_FUNCTION(ldap_ctrl_sync_state); +PHP_FUNCTION(ldap_ctrl_sync_done); +/* TODO: need to handle the SYNC intermediate response message (LDAPIRM) */ +#endif +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_chaining); +#endif +#ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_incremental_values); +#endif +#ifdef LDAP_CONTROL_X_DOMAIN_SCOPE +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_domain_scope); +#endif +#ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_permissive_modify); +#endif +#ifdef LDAP_CONTROL_X_SEARCH_OPTIONS +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_search_options); +#endif +#ifdef LDAP_CONTROL_X_TREE_DELETE +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_tree_delete); +#endif +#ifdef LDAP_CONTROL_X_EXTENDED_DN +/* MS Active Directory */ +PHP_FUNCTION(ldap_ctrl_extended_dn); +#endif +#ifdef LDAP_CONTROL_DUPENT +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_dupent); +PHP_FUNCTION(ldap_ctrl_dupent_resp); +PHP_FUNCTION(ldap_ctrl_dupent_done_resp); +#endif +#ifdef LDAP_CONTROL_PERSIST_REQUEST +/* ? */ +#endif +#ifdef LDAP_CONTROL_VLVREQUEST +/* (a Work in Progress) */ +PHP_FUNCTION(ldap_ctrl_vlv); +PHP_FUNCTION(ldap_ctrl_vlv_resp); +#endif + + ZEND_BEGIN_MODULE_GLOBALS(ldap) long num_links; long max_links; ZEND_END_MODULE_GLOBALS(ldap)