diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 962d4fadd27f1..857feb0d7f132 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -2491,351 +2491,254 @@ PHP_FUNCTION(ldap_delete_ext) } /* }}} */ -/* {{{ _ldap_str_equal_to_const */ -static size_t _ldap_str_equal_to_const(const char *str, size_t str_len, const char *cstr) -{ - size_t i; - - if (strlen(cstr) != str_len) - return 0; - - for (i = 0; i < str_len; ++i) { - if (str[i] != cstr[i]) { - return 0; - } - } - - return 1; -} -/* }}} */ - -/* {{{ _ldap_strlen_max */ -static size_t _ldap_strlen_max(const char *str, size_t max_len) -{ - size_t i; - - for (i = 0; i < max_len; ++i) { - if (str[i] == '\0') { - return i; - } - } - - return max_len; -} -/* }}} */ - -/* {{{ _ldap_hash_fetch */ -static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out) -{ - *out = zend_hash_str_find(Z_ARRVAL_P(hashTbl), key, strlen(key)); -} -/* }}} */ - /* {{{ Perform multiple modifications as part of one operation */ PHP_FUNCTION(ldap_modify_batch) { - zval *serverctrls = NULL; - ldap_linkdata *ld; - zval *link, *mods, *mod, *modinfo; - zend_string *modval; - zval *attrib, *modtype, *vals; - zval *fetched; + zval *server_controls_zv = NULL; + zval *link; char *dn; size_t dn_len; - int i, j, k; - int num_mods, num_modprops, num_modvals; - LDAPMod **ldap_mods; + HashTable *modifications; LDAPControl **lserverctrls = NULL; - uint32_t oper; /* - $mods = array( - array( + $mods = [ + [ "attrib" => "unicodePwd", "modtype" => LDAP_MODIFY_BATCH_REMOVE, - "values" => array($oldpw) - ), - array( + "values" => [$old_pwd] + ], + [ "attrib" => "unicodePwd", "modtype" => LDAP_MODIFY_BATCH_ADD, - "values" => array($newpw) - ), - array( + "values" => [$new_pwd] + ], + [ "attrib" => "userPrincipalName", "modtype" => LDAP_MODIFY_BATCH_REPLACE, - "values" => array("janitor@corp.contoso.com") - ), - array( + "values" => ["janitor@corp.contoso.com"] + ], + [ "attrib" => "userCert", "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL - ) - ); + ], + ]; */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa/|a!", &link, ldap_link_ce, &dn, &dn_len, &mods, &serverctrls) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osh/|a!", &link, ldap_link_ce, &dn, &dn_len, &modifications, &server_controls_zv) != SUCCESS) { RETURN_THROWS(); } - ld = Z_LDAP_LINK_P(link); + ldap_linkdata *ld = Z_LDAP_LINK_P(link); VERIFY_LDAP_LINK_CONNECTED(ld); /* perform validation */ - { - zend_string *modkey; - zend_long modtype; + /* make sure the DN contains no NUL bytes */ + if (zend_char_has_nul_byte(dn, dn_len)) { + zend_argument_value_error(2, "must not contain null bytes"); + RETURN_THROWS(); + } - /* to store the wrongly-typed keys */ - zend_ulong tmpUlong; + /* make sure the top level is a normal array */ + if (zend_hash_num_elements(modifications) == 0) { + zend_argument_must_not_be_empty_error(3); + RETURN_THROWS(); + } + if (!zend_array_is_list(modifications)) { + zend_argument_value_error(3, "must be a list"); + RETURN_THROWS(); + } - /* make sure the DN contains no NUL bytes */ - if (_ldap_strlen_max(dn, dn_len) != dn_len) { - zend_argument_type_error(2, "must not contain null bytes"); + zval *modification_zv = NULL; + ZEND_HASH_FOREACH_VAL(modifications, modification_zv) { + if (Z_TYPE_P(modification_zv) != IS_ARRAY) { + zend_argument_type_error(3, "must only contain arrays"); RETURN_THROWS(); } - /* make sure the top level is a normal array */ - zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods)); - if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) { - zend_argument_type_error(3, "must be integer-indexed"); + SEPARATE_ARRAY(modification_zv); + const HashTable *modification = Z_ARRVAL_P(modification_zv); + uint32_t modification_size = zend_hash_num_elements(modification); + + if (modification_size != 2 && modification_size != 3) { + zend_argument_value_error(3, "a modification entry must only contain the keys " + "\"" LDAP_MODIFY_BATCH_ATTRIB "\", \"" LDAP_MODIFY_BATCH_MODTYPE "\", and \"" LDAP_MODIFY_BATCH_VALUES "\""); RETURN_THROWS(); } - num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods)); + const zval *attrib = zend_hash_str_find(modification, LDAP_MODIFY_BATCH_ATTRIB, strlen(LDAP_MODIFY_BATCH_ATTRIB)); + if (UNEXPECTED(attrib == NULL)) { + zend_argument_value_error(3, "a modification entry must contain the \"" LDAP_MODIFY_BATCH_ATTRIB "\" option"); + RETURN_THROWS(); + } + if (UNEXPECTED(Z_TYPE_P(attrib) != IS_STRING)) { + zend_argument_type_error(3, "the value for option \"" LDAP_MODIFY_BATCH_ATTRIB "\" must be of type string, %s given", zend_zval_value_name(attrib)); + RETURN_THROWS(); + } + if (zend_str_has_nul_byte(Z_STR_P(attrib))) { + zend_argument_value_error(3, "the value for option \"" LDAP_MODIFY_BATCH_ATTRIB "\" must not contain null bytes"); + RETURN_THROWS(); + } - for (i = 0; i < num_mods; i++) { - /* is the numbering consecutive? */ - if ((fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i)) == NULL) { - zend_argument_value_error(3, "must have consecutive integer indices starting from 0"); - RETURN_THROWS(); - } - mod = fetched; + const zval *modtype_zv = zend_hash_str_find(modification, LDAP_MODIFY_BATCH_MODTYPE, strlen(LDAP_MODIFY_BATCH_MODTYPE)); + if (UNEXPECTED(modtype_zv == NULL)) { + zend_argument_value_error(3, "a modification entry must contain the \"" LDAP_MODIFY_BATCH_MODTYPE "\" option"); + RETURN_THROWS(); + } + if (UNEXPECTED(Z_TYPE_P(modtype_zv) != IS_LONG)) { + zend_argument_type_error(3, "the value for option \"" LDAP_MODIFY_BATCH_MODTYPE "\" must be of type int, %s given", zend_zval_value_name(attrib)); + RETURN_THROWS(); + } + zend_long modtype = Z_LVAL_P(modtype_zv); + if ( + modtype != LDAP_MODIFY_BATCH_ADD && + modtype != LDAP_MODIFY_BATCH_REMOVE && + modtype != LDAP_MODIFY_BATCH_REPLACE && + modtype != LDAP_MODIFY_BATCH_REMOVE_ALL + ) { + zend_argument_value_error(3, "the value for option \"" LDAP_MODIFY_BATCH_MODTYPE "\" must be" + " LDAP_MODIFY_BATCH_ADD, LDAP_MODIFY_BATCH_REMOVE, LDAP_MODIFY_BATCH_REPLACE," + " or LDAP_MODIFY_BATCH_REMOVE_ALL"); + RETURN_THROWS(); + } + /* We assume that the modification array is well-formed and only ever contains an extra "values" key */ + if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL && modification_size == 3) { + zend_argument_value_error(3, "a modification entry must not contain the " + "\"" LDAP_MODIFY_BATCH_VALUES "\" option when option \"" LDAP_MODIFY_BATCH_MODTYPE "\" " + "is LDAP_MODIFY_BATCH_REMOVE_ALL"); + RETURN_THROWS(); + } - /* is it an array? */ - if (Z_TYPE_P(mod) != IS_ARRAY) { - zend_argument_value_error(3, "must only contain arrays"); + zval *modification_values_zv = zend_hash_str_find(modification, LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES)); + if (modification_values_zv == NULL) { + if (modtype != LDAP_MODIFY_BATCH_REMOVE_ALL) { + zend_argument_value_error(3, "a modification entry must contain the " + "\"" LDAP_MODIFY_BATCH_VALUES "\" option when the \"" LDAP_MODIFY_BATCH_MODTYPE "\" option " + "is not LDAP_MODIFY_BATCH_REMOVE_ALL"); RETURN_THROWS(); } + continue; + } + if (Z_TYPE_P(modification_values_zv) != IS_ARRAY) { + zend_argument_type_error(3, "the value for option \"" LDAP_MODIFY_BATCH_VALUES "\" must be of type array, %s given", zend_zval_value_name(attrib)); + RETURN_THROWS(); + } - SEPARATE_ARRAY(mod); - /* for the modification hashtable... */ - zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod)); - num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod)); - bool has_attrib_key = false; - bool has_modtype_key = false; - - for (j = 0; j < num_modprops; j++) { - - /* are the keys strings? */ - if (zend_hash_get_current_key(Z_ARRVAL_P(mod), &modkey, &tmpUlong) != HASH_KEY_IS_STRING) { - zend_argument_type_error(3, "must only contain string-indexed arrays"); - RETURN_THROWS(); - } - - /* is this a valid entry? */ - if ( - !_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB) && - !_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE) && - !_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES) - ) { - zend_argument_value_error(3, "must contain arrays only containing the \"" LDAP_MODIFY_BATCH_ATTRIB "\", \"" LDAP_MODIFY_BATCH_MODTYPE "\" and \"" LDAP_MODIFY_BATCH_VALUES "\" keys"); - RETURN_THROWS(); - } - - fetched = zend_hash_get_current_data(Z_ARRVAL_P(mod)); - modinfo = fetched; - - /* does the value type match the key? */ - if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB)) { - has_attrib_key = true; - if (Z_TYPE_P(modinfo) != IS_STRING) { - zend_type_error("%s(): Option \"" LDAP_MODIFY_BATCH_ATTRIB "\" must be of type string, %s given", get_active_function_name(), zend_zval_value_name(modinfo)); - RETURN_THROWS(); - } - - if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) { - zend_type_error("%s(): Option \"" LDAP_MODIFY_BATCH_ATTRIB "\" cannot contain null-bytes", get_active_function_name()); - RETURN_THROWS(); - } - } - else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE)) { - has_modtype_key = true; - if (Z_TYPE_P(modinfo) != IS_LONG) { - zend_type_error("%s(): Option \"" LDAP_MODIFY_BATCH_MODTYPE "\" must be of type int, %s given", get_active_function_name(), zend_zval_value_name(modinfo)); - RETURN_THROWS(); - } - - /* is the value in range? */ - modtype = Z_LVAL_P(modinfo); - if ( - modtype != LDAP_MODIFY_BATCH_ADD && - modtype != LDAP_MODIFY_BATCH_REMOVE && - modtype != LDAP_MODIFY_BATCH_REPLACE && - modtype != LDAP_MODIFY_BATCH_REMOVE_ALL - ) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_MODTYPE "\" must be one of the LDAP_MODIFY_BATCH_* constants", get_active_function_name()); - RETURN_THROWS(); - } - - /* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */ - if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) { - if (zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) { - zend_value_error("%s(): If option \"" LDAP_MODIFY_BATCH_MODTYPE "\" is LDAP_MODIFY_BATCH_REMOVE_ALL, option \"" LDAP_MODIFY_BATCH_VALUES "\" cannot be provided", get_active_function_name()); - RETURN_THROWS(); - } - } - else { - if (!zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) { - zend_value_error("%s(): If option \"" LDAP_MODIFY_BATCH_MODTYPE "\" is not LDAP_MODIFY_BATCH_REMOVE_ALL, option \"" LDAP_MODIFY_BATCH_VALUES "\" must be provided", get_active_function_name()); - RETURN_THROWS(); - } - } - } - else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES)) { - if (Z_TYPE_P(modinfo) != IS_ARRAY) { - zend_type_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be of type array, %s given", get_active_function_name(), zend_zval_value_name(modinfo)); - RETURN_THROWS(); - } - - SEPARATE_ARRAY(modinfo); - /* is the array not empty? */ - zend_hash_internal_pointer_reset(Z_ARRVAL_P(modinfo)); - num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo)); - if (num_modvals == 0) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must not be empty", get_active_function_name()); - RETURN_THROWS(); - } - - /* are its keys integers? */ - if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be integer-indexed", get_active_function_name()); - RETURN_THROWS(); - } - - /* are the keys consecutive? */ - for (k = 0; k < num_modvals; k++) { - if ((fetched = zend_hash_index_find(Z_ARRVAL_P(modinfo), k)) == NULL) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must have consecutive integer indices starting from 0", get_active_function_name()); - RETURN_THROWS(); - } - } - } - - zend_hash_move_forward(Z_ARRVAL_P(mod)); - } + SEPARATE_ARRAY(modification_values_zv); + const HashTable *modification_values = Z_ARRVAL_P(modification_values_zv); + /* is the array not empty? */ + uint32_t num_modvals = zend_hash_num_elements(modification_values); + if (num_modvals == 0) { + zend_argument_value_error(3, "the value for option \"" LDAP_MODIFY_BATCH_VALUES "\" must not be empty"); + RETURN_THROWS(); + } + if (!zend_array_is_list(modification_values)) { + zend_argument_value_error(3, "the value for option \"" LDAP_MODIFY_BATCH_VALUES "\" must be a list"); + RETURN_THROWS(); + } + } ZEND_HASH_FOREACH_END(); + /* validation of modifications array was successful */ - if (!has_attrib_key) { - zend_value_error("%s(): Required option \"" LDAP_MODIFY_BATCH_ATTRIB "\" is missing", get_active_function_name()); - RETURN_THROWS(); - } - if (!has_modtype_key) { - zend_value_error("%s(): Required option \"" LDAP_MODIFY_BATCH_MODTYPE "\" is missing", get_active_function_name()); - RETURN_THROWS(); - } + /* Check that the LDAP server controls array is valid */ + if (server_controls_zv) { + lserverctrls = _php_ldap_controls_from_array(ld->link, server_controls_zv, 4); + if (lserverctrls == NULL) { + _php_ldap_controls_free(&lserverctrls); + RETURN_FALSE; } } - /* validation was successful */ /* allocate array of modifications */ - ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0); + uint32_t num_mods = zend_hash_num_elements(modifications); + LDAPMod **ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0); /* for each modification */ - for (i = 0; i < num_mods; i++) { - /* allocate the modification struct */ - ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0); + zend_ulong modification_index = 0; + ZEND_HASH_FOREACH_NUM_KEY_VAL(modifications, modification_index, modification_zv) { + ldap_mods[modification_index] = safe_emalloc(1, sizeof(LDAPMod), 0); - /* fetch the relevant data */ - fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i); - mod = fetched; - - _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib); - _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype); - _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals); + zval *attrib_zv = zend_hash_str_find(Z_ARRVAL_P(modification_zv), LDAP_MODIFY_BATCH_ATTRIB, strlen(LDAP_MODIFY_BATCH_ATTRIB)); + ZEND_ASSERT(Z_TYPE_P(attrib_zv) == IS_STRING); + zval *modtype_zv = zend_hash_str_find(Z_ARRVAL_P(modification_zv), LDAP_MODIFY_BATCH_MODTYPE, strlen(LDAP_MODIFY_BATCH_MODTYPE)); + ZEND_ASSERT(Z_TYPE_P(modtype_zv) == IS_LONG); + zval *modification_values = zend_hash_str_find(Z_ARRVAL_P(modification_zv), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES)); + ZEND_ASSERT(modification_values == NULL || Z_TYPE_P(modification_values) == IS_ARRAY); /* map the modification type */ - switch (Z_LVAL_P(modtype)) { + int ldap_operation; + switch (Z_LVAL_P(modtype_zv)) { case LDAP_MODIFY_BATCH_ADD: - oper = LDAP_MOD_ADD; + ldap_operation = LDAP_MOD_ADD; break; case LDAP_MODIFY_BATCH_REMOVE: case LDAP_MODIFY_BATCH_REMOVE_ALL: - oper = LDAP_MOD_DELETE; + ldap_operation = LDAP_MOD_DELETE; break; case LDAP_MODIFY_BATCH_REPLACE: - oper = LDAP_MOD_REPLACE; + ldap_operation = LDAP_MOD_REPLACE; break; - default: - zend_throw_error(NULL, "Unknown and uncaught modification type."); - RETVAL_FALSE; - efree(ldap_mods[i]); - num_mods = i; - goto cleanup; + EMPTY_SWITCH_DEFAULT_CASE(); } /* fill in the basic info */ - ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES; - ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib)); + ldap_mods[modification_index]->mod_op = ldap_operation | LDAP_MOD_BVALUES; + ldap_mods[modification_index]->mod_type = estrndup(Z_STRVAL_P(attrib_zv), Z_STRLEN_P(attrib_zv)); - if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) { + if (Z_LVAL_P(modtype_zv) == LDAP_MODIFY_BATCH_REMOVE_ALL) { /* no values */ - ldap_mods[i]->mod_bvalues = NULL; - } - else { + ldap_mods[modification_index]->mod_bvalues = NULL; + } else { /* allocate space for the values as part of this modification */ - num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals)); - ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0); + uint32_t num_modification_values = zend_hash_num_elements(Z_ARRVAL_P(modification_values)); + ldap_mods[modification_index]->mod_bvalues = safe_emalloc((num_modification_values+1), sizeof(struct berval *), 0); /* for each value */ - for (j = 0; j < num_modvals; j++) { - /* fetch it */ - fetched = zend_hash_index_find(Z_ARRVAL_P(vals), j); - modval = zval_get_string(fetched); + zend_ulong value_index = 0; + zval *modification_value_zv = NULL; + ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(modification_values), value_index, modification_value_zv) { + zend_string *modval = zval_get_string(modification_value_zv); if (EG(exception)) { RETVAL_FALSE; - ldap_mods[i]->mod_bvalues[j] = NULL; - num_mods = i + 1; + ldap_mods[modification_index]->mod_bvalues[value_index] = NULL; + num_mods = modification_index + 1; goto cleanup; } /* allocate the data struct */ - ldap_mods[i]->mod_bvalues[j] = safe_emalloc(1, sizeof(struct berval), 0); + ldap_mods[modification_index]->mod_bvalues[value_index] = safe_emalloc(1, sizeof(struct berval), 0); /* fill it */ - ldap_mods[i]->mod_bvalues[j]->bv_len = ZSTR_LEN(modval); - ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval)); + ldap_mods[modification_index]->mod_bvalues[value_index]->bv_len = ZSTR_LEN(modval); + ldap_mods[modification_index]->mod_bvalues[value_index]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval)); zend_string_release(modval); - } + } ZEND_HASH_FOREACH_END(); /* NULL-terminate values */ - ldap_mods[i]->mod_bvalues[num_modvals] = NULL; + ldap_mods[modification_index]->mod_bvalues[num_modification_values] = NULL; } - } + } ZEND_HASH_FOREACH_END(); /* NULL-terminate modifications */ ldap_mods[num_mods] = NULL; - if (serverctrls) { - lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls, 4); - if (lserverctrls == NULL) { - RETVAL_FALSE; - goto cleanup; - } - } - /* perform (finally) */ - if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL)) != LDAP_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Batch Modify: %s", ldap_err2string(i)); + int ldap_status = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL); + if (ldap_status != LDAP_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Batch Modify: %s", ldap_err2string(ldap_status)); RETVAL_FALSE; - } else RETVAL_TRUE; + } else { + RETVAL_TRUE; + } /* clean up */ cleanup: { - for (i = 0; i < num_mods; i++) { + for (uint32_t i = 0; i < num_mods; i++) { /* attribute */ efree(ldap_mods[i]->mod_type); if (ldap_mods[i]->mod_bvalues != NULL) { /* each BER value */ - for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) { + for (int j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) { /* free the data bytes */ efree(ldap_mods[i]->mod_bvalues[j]->bv_val); diff --git a/ext/ldap/tests/gh16032-1.phpt b/ext/ldap/tests/gh16032-1.phpt index dbaf8213933d7..8e01561a3b1dc 100644 --- a/ext/ldap/tests/gh16032-1.phpt +++ b/ext/ldap/tests/gh16032-1.phpt @@ -23,4 +23,4 @@ try { ?> --EXPECT-- -ValueError: ldap_modify_batch(): Required option "attrib" is missing +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must contain the "attrib" option diff --git a/ext/ldap/tests/gh16032-2.phpt b/ext/ldap/tests/gh16032-2.phpt index 531415807f674..d4b4cddb15a80 100644 --- a/ext/ldap/tests/gh16032-2.phpt +++ b/ext/ldap/tests/gh16032-2.phpt @@ -23,4 +23,4 @@ try { ?> --EXPECT-- -ValueError: ldap_modify_batch(): Required option "modtype" is missing +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must contain the "modtype" option diff --git a/ext/ldap/tests/ldap_modify_batch_modifications_with_references.phpt b/ext/ldap/tests/ldap_modify_batch_modifications_with_references.phpt new file mode 100644 index 0000000000000..418f7b2a1cc87 --- /dev/null +++ b/ext/ldap/tests/ldap_modify_batch_modifications_with_references.phpt @@ -0,0 +1,67 @@ +--TEST-- +ldap_modify_batch() - modification array contains references +--EXTENSIONS-- +ldap +--FILE-- + $r, + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_attrib_reference_string)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modtype = LDAP_MODIFY_BATCH_ADD; +$r =& $modtype; +$modification_modtype_reference_int = [ + [ + "attrib" => "attrib1", + "modtype" => $r, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_modtype_reference_int)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + + +$values = ["value1"]; +$r =& $values; +$modification_values_reference_array = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => $r, + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_values_reference_array)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Warning: ldap_modify_batch(): Batch Modify: Can't contact LDAP server in %s on line %d +bool(false) + +Warning: ldap_modify_batch(): Batch Modify: Can't contact LDAP server in %s on line %d +bool(false) + +Warning: ldap_modify_batch(): Batch Modify: Can't contact LDAP server in %s on line %d +bool(false) diff --git a/ext/ldap/tests/ldap_modify_batch_programming_error.phpt b/ext/ldap/tests/ldap_modify_batch_programming_error.phpt new file mode 100644 index 0000000000000..eadbf7289d6b2 --- /dev/null +++ b/ext/ldap/tests/ldap_modify_batch_programming_error.phpt @@ -0,0 +1,276 @@ +--TEST-- +ldap_modify_batch() - ValueErrors and TypeErrors +--EXTENSIONS-- +ldap +--FILE-- +getMessage(), PHP_EOL; +} + +$empty_list = []; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $empty_list)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$not_list1 = [ + 'entry1' => [], + 'entry2' => [], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $not_list1)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$not_list2 = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], + 2 => [], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $not_list2)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + + +$not_list_of_arrays = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], + 'not an array', +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $not_list_of_arrays)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modifications_not_all_dicts = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], + [ + "attrib" => "attrib2", + "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL, + 4 => ["value2"], + ], + [ + "attrib" => "attrib3", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value3"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modifications_not_all_dicts)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_has_invalid_key = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + "random" => "what", + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_has_invalid_key)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_attrib_not_string = [ + [ + "attrib" => 25, + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_attrib_not_string)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_attrib_has_nul_byte = [ + [ + "attrib" => "attrib\0with\0nul\0byte", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_attrib_has_nul_byte)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_modtype_not_int = [ + [ + "attrib" => "attrib1", + "modtype" => new stdClass(), + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_modtype_not_int)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_modtype_invalid_value = [ + [ + "attrib" => "attrib1", + "modtype" => 42, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_modtype_invalid_value)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_modtype_remove_all_with_values_key = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_modtype_remove_all_with_values_key)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_modtype_not_remove_all_without_values_key = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_modtype_not_remove_all_without_values_key)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_values_invalid_value = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => "value1", + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_values_invalid_value)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_values_empty_array = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => [], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_values_empty_array)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_values_not_list1 = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => $not_list1, + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_values_not_list1)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_values_not_list2 = [ + [ + "attrib" => "attrib1", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => $not_list2, + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_values_not_list2)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_missing_attrib_key = [ + [ + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_missing_attrib_key)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$modification_missing_modtype_key = [ + [ + "attrib" => "attrib1", + "values" => ["value1"], + ], +]; +try { + var_dump(ldap_modify_batch($ldap, $valid_dn, $modification_missing_modtype_key)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +ValueError: ldap_modify_batch(): Argument #2 ($dn) must not contain null bytes +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) must not be empty +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) must be a list +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) must be a list +TypeError: ldap_modify_batch(): Argument #3 ($modifications_info) must only contain arrays +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must not contain the "values" option when option "modtype" is LDAP_MODIFY_BATCH_REMOVE_ALL +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must only contain the keys "attrib", "modtype", and "values" +TypeError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "attrib" must be of type string, int given +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "attrib" must not contain null bytes +TypeError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "modtype" must be of type int, string given +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "modtype" must be LDAP_MODIFY_BATCH_ADD, LDAP_MODIFY_BATCH_REMOVE, LDAP_MODIFY_BATCH_REPLACE, or LDAP_MODIFY_BATCH_REMOVE_ALL +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must not contain the "values" option when option "modtype" is LDAP_MODIFY_BATCH_REMOVE_ALL +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must contain the "values" option when the "modtype" option is not LDAP_MODIFY_BATCH_REMOVE_ALL +TypeError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "values" must be of type array, string given +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "values" must not be empty +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "values" must be a list +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) the value for option "values" must be a list +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must contain the "attrib" option +ValueError: ldap_modify_batch(): Argument #3 ($modifications_info) a modification entry must contain the "modtype" option