1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

r16028: Re-add the objectclass module, in the new async scheme.

Add a test to show that we need this, and to prove it works (for add
at least).

Andrew Bartlett
(This used to be commit f72079029a)
This commit is contained in:
Andrew Bartlett 2006-06-03 11:57:20 +00:00 committed by Gerald (Jerry) Carter
parent dc4ccc6f65
commit b31c685ec2
4 changed files with 484 additions and 130 deletions

View File

@ -38,15 +38,15 @@ OBJ_FILES = \
# End MODULE ldb_operational
################################################
# ################################################
# # Start MODULE ldb_objectclass
# [MODULE::ldb_objectclass]
# INIT_FUNCTION = ldb_objectclass_init
# SUBSYSTEM = ldb
# OBJ_FILES = \
# modules/objectclass.o
# # End MODULE ldb_objectclass
# ################################################
################################################
# Start MODULE ldb_objectclass
[MODULE::ldb_objectclass]
INIT_FUNCTION = ldb_objectclass_init
SUBSYSTEM = ldb
OBJ_FILES = \
modules/objectclass.o
# End MODULE ldb_objectclass
################################################
################################################
# Start MODULE ldb_rdn_name

View File

@ -1,7 +1,8 @@
/*
ldb database library
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
Copyright (C) Simo Sorce 2006
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
@ -35,29 +36,213 @@
#include "includes.h"
#include "ldb/include/includes.h"
/* It turns out the MMC assumes that the last objectClass in the list
* is the most specific subclass. As such, we must sort the list,
* according to the schema.
*
* For performance, we do this on the add/modify, not on the search
*
* We perform the original add/modify, then search for that is now in
* the objectClass list. We can then then replace that with the new
* sorted list. The backend is expected to preserve ordering for
* subsequent searches.
*
* We are in a transaction, so this is all perfectly safe...
*/
struct oc_async_context {
static int objectclass_handle(struct ldb_module *module, struct ldb_request *req, const struct ldb_message *msg)
enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
struct ldb_module *module;
struct ldb_request *orig_req;
struct ldb_request *down_req;
struct ldb_request *search_req;
struct ldb_async_result *search_res;
struct ldb_request *mod_req;
};
static struct ldb_async_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
{
struct oc_async_context *ac;
struct ldb_async_handle *h;
h = talloc_zero(req, struct ldb_async_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct oc_async_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
talloc_free(h);
return NULL;
}
h->private_data = (void *)ac;
h->state = LDB_ASYNC_INIT;
h->status = LDB_SUCCESS;
ac->module = module;
ac->orig_req = req;
return h;
}
static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_async_handle *h;
struct oc_async_context *ac;
struct ldb_message_element *objectClassAttr;
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
return ldb_next_request(module, req);
}
objectClassAttr = ldb_msg_find_element(req->op.add.message, "objectClass");
/* If no part of this touches the objectClass, then we don't
* need to make any changes. */
/* If the only operation is the deletion of the objectClass then go on */
if (!objectClassAttr) {
ldb_set_errstring(module->ldb, talloc_asprintf(ac, "Object class violation: no objectClass present"));
return LDB_ERR_OBJECT_CLASS_VIOLATION;
}
h = oc_init_handle(req, module);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct oc_async_context);
/* return or own handle to deal with this call */
req->async.handle = h;
/* prepare the first operation */
ac->down_req = talloc_zero(ac, struct ldb_request);
if (ac->down_req == NULL) {
ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
return LDB_ERR_OPERATIONS_ERROR;
}
*(ac->down_req) = *req; /* copy the request */
ac->down_req->async.context = NULL;
ac->down_req->async.callback = NULL;
ac->step = OC_DO_REQ;
return ldb_next_request(module, ac->down_req);
}
static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_async_handle *h;
struct oc_async_context *ac;
struct ldb_message_element *objectClassAttr;
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
return ldb_next_request(module, req);
}
objectClassAttr = ldb_msg_find_element(req->op.mod.message, "objectClass");
/* If no part of this touches the objectClass, then we don't
* need to make any changes. */
/* If the only operation is the deletion of the objectClass then go on */
if (!objectClassAttr) {
return ldb_next_request(module, req);
}
h = oc_init_handle(req, module);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct oc_async_context);
/* return or own handle to deal with this call */
req->async.handle = h;
/* prepare the first operation */
ac->down_req = talloc_zero(ac, struct ldb_request);
if (ac->down_req == NULL) {
ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
return LDB_ERR_OPERATIONS_ERROR;
}
*(ac->down_req) = *req; /* copy the request */
ac->down_req->async.context = NULL;
ac->down_req->async.callback = NULL;
ac->step = OC_DO_REQ;
return ldb_next_request(module, ac->down_req);
}
static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
{
struct oc_async_context *ac;
if (!context || !ares) {
ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(context, struct oc_async_context);
/* we are interested only in the single reply (base search) we receive here */
if (ares->type == LDB_REPLY_ENTRY) {
if (ac->search_res != NULL) {
ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
ac->search_res = talloc_steal(ac, ares);
} else {
talloc_free(ares);
}
return LDB_SUCCESS;
}
static int objectclass_search_self(struct ldb_async_handle *h) {
struct oc_async_context *ac;
static const char * const attrs[] = { "objectClass", NULL };
ac = talloc_get_type(h->private_data, struct oc_async_context);
/* prepare the search operation */
ac->search_req = talloc_zero(ac, struct ldb_request);
if (ac->search_req == NULL) {
ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
return LDB_ERR_OPERATIONS_ERROR;
}
ac->search_req->operation = LDB_SEARCH;
ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
ac->search_req->op.search.scope = LDB_SCOPE_BASE;
ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
if (ac->search_req->op.search.tree == NULL) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: Internal error producing null search"));
return LDB_ERR_OPERATIONS_ERROR;
}
ac->search_req->op.search.attrs = attrs;
ac->search_req->controls = NULL;
ac->search_req->async.context = ac;
ac->search_req->async.callback = get_self_callback;
ac->search_req->async.timeout = ac->orig_req->async.timeout;
ac->step = OC_SEARCH_SELF;
return ldb_next_request(ac->module, ac->search_req);
}
static int objectclass_do_mod(struct ldb_async_handle *h) {
struct oc_async_context *ac;
struct ldb_message_element *objectclass_element;
struct ldb_message *msg;
TALLOC_CTX *mem_ctx;
int ret;
struct ldb_request *search_request;
struct ldb_request *modify_request;
struct ldb_message *modify_msg;
struct ldb_result *res;
const char *attrs[] = { "objectClass", NULL };
struct class_list {
struct class_list *prev, *next;
const char *objectclass;
@ -66,67 +251,41 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
*subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
int i;
int layer;
int ret;
struct ldb_message_element *objectclass_element;
ac = talloc_get_type(h->private_data, struct oc_async_context);
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_handle\n");
if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
return ldb_next_request(module, req);
}
ret = ldb_next_request(module, req);
if (ret) {
return ret;
}
if (ldb_msg_find_element(msg, "objectClass") == NULL ) {
/* No sign of the objectClass: no change, nothing to see here */
return ret;
}
/* Thanks to transactions: Now do a search, find the full list
* of objectClasses and do the sort */
mem_ctx = talloc_new(module);
if (!mem_ctx) {
mem_ctx = talloc_new(ac);
if (mem_ctx == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
search_request = talloc(mem_ctx, struct ldb_request);
if (!search_request) {
ac->mod_req = talloc(ac, struct ldb_request);
if (ac->mod_req == NULL) {
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
search_request->operation = LDB_REQ_SEARCH;
search_request->op.search.base = msg->dn;
search_request->op.search.scope = LDB_SCOPE_BASE;
search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
search_request->op.search.attrs = attrs;
search_request->controls = NULL;
ac->mod_req->operation = LDB_MODIFY;
ac->mod_req->controls = NULL;
ac->mod_req->async.context = ac;
ac->mod_req->async.callback = NULL;
ac->mod_req->async.timeout = ac->orig_req->async.timeout;
ret = ldb_next_request(module, search_request);
if (ret) {
return ret;
}
res = search_request->op.search.res;
talloc_steal(mem_ctx, res);
if (res->count != 1) {
ldb_set_errstring(module->ldb,
talloc_asprintf(mem_ctx, "objectClass_handle: "
"search for %s found %d != 1 objects, for entry we just added/modified",
ldb_dn_linearize(mem_ctx, msg->dn),
res->count));
/* What happened? The above add/modify worked... */
/* use a new message structure */
ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
if (msg == NULL) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not create new modify msg"));
talloc_free(mem_ctx);
return LDB_ERR_NO_SUCH_OBJECT;
return LDB_ERR_OPERATIONS_ERROR;
}
/* modify dn */
msg->dn = ac->orig_req->op.mod.message->dn;
/* This is now the objectClass list from the database */
objectclass_element = ldb_msg_find_element(res->msgs[0], "objectClass");
objectclass_element = ldb_msg_find_element(ac->search_res->message,
"objectClass");
if (!objectclass_element) {
/* Perhaps the above was a remove? Move along now, nothing to see here */
talloc_free(mem_ctx);
@ -166,6 +325,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
for (i=0; i < objectclass_element->num_values; i++) {
current = talloc(mem_ctx, struct class_list);
if (!current) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: out of memory allocating objectclass list"));
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
@ -189,7 +349,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
/* Ensure we don't bother if there are no unsorted entries left */
for (current = parent_class; unsorted && current; current = current->next) {
const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass);
const char **subclasses = ldb_subclass_list(ac->module->ldb, current->objectclass);
/* Walk the list of possible subclasses in unsorted */
for (poss_subclass = unsorted; poss_subclass; ) {
@ -231,78 +391,162 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
*/
DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
modify_msg = ldb_msg_new(mem_ctx);
if (!modify_msg) {
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
modify_msg->dn = talloc_reference(modify_msg, msg->dn);
/* We must completely replace the existing objectClass entry.
* We could do a constrained add/del, but we are meant to be
* in a transaction... */
ret = ldb_msg_add_empty(modify_msg, "objectClass", LDB_FLAG_MOD_REPLACE);
ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not clear objectclass in modify msg"));
talloc_free(mem_ctx);
return ret;
}
/* Move from the linked list back into an ldb msg */
for (current = sorted; current; current = current->next) {
ret = ldb_msg_add_string(modify_msg, "objectClass", current->objectclass);
ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not re-add sorted objectclass to modify msg"));
talloc_free(mem_ctx);
return ret;
}
}
ret = ldb_msg_sanity_check(modify_msg);
ret = ldb_msg_sanity_check(msg);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
return ret;
}
modify_request = talloc(mem_ctx, struct ldb_request);
if (!modify_request) {
h->state = LDB_ASYNC_INIT;
h->status = LDB_SUCCESS;
ac->step = OC_DO_MOD;
talloc_free(mem_ctx);
/* perform the search */
return ldb_next_request(ac->module, ac->mod_req);
}
static int oc_async_wait(struct ldb_async_handle *handle) {
struct oc_async_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
modify_request->operation = LDB_REQ_MODIFY;
modify_request->op.mod.message = modify_msg;
modify_request->controls = NULL;
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
/* And now push the write into the database */
ret = ldb_next_request(module, modify_request);
handle->state = LDB_ASYNC_PENDING;
handle->status = LDB_SUCCESS;
talloc_free(mem_ctx);
ac = talloc_get_type(handle->private_data, struct oc_async_context);
switch (ac->step) {
case OC_DO_REQ:
ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->down_req->async.handle->status != LDB_SUCCESS) {
handle->status = ac->down_req->async.handle->status;
goto done;
}
if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* mods done, go on */
return objectclass_search_self(handle);
case OC_SEARCH_SELF:
ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->search_req->async.handle->status != LDB_SUCCESS) {
handle->status = ac->search_req->async.handle->status;
goto done;
}
if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* self search done, go on */
return objectclass_do_mod(handle);
case OC_DO_MOD:
ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->mod_req->async.handle->status != LDB_SUCCESS) {
handle->status = ac->mod_req->async.handle->status;
goto done;
}
if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
break;
default:
ret = LDB_ERR_OPERATIONS_ERROR;
goto done;
}
ret = LDB_SUCCESS;
done:
handle->state = LDB_ASYNC_DONE;
return ret;
}
static int objectclass_request(struct ldb_module *module, struct ldb_request *req)
static int oc_async_wait_all(struct ldb_async_handle *handle) {
int ret;
while (handle->state != LDB_ASYNC_DONE) {
ret = oc_async_wait(handle);
if (ret != LDB_SUCCESS) {
return ret;
}
}
return handle->status;
}
static int objectclass_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
{
switch (req->operation) {
/* only care about add and modify requests */
case LDB_REQ_ADD:
return objectclass_handle(module, req, req->op.add.message);
case LDB_REQ_MODIFY:
return objectclass_handle(module, req, req->op.mod.message);
default:
return ldb_next_request(module, req);
if (type == LDB_WAIT_ALL) {
return oc_async_wait_all(handle);
} else {
return oc_async_wait(handle);
}
}
static const struct ldb_module_ops objectclass_ops = {
.name = "objectclass",
.request = objectclass_request,
.add = objectclass_add,
.modify = objectclass_modify,
.async_wait = objectclass_async_wait
};
int ldb_objectclass_init(void)
{
return ldb_register_module(&objectclass_ops);
}

View File

@ -85,5 +85,5 @@ vendorVersion: ${VERSION}
# - samldb must be before password_hash, because password_hash checks that the objectclass is of type person (filled in by samldb)
dn: @MODULES
@LIST: rootdse,kludge_acl,paged_results,server_sort,extended_dn,asq,samldb,password_hash,operational,objectguid,rdn_name
@LIST: rootdse,kludge_acl,paged_results,server_sort,extended_dn,asq,samldb,objectclass,password_hash,operational,objectguid,rdn_name

View File

@ -36,37 +36,119 @@ objectClass: user
objectClass: person
cn: LDAPtestUSER
");
if (!ok) {
ok = ldb.del("cn=ldaptestuser,cn=users," + base_dn);
if (!ok) {
println(ldb.errstring());
assert(ok);
}
ok = ldb.add("
dn: cn=ldaptestuser,cn=users," + base_dn + "
objectClass: user
objectClass: person
cn: LDAPtestUSER
");
if (!ok) {
println(ldb.errstring());
assert(ok);
}
}
ok = ldb.add("
dn: cn=ldaptestuser2,cn=users," + base_dn + "
objectClass: user
objectClass: person
objectClass: user
cn: LDAPtestUSER2
");
if (!ok) {
ok = ldb.del("cn=ldaptestuser2,cn=users," + base_dn);
if (!ok) {
println(ldb.errstring());
assert(ok);
}
ok = ldb.add("
dn: cn=ldaptestuser2,cn=users," + base_dn + "
objectClass: person
objectClass: user
cn: LDAPtestUSER2
");
if (!ok) {
println(ldb.errstring());
assert(ok);
}
}
ok = ldb.add("
dn: cn=ldaptestutf8user èùéìòà ,cn=users," + base_dn + "
objectClass: user
");
if (!ok) {
ok = ldb.del("cn=ldaptestutf8user èùéìòà ,cn=users," + base_dn);
if (!ok) {
println(ldb.errstring());
assert(ok);
}
ok = ldb.add("
dn: cn=ldaptestutf8user èùéìòà ,cn=users," + base_dn + "
objectClass: user
");
if (!ok) {
println(ldb.errstring());
assert(ok);
}
}
println("Testing ldb.search");
ok = ldb.add("
dn: cn=ldaptestutf8user2 èùéìòà ,cn=users," + base_dn + "
objectClass: user
");
if (!ok) {
ok = ldb.del("cn=ldaptestutf8user2 èùéìòà ,cn=users," + base_dn);
if (!ok) {
println(ldb.errstring());
assert(ok);
}
ok = ldb.add("
dn: cn=ldaptestutf8user2 èùéìòà ,cn=users," + base_dn + "
objectClass: user
");
if (!ok) {
println(ldb.errstring());
assert(ok);
}
}
println("Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))");
var res = ldb.search("(&(cn=ldaptestuser)(objectClass=user))");
if (res.length != 1) {
println("Could not find (&(cn=ldaptestuser)(objectClass=user))");
assert(res.length == 1);
}
assert(res[0].dn == "cn=ldaptestuser,cn=users," + base_dn);
assert(res[0].cn == "ldaptestuser");
assert(res[0].name == "ldaptestuser");
assert(res[0].objectClass[0] == "top");
assert(res[0].objectClass[1] == "person");
assert(res[0].objectClass[2] == "organizationalPerson");
assert(res[0].objectClass[3] == "user");
assert(res[0].objectGUID != undefined);
assert(res[0].whenCreated != undefined);
println("Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))");
var res = ldb.search("(&(cn=ldaptestuser)(objectClass=user))");
if (res.length != 1) {
println("Could not find (&(cn=ldaptestuser)(objectClass=user))");
assert(res.length == 1);
}
assert(res[0].dn == "cn=ldaptestuser,cn=users," + base_dn);
assert(res[0].cn == "ldaptestuser");
assert(res[0].name == "ldaptestuser");
assert(res[0].objectClass[0] == "top");
assert(res[0].objectClass[1] == "person");
assert(res[0].objectClass[2] == "organizationalPerson");
assert(res[0].objectClass[3] == "user");
assert(res[0].objectGUID != undefined);
assert(res[0].whenCreated != undefined);
@ -76,12 +158,20 @@ objectClass: user
assert(ok);
}
println("Testing ldb.search");
println("Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))");
var res = ldb.search("(&(cn=ldaptestUSer2)(objectClass=user))");
if (res.length != 1) {
println("Could not find (&(cn=ldaptestUSer2)(objectClass=user))");
assert(res.length == 1);
}
assert(res[0].dn == "cn=ldaptestuser2,cn=users," + base_dn);
assert(res[0].cn == "ldaptestuser2");
assert(res[0].name == "ldaptestuser2");
assert(res[0].objectClass[0] == "top");
assert(res[0].objectClass[1] == "person");
assert(res[0].objectClass[2] == "organizationalPerson");
assert(res[0].objectClass[3] == "user");
assert(res[0].objectGUID != undefined);
assert(res[0].whenCreated != undefined);
@ -91,12 +181,21 @@ objectClass: user
assert(ok);
}
println("Testing ldb.search");
println("Testing ldb.search for (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))");
var res = ldb.search("(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))");
if (res.length != 1) {
println("Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))");
assert(res.length == 1);
}
assert(res[0].dn == "cn=ldaptestutf8user èùéìòà,cn=users," + base_dn);
assert(res[0].cn == "ldaptestutf8user èùéìòà");
assert(res[0].name == "ldaptestutf8user èùéìòà");
assert(res[0].objectClass[0] == "top");
assert(res[0].objectClass[1] == "person");
assert(res[0].objectClass[2] == "organizationalPerson");
assert(res[0].objectClass[3] == "user");
assert(res[0].objectGUID != undefined);
assert(res[0].whenCreated != undefined);
@ -105,6 +204,17 @@ objectClass: user
println(ldb.errstring());
assert(ok);
}
println("Testing ldb.search for (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))");
var res = ldb.search("(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))");
if (res.length != 1) {
println("Could not find (expect space collapse, win2k3 fails) (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))");
} else {
assert(res[0].dn == "cn=ldaptestutf8user2 èùéìòà,cn=users," + base_dn);
assert(res[0].cn == "ldaptestutf8user2 èùéìòà");
}
}
function find_basedn(ldb)