php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login

Patch ldap-ctrl-exop56.patch-manu_at_netbsd_dot_org for LDAP related Bug #36194

Patch version 2015-09-28 15:46 UTC

Return to Bug #36194 | Download this patch
Patch Revisions:

Developer: manu@netbsd.org

--- ext/ldap/ldap.c.orig	2015-03-19 01:19:30.000000000 +0100
+++ ext/ldap/ldap.c	2015-04-13 05:51:05.000000000 +0200
@@ -66,8 +66,13 @@
 #elif defined(HAVE_LDAP_SASL_SASL_H)
 #include <sasl/sasl.h>
 #endif
 
+/* XXX Not detected by configure... */
+#ifdef LDAP_EXOP_REFRESH
+#define HAVE_LDAP_REFRESH 1
+#endif
+
 #define PHP_LDAP_ESCAPE_FILTER 0x01
 #define PHP_LDAP_ESCAPE_DN     0x02
 
 typedef struct {
@@ -91,31 +96,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;
 
@@ -206,8 +226,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 */
+
 	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);
 
 	le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number);
@@ -291,15 +324,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 <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.
+
+			 */
+
+			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;
@@ -333,23 +527,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;
@@ -435,17 +647,33 @@
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Password contains a null byte");
 		RETURN_FALSE;
 	}
 
-	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;
@@ -568,8 +796,9 @@
 	_php_sasl_freedefs(ctx);
 }
 /* }}} */
 #endif /* HAVE_LDAP_SASL */
+/* }}} */
 
 /* {{{ proto bool ldap_unbind(resource link)
    Unbind from LDAP directory */
 PHP_FUNCTION(ldap_unbind)
@@ -1265,9 +1494,14 @@
 	for (i = 0; i<count; i++) {
 		add_index_string(return_value, i, ldap_value[i], 1);
 	}
 
+#ifdef LDAP_API_FEATURE_X_OPENLDAP
+	/* ldap_value_free() is deprecated */
+	ber_memvfree((void **)ldap_value);
+#else /* ! LDAP_API_FEATURE_X_OPENLDAP */
 	ldap_value_free(ldap_value);
+#endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
 }
 /* }}} */
 
 /* {{{ proto string ldap_dn2ufn(string dn)
@@ -1298,38 +1532,67 @@
 /* added to fix use of ldap_modify_add for doing an ldap_add, gerrit thomson. */
 #define PHP_LD_FULL_ADD 0xff
 /* {{{ php_ldap_do_modify
  */
-static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper)
+static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
 {
-	zval *link, *entry, **value, **ivalue;
+	zval *link, *entry, **value, **ivalue, **sctrls, **cctrls;
 	ldap_linkdata *ld;
 	char *dn;
 	LDAPMod **ldap_mods;
 	int i, j, num_attribs, num_values, dn_len;
 	int *num_berval;
 	char *attribute;
 	ulong index;
 	int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */
+	int rc, msgid, myargcount = ZEND_NUM_ARGS();
+	LDAPMessage *ldap_res;
+	LDAPControl	**lsctrls = NULL, **lcctrls = NULL;
+	if (ext) {
+		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsaZZ", &link, &dn, &dn_len, &entry, &sctrls, &cctrls) != SUCCESS)
+			WRONG_PARAM_COUNT;
+	} else { 
+		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &entry) != SUCCESS)
+			WRONG_PARAM_COUNT;
+	}
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &entry) != SUCCESS) {
-		return;
-	}	
+	if (Z_TYPE_PP(&entry) != IS_ARRAY) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected Array as the last element");
+		RETURN_FALSE;
+	}
 
-	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));
 
 	/* added by gerrit thomson to fix ldap_add using ldap_mod_add */
 	if (oper == PHP_LD_FULL_ADD) {
 		oper = LDAP_MOD_ADD;
 		is_full_add = 1;
 	}
 	/* end additional , gerrit thomson */
 
