On Wed, 2005-11-09 at 10:03 +0200, Jani Taskinen wrote: > Anyway, it might be the time to cleanup the API of this extension > and make it simpler. It's pretty confusing as it is. > The returned arrays are also weird with their "count" fields and such.
OK, Jani. I've removed those "count" stuff; also, I noted that I forgot things like declaring the new arg to ldap_parse_result() to be passed by reference, and few more details which I fixed. I also removed all the occurrences of the ldap_*value*_len substitutions. This is in <php-ldap-result-controls.2.txt>. I've also added a tentative <php-ldap-known-controls.0.txt> that partially implements encoding and decoding the pagedResults control (I use it as a benchmark because it goes on the request and on the response). If you find this interface useful, I'd extend it to most of the standard track controls we support in OpenLDAP. As a C programmer, I prefer to provide a nice and clean high level PHP interface to known controls in addition to access to the raw encoding for skilled developers. In my view, the API should be, for each control: a) a ldap_ctrl_<name>() call to encode it; b) a(n optional) ldap_ctrl_<name>_resp() call to decode the related response, if the control requires any. Furthermore, the (a) call: a.1) when passed a valid LDAP handler, should set the control using the LDAP-level ldap_set_option() API; a.2) otherwise, it could return a PHP array containing the OID, the value and the criticality flag, ready to be passed to the PHP-level ldap_set_option(); this would allow to incrementally build controls together. The (b) call should return all the requested fields contained in the control response. The pagedResults calls contained in the second patch should provide a clear example, although the second functionality of the ldap_ctrl_paged_results() call is not implemented yet. Its usage is exemplified in the attached <pagedResults.php> (to test it, you only need to run OpenLDAP 2.3 test003 with "./run -k test003" from the tests/ directory; the "-k" will keep the server alive after populating it with the right stuff). This second patch is clearly a work in progress, I don't expect it to be considered for inclusion in its current state; I'd rather appreciate some feedback while I keep developing it. p. Ing. Pierangelo Masarati Responsabile Open Solution SysNet s.n.c. Via Dossi, 8 - 27100 Pavia - ITALIA http://www.sys-net.it ------------------------------------------ Office: +39.02.23998309 Mobile: +39.333.4963172 Email: [EMAIL PROTECTED] ------------------------------------------
Index: ext/ldap/ldap.c =================================================================== RCS file: /repository/php-src/ext/ldap/ldap.c,v retrieving revision 1.162 diff -u -r1.162 ldap.c --- ext/ldap/ldap.c 22 Aug 2005 12:22:08 -0000 1.162 +++ ext/ldap/ldap.c 10 Nov 2005 17:25:16 -0000 @@ -80,13 +80,22 @@ ZEND_DECLARE_MODULE_GLOBALS(ldap) static - ZEND_BEGIN_ARG_INFO(arg3to6of6_force_ref, 0) + ZEND_BEGIN_ARG_INFO(arg3to4of4_force_ref, 0) + ZEND_ARG_PASS_INFO(0) + ZEND_ARG_PASS_INFO(0) + ZEND_ARG_PASS_INFO(1) + ZEND_ARG_PASS_INFO(1) + ZEND_END_ARG_INFO(); + +static + ZEND_BEGIN_ARG_INFO(arg3to7of7_force_ref, 0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(1) ZEND_ARG_PASS_INFO(1) ZEND_ARG_PASS_INFO(1) ZEND_ARG_PASS_INFO(1) + ZEND_ARG_PASS_INFO(1) ZEND_END_ARG_INFO(); static int le_link, le_result, le_result_entry, le_ber_entry; @@ -144,10 +153,10 @@ PHP_FE(ldap_first_reference, NULL) PHP_FE(ldap_next_reference, NULL) #ifdef HAVE_LDAP_PARSE_REFERENCE - PHP_FE(ldap_parse_reference, third_arg_force_ref) + PHP_FE(ldap_parse_reference, arg3to4of4_force_ref) #endif #ifdef HAVE_LDAP_PARSE_RESULT - PHP_FE(ldap_parse_result, arg3to6of6_force_ref) + PHP_FE(ldap_parse_result, arg3to7of7_force_ref) #endif #ifdef HAVE_LDAP_START_TLS_S PHP_FE(ldap_start_tls, NULL) @@ -191,7 +211,9 @@ { ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr; - ldap_unbind_s(ld->link); + /* ldap_unbind_s() is deprecated; + * the distinction between ldap_unbind() and ldap_unbind_s() is moot */ + ldap_unbind_ext(ld->link, NULL, NULL); #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC) if (ld->rebindproc != NULL) { zval_dtor(ld->rebindproc); @@ -215,6 +237,103 @@ efree(entry); } +static int _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; + } + +#if ando_0 + /* It has been suggested by Jani Taskinen to avoid the "count" entry; + * it was added for consistency with entry data, where attributes and + * attribute values have one. + */ + add_assoc_long(*referrals, "count", num_referrals); +#endif + + return num_referrals; +} + +static int _parse_server_controls_resp(LDAPControl ***lserverctrlsp, zval **serverctrls) +{ + int num_serverctrls = 0; + + if (*lserverctrlsp != NULL) { + int error = 0; + LDAPControl **ctrlp = *lserverctrlsp; + + 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); + } +#if ando_0 + /* As per <draft-ietf-ldapbis-protocol>: + * + * 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. + + */ + if ((*ctrlp)->ldctl_iscritical) { + add_assoc_bool(ctrlval, "iscritical", (*ctrlp)->ldctl_iscritical); + } +#endif + + add_next_index_zval(*serverctrls, ctrlval); + + num_serverctrls++; + ctrlp++; + } + ldap_controls_free(*lserverctrlsp); + *lserverctrlsp = NULL; + + if (error) { + /* ... */ + return -1; + } + } + +#if ando_0 + /* It has been suggested by Jani Taskinen to avoid the "count" entry; + * it was added for consistency with entry data, where attributes and + * attribute values have one. + */ + if (num_serverctrls != -1) { + add_assoc_long(*serverctrls, "count", num_serverctrls); + } +#endif + + return num_serverctrls; +} + /* {{{ PHP_INI_BEGIN */ PHP_INI_BEGIN() @@ -370,7 +489,11 @@ { char *host = NULL; int hostlen; +#ifdef LDAP_API_FEATURE_X_OPENLDAP + long port = LDAP_PORT; +#else long port = 389; /* Default port */ +#endif #ifdef HAVE_ORALDAP char *wallet, *walletpasswd; int walletlen, walletpasswdlen; @@ -406,17 +529,33 @@ 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 */ + { + 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:%ld", host ? host : "", port ? port : LDAP_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 ldap = ldap_open(host, port); @@ -479,7 +618,21 @@ 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 { @@ -1363,7 +1518,11 @@ add_index_string(return_value, i, ldap_value[i], 1); } +#ifdef LDAP_API_FEATURE_X_OPENLDAP + ber_memvfree((void **)ldap_value); +#else ldap_value_free(ldap_value); +#endif } /* }}} */ @@ -1933,14 +2092,15 @@ 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(); - if (myargcount < 3 || myargcount > 6 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals) == FAILURE) { + if (myargcount < 3 || myargcount > 7 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) == FAILURE) { WRONG_PARAM_COUNT; } @@ -1951,7 +2111,7 @@ 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)); @@ -1963,17 +2123,15 @@ /* 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); + _parse_server_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); - } + _parse_referrals_resp(&lreferrals, referrals); case 5: zval_dtor(*errmsg); if (lerrmsg == NULL) { @@ -2057,32 +2215,38 @@ Extract information from reference entry */ PHP_FUNCTION(ldap_parse_reference) { - zval **link, **result_entry, **referrals; + zval **link, **result_entry, **referrals, **serverctrls; ldap_linkdata *ld; ldap_resultentry *resultentry; - char **lreferrals, **refp; + char **lreferrals; + LDAPControl **lserverctrls; + int myargcount = ZEND_NUM_ARGS(); - if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &link, &result_entry, &referrals) == FAILURE) { + if (myargcount < 3 || myargcount > 4 || zend_get_parameters_ex(4, &link, &result_entry, &referrals, &serverctrls) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, result_entry, -1, "ldap result entry", le_result_entry); - if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, NULL /* &serverctrls */, 0) != LDAP_SUCCESS) { + if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, &lserverctrls, 0) != LDAP_SUCCESS) { RETURN_FALSE; } - 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); + + /* Reverse -> fall through */ + switch (myargcount) { + case 4: + /* use arg #4 as the array of controls returned by the server */ + zval_dtor(*serverctrls); + array_init(*serverctrls); + _parse_server_controls_resp(&lserverctrls, serverctrls); + case 3: + zval_dtor(*referrals); + array_init(*referrals); + _parse_referrals_resp(&lreferrals, referrals); } + RETURN_TRUE; } /* }}} */
Index: ext/ldap/ldap.c =================================================================== RCS file: /repository/php-src/ext/ldap/ldap.c,v retrieving revision 1.162 diff -u -r1.162 ldap.c --- ext/ldap/ldap.c 22 Aug 2005 12:22:08 -0000 1.162 +++ ext/ldap/ldap.c 10 Nov 2005 17:25:16 -0000 @@ -163,6 +172,17 @@ PHP_FE(ldap_8859_to_t61, NULL) #endif +/* routines to handle standard track controls, Pierangelo Masarati */ +#ifdef LDAP_CONTROL_PAGEDRESULTS + PHP_FE(ldap_ctrl_paged_results, fourth_arg_force_ref) + PHP_FE(ldap_ctrl_paged_results_resp, arg3to4of4_force_ref) +#endif /* LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + PHP_FE(ldap_ctrl_ppolicy, NULL) + PHP_FE(ldap_ctrl_ppolicy_resp, NULL) +#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ +/* end of ando */ + {NULL, NULL, NULL} }; /* }}} */ @@ -2297,6 +2461,193 @@ /* }}} */ #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 (myargcount < 1 || myargcount > 4 || zend_get_parameters_ex(myargcount, &link, &pagesize, &iscritical, &cookie) == 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; + } + + ber = ber_alloc_t(LBER_USE_DER); + if (ber == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER encoding resources"); + RETURN_FALSE; + } + + 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 */ + case 2: + 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 pagedResults control"); + RETURN_FALSE; + } + + if (ldap) { + /* directly set the option */ + ctrl.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; + + 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 pagedResults control: %s", ldap_err2string(rc)); + RETURN_FALSE; + } + + } else { + /* TODO: return a PHP control object */ + } + + 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; + BerElement *ber; + ber_tag_t tag; + int rc, i, lerrcode, myargcount = ZEND_NUM_ARGS(); + + if (myargcount < 3 || myargcount > 4 || zend_get_parameters_ex(myargcount, &link, &result, &cookie, &estimated) == FAILURE) { + 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", ldap_err2string(rc)); + RETURN_FALSE; + } + + if (lerrcode != LDAP_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is: %s", ldap_err2string(lerrcode)); + RETURN_FALSE; + } + + if (lserverctrls == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No server controls in result"); + RETURN_FALSE; + } + + for (i = 0; lserverctrls[i] != NULL; i++) { + if (strcmp(lserverctrls[i]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) { + break; + } + } + + if (lserverctrls[i] == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No paged results control in result"); + RETURN_FALSE; + } + + ber = ber_init(&lserverctrls[i]->ldctl_value); + if (ber == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to alloc BER decoding resources"); + RETURN_FALSE; + } + + tag = ber_scanf(ber, "{io}", &lestimated, &lcookie ); + (void)ber_free(ber, 1); + + if (tag == LBER_ERROR) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to decode paged results control response"); + RETURN_FALSE; + } + + if (lestimated < 0) { + 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) + Inject password policy control */ +PHP_FUNCTION(ldap_ctrl_ppolicy) +{ +} +/* }}} */ + +/* {{{ proto bool ldap_ctrl_ppolicy_resp(resource link, resource result, string cookie, int estimated) + Extract password policy control response */ +PHP_FUNCTION(ldap_ctrl_ppolicy_resp) +{ +} +/* }}} */ +#endif + /* * Local variables: * tab-width: 4 Index: ext/ldap/php_ldap.h =================================================================== RCS file: /repository/php-src/ext/ldap/php_ldap.h,v retrieving revision 1.32 diff -u -r1.32 php_ldap.h --- ext/ldap/php_ldap.h 3 Aug 2005 14:07:23 -0000 1.32 +++ ext/ldap/php_ldap.h 10 Nov 2005 17:25:19 -0000 @@ -97,6 +97,15 @@ PHP_FUNCTION(ldap_8859_to_t61); #endif +#ifdef LDAP_CONTROL_PAGEDRESULTS +PHP_FUNCTION(ldap_ctrl_paged_results); +PHP_FUNCTION(ldap_ctrl_paged_results_resp); +#endif +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +PHP_FUNCTION(ldap_ctrl_ppolicy); +PHP_FUNCTION(ldap_ctrl_ppolicy_resp); +#endif + ZEND_BEGIN_MODULE_GLOBALS(ldap) long num_links; long max_links;
pagedResults.php
Description: application/php
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php