summaryrefslogtreecommitdiff
path: root/databases/php-ldap
diff options
context:
space:
mode:
authormanu <manu@pkgsrc.org>2017-07-19 16:20:07 +0000
committermanu <manu@pkgsrc.org>2017-07-19 16:20:07 +0000
commit727d3a92a9521845040e1af2345531e4c192306d (patch)
treefa3210d61351370b531e3d8238372ce27284c434 /databases/php-ldap
parentbc26021b8096f792f1b6f3e4f9fec8be56df1b7a (diff)
downloadpkgsrc-727d3a92a9521845040e1af2345531e4c192306d.tar.gz
Add php70 and php71 patches for the exop option
The LDAP controls and extended operations (EXOP) has been merged upstream with some modifications. The patches backports upstream code to php70 and php71, with additionnal EXOP REFRESH support that is missing for now.
Diffstat (limited to 'databases/php-ldap')
-rw-r--r--databases/php-ldap/Makefile3
-rw-r--r--databases/php-ldap/files/ldap-ctrl-exop70.patch561
-rw-r--r--databases/php-ldap/files/ldap-ctrl-exop71.patch561
3 files changed, 1124 insertions, 1 deletions
diff --git a/databases/php-ldap/Makefile b/databases/php-ldap/Makefile
index 749126b79d7..5d835e60ba2 100644
--- a/databases/php-ldap/Makefile
+++ b/databases/php-ldap/Makefile
@@ -1,6 +1,7 @@
-# $NetBSD: Makefile,v 1.27 2016/04/02 09:05:22 taca Exp $
+# $NetBSD: Makefile,v 1.28 2017/07/19 16:20:07 manu Exp $
MODNAME= ldap
+PKGREVISION= 1
CATEGORIES+= databases
COMMENT= PHP extension for LDAP database access
diff --git a/databases/php-ldap/files/ldap-ctrl-exop70.patch b/databases/php-ldap/files/ldap-ctrl-exop70.patch
new file mode 100644
index 00000000000..9f0ca42b05a
--- /dev/null
+++ b/databases/php-ldap/files/ldap-ctrl-exop70.patch
@@ -0,0 +1,561 @@
+From upstream:
+git diff 035a27cbc63d87a6acc761ce51109bcf47f9c27b \
+ ed8bfcc6ea60c9d5b8c6b10f9e5175237fe28751 -- ext/ldap/ldap.c \
+ ext/ldap/config.m4 ext/ldap/config.w32
+
+Addditionnal uncommitted yet modification for ldap_exop_refresh
+(NB: previous patch had ldap_refresh with different prototype)
+
+--- ext/ldap/config.m4.orig
++++ ext/ldap/config.m4
+@@ -203,9 +203,9 @@
+ 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 ldap_control_find])
++ AC_CHECK_FUNCS([ldap_parse_result ldap_parse_reference ldap_start_tls_s ldap_control_find ldap_parse_extended_result ldap_extended_operation ldap_extended_operation_s ldap_passwd_s ldap_whoami_s ldap_refresh_s])
+
+ dnl
+ dnl SASL check
+ dnl
+--- ext/ldap/config.w32.orig
++++ ext/ldap/config.w32
+@@ -22,8 +22,14 @@
+ AC_DEFINE('HAVE_LDAP_SASL', 1);
+ AC_DEFINE('HAVE_LDAP_SASL_SASL_H', 1);
+ AC_DEFINE('LDAP_DEPRECATED', 1);
+ AC_DEFINE('HAVE_LDAP_CONTROL_FIND', 1);
++ AC_DEFINE('HAVE_LDAP_PARSE_EXTENDED_RESULT', 1);
++ AC_DEFINE('HAVE_LDAP_EXTENDED_OPERATION_S', 1);
++ AC_DEFINE('HAVE_LDAP_PASSWD_S', 1);
++ AC_DEFINE('HAVE_LDAP_WHOAMI_S', 1);
++ AC_DEFINE('HAVE_LDAP_REFRESH_S', 1);
++ AC_DEFINE('HAVE_LDAP_EXTENDED_OPERATION', 1);
+
+ } else {
+ WARNING("ldap not enabled; libraries and headers not found");
+ }
+--- ext/ldap/ldap.c.orig
++++ ext/ldap/ldap.c
+@@ -239,8 +239,86 @@
+
+ REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT | CONST_CS);
+
++#ifdef HAVE_LDAP_EXTENDED_OPERATION_S
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_START_TLS", LDAP_EXOP_START_TLS, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_MODIFY_PASSWD", LDAP_EXOP_MODIFY_PASSWD, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_REFRESH", LDAP_EXOP_REFRESH, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_WHO_AM_I", LDAP_EXOP_WHO_AM_I, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_TURN", LDAP_EXOP_TURN, CONST_PERSISTENT | CONST_CS);
++#endif
++
++/* LDAP Controls */
++/* standard track controls */
++#ifdef LDAP_CONTROL_MANAGEDSAIT
++ /* RFC 3296 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_MANAGEDSAIT", LDAP_CONTROL_MANAGEDSAIT, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_PROXY_AUTHZ
++ /* RFC 4370 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PROXY_AUTHZ", LDAP_CONTROL_PROXY_AUTHZ, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SUBENTRIES
++ /* RFC 3672 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SUBENTRIES", LDAP_CONTROL_SUBENTRIES, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_VALUESRETURNFILTER
++ /* RFC 3876 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VALUESRETURNFILTER", LDAP_CONTROL_VALUESRETURNFILTER, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_ASSERT
++ /* RFC 4528 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_ASSERT", LDAP_CONTROL_ASSERT, CONST_PERSISTENT | CONST_CS);
++ /* RFC 4527 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PRE_READ", LDAP_CONTROL_PRE_READ, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_POST_READ", LDAP_CONTROL_POST_READ, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SORTREQUEST
++ /* RFC 2891 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTREQUEST", LDAP_CONTROL_SORTREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTRESPONSE", LDAP_CONTROL_SORTRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++/* non-standard track controls */
++#ifdef LDAP_CONTROL_PAGEDRESULTS
++ /* RFC 2696 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PAGEDRESULTS", LDAP_CONTROL_PAGEDRESULTS, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_AUTHZID_REQUEST
++ /* RFC 3829 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_REQUEST", LDAP_CONTROL_AUTHZID_REQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_RESPONSE", LDAP_CONTROL_AUTHZID_RESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SYNC
++ /* LDAP Content Synchronization Operation -- RFC 4533 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC", LDAP_CONTROL_SYNC, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_STATE", LDAP_CONTROL_SYNC_STATE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_DONE", LDAP_CONTROL_SYNC_DONE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_DONTUSECOPY
++ /* LDAP Don't Use Copy Control (RFC 6171) */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_DONTUSECOPY", LDAP_CONTROL_DONTUSECOPY, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
++ /* Password policy Controls */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYREQUEST", LDAP_CONTROL_PASSWORDPOLICYREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYRESPONSE", LDAP_CONTROL_PASSWORDPOLICYRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
++ /* MS Active Directory controls */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_INCREMENTAL_VALUES", LDAP_CONTROL_X_INCREMENTAL_VALUES, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_DOMAIN_SCOPE", LDAP_CONTROL_X_DOMAIN_SCOPE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_PERMISSIVE_MODIFY", LDAP_CONTROL_X_PERMISSIVE_MODIFY, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_SEARCH_OPTIONS", LDAP_CONTROL_X_SEARCH_OPTIONS, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_TREE_DELETE", LDAP_CONTROL_X_TREE_DELETE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_EXTENDED_DN", LDAP_CONTROL_X_EXTENDED_DN, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
++ /* LDAP VLV */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVREQUEST", LDAP_CONTROL_VLVREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVRESPONSE", LDAP_CONTROL_VLVRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++
+ 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);
+
+@@ -2184,11 +2262,41 @@
+ zval_ptr_dtor(retval);
+ ZVAL_STRING(retval, val);
+ ldap_memfree(val);
+ } break;
+-/* options not implemented
+ case LDAP_OPT_SERVER_CONTROLS:
+ case LDAP_OPT_CLIENT_CONTROLS:
++ {
++ zval tmp1;
++ int num_entries;
++ LDAPControl **ctrls = NULL, **ctrlp;
++
++ if (ldap_get_option(ld->link, option, &ctrls) || ctrls == NULL) {
++ if (ctrls) {
++ ldap_memfree(ctrls);
++ }
++ RETURN_FALSE;
++ }
++
++ zval_ptr_dtor(retval);
++ array_init(retval);
++ num_entries = 0;
++ ctrlp = ctrls;
++ while (*ctrlp != NULL)
++ {
++ array_init(&tmp1);
++ add_assoc_string(&tmp1, "oid", (*ctrlp)->ldctl_oid);
++ add_assoc_bool(&tmp1, "iscritical", ((*ctrlp)->ldctl_iscritical != 0));
++ if ((*ctrlp)->ldctl_value.bv_len) {
++ add_assoc_stringl(&tmp1, "value", (*ctrlp)->ldctl_value.bv_val, (*ctrlp)->ldctl_value.bv_len);
++ }
++ zend_hash_index_update(Z_ARRVAL_P(retval), num_entries, &tmp1);
++ num_entries++;
++ ctrlp++;
++ }
++ ldap_controls_free(ctrls);
++ } break;
++/* options not implemented
+ case LDAP_OPT_API_INFO:
+ case LDAP_OPT_API_FEATURE_INFO:
+ */
+ default:
+@@ -2455,8 +2563,69 @@
+ }
+ /* }}} */
+ #endif
+
++/* {{{ Extended operation response parsing, Pierangelo Masarati */
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++/* {{{ proto bool ldap_parse_exop(resource link, resource result [, string retdata [, string retoid]])
++ Extract information from extended operation result */
++PHP_FUNCTION(ldap_parse_exop)
++{
++ zval *link, *result, *retdata, *retoid;
++ ldap_linkdata *ld;
++ LDAPMessage *ldap_result;
++ char *lretoid;
++ struct berval *lretdata;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|z/z/", &link, &result, &retdata, &retoid) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ rc = ldap_parse_extended_result(ld->link, ldap_result,
++ myargcount > 3 ? &lretoid: NULL,
++ myargcount > 2 ? &lretdata: NULL,
++ 0);
++ if (rc != LDAP_SUCCESS) {
++ php_error_docref(NULL, E_WARNING, "Unable to parse extended operation result: %s", ldap_err2string(rc));
++ RETURN_FALSE;
++ }
++
++ /* Reverse -> fall through */
++ switch (myargcount) {
++ case 4:
++ zval_dtor(retoid);
++ if (lretoid == NULL) {
++ ZVAL_EMPTY_STRING(retoid);
++ } else {
++ ZVAL_STRING(retoid, lretoid);
++ ldap_memfree(lretoid);
++ }
++ case 3:
++ /* use arg #3 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);
++ ldap_memfree(lretdata->bv_val);
++ ldap_memfree(lretdata);
++ }
++ }
++ RETURN_TRUE;
++}
++/* }}} */
++#endif
++/* }}} */
++
+ /* {{{ proto resource ldap_first_reference(resource link, resource result)
+ Return first reference */
+ PHP_FUNCTION(ldap_first_reference)
+ {
+@@ -3035,8 +3204,245 @@
+ }
+ /* }}} */
+ #endif
+
++/* {{{ Extended operations, Pierangelo Masarati */
++#ifdef HAVE_LDAP_EXTENDED_OPERATION_S
++/* {{{ proto ? ldap_exop(resource link, string reqoid [, string reqdata [, string retdata [, string retoid]]])
++ Extended operation */
++PHP_FUNCTION(ldap_exop)
++{
++ zval *link, *reqoid, *reqdata, *retdata, *retoid;
++ char *lreqoid, *lretoid = NULL;
++ struct berval lreqdata, *lretdata = NULL;
++ ldap_linkdata *ld;
++ LDAPMessage *ldap_res;
++ int rc, msgid, myargcount = ZEND_NUM_ARGS();
++ /* int reqoid_len, reqdata_len, retdata_len, retoid_len, retdat_len; */
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz|zz/z/", &link, &reqoid, &reqdata, &retdata, &retoid) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ switch (myargcount) {
++ case 5:
++ case 4:
++ case 3:
++ convert_to_string_ex(reqdata);
++ lreqdata.bv_val = Z_STRVAL_P(reqdata);
++ lreqdata.bv_len = Z_STRLEN_P(reqdata);
++ /* fallthru */
++ case 2:
++ convert_to_string_ex(reqoid);
++ lreqoid = Z_STRVAL_P(reqoid);
++ }
++
++ if (myargcount > 3) {
++ /* synchronous call */
++ rc = ldap_extended_operation_s(ld->link, lreqoid,
++ lreqdata.bv_len > 0 ? &lreqdata: NULL,
++ NULL,
++ NULL,
++ myargcount > 4 ? &lretoid : NULL,
++ &lretdata );
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Extended operation %s failed: %s (%d)", lreqoid, ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ /* Reverse -> fall through */
++ switch (myargcount) {
++ case 5:
++ zval_dtor(retoid);
++ if (lretoid == NULL) {
++ ZVAL_EMPTY_STRING(retoid);
++ } else {
++ ZVAL_STRING(retoid, lretoid);
++ ldap_memfree(lretoid);
++ }
++ 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);
++ ldap_memfree(lretdata->bv_val);
++ ldap_memfree(lretdata);
++ }
++ }
++
++ 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, 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, E_WARNING, "Extended operation %s failed", lreqoid);
++ RETURN_FALSE;
++ }
++
++ /* return a PHP control object */
++ RETVAL_RES(zend_register_resource(ldap_res, le_result));
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_PASSWD_S
++/* {{{ proto bool|string ldap_exop_passwd(resource link [, string user [, string oldpw [, string newpw ]]])
++ Passwd modify extended operation */
++PHP_FUNCTION(ldap_exop_passwd)
++{
++ zval *link, *user, *newpw, *oldpw;
++ struct berval luser, loldpw, lnewpw, lgenpasswd;
++ ldap_linkdata *ld;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|zzz", &link, &user, &oldpw, &newpw) == FAILURE) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ luser.bv_len = 0;
++ loldpw.bv_len = 0;
++ lnewpw.bv_len = 0;
++
++ switch (myargcount) {
++ case 4:
++ convert_to_string_ex(newpw);
++ lnewpw.bv_val = Z_STRVAL_P(newpw);
++ lnewpw.bv_len = Z_STRLEN_P(newpw);
++
++ case 3:
++ convert_to_string_ex(oldpw);
++ loldpw.bv_val = Z_STRVAL_P(oldpw);
++ loldpw.bv_len = Z_STRLEN_P(oldpw);
++
++ case 2:
++ convert_to_string_ex(user);
++ luser.bv_val = Z_STRVAL_P(user);
++ luser.bv_len = Z_STRLEN_P(user);
++ }
++
++ /* synchronous call */
++ rc = ldap_passwd_s(ld->link, &luser,
++ loldpw.bv_len > 0 ? &loldpw : NULL,
++ lnewpw.bv_len > 0 ? &lnewpw : NULL,
++ &lgenpasswd, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ if (lnewpw.bv_len == 0) {
++ if (lgenpasswd.bv_len == 0) {
++ RETVAL_EMPTY_STRING();
++ } else {
++ RETVAL_STRINGL(lgenpasswd.bv_val, lgenpasswd.bv_len);
++ }
++ } else {
++ RETURN_TRUE;
++ }
++
++ ldap_memfree(lgenpasswd.bv_val);
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_WHOAMI_S
++/* {{{ proto bool|string ldap_exop_whoami(resource link)
++ Whoami extended operation */
++PHP_FUNCTION(ldap_exop_whoami)
++{
++ zval *link;
++ struct berval *lauthzid;
++ ldap_linkdata *ld;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) == FAILURE) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ /* synchronous call */
++ rc = ldap_whoami_s(ld->link, &lauthzid, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Whoami extended operation failed: %s (%d)", ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ if (lauthzid == NULL) {
++ RETVAL_EMPTY_STRING();
++ } else {
++ RETVAL_STRINGL(lauthzid->bv_val, lauthzid->bv_len);
++ ldap_memfree(lauthzid->bv_val);
++ ldap_memfree(lauthzid);
++ }
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_REFRESH_S
++/* {{{ proto int ldap_exop_refresh(resource link , string dn , int ttl)
++ DDS refresh extended operation */
++PHP_FUNCTION(ldap_exop_refresh)
++{
++ zval *link, *dn, *ttl;
++ struct berval ldn;
++ ber_int_t lttl;
++ ber_int_t newttl;
++ ldap_linkdata *ld;
++ int rc;
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rzz", &link, &dn, &ttl) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ ldn.bv_len = 0;
++ convert_to_string_ex(dn);
++ ldn.bv_val = Z_STRVAL_P(dn);
++ ldn.bv_len = Z_STRLEN_P(dn);
++
++ convert_to_long_ex(ttl);
++ lttl = (ber_int_t)Z_LVAL_P(ttl);
++
++ rc = ldap_refresh_s(ld->link, &ldn, lttl, &newttl, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING,
++ "Refresh extended operation failed: %s (%d)",
++ ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ RETURN_LONG(newttl);
++}
++/* }}} */
++#endif
++
++/* }}} */
++
+ /* {{{ arginfo */
+ ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0)
+ ZEND_ARG_INFO(0, hostname)
+ ZEND_ARG_INFO(0, port)
+@@ -3310,8 +3716,50 @@
+ 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(0, reqdata)
++ ZEND_ARG_INFO(1, retdata)
++ ZEND_ARG_INFO(1, retoid)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_PASSWD_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_passwd, 0, 0, 4)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, user)
++ ZEND_ARG_INFO(0, oldpw)
++ ZEND_ARG_INFO(0, newpw)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_WHOAMI_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_whoami, 0, 0, 1)
++ ZEND_ARG_INFO(0, link)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_REFRESH_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_refresh, 0, 0, 3)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, dn)
++ ZEND_ARG_INFO(0, ttl)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_exop, 0, 0, 4)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, result)
++ ZEND_ARG_INFO(1, retdata)
++ ZEND_ARG_INFO(1, retoid)
++ZEND_END_ARG_INFO()
++#endif
+ /* }}} */
+
+ /*
+ This is just a small subset of the functionality provided by the LDAP library. All the
+@@ -3374,8 +3822,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)
++#endif
++#ifdef HAVE_LDAP_PASSWD_S
++ PHP_FE(ldap_exop_passwd, arginfo_ldap_exop_passwd)
++#endif
++#ifdef HAVE_LDAP_WHOAMI_S
++ PHP_FE(ldap_exop_whoami, arginfo_ldap_exop_whoami)
++#endif
++#ifdef HAVE_LDAP_REFRESH_S
++ PHP_FE(ldap_exop_refresh, arginfo_ldap_exop_refresh)
++#endif
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++ PHP_FE(ldap_parse_exop, arginfo_ldap_parse_exop)
++#endif
+ #endif
+
+ #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
+ PHP_FE(ldap_set_rebind_proc, arginfo_ldap_set_rebind_proc)
diff --git a/databases/php-ldap/files/ldap-ctrl-exop71.patch b/databases/php-ldap/files/ldap-ctrl-exop71.patch
new file mode 100644
index 00000000000..112aad548f7
--- /dev/null
+++ b/databases/php-ldap/files/ldap-ctrl-exop71.patch
@@ -0,0 +1,561 @@
+From upstream:
+git diff 035a27cbc63d87a6acc761ce51109bcf47f9c27b \
+ ed8bfcc6ea60c9d5b8c6b10f9e5175237fe28751 -- ext/ldap/ldap.c \
+ ext/ldap/config.m4 ext/ldap/config.w32
+
+Addditionnal uncommitted yet modification for ldap_exop_refresh
+(NB: previous patch had ldap_refresh with different prototype)
+
+--- ext/ldap/config.m4.orig
++++ ext/ldap/config.m4
+@@ -203,9 +203,9 @@
+ 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 ldap_control_find])
++ AC_CHECK_FUNCS([ldap_parse_result ldap_parse_reference ldap_start_tls_s ldap_control_find ldap_parse_extended_result ldap_extended_operation ldap_extended_operation_s ldap_passwd_s ldap_whoami_s ldap_refresh_s])
+
+ dnl
+ dnl SASL check
+ dnl
+--- ext/ldap/config.w32.orig
++++ ext/ldap/config.w32
+@@ -20,8 +20,14 @@
+ AC_DEFINE('HAVE_LDAP_SASL', 1);
+ AC_DEFINE('HAVE_LDAP_SASL_SASL_H', 1);
+ AC_DEFINE('LDAP_DEPRECATED', 1);
+ AC_DEFINE('HAVE_LDAP_CONTROL_FIND', 1);
++ AC_DEFINE('HAVE_LDAP_PARSE_EXTENDED_RESULT', 1);
++ AC_DEFINE('HAVE_LDAP_EXTENDED_OPERATION_S', 1);
++ AC_DEFINE('HAVE_LDAP_PASSWD_S', 1);
++ AC_DEFINE('HAVE_LDAP_WHOAMI_S', 1);
++ AC_DEFINE('HAVE_LDAP_REFRESH_S', 1);
++ AC_DEFINE('HAVE_LDAP_EXTENDED_OPERATION', 1);
+
+ } else {
+ WARNING("ldap not enabled; libraries and headers not found");
+ }
+--- ext/ldap/ldap.c.orig
++++ ext/ldap/ldap.c
+@@ -288,8 +288,86 @@
+
+ REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT | CONST_CS);
+
++#ifdef HAVE_LDAP_EXTENDED_OPERATION_S
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_START_TLS", LDAP_EXOP_START_TLS, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_MODIFY_PASSWD", LDAP_EXOP_MODIFY_PASSWD, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_REFRESH", LDAP_EXOP_REFRESH, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_WHO_AM_I", LDAP_EXOP_WHO_AM_I, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_EXOP_TURN", LDAP_EXOP_TURN, CONST_PERSISTENT | CONST_CS);
++#endif
++
++/* LDAP Controls */
++/* standard track controls */
++#ifdef LDAP_CONTROL_MANAGEDSAIT
++ /* RFC 3296 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_MANAGEDSAIT", LDAP_CONTROL_MANAGEDSAIT, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_PROXY_AUTHZ
++ /* RFC 4370 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PROXY_AUTHZ", LDAP_CONTROL_PROXY_AUTHZ, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SUBENTRIES
++ /* RFC 3672 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SUBENTRIES", LDAP_CONTROL_SUBENTRIES, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_VALUESRETURNFILTER
++ /* RFC 3876 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VALUESRETURNFILTER", LDAP_CONTROL_VALUESRETURNFILTER, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_ASSERT
++ /* RFC 4528 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_ASSERT", LDAP_CONTROL_ASSERT, CONST_PERSISTENT | CONST_CS);
++ /* RFC 4527 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PRE_READ", LDAP_CONTROL_PRE_READ, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_POST_READ", LDAP_CONTROL_POST_READ, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SORTREQUEST
++ /* RFC 2891 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTREQUEST", LDAP_CONTROL_SORTREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTRESPONSE", LDAP_CONTROL_SORTRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++/* non-standard track controls */
++#ifdef LDAP_CONTROL_PAGEDRESULTS
++ /* RFC 2696 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PAGEDRESULTS", LDAP_CONTROL_PAGEDRESULTS, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_AUTHZID_REQUEST
++ /* RFC 3829 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_REQUEST", LDAP_CONTROL_AUTHZID_REQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_RESPONSE", LDAP_CONTROL_AUTHZID_RESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_SYNC
++ /* LDAP Content Synchronization Operation -- RFC 4533 */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC", LDAP_CONTROL_SYNC, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_STATE", LDAP_CONTROL_SYNC_STATE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_DONE", LDAP_CONTROL_SYNC_DONE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_DONTUSECOPY
++ /* LDAP Don't Use Copy Control (RFC 6171) */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_DONTUSECOPY", LDAP_CONTROL_DONTUSECOPY, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
++ /* Password policy Controls */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYREQUEST", LDAP_CONTROL_PASSWORDPOLICYREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYRESPONSE", LDAP_CONTROL_PASSWORDPOLICYRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
++ /* MS Active Directory controls */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_INCREMENTAL_VALUES", LDAP_CONTROL_X_INCREMENTAL_VALUES, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_DOMAIN_SCOPE", LDAP_CONTROL_X_DOMAIN_SCOPE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_PERMISSIVE_MODIFY", LDAP_CONTROL_X_PERMISSIVE_MODIFY, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_SEARCH_OPTIONS", LDAP_CONTROL_X_SEARCH_OPTIONS, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_TREE_DELETE", LDAP_CONTROL_X_TREE_DELETE, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_EXTENDED_DN", LDAP_CONTROL_X_EXTENDED_DN, CONST_PERSISTENT | CONST_CS);
++#endif
++#ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
++ /* LDAP VLV */
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVREQUEST", LDAP_CONTROL_VLVREQUEST, CONST_PERSISTENT | CONST_CS);
++ REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVRESPONSE", LDAP_CONTROL_VLVRESPONSE, CONST_PERSISTENT | CONST_CS);
++#endif
++
+ 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);
+
+@@ -2270,11 +2348,41 @@
+ zval_ptr_dtor(retval);
+ ZVAL_STRING(retval, val);
+ ldap_memfree(val);
+ } break;
+-/* options not implemented
+ case LDAP_OPT_SERVER_CONTROLS:
+ case LDAP_OPT_CLIENT_CONTROLS:
++ {
++ zval tmp1;
++ int num_entries;
++ LDAPControl **ctrls = NULL, **ctrlp;
++
++ if (ldap_get_option(ld->link, option, &ctrls) || ctrls == NULL) {
++ if (ctrls) {
++ ldap_memfree(ctrls);
++ }
++ RETURN_FALSE;
++ }
++
++ zval_ptr_dtor(retval);
++ array_init(retval);
++ num_entries = 0;
++ ctrlp = ctrls;
++ while (*ctrlp != NULL)
++ {
++ array_init(&tmp1);
++ add_assoc_string(&tmp1, "oid", (*ctrlp)->ldctl_oid);
++ add_assoc_bool(&tmp1, "iscritical", ((*ctrlp)->ldctl_iscritical != 0));
++ if ((*ctrlp)->ldctl_value.bv_len) {
++ add_assoc_stringl(&tmp1, "value", (*ctrlp)->ldctl_value.bv_val, (*ctrlp)->ldctl_value.bv_len);
++ }
++ zend_hash_index_update(Z_ARRVAL_P(retval), num_entries, &tmp1);
++ num_entries++;
++ ctrlp++;
++ }
++ ldap_controls_free(ctrls);
++ } break;
++/* options not implemented
+ case LDAP_OPT_API_INFO:
+ case LDAP_OPT_API_FEATURE_INFO:
+ */
+ default:
+@@ -2569,8 +2677,69 @@
+ }
+ /* }}} */
+ #endif
+
++/* {{{ Extended operation response parsing, Pierangelo Masarati */
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++/* {{{ proto bool ldap_parse_exop(resource link, resource result [, string retdata [, string retoid]])
++ Extract information from extended operation result */
++PHP_FUNCTION(ldap_parse_exop)
++{
++ zval *link, *result, *retdata, *retoid;
++ ldap_linkdata *ld;
++ LDAPMessage *ldap_result;
++ char *lretoid;
++ struct berval *lretdata;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|z/z/", &link, &result, &retdata, &retoid) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ rc = ldap_parse_extended_result(ld->link, ldap_result,
++ myargcount > 3 ? &lretoid: NULL,
++ myargcount > 2 ? &lretdata: NULL,
++ 0);
++ if (rc != LDAP_SUCCESS) {
++ php_error_docref(NULL, E_WARNING, "Unable to parse extended operation result: %s", ldap_err2string(rc));
++ RETURN_FALSE;
++ }
++
++ /* Reverse -> fall through */
++ switch (myargcount) {
++ case 4:
++ zval_dtor(retoid);
++ if (lretoid == NULL) {
++ ZVAL_EMPTY_STRING(retoid);
++ } else {
++ ZVAL_STRING(retoid, lretoid);
++ ldap_memfree(lretoid);
++ }
++ case 3:
++ /* use arg #3 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);
++ ldap_memfree(lretdata->bv_val);
++ ldap_memfree(lretdata);
++ }
++ }
++ RETURN_TRUE;
++}
++/* }}} */
++#endif
++/* }}} */
++
+ /* {{{ proto resource ldap_first_reference(resource link, resource result)
+ Return first reference */
+ PHP_FUNCTION(ldap_first_reference)
+ {
+@@ -3156,8 +3325,245 @@
+ }
+ /* }}} */
+ #endif
+
++/* {{{ Extended operations, Pierangelo Masarati */
++#ifdef HAVE_LDAP_EXTENDED_OPERATION_S
++/* {{{ proto ? ldap_exop(resource link, string reqoid [, string reqdata [, string retdata [, string retoid]]])
++ Extended operation */
++PHP_FUNCTION(ldap_exop)
++{
++ zval *link, *reqoid, *reqdata, *retdata, *retoid;
++ char *lreqoid, *lretoid = NULL;
++ struct berval lreqdata, *lretdata = NULL;
++ ldap_linkdata *ld;
++ LDAPMessage *ldap_res;
++ int rc, msgid, myargcount = ZEND_NUM_ARGS();
++ /* int reqoid_len, reqdata_len, retdata_len, retoid_len, retdat_len; */
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz|zz/z/", &link, &reqoid, &reqdata, &retdata, &retoid) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ switch (myargcount) {
++ case 5:
++ case 4:
++ case 3:
++ convert_to_string_ex(reqdata);
++ lreqdata.bv_val = Z_STRVAL_P(reqdata);
++ lreqdata.bv_len = Z_STRLEN_P(reqdata);
++ /* fallthru */
++ case 2:
++ convert_to_string_ex(reqoid);
++ lreqoid = Z_STRVAL_P(reqoid);
++ }
++
++ if (myargcount > 3) {
++ /* synchronous call */
++ rc = ldap_extended_operation_s(ld->link, lreqoid,
++ lreqdata.bv_len > 0 ? &lreqdata: NULL,
++ NULL,
++ NULL,
++ myargcount > 4 ? &lretoid : NULL,
++ &lretdata );
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Extended operation %s failed: %s (%d)", lreqoid, ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ /* Reverse -> fall through */
++ switch (myargcount) {
++ case 5:
++ zval_dtor(retoid);
++ if (lretoid == NULL) {
++ ZVAL_EMPTY_STRING(retoid);
++ } else {
++ ZVAL_STRING(retoid, lretoid);
++ ldap_memfree(lretoid);
++ }
++ 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);
++ ldap_memfree(lretdata->bv_val);
++ ldap_memfree(lretdata);
++ }
++ }
++
++ 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, 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, E_WARNING, "Extended operation %s failed", lreqoid);
++ RETURN_FALSE;
++ }
++
++ /* return a PHP control object */
++ RETVAL_RES(zend_register_resource(ldap_res, le_result));
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_PASSWD_S
++/* {{{ proto bool|string ldap_exop_passwd(resource link [, string user [, string oldpw [, string newpw ]]])
++ Passwd modify extended operation */
++PHP_FUNCTION(ldap_exop_passwd)
++{
++ zval *link, *user, *newpw, *oldpw;
++ struct berval luser, loldpw, lnewpw, lgenpasswd;
++ ldap_linkdata *ld;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|zzz", &link, &user, &oldpw, &newpw) == FAILURE) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ luser.bv_len = 0;
++ loldpw.bv_len = 0;
++ lnewpw.bv_len = 0;
++
++ switch (myargcount) {
++ case 4:
++ convert_to_string_ex(newpw);
++ lnewpw.bv_val = Z_STRVAL_P(newpw);
++ lnewpw.bv_len = Z_STRLEN_P(newpw);
++
++ case 3:
++ convert_to_string_ex(oldpw);
++ loldpw.bv_val = Z_STRVAL_P(oldpw);
++ loldpw.bv_len = Z_STRLEN_P(oldpw);
++
++ case 2:
++ convert_to_string_ex(user);
++ luser.bv_val = Z_STRVAL_P(user);
++ luser.bv_len = Z_STRLEN_P(user);
++ }
++
++ /* synchronous call */
++ rc = ldap_passwd_s(ld->link, &luser,
++ loldpw.bv_len > 0 ? &loldpw : NULL,
++ lnewpw.bv_len > 0 ? &lnewpw : NULL,
++ &lgenpasswd, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ if (lnewpw.bv_len == 0) {
++ if (lgenpasswd.bv_len == 0) {
++ RETVAL_EMPTY_STRING();
++ } else {
++ RETVAL_STRINGL(lgenpasswd.bv_val, lgenpasswd.bv_len);
++ }
++ } else {
++ RETURN_TRUE;
++ }
++
++ ldap_memfree(lgenpasswd.bv_val);
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_WHOAMI_S
++/* {{{ proto bool|string ldap_exop_whoami(resource link)
++ Whoami extended operation */
++PHP_FUNCTION(ldap_exop_whoami)
++{
++ zval *link;
++ struct berval *lauthzid;
++ ldap_linkdata *ld;
++ int rc, myargcount = ZEND_NUM_ARGS();
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) == FAILURE) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ /* synchronous call */
++ rc = ldap_whoami_s(ld->link, &lauthzid, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING, "Whoami extended operation failed: %s (%d)", ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ if (lauthzid == NULL) {
++ RETVAL_EMPTY_STRING();
++ } else {
++ RETVAL_STRINGL(lauthzid->bv_val, lauthzid->bv_len);
++ ldap_memfree(lauthzid->bv_val);
++ ldap_memfree(lauthzid);
++ }
++}
++/* }}} */
++#endif
++
++#ifdef HAVE_LDAP_REFRESH_S
++/* {{{ proto int ldap_exop_refresh(resource link , string dn , int ttl)
++ DDS refresh extended operation */
++PHP_FUNCTION(ldap_exop_refresh)
++{
++ zval *link, *dn, *ttl;
++ struct berval ldn;
++ ber_int_t lttl;
++ ber_int_t newttl;
++ ldap_linkdata *ld;
++ int rc;
++
++ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rzz", &link, &dn, &ttl) != SUCCESS) {
++ WRONG_PARAM_COUNT;
++ }
++
++ if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
++ RETURN_FALSE;
++ }
++
++ ldn.bv_len = 0;
++ convert_to_string_ex(dn);
++ ldn.bv_val = Z_STRVAL_P(dn);
++ ldn.bv_len = Z_STRLEN_P(dn);
++
++ convert_to_long_ex(ttl);
++ lttl = (ber_int_t)Z_LVAL_P(ttl);
++
++ rc = ldap_refresh_s(ld->link, &ldn, lttl, &newttl, NULL, NULL);
++ if (rc != LDAP_SUCCESS ) {
++ php_error_docref(NULL, E_WARNING,
++ "Refresh extended operation failed: %s (%d)",
++ ldap_err2string(rc), rc);
++ RETURN_FALSE;
++ }
++
++ RETURN_LONG(newttl);
++}
++/* }}} */
++#endif
++
++/* }}} */
++
+ /* {{{ arginfo */
+ ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0)
+ ZEND_ARG_INFO(0, hostname)
+ ZEND_ARG_INFO(0, port)
+@@ -3431,8 +3837,50 @@
+ 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(0, reqdata)
++ ZEND_ARG_INFO(1, retdata)
++ ZEND_ARG_INFO(1, retoid)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_PASSWD_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_passwd, 0, 0, 4)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, user)
++ ZEND_ARG_INFO(0, oldpw)
++ ZEND_ARG_INFO(0, newpw)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_WHOAMI_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_whoami, 0, 0, 1)
++ ZEND_ARG_INFO(0, link)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_REFRESH_S
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_refresh, 0, 0, 3)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, dn)
++ ZEND_ARG_INFO(0, ttl)
++ZEND_END_ARG_INFO()
++#endif
++
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_exop, 0, 0, 4)
++ ZEND_ARG_INFO(0, link)
++ ZEND_ARG_INFO(0, result)
++ ZEND_ARG_INFO(1, retdata)
++ ZEND_ARG_INFO(1, retoid)
++ZEND_END_ARG_INFO()
++#endif
+ /* }}} */
+
+ /*
+ This is just a small subset of the functionality provided by the LDAP library. All the
+@@ -3495,8 +3943,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)
++#endif
++#ifdef HAVE_LDAP_PASSWD_S
++ PHP_FE(ldap_exop_passwd, arginfo_ldap_exop_passwd)
++#endif
++#ifdef HAVE_LDAP_WHOAMI_S
++ PHP_FE(ldap_exop_whoami, arginfo_ldap_exop_whoami)
++#endif
++#ifdef HAVE_LDAP_REFRESH_S
++ PHP_FE(ldap_exop_refresh, arginfo_ldap_exop_refresh)
++#endif
++#ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
++ PHP_FE(ldap_parse_exop, arginfo_ldap_parse_exop)
++#endif
+ #endif
+
+ #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
+ PHP_FE(ldap_set_rebind_proc, arginfo_ldap_set_rebind_proc)