+	if (myargcount > 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;
@@ -1387,19 +1650,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;
+#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 {
-		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_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++) {
@@ -1412,46 +1734,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)
@@ -1875,38 +2208,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)
@@ -1938,59 +2342,233 @@
 	RETURN_TRUE;
 }
 /* }}} */
 
-#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP
-/* {{{ 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;
 
-			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;
+	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, 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);
+	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, "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:
@@ -2213,21 +2791,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);
@@ -2235,9 +2815,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;
@@ -2247,19 +2827,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);
@@ -2280,8 +2856,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 <draft-zeilenga-ldap-authzid> 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)
 {
@@ -2841,53 +3551,735 @@
 }
 /* }}} */
 #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)
+		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)
@@ -3090,8 +4482,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
 
@@ -3116,8 +4509,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 
@@ -3180,9 +4605,22 @@
 #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
@@ -3193,8 +4631,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 */
+
+
+
 #ifdef LDAP_CONTROL_PAGEDRESULTS
 	PHP_FE(ldap_control_paged_result,							arginfo_ldap_control_paged_result)
 	PHP_FE(ldap_control_paged_result_response,		arginfo_ldap_control_paged_result_response)
 #endif
@@ -3219,8 +4682,10 @@
 	STANDARD_MODULE_PROPERTIES_EX
 };
 /* }}} */
 
+
+
 /*
  * Local variables:
  * tab-width: 4
  * c-basic-offset: 4
--- ext/ldap/php_ldap.h.orig	2015-03-19 01:19:30.000000000 +0100
+++ ext/ldap/php_ldap.h	2015-04-13 05:48:10.000000000 +0200
@@ -28,16 +28,148 @@
 #endif
 
 #include <ldap.h>
 
+#define HAVE_3ARG_SETREBINDPROC
+#define HAVE_LDAP_ADD_EXT_S
+#define HAVE_LDAP_MODIFY_EXT_S
+#define HAVE_LDAP_COMPARE_EXT_S
+#define HAVE_LDAP_DELETE_EXT_S
+#define HAVE_LDAP_PARSE_EXTENDED_RESULT
+#define HAVE_LDAP_PARSE_PASSWD
+#define HAVE_LDAP_PARSE_WHOAMI
+#define HAVE_LDAP_EXTENDED_OPERATION_S
+#define HAVE_LDAP_PASSWD_S
+#define HAVE_LDAP_WHOAMI_S
+#define HAVE_LDAP_REFRESH
+#define HAVE_LDAP_SASL
+#define HAVE_LDAP_EXTENDED_OPERATION_S
+#define HAVE_LDAP_REFRESH
+#define HAVE_LDAP_EXTENDED_OPERATION_S
+#define HAVE_LDAP_REFRESH
+#define HAVE_LDAP_EXTENDED_OPERATION
+
 extern zend_module_entry ldap_module_entry;
 #define ldap_module_ptr &ldap_module_entry
 
 /* LDAP functions */
 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
+/* <draft-weltman-ldapv3-proxy> (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
+/* <draft-zeilenga-ldap-assert> (a Work in Progress) */
+PHP_FUNCTION(ldap_ctrl_assert);
+#endif
+#ifdef LDAP_CONTROL_PRE_READ
+/* <draft-zeilenga-ldap-readentry> (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
+/* <draft-behera-ldap-password-policy> (a Work in Progress) */
+PHP_FUNCTION(ldap_ctrl_ppolicy);
+PHP_FUNCTION(ldap_ctrl_ppolicy_resp);
+#endif
+#ifdef LDAP_CONTROL_NOOP
+/* <draft-zeilenga-ldap-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
+/* <draft-zeilenga-ldup-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
+/* <draft-sermersheim-ldap-chaining> (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
+/* <draft-ietf-ldapext-ldapv3-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
+/* <draft-ietf-ldapext-ldapv3-vlv> (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)
 
PHP Copyright © 2001-2023 The PHP Group
All rights reserved.
Last updated: Fri Jan 27 14:03:50 2023 UTC