diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 7a089625aa48..84688fb91113 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -27,6 +27,7 @@ #include "php.h" #include "php_ini.h" #include "Zend/zend_attributes.h" +#include "Zend/zend_exceptions.h" #include @@ -90,7 +91,7 @@ typedef struct { ZEND_DECLARE_MODULE_GLOBALS(ldap) static PHP_GINIT_FUNCTION(ldap); -static zend_class_entry *ldap_link_ce, *ldap_result_ce, *ldap_result_entry_ce; +static zend_class_entry *ldap_link_ce, *ldap_exception_ce, *ldap_result_ce, *ldap_result_entry_ce; static zend_object_handlers ldap_link_object_handlers, ldap_result_object_handlers, ldap_result_entry_object_handlers; #ifdef COMPILE_DL_LDAP @@ -113,11 +114,6 @@ static zend_object *ldap_link_create_object(zend_class_entry *class_type) { return &intern->std; } -static zend_function *ldap_link_get_constructor(zend_object *object) { - zend_throw_error(NULL, "Cannot directly construct LDAP\\Connection, use ldap_connect() instead"); - return NULL; -} - static void ldap_link_free(ldap_linkdata *ld) { /* We use ldap_destroy rather than ldap_unbind here, because ldap_unbind @@ -430,6 +426,7 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT control_oid = zval_try_get_tmp_string(val, &control_oid_tmp); if (!control_oid) { + zend_value_error("%s(): Control oid must be a string", get_active_function_name()); return -1; } @@ -871,10 +868,11 @@ PHP_MINIT_FUNCTION(ldap) ldap_link_ce->create_object = ldap_link_create_object; ldap_link_ce->default_object_handlers = &ldap_link_object_handlers; + ldap_exception_ce = register_class_LDAP_LdapException(zend_ce_exception); + memcpy(&ldap_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); ldap_link_object_handlers.offset = offsetof(ldap_linkdata, std); ldap_link_object_handlers.free_obj = ldap_link_free_obj; - ldap_link_object_handlers.get_constructor = ldap_link_get_constructor; ldap_link_object_handlers.clone_obj = NULL; ldap_link_object_handlers.compare = zend_objects_not_comparable; @@ -954,6 +952,60 @@ PHP_MINFO_FUNCTION(ldap) } /* }}} */ +/* {{{ Creates new LDAP\Connection object */ +PHP_METHOD(LDAP_Connection, __construct) +{ + char *url = NULL; + size_t urllen = 0; + ldap_linkdata *ld; + LDAP *ldap = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &url, &urllen) != SUCCESS) { + RETURN_THROWS(); + } + + if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) { + zend_throw_error(NULL, "Too many open links (" ZEND_LONG_FMT ")", LDAPG(num_links)); + RETURN_THROWS(); + } + + { + int rc = LDAP_SUCCESS; + if (url && !ldap_is_ldap_url(url)) { + zend_argument_value_error(1, "is not a valid LDAP URI"); + RETURN_THROWS(); + } + +#ifdef LDAP_OPT_X_TLS_NEWCTX + if (LDAPG(tls_newctx) && url && !strncmp(url, "ldaps:", 6)) { + int val = 0; + + /* ensure all pending TLS options are applied in a new context */ + if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) { + zend_throw_error(NULL, "Could not create new security context"); + RETURN_THROWS(); + } + LDAPG(tls_newctx) = false; + } +#endif + ld = Z_LDAP_LINK_P(ZEND_THIS); + rc = ldap_initialize(&ldap, url); + + if (rc != LDAP_SUCCESS) { + zend_throw_error(NULL, "Could not create session handle: %s", ldap_err2string(rc)); + RETURN_THROWS(); + } + } + + if (ldap == NULL) { + RETURN_THROWS(); + } else { + LDAPG(num_links)++; + ld->link = ldap; + } +} +/* }}} */ + /* {{{ Connect to an LDAP server */ PHP_FUNCTION(ldap_connect) { @@ -1205,27 +1257,20 @@ PHP_FUNCTION(ldap_bind) } /* }}} */ -/* {{{ Bind to LDAP directory */ -PHP_FUNCTION(ldap_bind_ext) + +/* {{{ php_ldap_do_bind_ext */ +static void php_ldap_do_bind_ext(zval *link, char *ldap_bind_dn, size_t ldap_bind_dnlen, char* ldap_bind_pw, size_t ldap_bind_pwlen, HashTable *server_controls_ht, zval *return_value, char **exception_message) { - zval *link; - char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL; - size_t ldap_bind_dnlen, ldap_bind_pwlen; - HashTable *server_controls_ht = NULL; ldap_linkdata *ld; LDAPControl **lserverctrls = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|p!p!h!", &link, ldap_link_ce, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) { - RETURN_THROWS(); - } - ld = Z_LDAP_LINK_P(link); VERIFY_LDAP_LINK_CONNECTED(ld); if (server_controls_ht) { lserverctrls = php_ldap_controls_from_array(ld->link, server_controls_ht, 4); if (lserverctrls == NULL) { - RETVAL_FALSE; + spprintf(exception_message, 0, "Failed to parse controls"); goto cleanup; } } @@ -1242,16 +1287,14 @@ PHP_FUNCTION(ldap_bind_ext) rc = ldap_sasl_bind(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred, lserverctrls, NULL, &msgid); if (rc != LDAP_SUCCESS ) { - php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc); - RETVAL_FALSE; + spprintf(exception_message, 0, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc); goto cleanup; } LDAPMessage *ldap_res; rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); if (rc == -1) { - php_error_docref(NULL, E_WARNING, "Bind operation failed"); - RETVAL_FALSE; + spprintf(exception_message, 0, "Bind operation failed"); goto cleanup; } @@ -1270,6 +1313,50 @@ PHP_FUNCTION(ldap_bind_ext) } /* }}} */ + +/* {{{ Bind to LDAP directory */ +PHP_METHOD(LDAP_Connection, bind) +{ + char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL; + size_t ldap_bind_dnlen, ldap_bind_pwlen; + HashTable *server_controls_ht = NULL; + char *exception_message = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!p!h!", &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } + + php_ldap_do_bind_ext(ZEND_THIS, ldap_bind_dn, ldap_bind_dnlen, ldap_bind_pw, ldap_bind_pwlen, server_controls_ht, return_value, &exception_message); + if (exception_message) { + zend_throw_exception_ex(ldap_exception_ce, 0, "%s", exception_message); + efree(exception_message); + RETURN_THROWS(); + } +} +/* }}} */ + +/* {{{ Bind to LDAP directory */ +PHP_FUNCTION(ldap_bind_ext) +{ + zval *link; + char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL; + size_t ldap_bind_dnlen, ldap_bind_pwlen; + HashTable *server_controls_ht = NULL; + char *exception_message = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|p!p!h!", &link, ldap_link_ce, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } + + php_ldap_do_bind_ext(link, ldap_bind_dn, ldap_bind_dnlen, ldap_bind_pw, ldap_bind_pwlen, server_controls_ht, return_value, &exception_message); + if (exception_message) { + php_error_docref(NULL, E_WARNING, "%s", exception_message); + efree(exception_message); + RETVAL_FALSE; + } +} +/* }}} */ + #ifdef HAVE_LDAP_SASL typedef struct { char *mech; @@ -1404,6 +1491,22 @@ PHP_FUNCTION(ldap_sasl_bind) /* }}} */ #endif /* HAVE_LDAP_SASL */ +/* {{{ Unbind from LDAP directory */ +PHP_METHOD(LDAP_Connection, unbind) +{ + ldap_linkdata *ld; + + ZEND_PARSE_PARAMETERS_NONE(); + + ld = Z_LDAP_LINK_P(ZEND_THIS); + VERIFY_LDAP_LINK_CONNECTED(ld); + + ldap_link_free(ld); + + RETURN_TRUE; +} +/* }}} */ + /* {{{ Unbind from LDAP directory */ PHP_FUNCTION(ldap_unbind) { @@ -2270,8 +2373,15 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) size_t dn_len; bool is_full_add = false; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oph/|h!", &link, ldap_link_ce, &dn, &dn_len, &attributes_ht, &server_controls_ht) != SUCCESS) { - RETURN_THROWS(); + if (ZEND_IS_METHOD_CALL()) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ph/|h!", &dn, &dn_len, &attributes_ht, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } + link = ZEND_THIS; + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oph/|h!", &link, ldap_link_ce, &dn, &dn_len, &attributes_ht, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } } ld = Z_LDAP_LINK_P(link); @@ -2300,17 +2410,14 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) ZEND_HASH_FOREACH_STR_KEY_VAL(attributes_ht, attribute, attribute_values) { if (attribute == NULL) { zend_argument_value_error(3, "must be an associative array of attribute => values"); - RETVAL_FALSE; goto cleanup; } if (ZSTR_LEN(attribute) == 0) { zend_argument_value_error(3, "key must not be empty"); - RETVAL_FALSE; goto cleanup; } if (zend_str_has_nul_byte(attribute)) { zend_argument_value_error(3, "key must not contain any null bytes"); - RETVAL_FALSE; goto cleanup; } @@ -2326,7 +2433,7 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) if (Z_TYPE_P(attribute_values) != IS_ARRAY) { zend_string *value = php_ldap_try_get_ldap_value_from_zval(attribute_values); if (UNEXPECTED(value == NULL)) { - RETVAL_FALSE; + zend_argument_value_error(3, "attribute values must be either array or string"); goto cleanup; } ldap_mods[attribute_index]->mod_bvalues = safe_emalloc(2, sizeof(struct berval *), 0); @@ -2341,7 +2448,6 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) if (num_values == 0) { if (UNEXPECTED(oper == LDAP_MOD_ADD)) { zend_argument_value_error(3, "attribute \"%s\" must be a non-empty list of attribute values", ZSTR_VAL(attribute)); - RETVAL_FALSE; goto cleanup; } /* When we modify, we mean we delete the attribute */ @@ -2350,7 +2456,6 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) } if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(attribute_values))) { zend_argument_value_error(3, "attribute \"%s\" must be an array of attribute values with numeric keys", ZSTR_VAL(attribute)); - RETVAL_FALSE; goto cleanup; } @@ -2363,7 +2468,7 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(attribute_values), attribute_value) { zend_string *value = php_ldap_try_get_ldap_value_from_zval(attribute_value); if (UNEXPECTED(value == NULL)) { - RETVAL_FALSE; + zend_argument_value_error(3, "attribute values must be either array or string"); goto cleanup; } ldap_mods[attribute_index]->mod_bvalues[attribute_value_index] = (struct berval *) emalloc (sizeof(struct berval)); @@ -2397,13 +2502,21 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) ldap_status_code = ldap_add_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL); } if (ldap_status_code != LDAP_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(ldap_status_code)); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, ldap_status_code, "Add: %s", ldap_err2string(ldap_status_code)); + } else { + php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(ldap_status_code)); + RETVAL_FALSE; + } } else if (ext) { ldap_status_code = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); if (ldap_status_code == -1) { - php_error_docref(NULL, E_WARNING, "Add operation failed"); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, 0, "Add operation failed"); + } else { + php_error_docref(NULL, E_WARNING, "Add operation failed"); + RETVAL_FALSE; + } goto cleanup; } @@ -2419,13 +2532,21 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, bool ext) ldap_status_code = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL); } if (ldap_status_code != LDAP_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(ldap_status_code)); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, ldap_status_code, "Modify: %s", ldap_err2string(ldap_status_code)); + } else { + php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(ldap_status_code)); + RETVAL_FALSE; + } } else if (ext) { ldap_status_code = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); if (ldap_status_code == -1) { - php_error_docref(NULL, E_WARNING, "Modify operation failed"); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, 0, "Modify operation failed"); + } else { + php_error_docref(NULL, E_WARNING, "Modify operation failed"); + RETVAL_FALSE; + } goto cleanup; } @@ -2470,6 +2591,11 @@ PHP_FUNCTION(ldap_add) /* }}} */ /* {{{ Add entries to LDAP directory */ +PHP_METHOD(LDAP_Connection, add) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, true); +} + PHP_FUNCTION(ldap_add_ext) { php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, true); @@ -2486,6 +2612,11 @@ PHP_FUNCTION(ldap_mod_replace) /* }}} */ /* {{{ Replace attribute values with new ones */ +PHP_METHOD(LDAP_Connection, mod_replace) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, true); +} + PHP_FUNCTION(ldap_mod_replace_ext) { php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, true); @@ -2500,6 +2631,11 @@ PHP_FUNCTION(ldap_mod_add) /* }}} */ /* {{{ Add attribute values to current */ +PHP_METHOD(LDAP_Connection, mod_add) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, true); +} + PHP_FUNCTION(ldap_mod_add_ext) { php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, true); @@ -2514,6 +2650,11 @@ PHP_FUNCTION(ldap_mod_del) /* }}} */ /* {{{ Delete attribute values */ +PHP_METHOD(LDAP_Connection, mod_del) +{ + php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, true); +} + PHP_FUNCTION(ldap_mod_del_ext) { php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, true); @@ -2531,8 +2672,15 @@ static void php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS, bool ext) int rc, msgid; size_t dn_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Op|h!", &link, ldap_link_ce, &dn, &dn_len, &server_controls_ht) != SUCCESS) { - RETURN_THROWS(); + if (ZEND_IS_METHOD_CALL()) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|h!", &dn, &dn_len, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } + link = ZEND_THIS; + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Op|h!", &link, ldap_link_ce, &dn, &dn_len, &server_controls_ht) != SUCCESS) { + RETURN_THROWS(); + } } ld = Z_LDAP_LINK_P(link); @@ -2552,15 +2700,23 @@ static void php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS, bool ext) rc = ldap_delete_ext_s(ld->link, dn, lserverctrls, NULL); } if (rc != LDAP_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Delete: %s", ldap_err2string(rc)); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, rc, "Delete: %s", ldap_err2string(rc)); + } else { + php_error_docref(NULL, E_WARNING, "Delete: %s", ldap_err2string(rc)); + RETVAL_FALSE; + } goto cleanup; } else if (ext) { LDAPMessage *ldap_res; rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res); if (rc == -1) { - php_error_docref(NULL, E_WARNING, "Delete operation failed"); - RETVAL_FALSE; + if (ZEND_IS_METHOD_CALL()) { + zend_throw_exception_ex(ldap_exception_ce, 0, "Delete operation failed"); + } else { + php_error_docref(NULL, E_WARNING, "Delete operation failed"); + RETVAL_FALSE; + } goto cleanup; } @@ -2589,6 +2745,11 @@ PHP_FUNCTION(ldap_delete) /* }}} */ /* {{{ Delete an entry from a directory */ +PHP_METHOD(LDAP_Connection, delete) +{ + php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + PHP_FUNCTION(ldap_delete_ext) { php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); diff --git a/ext/ldap/ldap.stub.php b/ext/ldap/ldap.stub.php index 52cf3828dba3..6bb7dfbaa150 100644 --- a/ext/ldap/ldap.stub.php +++ b/ext/ldap/ldap.stub.php @@ -829,12 +829,29 @@ function ldap_parse_exop(LDAP\Connection $ldap, LDAP\Result $result, &$response_ } namespace LDAP { + /** @strict-properties */ + final class LdapException extends \Exception + { + } + /** * @strict-properties * @not-serializable */ final class Connection { + public function __construct(?string $uri = null); + public function bind( + ?string $dn = null, + #[\SensitiveParameter] ?string $password = null, + ?array $controls = null, + ): Result; + public function unbind(): bool; + public function add(string $dn, array $entry, ?array $controls = null): Result; + public function mod_add(string $dn, array $entry, ?array $controls = null): Result; + public function mod_del(string $dn, array $entry, ?array $controls = null): Result; + public function mod_replace(string $dn, array $entry, ?array $controls = null): Result; + public function delete(string $dn, ?array $controls = null): Result; } /** diff --git a/ext/ldap/ldap_arginfo.h b/ext/ldap/ldap_arginfo.h index 8f5e7e34ba32..46c191bd385d 100644 --- a/ext/ldap/ldap_arginfo.h +++ b/ext/ldap/ldap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit ldap.stub.php instead. - * Stub hash: 0dde8fd813f43640dee842c03365d7431858a56d */ + * Stub hash: 3082471b9217bbd18bdc8e3012ca0bd77ab2707e */ #if defined(HAVE_ORALDAP) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ldap_connect, 0, 0, LDAP\\Connection, MAY_BE_FALSE) @@ -344,6 +344,36 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ldap_parse_exop, 0, 2, _IS_BOOL, ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_LDAP_Connection___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, uri, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_LDAP_Connection_bind, 0, 0, LDAP\\Result, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, dn, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, password, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_LDAP_Connection_unbind, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_LDAP_Connection_add, 0, 2, LDAP\\Result, 0) + ZEND_ARG_TYPE_INFO(0, dn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, entry, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_LDAP_Connection_mod_add arginfo_class_LDAP_Connection_add + +#define arginfo_class_LDAP_Connection_mod_del arginfo_class_LDAP_Connection_add + +#define arginfo_class_LDAP_Connection_mod_replace arginfo_class_LDAP_Connection_add + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_LDAP_Connection_delete, 0, 1, LDAP\\Result, 0) + ZEND_ARG_TYPE_INFO(0, dn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + #if defined(HAVE_ORALDAP) ZEND_FUNCTION(ldap_connect); #endif @@ -431,6 +461,14 @@ ZEND_FUNCTION(ldap_exop_refresh); #if defined(HAVE_LDAP_PARSE_EXTENDED_RESULT) ZEND_FUNCTION(ldap_parse_exop); #endif +ZEND_METHOD(LDAP_Connection, __construct); +ZEND_METHOD(LDAP_Connection, bind); +ZEND_METHOD(LDAP_Connection, unbind); +ZEND_METHOD(LDAP_Connection, add); +ZEND_METHOD(LDAP_Connection, mod_add); +ZEND_METHOD(LDAP_Connection, mod_del); +ZEND_METHOD(LDAP_Connection, mod_replace); +ZEND_METHOD(LDAP_Connection, delete); static const zend_function_entry ext_functions[] = { #if defined(HAVE_ORALDAP) @@ -526,6 +564,18 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class_LDAP_Connection_methods[] = { + ZEND_ME(LDAP_Connection, __construct, arginfo_class_LDAP_Connection___construct, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, bind, arginfo_class_LDAP_Connection_bind, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, unbind, arginfo_class_LDAP_Connection_unbind, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, add, arginfo_class_LDAP_Connection_add, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, mod_add, arginfo_class_LDAP_Connection_mod_add, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, mod_del, arginfo_class_LDAP_Connection_mod_del, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, mod_replace, arginfo_class_LDAP_Connection_mod_replace, ZEND_ACC_PUBLIC) + ZEND_ME(LDAP_Connection, delete, arginfo_class_LDAP_Connection_delete, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static void register_ldap_symbols(int module_number) { REGISTER_LONG_CONSTANT("LDAP_DEREF_NEVER", LDAP_DEREF_NEVER, CONST_PERSISTENT); @@ -768,13 +818,26 @@ static void register_ldap_symbols(int module_number) #endif } +static zend_class_entry *register_class_LDAP_LdapException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "LDAP", "LdapException", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Exception, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + return class_entry; +} + static zend_class_entry *register_class_LDAP_Connection(void) { zend_class_entry ce, *class_entry; - INIT_NS_CLASS_ENTRY(ce, "LDAP", "Connection", NULL); + INIT_NS_CLASS_ENTRY(ce, "LDAP", "Connection", class_LDAP_Connection_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "bind", sizeof("bind") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + return class_entry; } diff --git a/ext/ldap/tests/ldap_add_error.phpt b/ext/ldap/tests/ldap_add_error.phpt index 5ad0a7abc6e7..8ce351c205dc 100644 --- a/ext/ldap/tests/ldap_add_error.phpt +++ b/ext/ldap/tests/ldap_add_error.phpt @@ -13,6 +13,15 @@ require "connect.inc"; $link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version); +function dumpResult(LDAP\Connection $link, LDAP\Result $result): void { + var_dump( + $result, + ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls), + $errmsg, + $errcode, + ); +} + // Invalid DN var_dump( ldap_add( @@ -24,6 +33,15 @@ var_dump( ldap_errno($link) ); +// Invalid DN +dumpResult( + $link, + $link->add( + "weirdAttribute=val", + ["weirdAttribute" => "val"], + ), +); + // Duplicate entry for ($i = 0; $i < 2; $i++) { var_dump( @@ -77,6 +95,11 @@ Warning: ldap_add(): Add: Invalid DN syntax in %s on line %d bool(false) string(17) "Invalid DN syntax" int(34) +object(LDAP\Result)#%d (0) { +} +bool(true) +string(10) "invalid DN" +int(34) bool(true) Warning: ldap_add(): Add: Already exists in %s on line %d diff --git a/ext/ldap/tests/ldap_add_ext.phpt b/ext/ldap/tests/ldap_add_ext.phpt index fa02e296d00f..f7e5e4315685 100644 --- a/ext/ldap/tests/ldap_add_ext.phpt +++ b/ext/ldap/tests/ldap_add_ext.phpt @@ -16,22 +16,35 @@ require "connect.inc"; $link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version); -var_dump( - $result = ldap_add_ext($link, "o=test_ldap_add_ext,$base", array( +$parameters = [ + "o=test_ldap_add_ext,$base", array( "objectClass" => array( "top", "organization"), "o" => "test_ldap_add_ext", - ), [['oid' => LDAP_CONTROL_POST_READ, 'iscritical' => TRUE, 'value' => ['attrs' => ['o']]]]), - ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls), - $errcode, - $errmsg, - $ctrls[LDAP_CONTROL_POST_READ], - ldap_get_entries( - $link, - ldap_search($link, "$base", "(o=test_ldap_add_ext)") - ) -); + ), [['oid' => LDAP_CONTROL_POST_READ, 'iscritical' => TRUE, 'value' => ['attrs' => ['o']]]] +]; + +function dumpResult(LDAP\Connection $link, LDAP\Result $result): void { + global $base; + var_dump( + $result, + ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls), + $errcode, + $errmsg, + $ctrls[LDAP_CONTROL_POST_READ], + ldap_get_entries( + $link, + ldap_search($link, "$base", "(o=test_ldap_add_ext)") + ) + ); +} + +$result = ldap_add_ext($link, ...$parameters); +dumpResult($link, $result); +ldap_delete($link, "o=test_ldap_add_ext,$base"); +$result = $link->add(...$parameters); +dumpResult($link, $result); ?> --CLEAN-- + string(14) "1.3.6.1.1.13.2" + ["value"]=> + array(2) { + ["dn"]=> + string(%d) "o=test_ldap_add_ext,%s" + ["o"]=> + array(1) { + [0]=> + string(17) "test_ldap_add_ext" + } + } +} +array(2) { + ["count"]=> + int(1) + [0]=> + array(6) { + ["objectclass"]=> + array(3) { + ["count"]=> + int(2) + [0]=> + string(3) "top" + [1]=> + string(12) "organization" + } + [0]=> + string(11) "objectclass" + ["o"]=> + array(2) { + ["count"]=> + int(1) + [0]=> + string(17) "test_ldap_add_ext" + } + [1]=> + string(1) "o" + ["count"]=> + int(2) + ["dn"]=> + string(%d) "o=test_ldap_add_ext,%s" + } +} diff --git a/ext/ldap/tests/ldap_bind_ext.phpt b/ext/ldap/tests/ldap_bind_ext.phpt index b6b3b3da0c37..a9887cb5f6c5 100644 --- a/ext/ldap/tests/ldap_bind_ext.phpt +++ b/ext/ldap/tests/ldap_bind_ext.phpt @@ -30,6 +30,20 @@ var_dump( $ctrls ); +// Same thing using Connection::bind +var_dump( + $result = $link->bind($user, $passwd), + ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls), + $errcode, + $errmsg, + $ctrls, + $result = $link->bind($user, $passwd, [['oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST]]), + ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls), + $errcode, + $errmsg, + $ctrls +); + /* Failures */ var_dump( $result = ldap_bind_ext($link, $user, "wrongPassword", [['oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST]]), @@ -62,6 +76,20 @@ array(0) { object(LDAP\Result)#%d (0) { } bool(true) +int(0) +string(0) "" +array(0) { +} +object(LDAP\Result)#%d (0) { +} +bool(true) +int(0) +string(0) "" +array(0) { +} +object(LDAP\Result)#%d (0) { +} +bool(true) int(49) string(0) "" array(0) { diff --git a/ext/ldap/tests/ldap_constructor.phpt b/ext/ldap/tests/ldap_constructor.phpt index f48d14dfa983..20493adbb501 100644 --- a/ext/ldap/tests/ldap_constructor.phpt +++ b/ext/ldap/tests/ldap_constructor.phpt @@ -1,15 +1,37 @@ --TEST-- -Attempt to instantiate an LDAP\Connection directly +new \LDAP\Connection() - Variation --EXTENSIONS-- ldap --FILE-- getMessage(), "\n"; +foreach ($constructorCalls as $parameters) { + $link = new \LDAP\Connection(...$parameters); + var_dump($link); + ldap_get_option($link, LDAP_OPT_HOST_NAME, $hostname); + var_dump($hostname); } ?> ---EXPECT-- -Error: Cannot directly construct LDAP\Connection, use ldap_connect() instead +--EXPECTF-- +object(LDAP\Connection)#%d (0) { +} +string(%d) "%s:%d" +object(LDAP\Connection)#%d (0) { +} +string(12) "hostname:389" +object(LDAP\Connection)#%d (0) { +} +string(12) "hostname:689" +object(LDAP\Connection)#%d (0) { +} +string(12) "hostname:389" diff --git a/ext/ldap/tests/ldap_unbind_basic.phpt b/ext/ldap/tests/ldap_unbind_basic.phpt index 76333ecf9b92..9a8c36d23230 100644 --- a/ext/ldap/tests/ldap_unbind_basic.phpt +++ b/ext/ldap/tests/ldap_unbind_basic.phpt @@ -14,6 +14,11 @@ require "connect.inc"; $link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version); var_dump(ldap_unbind($link)); + +$link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version); + +var_dump($link->unbind()); ?> --EXPECT-- bool(true) +bool(true)