wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+488
View File
@@ -0,0 +1,488 @@
/*
ldb database library
Copyright (C) Simo Sorce 2005
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: ldb attribute scoped query control module
*
* Description: this module searches all the the objects pointed
* by the DNs contained in the references attribute
*
* Author: Simo Sorce
*/
#include "includes.h"
#include "ldb/include/includes.h"
struct asq_context {
enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
struct ldb_module *module;
void *up_context;
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
const char * const *req_attrs;
char *req_attribute;
enum {
ASQ_CTRL_SUCCESS = 0,
ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX = 21,
ASQ_CTRL_UNWILLING_TO_PERFORM = 53,
ASQ_CTRL_AFFECTS_MULTIPLE_DSA = 71
} asq_ret;
struct ldb_request *base_req;
struct ldb_reply *base_res;
struct ldb_request **reqs;
int num_reqs;
int cur_req;
struct ldb_control **controls;
};
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
void *context,
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
{
struct asq_context *ac;
struct ldb_handle *h;
h = talloc_zero(mem_ctx, struct ldb_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct asq_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, "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->up_context = context;
ac->up_callback = callback;
return h;
}
static int asq_terminate(struct ldb_handle *handle)
{
struct asq_context *ac;
struct ldb_reply *ares;
struct ldb_asq_control *asq;
int i;
ac = talloc_get_type(handle->private_data, struct asq_context);
if (ac == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
handle->status = LDB_SUCCESS;
handle->state = LDB_ASYNC_DONE;
ares = talloc_zero(ac, struct ldb_reply);
if (ares == NULL)
return LDB_ERR_OPERATIONS_ERROR;
ares->type = LDB_REPLY_DONE;
if (ac->controls) {
for (i = 0; ac->controls[i]; i++);
ares->controls = talloc_move(ares, &ac->controls);
} else {
i = 0;
}
ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, i + 2);
if (ares->controls == NULL)
return LDB_ERR_OPERATIONS_ERROR;
ares->controls[i] = talloc(ares->controls, struct ldb_control);
if (ares->controls[i] == NULL)
return LDB_ERR_OPERATIONS_ERROR;
ares->controls[i]->oid = LDB_CONTROL_ASQ_OID;
ares->controls[i]->critical = 0;
asq = talloc_zero(ares->controls[i], struct ldb_asq_control);
if (asq == NULL)
return LDB_ERR_OPERATIONS_ERROR;
asq->result = ac->asq_ret;
ares->controls[i]->data = asq;
ares->controls[i + 1] = NULL;
ac->up_callback(ac->module->ldb, ac->up_context, ares);
return LDB_SUCCESS;
}
static int asq_base_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct asq_context *ac;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct asq_context);
if (ac == NULL) {
goto error;
}
/* we are interested only in the single reply (base search) we receive here */
if (ares->type == LDB_REPLY_ENTRY) {
ac->base_res = talloc_move(ac, &ares);
} else {
talloc_free(ares);
}
return LDB_SUCCESS;
error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
static int asq_reqs_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct asq_context *ac;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct asq_context);
if (ac == NULL) {
goto error;
}
/* we are interested only in the single reply (base search) we receive here */
if (ares->type == LDB_REPLY_ENTRY) {
/* pass the message up to the original callback as we
* do not have to elaborate on it any further */
return ac->up_callback(ac->module->ldb, ac->up_context, ares);
} else { /* ignore any REFERRAL or DONE reply */
talloc_free(ares);
}
return LDB_SUCCESS;
error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
static int asq_search(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_control *control;
struct ldb_asq_control *asq_ctrl;
struct asq_context *ac;
struct ldb_handle *h;
char **base_attrs;
int ret;
/* check if there's a paged request control */
control = get_control_from_list(req->controls, LDB_CONTROL_ASQ_OID);
if (control == NULL) {
/* not found go on */
return ldb_next_request(module, req);
}
req->handle = NULL;
if (!req->callback || !req->context) {
ldb_set_errstring(module->ldb,
"Async interface called with NULL callback function or NULL context");
return LDB_ERR_OPERATIONS_ERROR;
}
asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
if (!asq_ctrl) {
return LDB_ERR_PROTOCOL_ERROR;
}
h = init_handle(req, module, req->context, req->callback);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct asq_context);
req->handle = h;
/* check the search is well formed */
if (req->op.search.scope != LDB_SCOPE_BASE) {
ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
return asq_terminate(h);
}
ac->req_attrs = req->op.search.attrs;
ac->req_attribute = talloc_strdup(ac, asq_ctrl->source_attribute);
if (ac->req_attribute == NULL)
return LDB_ERR_OPERATIONS_ERROR;
/* get the object to retrieve the DNs to search */
ac->base_req = talloc_zero(req, struct ldb_request);
if (ac->base_req == NULL)
return LDB_ERR_OPERATIONS_ERROR;
ac->base_req->operation = req->operation;
ac->base_req->op.search.base = req->op.search.base;
ac->base_req->op.search.scope = LDB_SCOPE_BASE;
ac->base_req->op.search.tree = req->op.search.tree;
base_attrs = talloc_array(ac->base_req, char *, 2);
if (base_attrs == NULL)
return LDB_ERR_OPERATIONS_ERROR;
base_attrs[0] = talloc_strdup(base_attrs, asq_ctrl->source_attribute);
if (base_attrs[0] == NULL)
return LDB_ERR_OPERATIONS_ERROR;
base_attrs[1] = NULL;
ac->base_req->op.search.attrs = (const char * const *)base_attrs;
ac->base_req->context = ac;
ac->base_req->callback = asq_base_callback;
ldb_set_timeout_from_prev_req(module->ldb, req, ac->base_req);
ac->step = ASQ_SEARCH_BASE;
ret = ldb_request(module->ldb, ac->base_req);
if (ret != LDB_SUCCESS) {
return ret;
}
return LDB_SUCCESS;
}
static int asq_requests(struct ldb_handle *handle) {
struct asq_context *ac;
struct ldb_message_element *el;
int i;
ac = talloc_get_type(handle->private_data, struct asq_context);
if (ac == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* look up the DNs */
if (ac->base_res == NULL) {
return LDB_ERR_NO_SUCH_OBJECT;
}
el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
/* no values found */
if (el == NULL) {
ac->asq_ret = ASQ_CTRL_SUCCESS;
return asq_terminate(handle);
}
/* build up the requests call chain */
ac->num_reqs = el->num_values;
ac->cur_req = 0;
ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
if (ac->reqs == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
for (i = 0; i < el->num_values; i++) {
ac->reqs[i] = talloc_zero(ac->reqs, struct ldb_request);
if (ac->reqs[i] == NULL)
return LDB_ERR_OPERATIONS_ERROR;
ac->reqs[i]->operation = LDB_SEARCH;
ac->reqs[i]->op.search.base = ldb_dn_new(ac->reqs[i], ac->module->ldb, (const char *)el->values[i].data);
if ( ! ldb_dn_validate(ac->reqs[i]->op.search.base)) {
ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
return asq_terminate(handle);
}
ac->reqs[i]->op.search.scope = LDB_SCOPE_BASE;
ac->reqs[i]->op.search.tree = ac->base_req->op.search.tree;
ac->reqs[i]->op.search.attrs = ac->req_attrs;
ac->reqs[i]->context = ac;
ac->reqs[i]->callback = asq_reqs_callback;
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->base_req, ac->reqs[i]);
}
ac->step = ASQ_SEARCH_MULTI;
return LDB_SUCCESS;
}
static int asq_wait_none(struct ldb_handle *handle)
{
struct asq_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
handle->state = LDB_ASYNC_PENDING;
handle->status = LDB_SUCCESS;
ac = talloc_get_type(handle->private_data, struct asq_context);
if (ac == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
switch (ac->step) {
case ASQ_SEARCH_BASE:
ret = ldb_wait(ac->base_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->base_req->handle->status != LDB_SUCCESS) {
handle->status = ac->base_req->handle->status;
goto done;
}
if (ac->base_req->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
ret = asq_requests(handle);
/* no break nor return,
* the set of requests is performed in ASQ_SEARCH_MULTI
*/
case ASQ_SEARCH_MULTI:
if (ac->reqs[ac->cur_req]->handle == NULL) {
ret = ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
if (ret != LDB_SUCCESS) {
return ret;
}
}
ret = ldb_wait(ac->reqs[ac->cur_req]->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->reqs[ac->cur_req]->handle->status != LDB_SUCCESS) {
handle->status = ac->reqs[ac->cur_req]->handle->status;
}
if (ac->reqs[ac->cur_req]->handle->state == LDB_ASYNC_DONE) {
ac->cur_req++;
}
if (ac->cur_req < ac->num_reqs) {
return LDB_SUCCESS;
}
return asq_terminate(handle);
default:
ret = LDB_ERR_OPERATIONS_ERROR;
goto done;
}
ret = LDB_SUCCESS;
done:
handle->state = LDB_ASYNC_DONE;
return ret;
}
static int asq_wait_all(struct ldb_handle *handle)
{
int ret;
while (handle->state != LDB_ASYNC_DONE) {
ret = asq_wait_none(handle);
if (ret != LDB_SUCCESS) {
return ret;
}
}
return handle->status;
}
static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
if (type == LDB_WAIT_ALL) {
return asq_wait_all(handle);
} else {
return asq_wait_none(handle);
}
}
static int asq_init(struct ldb_module *module)
{
struct ldb_request *req;
int ret;
req = talloc_zero(module, struct ldb_request);
if (req == NULL) {
ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Out of memory!\n");
return LDB_ERR_OPERATIONS_ERROR;
}
req->operation = LDB_REQ_REGISTER_CONTROL;
req->op.reg_control.oid = LDB_CONTROL_ASQ_OID;
ret = ldb_request(module->ldb, req);
if (ret != LDB_SUCCESS) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n");
}
return ldb_next_init(module);
}
static const struct ldb_module_ops asq_ops = {
.name = "asq",
.search = asq_search,
.wait = asq_wait,
.init_context = asq_init
};
int ldb_asq_init(void)
{
return ldb_register_module(&asq_ops);
}
File diff suppressed because it is too large Load Diff
+158
View File
@@ -0,0 +1,158 @@
/*
ldb database mapping module
Copyright (C) Jelmer Vernooij 2005
Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
* NOTICE: this module is NOT released under the GNU LGPL license as
* other ldb code. This module is release under the GNU GPL v2 or
* later license.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __LDB_MAP_H__
#define __LDB_MAP_H__
/* ldb_map is a skeleton LDB module that can be used for any other modules
* that need to map attributes.
*
* The term 'remote' in this header refers to the connection where the
* original schema is used on while 'local' means the local connection
* that any upper layers will use.
*
* All local attributes will have to have a definition. Not all remote
* attributes need a definition as LDB is a lot less strict than LDAP
* (in other words, sending unknown attributes to an LDAP server hurts us,
* while returning too many attributes in ldb_search() doesn't)
*/
/* Name of the internal attribute pointing from the local to the
* remote part of a record */
#define IS_MAPPED "isMapped"
struct ldb_map_context;
/* convert a local ldb_val to a remote ldb_val */
typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
#define LDB_MAP_MAX_REMOTE_NAMES 10
/* map from local to remote attribute */
struct ldb_map_attribute {
const char *local_name; /* local name */
enum ldb_map_attr_type {
MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely. */
MAP_KEEP, /* Keep as is. Same name locally and remotely. */
MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
MAP_CONVERT, /* Rename + convert data */
MAP_GENERATE /* Use generate function for generating new name/data.
Used for generating attributes based on
multiple remote attributes. */
} type;
/* if set, will be called for search expressions that contain this attribute */
int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *);
union {
struct {
const char *remote_name;
} rename;
struct {
const char *remote_name;
/* Convert local to remote data */
ldb_map_convert_func convert_local;
/* Convert remote to local data */
/* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name
* that is non-NULL before it. */
ldb_map_convert_func convert_remote;
} convert;
struct {
/* Generate the local attribute from remote message */
struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
/* Update remote message with information from local message */
void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
/* Name(s) for this attribute on the remote server. This is an array since
* one local attribute's data can be split up into several attributes
* remotely */
const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
/* Names of additional remote attributes
* required for the generation. NULL
* indicates that `local_attr' suffices. */
/*
#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
*/
} generate;
} u;
};
#define LDB_MAP_MAX_SUBCLASSES 10
#define LDB_MAP_MAX_MUSTS 10
#define LDB_MAP_MAX_MAYS 50
/* map from local to remote objectClass */
struct ldb_map_objectclass {
const char *local_name;
const char *remote_name;
const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
const char *musts[LDB_MAP_MAX_MUSTS];
const char *mays[LDB_MAP_MAX_MAYS];
};
/* private context data */
struct ldb_map_context {
struct ldb_map_attribute *attribute_maps;
/* NOTE: Always declare base classes first here */
const struct ldb_map_objectclass *objectclass_maps;
/* Remote (often operational) attributes that should be added
* to any wildcard search */
const char * const *wildcard_attributes;
/* struct ldb_context *mapped_ldb; */
struct ldb_dn *local_base_dn;
struct ldb_dn *remote_base_dn;
};
/* Global private data */
struct map_private {
void *caller_private;
struct ldb_map_context *context;
};
/* Initialize global private data. */
int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
const struct ldb_map_objectclass *ocls,
const char * const *wildcard_attributes,
const char *name);
/* get copy of map_ops */
struct ldb_module_ops
ldb_map_get_ops(void);
#endif /* __LDB_MAP_H__ */
@@ -0,0 +1,724 @@
/*
ldb database mapping module
Copyright (C) Jelmer Vernooij 2005
Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
* NOTICE: this module is NOT released under the GNU LGPL license as
* other ldb code. This module is release under the GNU GPL v2 or
* later license.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "ldb/include/includes.h"
#include "ldb/modules/ldb_map.h"
#include "ldb/modules/ldb_map_private.h"
/* Mapping message elements
* ======================== */
/* Map a message element into the remote partition. */
static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
{
struct ldb_message_element *el;
int i;
el = talloc_zero(mem_ctx, struct ldb_message_element);
if (el == NULL) {
map_oom(module);
return NULL;
}
el->num_values = old->num_values;
el->values = talloc_array(el, struct ldb_val, el->num_values);
if (el->values == NULL) {
talloc_free(el);
map_oom(module);
return NULL;
}
el->name = map_attr_map_local(el, map, old->name);
for (i = 0; i < el->num_values; i++) {
el->values[i] = ldb_val_map_local(module, el->values, map, &old->values[i]);
}
return el;
}
/* Add a message element either to a local or to a remote message,
* depending on whether it goes into the local or remote partition. */
static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
{
const struct ldb_map_context *data = map_get_context(module);
const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
struct ldb_message_element *el=NULL;
/* Unknown attribute: ignore */
if (map == NULL) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
"Not mapping attribute '%s': no mapping found\n",
old->name);
goto local;
}
switch (map->type) {
case MAP_IGNORE:
goto local;
case MAP_CONVERT:
if (map->u.convert.convert_local == NULL) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
"Not mapping attribute '%s': "
"'convert_local' not set\n",
map->local_name);
goto local;
}
/* fall through */
case MAP_KEEP:
case MAP_RENAME:
el = ldb_msg_el_map_local(module, remote, map, old);
break;
case MAP_GENERATE:
if (map->u.generate.generate_remote == NULL) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
"Not mapping attribute '%s': "
"'generate_remote' not set\n",
map->local_name);
goto local;
}
/* TODO: if this attr requires context:
* make sure all context attrs are mappable (in 'names')
* make sure all context attrs have already been mapped?
* maybe postpone generation until they have been mapped?
*/
map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
return 0;
}
if (el == NULL) {
return -1;
}
return ldb_msg_add(remote, el, old->flags);
local:
el = talloc(local, struct ldb_message_element);
if (el == NULL) {
map_oom(module);
return -1;
}
*el = *old; /* copy the old element */
return ldb_msg_add(local, el, old->flags);
}
/* Mapping messages
* ================ */
/* Check whether a message will be (partially) mapped into the remote partition. */
static BOOL ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
{
const struct ldb_map_context *data = map_get_context(module);
BOOL ret;
int i;
for (i = 0; i < msg->num_elements; i++) {
ret = map_attr_check_remote(data, msg->elements[i].name);
if (ret) {
return ret;
}
}
return False;
}
/* Split message elements that stay in the local partition from those
* that are mapped into the remote partition. */
static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
{
/* const char * const names[]; */
int i, ret;
for (i = 0; i < msg->num_elements; i++) {
/* Skip 'IS_MAPPED' */
if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
"Skipping attribute '%s'\n",
msg->elements[i].name);
continue;
}
ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
if (ret) {
return ret;
}
}
return 0;
}
/* Inbound requests: add, modify, rename, delete
* ============================================= */
/* Add the remote record. */
int map_add_do_remote(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
ac->step = MAP_ADD_REMOTE;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_remote_request(ac->module, ac->remote_req);
}
/* Add the local record. */
int map_add_do_local(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
ac->step = MAP_ADD_LOCAL;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_request(ac->module, ac->local_req);
}
/* Add a record. */
int map_add(struct ldb_module *module, struct ldb_request *req)
{
const struct ldb_message *msg = req->op.add.message;
struct ldb_handle *h;
struct map_context *ac;
struct ldb_message *local, *remote;
const char *dn;
/* Do not manipulate our control entries */
if (ldb_dn_is_special(msg->dn)) {
return ldb_next_request(module, req);
}
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
if (!ldb_dn_check_local(module, msg->dn)) {
return ldb_next_request(module, req);
}
/* No mapping needed, fail */
if (!ldb_msg_check_remote(module, msg)) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* Prepare context and handle */
h = map_init_handle(req, module);
if (h == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct map_context);
/* Prepare the local operation */
ac->local_req = talloc(ac, struct ldb_request);
if (ac->local_req == NULL) {
goto oom;
}
*(ac->local_req) = *req; /* copy the request */
ac->local_req->context = NULL;
ac->local_req->callback = NULL;
/* Prepare the remote operation */
ac->remote_req = talloc(ac, struct ldb_request);
if (ac->remote_req == NULL) {
goto oom;
}
*(ac->remote_req) = *req; /* copy the request */
ac->remote_req->context = NULL;
ac->remote_req->callback = NULL;
/* Prepare the local message */
local = ldb_msg_new(ac->local_req);
if (local == NULL) {
goto oom;
}
local->dn = msg->dn;
/* Prepare the remote message */
remote = ldb_msg_new(ac->remote_req);
if (remote == NULL) {
goto oom;
}
remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
/* Split local from remote message */
ldb_msg_partition(module, local, remote, msg);
ac->local_req->op.add.message = local;
ac->remote_req->op.add.message = remote;
if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
/* No local data or db, just run the remote request */
talloc_free(ac->local_req);
req->handle = h; /* return our own handle to deal with this call */
return map_add_do_remote(h);
}
/* Store remote DN in 'IS_MAPPED' */
/* TODO: use GUIDs here instead */
dn = ldb_dn_alloc_linearized(local, remote->dn);
if (ldb_msg_add_string(local, IS_MAPPED, dn) != 0) {
goto failed;
}
req->handle = h; /* return our own handle to deal with this call */
return map_add_do_local(h);
oom:
map_oom(module);
failed:
talloc_free(h);
return LDB_ERR_OPERATIONS_ERROR;
}
/* Modify the remote record. */
int map_modify_do_remote(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
ac->step = MAP_MODIFY_REMOTE;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_remote_request(ac->module, ac->remote_req);
}
/* Modify the local record. */
int map_modify_do_local(struct ldb_handle *handle)
{
struct map_context *ac;
struct ldb_message *msg;
char *dn;
ac = talloc_get_type(handle->private_data, struct map_context);
if (ac->local_dn == NULL) {
/* No local record present, add it instead */
msg = discard_const_p(struct ldb_message, ac->local_req->op.mod.message);
/* Add local 'IS_MAPPED' */
/* TODO: use GUIDs here instead */
if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD, NULL) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
dn = ldb_dn_alloc_linearized(msg, ac->remote_req->op.mod.message->dn);
if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* Turn request into 'add' */
ac->local_req->operation = LDB_ADD;
ac->local_req->op.add.message = msg;
/* TODO: Could I just leave msg in there? I think so,
* but it looks clearer this way. */
}
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
ac->step = MAP_MODIFY_LOCAL;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_request(ac->module, ac->local_req);
}
/* Modify a record. */
int map_modify(struct ldb_module *module, struct ldb_request *req)
{
const struct ldb_message *msg = req->op.mod.message;
struct ldb_handle *h;
struct map_context *ac;
struct ldb_message *local, *remote;
/* Do not manipulate our control entries */
if (ldb_dn_is_special(msg->dn)) {
return ldb_next_request(module, req);
}
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
if (!ldb_dn_check_local(module, msg->dn)) {
return ldb_next_request(module, req);
}
/* No mapping needed, skip to next module */
/* TODO: What if the remote part exists, the local doesn't,
* and this request wants to modify local data and thus
* add the local record? */
if (!ldb_msg_check_remote(module, msg)) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* Prepare context and handle */
h = map_init_handle(req, module);
if (h == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct map_context);
/* Prepare the local operation */
ac->local_req = talloc(ac, struct ldb_request);
if (ac->local_req == NULL) {
goto oom;
}
*(ac->local_req) = *req; /* copy the request */
ac->local_req->context = NULL;
ac->local_req->callback = NULL;
/* Prepare the remote operation */
ac->remote_req = talloc(ac, struct ldb_request);
if (ac->remote_req == NULL) {
goto oom;
}
*(ac->remote_req) = *req; /* copy the request */
ac->remote_req->context = NULL;
ac->remote_req->callback = NULL;
/* Prepare the local message */
local = ldb_msg_new(ac->local_req);
if (local == NULL) {
goto oom;
}
local->dn = msg->dn;
/* Prepare the remote message */
remote = ldb_msg_new(ac->remote_req);
if (remote == NULL) {
goto oom;
}
remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
/* Split local from remote message */
ldb_msg_partition(module, local, remote, msg);
ac->local_req->op.mod.message = local;
ac->remote_req->op.mod.message = remote;
if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
/* No local data or db, just run the remote request */
talloc_free(ac->local_req);
req->handle = h; /* return our own handle to deal with this call */
return map_modify_do_remote(h);
}
/* prepare the search operation */
ac->search_req = map_search_self_req(ac, msg->dn);
if (ac->search_req == NULL) {
goto failed;
}
ac->step = MAP_SEARCH_SELF_MODIFY;
req->handle = h; /* return our own handle to deal with this call */
return ldb_next_request(module, ac->search_req);
oom:
map_oom(module);
failed:
talloc_free(h);
return LDB_ERR_OPERATIONS_ERROR;
}
/* Delete the remote record. */
int map_delete_do_remote(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
ac->step = MAP_DELETE_REMOTE;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_remote_request(ac->module, ac->remote_req);
}
/* Delete the local record. */
int map_delete_do_local(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
/* No local record, continue remotely */
if (ac->local_dn == NULL) {
return map_delete_do_remote(handle);
}
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
ac->step = MAP_DELETE_LOCAL;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_request(ac->module, ac->local_req);
}
/* Delete a record. */
int map_delete(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_handle *h;
struct map_context *ac;
/* Do not manipulate our control entries */
if (ldb_dn_is_special(req->op.del.dn)) {
return ldb_next_request(module, req);
}
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
if (!ldb_dn_check_local(module, req->op.del.dn)) {
return ldb_next_request(module, req);
}
/* Prepare context and handle */
h = map_init_handle(req, module);
if (h == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct map_context);
/* Prepare the local operation */
ac->local_req = talloc(ac, struct ldb_request);
if (ac->local_req == NULL) {
goto oom;
}
*(ac->local_req) = *req; /* copy the request */
ac->local_req->op.del.dn = req->op.del.dn;
ac->local_req->context = NULL;
ac->local_req->callback = NULL;
/* Prepare the remote operation */
ac->remote_req = talloc(ac, struct ldb_request);
if (ac->remote_req == NULL) {
goto oom;
}
*(ac->remote_req) = *req; /* copy the request */
ac->remote_req->op.del.dn = ldb_dn_map_local(module, ac->remote_req, req->op.del.dn);
/* No local db, just run the remote request */
if (!map_check_local_db(ac->module)) {
req->handle = h; /* return our own handle to deal with this call */
return map_delete_do_remote(h);
}
ac->remote_req->context = NULL;
ac->remote_req->callback = NULL;
/* Prepare the search operation */
ac->search_req = map_search_self_req(ac, req->op.del.dn);
if (ac->search_req == NULL) {
goto failed;
}
req->handle = h; /* return our own handle to deal with this call */
ac->step = MAP_SEARCH_SELF_DELETE;
return ldb_next_request(module, ac->search_req);
oom:
map_oom(module);
failed:
talloc_free(h);
return LDB_ERR_OPERATIONS_ERROR;
}
/* Rename the remote record. */
int map_rename_do_remote(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
ac->step = MAP_RENAME_REMOTE;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_remote_request(ac->module, ac->remote_req);
}
/* Update the local 'IS_MAPPED' attribute. */
int map_rename_do_fixup(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
ac->step = MAP_RENAME_FIXUP;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_request(ac->module, ac->down_req);
}
/* Rename the local record. */
int map_rename_do_local(struct ldb_handle *handle)
{
struct map_context *ac;
ac = talloc_get_type(handle->private_data, struct map_context);
/* No local record, continue remotely */
if (ac->local_dn == NULL) {
return map_rename_do_remote(handle);
}
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
ac->step = MAP_RENAME_LOCAL;
handle->state = LDB_ASYNC_INIT;
handle->status = LDB_SUCCESS;
return ldb_next_request(ac->module, ac->local_req);
}
/* Rename a record. */
int map_rename(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_handle *h;
struct map_context *ac;
/* Do not manipulate our control entries */
if (ldb_dn_is_special(req->op.rename.olddn)) {
return ldb_next_request(module, req);
}
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
(!ldb_dn_check_local(module, req->op.rename.newdn))) {
return ldb_next_request(module, req);
}
/* Rename into/out of the mapped partition requested, bail out */
if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
!ldb_dn_check_local(module, req->op.rename.newdn)) {
return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
}
/* Prepare context and handle */
h = map_init_handle(req, module);
if (h == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct map_context);
/* Prepare the local operation */
ac->local_req = talloc(ac, struct ldb_request);
if (ac->local_req == NULL) {
goto oom;
}
*(ac->local_req) = *req; /* copy the request */
ac->local_req->op.rename.olddn = req->op.rename.olddn;
ac->local_req->op.rename.newdn = req->op.rename.newdn;
ac->local_req->context = NULL;
ac->local_req->callback = NULL;
/* Prepare the remote operation */
ac->remote_req = talloc(ac, struct ldb_request);
if (ac->remote_req == NULL) {
goto oom;
}
*(ac->remote_req) = *req; /* copy the request */
ac->remote_req->op.rename.olddn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.olddn);
ac->remote_req->op.rename.newdn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.newdn);
ac->remote_req->context = NULL;
ac->remote_req->callback = NULL;
/* No local db, just run the remote request */
if (!map_check_local_db(ac->module)) {
req->handle = h; /* return our own handle to deal with this call */
return map_rename_do_remote(h);
}
/* Prepare the fixup operation */
/* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
ac->down_req = map_build_fixup_req(ac, req->op.rename.newdn, ac->remote_req->op.rename.newdn);
if (ac->down_req == NULL) {
goto failed;
}
/* Prepare the search operation */
ac->search_req = map_search_self_req(ac, req->op.rename.olddn);
if (ac->search_req == NULL) {
goto failed;
}
req->handle = h; /* return our own handle to deal with this call */
ac->step = MAP_SEARCH_SELF_RENAME;
return ldb_next_request(module, ac->search_req);
oom:
map_oom(module);
failed:
talloc_free(h);
return LDB_ERR_OPERATIONS_ERROR;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
/* A handy macro to report Out of Memory conditions */
#define map_oom(module) ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
/* The type of search callback functions */
typedef int (*ldb_search_callback)(struct ldb_context *, void *, struct ldb_reply *);
/* The special DN from which the local and remote base DNs are fetched */
#define MAP_DN_NAME "@MAP"
#define MAP_DN_FROM "@FROM"
#define MAP_DN_TO "@TO"
/* Private data structures
* ======================= */
/* Context data for mapped requests */
struct map_context {
enum map_step {
MAP_SEARCH_REMOTE,
MAP_ADD_REMOTE,
MAP_ADD_LOCAL,
MAP_SEARCH_SELF_MODIFY,
MAP_MODIFY_REMOTE,
MAP_MODIFY_LOCAL,
MAP_SEARCH_SELF_DELETE,
MAP_DELETE_REMOTE,
MAP_DELETE_LOCAL,
MAP_SEARCH_SELF_RENAME,
MAP_RENAME_REMOTE,
MAP_RENAME_FIXUP,
MAP_RENAME_LOCAL
} step;
struct ldb_module *module;
struct ldb_dn *local_dn;
const struct ldb_parse_tree *local_tree;
const char * const *local_attrs;
const char * const *remote_attrs;
const char * const *all_attrs;
struct ldb_request *orig_req;
struct ldb_request *local_req;
struct ldb_request *remote_req;
struct ldb_request *down_req;
struct ldb_request *search_req;
/* for search, we may have a lot of contexts */
int num_searches;
struct ldb_request **search_reqs;
};
/* Context data for mapped search requests */
struct map_search_context {
struct map_context *ac;
struct ldb_reply *local_res;
struct ldb_reply *remote_res;
};
/* Common operations
* ================= */
/* The following definitions come from lib/ldb/modules/ldb_map.c */
const struct ldb_map_context *map_get_context(struct ldb_module *module);
struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares);
struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module);
int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
BOOL map_check_local_db(struct ldb_module *module);
BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
BOOL ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn);
const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback);
struct ldb_request *map_search_self_req(struct map_context *ac, struct ldb_dn *dn);
struct ldb_request *map_build_fixup_req(struct map_context *ac, struct ldb_dn *olddn, struct ldb_dn *newdn);
int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map);
/* LDB Requests
* ============ */
/* The following definitions come from lib/ldb/modules/ldb_map_inbound.c */
int map_add_do_remote(struct ldb_handle *handle);
int map_add_do_local(struct ldb_handle *handle);
int map_add(struct ldb_module *module, struct ldb_request *req);
int map_modify_do_remote(struct ldb_handle *handle);
int map_modify_do_local(struct ldb_handle *handle);
int map_modify(struct ldb_module *module, struct ldb_request *req);
int map_delete_do_remote(struct ldb_handle *handle);
int map_delete_do_local(struct ldb_handle *handle);
int map_delete(struct ldb_module *module, struct ldb_request *req);
int map_rename_do_remote(struct ldb_handle *handle);
int map_rename_do_fixup(struct ldb_handle *handle);
int map_rename_do_local(struct ldb_handle *handle);
int map_rename(struct ldb_module *module, struct ldb_request *req);
/* The following definitions come from lib/ldb/modules/ldb_map_outbound.c */
int map_search(struct ldb_module *module, struct ldb_request *req);
+694
View File
@@ -0,0 +1,694 @@
/*
ldb database library
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
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: objectClass sorting module
*
* Description: sort the objectClass attribute into the class hierarchy
*
* Author: Andrew Bartlett
*/
#include "includes.h"
#include "ldb/include/includes.h"
struct oc_context {
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_reply *search_res;
struct ldb_request *mod_req;
};
struct class_list {
struct class_list *prev, *next;
const char *objectclass;
};
static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
{
struct oc_context *ac;
struct ldb_handle *h;
h = talloc_zero(req, struct ldb_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct oc_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, "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_sort(struct ldb_module *module,
TALLOC_CTX *mem_ctx,
struct ldb_message_element *objectclass_element,
struct class_list **sorted_out)
{
int i;
int layer;
struct class_list *sorted = NULL, *parent_class = NULL,
*subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
/* DESIGN:
*
* We work on 4 different 'bins' (implemented here as linked lists):
*
* * sorted: the eventual list, in the order we wish to push
* into the database. This is the only ordered list.
*
* * parent_class: The current parent class 'bin' we are
* trying to find subclasses for
*
* * subclass: The subclasses we have found so far
*
* * unsorted: The remaining objectClasses
*
* The process is a matter of filtering objectClasses up from
* unsorted into sorted. Order is irrelevent in the later 3 'bins'.
*
* We start with 'top' (found and promoted to parent_class
* initially). Then we find (in unsorted) all the direct
* subclasses of 'top'. parent_classes is concatenated onto
* the end of 'sorted', and subclass becomes the list in
* parent_class.
*
* We then repeat, until we find no more subclasses. Any left
* over classes are added to the end.
*
*/
/* Firstly, dump all the objectClass elements into the
* unsorted bin, except for 'top', which is special */
for (i=0; i < objectclass_element->num_values; i++) {
current = talloc(mem_ctx, struct class_list);
if (!current) {
ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list");
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
current->objectclass = (const char *)objectclass_element->values[i].data;
/* this is the root of the tree. We will start
* looking for subclasses from here */
if (ldb_attr_cmp("top", current->objectclass) == 0) {
DLIST_ADD(parent_class, current);
} else {
DLIST_ADD(unsorted, current);
}
}
/* DEBUGGING aid: how many layers are we down now? */
layer = 0;
do {
layer++;
/* Find all the subclasses of classes in the
* parent_classes. Push them onto the subclass list */
/* 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);
/* Walk the list of possible subclasses in unsorted */
for (poss_subclass = unsorted; poss_subclass; ) {
struct class_list *next;
/* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
next = poss_subclass->next;
for (i = 0; subclasses && subclasses[i]; i++) {
if (ldb_attr_cmp(poss_subclass->objectclass, subclasses[i]) == 0) {
DLIST_REMOVE(unsorted, poss_subclass);
DLIST_ADD(subclass, poss_subclass);
break;
}
}
poss_subclass = next;
}
}
/* Now push the parent_classes as sorted, we are done with
these. Add to the END of the list by concatenation */
DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
/* and now find subclasses of these */
parent_class = subclass;
subclass = NULL;
/* If we didn't find any subclasses we will fall out
* the bottom here */
} while (parent_class);
/* This shouldn't happen, and would break MMC, but we can't
* afford to loose objectClasses. Perhaps there was no 'top',
* or some other schema error?
*
* Detecting schema errors is the job of the schema module, so
* at this layer we just try not to loose data
*/
DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
*sorted_out = sorted;
return LDB_SUCCESS;
}
static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_message_element *objectclass_element;
struct class_list *sorted, *current;
struct ldb_request *down_req;
struct ldb_message *msg;
int ret;
TALLOC_CTX *mem_ctx;
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);
}
objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass");
/* If no part of this add has an objectClass, then we don't
* need to make any changes. cn=rootdse doesn't have an objectClass */
if (!objectclass_element) {
return ldb_next_request(module, req);
}
mem_ctx = talloc_new(req);
if (mem_ctx == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
if (ret != LDB_SUCCESS) {
return ret;
}
/* prepare the first operation */
down_req = talloc(req, struct ldb_request);
if (down_req == NULL) {
ldb_set_errstring(module->ldb, "Out of memory!");
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
*down_req = *req; /* copy the request */
down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
if (down_req->op.add.message == NULL) {
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
ldb_msg_remove_attr(msg, "objectClass");
ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
return ret;
}
/* We must completely replace the existing objectClass entry,
* because we need it sorted */
/* Move from the linked list back into an ldb msg */
for (current = sorted; current; current = current->next) {
ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
talloc_free(mem_ctx);
return ret;
}
}
talloc_free(mem_ctx);
ret = ldb_msg_sanity_check(module->ldb, msg);
if (ret != LDB_SUCCESS) {
return ret;
}
/* go on with the call chain */
ret = ldb_next_request(module, down_req);
/* do not free down_req as the call results may be linked to it,
* it will be freed when the upper level request get freed */
if (ret == LDB_SUCCESS) {
req->handle = down_req->handle;
}
return ret;
}
static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_message_element *objectclass_element;
struct ldb_message *msg;
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);
}
objectclass_element = 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 (!objectclass_element) {
return ldb_next_request(module, req);
}
switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
case LDB_FLAG_MOD_DELETE:
/* Delete everything? Probably totally illigal, but hey! */
if (objectclass_element->num_values == 0) {
return ldb_next_request(module, req);
}
break;
case LDB_FLAG_MOD_REPLACE:
{
struct ldb_request *down_req;
struct class_list *sorted, *current;
TALLOC_CTX *mem_ctx;
int ret;
mem_ctx = talloc_new(req);
if (mem_ctx == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* prepare the first operation */
down_req = talloc(req, struct ldb_request);
if (down_req == NULL) {
ldb_set_errstring(module->ldb, "Out of memory!");
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
*down_req = *req; /* copy the request */
down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
if (down_req->op.add.message == NULL) {
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
if (ret != LDB_SUCCESS) {
return ret;
}
/* We must completely replace the existing objectClass entry,
* because we need it sorted */
ldb_msg_remove_attr(msg, "objectClass");
ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
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(msg, "objectClass", current->objectclass);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
talloc_free(mem_ctx);
return ret;
}
}
talloc_free(mem_ctx);
ret = ldb_msg_sanity_check(module->ldb, msg);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
return ret;
}
/* go on with the call chain */
ret = ldb_next_request(module, down_req);
/* do not free down_req as the call results may be linked to it,
* it will be freed when the upper level request get freed */
if (ret == LDB_SUCCESS) {
req->handle = down_req->handle;
}
return ret;
}
}
{
struct ldb_handle *h;
struct oc_context *ac;
h = oc_init_handle(req, module);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct oc_context);
/* return or own handle to deal with this call */
req->handle = h;
/* prepare the first operation */
ac->down_req = talloc(ac, struct ldb_request);
if (ac->down_req == NULL) {
ldb_set_errstring(module->ldb, "Out of memory!");
return LDB_ERR_OPERATIONS_ERROR;
}
*(ac->down_req) = *req; /* copy the request */
ac->down_req->context = NULL;
ac->down_req->callback = NULL;
ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
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_reply *ares)
{
struct oc_context *ac;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(context, struct oc_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, "Too many results");
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
ac->search_res = talloc_move(ac, &ares);
} else {
talloc_free(ares);
}
return LDB_SUCCESS;
}
static int objectclass_search_self(struct ldb_handle *h) {
struct oc_context *ac;
static const char * const attrs[] = { "objectClass", NULL };
ac = talloc_get_type(h->private_data, struct oc_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->search_req, NULL);
if (ac->search_req->op.search.tree == NULL) {
ldb_set_errstring(ac->module->ldb, "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->context = ac;
ac->search_req->callback = get_self_callback;
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
ac->step = OC_SEARCH_SELF;
return ldb_next_request(ac->module, ac->search_req);
}
static int objectclass_do_mod(struct ldb_handle *h) {
struct oc_context *ac;
struct ldb_message_element *objectclass_element;
struct ldb_message *msg;
TALLOC_CTX *mem_ctx;
struct class_list *sorted, *current;
int ret;
ac = talloc_get_type(h->private_data, struct oc_context);
mem_ctx = talloc_new(ac);
if (mem_ctx == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->mod_req = talloc(ac, struct ldb_request);
if (ac->mod_req == NULL) {
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
ac->mod_req->operation = LDB_MODIFY;
ac->mod_req->controls = NULL;
ac->mod_req->context = ac;
ac->mod_req->callback = NULL;
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
/* 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, "objectclass: could not create new modify msg");
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
/* This is now the objectClass list from the database */
objectclass_element = ldb_msg_find_element(ac->search_res->message,
"objectClass");
if (!objectclass_element) {
/* Where did it go? Move along now, nothing to see here */
talloc_free(mem_ctx);
return LDB_SUCCESS;
}
/* modify dn */
msg->dn = ac->orig_req->op.mod.message->dn;
ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted);
if (ret != LDB_SUCCESS) {
return ret;
}
/* 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(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(ac->module->ldb, "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(msg, "objectClass", current->objectclass);
if (ret != LDB_SUCCESS) {
ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
talloc_free(mem_ctx);
return ret;
}
}
ret = ldb_msg_sanity_check(ac->module->ldb, msg);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
return ret;
}
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_wait(struct ldb_handle *handle) {
struct oc_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
handle->state = LDB_ASYNC_PENDING;
handle->status = LDB_SUCCESS;
ac = talloc_get_type(handle->private_data, struct oc_context);
switch (ac->step) {
case OC_DO_REQ:
ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->down_req->handle->status != LDB_SUCCESS) {
handle->status = ac->down_req->handle->status;
goto done;
}
if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* mods done, go on */
return objectclass_search_self(handle);
case OC_SEARCH_SELF:
ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->search_req->handle->status != LDB_SUCCESS) {
handle->status = ac->search_req->handle->status;
goto done;
}
if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* self search done, go on */
return objectclass_do_mod(handle);
case OC_DO_MOD:
ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->mod_req->handle->status != LDB_SUCCESS) {
handle->status = ac->mod_req->handle->status;
goto done;
}
if (ac->mod_req->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 oc_wait_all(struct ldb_handle *handle) {
int ret;
while (handle->state != LDB_ASYNC_DONE) {
ret = oc_wait(handle);
if (ret != LDB_SUCCESS) {
return ret;
}
}
return handle->status;
}
static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
if (type == LDB_WAIT_ALL) {
return oc_wait_all(handle);
} else {
return oc_wait(handle);
}
}
static const struct ldb_module_ops objectclass_ops = {
.name = "objectclass",
.add = objectclass_add,
.modify = objectclass_modify,
.wait = objectclass_wait
};
int ldb_objectclass_init(void)
{
return ldb_register_module(&objectclass_ops);
}
+312
View File
@@ -0,0 +1,312 @@
/*
ldb database library
Copyright (C) Andrew Tridgell 2005
Copyright (C) Simo Sorce 2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
handle operational attributes
*/
/*
createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
for the above two, we do the search as normal, and if
createTimestamp or modifyTimestamp is asked for, then do
additional searches for whenCreated and whenChanged and fill in
the resulting values
we also need to replace these with the whenCreated/whenChanged
equivalent in the search expression trees
whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
on init we need to setup attribute handlers for these so
comparisons are done correctly. The resolution is 1 second.
on add we need to add both the above, for current time
on modify we need to change whenChanged
subschemaSubentry: HIDDEN, not-searchable,
points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
for this one we do the search as normal, then add the static
value if requested. How do we work out the $BASEDN from inside a
module?
structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
for this one we do the search as normal, then if requested ask
for objectclass, change the attribute name, and add it
allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
list of attributes that can be modified - requires schema lookup
attributeTypes: in schema only
objectClasses: in schema only
matchingRules: in schema only
matchingRuleUse: in schema only
creatorsName: not supported by w2k3?
modifiersName: not supported by w2k3?
*/
#include "includes.h"
#include "ldb/include/includes.h"
/*
construct a canonical name from a message
*/
static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
{
char *canonicalName;
canonicalName = ldb_dn_canonical_string(msg, msg->dn);
if (canonicalName == NULL) {
return -1;
}
return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
}
/*
a list of attribute names that should be substituted in the parse
tree before the search is done
*/
static const struct {
const char *attr;
const char *replace;
} parse_tree_sub[] = {
{ "createTimestamp", "whenCreated" },
{ "modifyTimestamp", "whenChanged" }
};
/*
a list of attribute names that are hidden, but can be searched for
using another (non-hidden) name to produce the correct result
*/
static const struct {
const char *attr;
const char *replace;
int (*constructor)(struct ldb_module *, struct ldb_message *);
} search_sub[] = {
{ "createTimestamp", "whenCreated", NULL },
{ "modifyTimestamp", "whenChanged", NULL },
{ "structuralObjectClass", "objectClass", NULL },
{ "canonicalName", "distinguishedName", construct_canonical_name }
};
/*
post process a search result record. For any search_sub[] attributes that were
asked for, we need to call the appropriate copy routine to copy the result
into the message, then remove any attributes that we added to the search but were
not asked for by the user
*/
static int operational_search_post_process(struct ldb_module *module,
struct ldb_message *msg,
const char * const *attrs)
{
int i, a=0;
for (a=0;attrs && attrs[a];a++) {
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
continue;
}
/* construct the new attribute, using either a supplied
constructor or a simple copy */
if (search_sub[i].constructor) {
if (search_sub[i].constructor(module, msg) != 0) {
goto failed;
}
} else if (ldb_msg_copy_attr(msg,
search_sub[i].replace,
search_sub[i].attr) != 0) {
goto failed;
}
/* remove the added search attribute, unless it was asked for
by the user */
if (search_sub[i].replace == NULL ||
ldb_attr_in_list(attrs, search_sub[i].replace) ||
ldb_attr_in_list(attrs, "*")) {
continue;
}
ldb_msg_remove_attr(msg, search_sub[i].replace);
}
}
return 0;
failed:
ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
"operational_search_post_process failed for attribute '%s'\n",
attrs[a]);
return -1;
}
/*
hook search operations
*/
struct operational_context {
struct ldb_module *module;
void *up_context;
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
const char * const *attrs;
};
static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct operational_context *ac;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct operational_context);
if (ares->type == LDB_REPLY_ENTRY) {
/* for each record returned post-process to add any derived
attributes that have been asked for */
if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
goto error;
}
}
return ac->up_callback(ldb, ac->up_context, ares);
error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
static int operational_search(struct ldb_module *module, struct ldb_request *req)
{
struct operational_context *ac;
struct ldb_request *down_req;
const char **search_attrs = NULL;
int i, a, ret;
req->handle = NULL;
ac = talloc(req, struct operational_context);
if (ac == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->module = module;
ac->up_context = req->context;
ac->up_callback = req->callback;
ac->attrs = req->op.search.attrs;
down_req = talloc_zero(req, struct ldb_request);
if (down_req == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
down_req->operation = req->operation;
down_req->op.search.base = req->op.search.base;
down_req->op.search.scope = req->op.search.scope;
down_req->op.search.tree = req->op.search.tree;
/* FIXME: I hink we should copy the tree and keep the original
* unmodified. SSS */
/* replace any attributes in the parse tree that are
searchable, but are stored using a different name in the
backend */
for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
ldb_parse_tree_attr_replace(req->op.search.tree,
parse_tree_sub[i].attr,
parse_tree_sub[i].replace);
}
/* in the list of attributes we are looking for, rename any
attributes to the alias for any hidden attributes that can
be fetched directly using non-hidden names */
for (a=0;ac->attrs && ac->attrs[a];a++) {
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
search_sub[i].replace) {
if (!search_attrs) {
search_attrs = ldb_attr_list_copy(req, ac->attrs);
if (search_attrs == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
}
search_attrs[a] = search_sub[i].replace;
}
}
}
/* use new set of attrs if any */
if (search_attrs) down_req->op.search.attrs = search_attrs;
else down_req->op.search.attrs = req->op.search.attrs;
down_req->controls = req->controls;
down_req->context = ac;
down_req->callback = operational_callback;
ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
/* perform the search */
ret = ldb_next_request(module, down_req);
/* do not free down_req as the call results may be linked to it,
* it will be freed when the upper level request get freed */
if (ret == LDB_SUCCESS) {
req->handle = down_req->handle;
}
return ret;
}
static int operational_init(struct ldb_module *ctx)
{
/* setup some standard attribute handlers */
ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
return ldb_next_init(ctx);
}
static const struct ldb_module_ops operational_ops = {
.name = "operational",
.search = operational_search,
.init_context = operational_init
};
int ldb_operational_init(void)
{
return ldb_register_module(&operational_ops);
}
@@ -0,0 +1,565 @@
/*
ldb database library
Copyright (C) Simo Sorce 2005-2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: paged_result
*
* Component: ldb paged results control module
*
* Description: this module caches a complete search and sends back
* results in chunks as asked by the client
*
* Author: Simo Sorce
*/
#include "includes.h"
#include "ldb/include/includes.h"
struct message_store {
/* keep the whole ldb_reply as an optimization
* instead of freeing and talloc-ing the container
* on each result */
struct ldb_reply *r;
struct message_store *next;
};
struct private_data;
struct results_store {
struct private_data *priv;
char *cookie;
time_t timestamp;
struct results_store *prev;
struct results_store *next;
struct message_store *first;
struct message_store *last;
int num_entries;
struct message_store *first_ref;
struct message_store *last_ref;
struct ldb_control **controls;
struct ldb_request *req;
};
struct private_data {
int next_free_id;
struct results_store *store;
};
int store_destructor(struct results_store *store)
{
if (store->prev) {
store->prev->next = store->next;
}
if (store->next) {
store->next->prev = store->prev;
}
if (store == store->priv->store) {
store->priv->store = NULL;
}
return 0;
}
static struct results_store *new_store(struct private_data *priv)
{
struct results_store *newr;
int new_id = priv->next_free_id++;
/* TODO: we should have a limit on the number of
* outstanding paged searches
*/
newr = talloc(priv, struct results_store);
if (!newr) return NULL;
newr->priv = priv;
newr->cookie = talloc_asprintf(newr, "%d", new_id);
if (!newr->cookie) {
talloc_free(newr);
return NULL;
}
newr->timestamp = time(NULL);
newr->first = NULL;
newr->num_entries = 0;
newr->first_ref = NULL;
newr->controls = NULL;
/* put this entry as first */
newr->prev = NULL;
newr->next = priv->store;
if (priv->store != NULL) priv->store->prev = newr;
priv->store = newr;
talloc_set_destructor(newr, store_destructor);
return newr;
}
struct paged_context {
struct ldb_module *module;
void *up_context;
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
int size;
struct results_store *store;
};
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
void *context,
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
{
struct paged_context *ac;
struct ldb_handle *h;
h = talloc_zero(mem_ctx, struct ldb_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct paged_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, "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->up_context = context;
ac->up_callback = callback;
return h;
}
static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct paged_context *ac = NULL;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct paged_context);
if (ares->type == LDB_REPLY_ENTRY) {
if (ac->store->first == NULL) {
ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
} else {
ac->store->last->next = talloc(ac->store, struct message_store);
ac->store->last = ac->store->last->next;
}
if (ac->store->last == NULL) {
goto error;
}
ac->store->num_entries++;
ac->store->last->r = talloc_steal(ac->store->last, ares);
ac->store->last->next = NULL;
}
if (ares->type == LDB_REPLY_REFERRAL) {
if (ac->store->first_ref == NULL) {
ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
} else {
ac->store->last_ref->next = talloc(ac->store, struct message_store);
ac->store->last_ref = ac->store->last_ref->next;
}
if (ac->store->last_ref == NULL) {
goto error;
}
ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
ac->store->last_ref->next = NULL;
}
if (ares->type == LDB_REPLY_DONE) {
ac->store->controls = talloc_move(ac->store, &ares->controls);
talloc_free(ares);
}
return LDB_SUCCESS;
error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
static int paged_search(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_control *control;
struct private_data *private_data;
struct ldb_paged_control *paged_ctrl;
struct ldb_control **saved_controls;
struct paged_context *ac;
struct ldb_handle *h;
int ret;
/* check if there's a paged request control */
control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
if (control == NULL) {
/* not found go on */
return ldb_next_request(module, req);
}
private_data = talloc_get_type(module->private_data, struct private_data);
req->handle = NULL;
if (!req->callback || !req->context) {
ldb_set_errstring(module->ldb,
"Async interface called with NULL callback function or NULL context");
return LDB_ERR_OPERATIONS_ERROR;
}
paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
if (!paged_ctrl) {
return LDB_ERR_PROTOCOL_ERROR;
}
h = init_handle(req, module, req->context, req->callback);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct paged_context);
ac->size = paged_ctrl->size;
/* check if it is a continuation search the store */
if (paged_ctrl->cookie_len == 0) {
ac->store = new_store(private_data);
if (ac->store == NULL) {
talloc_free(h);
return LDB_ERR_UNWILLING_TO_PERFORM;
}
ac->store->req = talloc(ac->store, struct ldb_request);
if (!ac->store->req)
return LDB_ERR_OPERATIONS_ERROR;
ac->store->req->operation = req->operation;
ac->store->req->op.search.base = req->op.search.base;
ac->store->req->op.search.scope = req->op.search.scope;
ac->store->req->op.search.tree = req->op.search.tree;
ac->store->req->op.search.attrs = req->op.search.attrs;
ac->store->req->controls = req->controls;
/* save it locally and remove it from the list */
/* we do not need to replace them later as we
* are keeping the original req intact */
if (!save_controls(control, ac->store->req, &saved_controls)) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->store->req->context = ac;
ac->store->req->callback = paged_search_callback;
ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
ret = ldb_next_request(module, ac->store->req);
} else {
struct results_store *current = NULL;
for (current = private_data->store; current; current = current->next) {
if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
current->timestamp = time(NULL);
break;
}
}
if (current == NULL) {
talloc_free(h);
return LDB_ERR_UNWILLING_TO_PERFORM;
}
ac->store = current;
ret = LDB_SUCCESS;
}
req->handle = h;
/* check if it is an abandon */
if (ac->size == 0) {
talloc_free(ac->store);
h->status = LDB_SUCCESS;
h->state = LDB_ASYNC_DONE;
return LDB_SUCCESS;
}
/* TODO: age out old outstanding requests */
return ret;
}
static int paged_results(struct ldb_handle *handle)
{
struct paged_context *ac;
struct ldb_paged_control *paged;
struct ldb_reply *ares;
struct message_store *msg;
int i, num_ctrls, ret;
ac = talloc_get_type(handle->private_data, struct paged_context);
if (ac->store == NULL)
return LDB_ERR_OPERATIONS_ERROR;
while (ac->store->num_entries > 0 && ac->size > 0) {
msg = ac->store->first;
ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
if (ret != LDB_SUCCESS) {
handle->status = ret;
handle->state = LDB_ASYNC_DONE;
return ret;
}
ac->store->first = msg->next;
talloc_free(msg);
ac->store->num_entries--;
ac->size--;
}
handle->state = LDB_ASYNC_DONE;
while (ac->store->first_ref != NULL) {
msg = ac->store->first_ref;
ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
if (ret != LDB_SUCCESS) {
handle->status = ret;
handle->state = LDB_ASYNC_DONE;
return ret;
}
ac->store->first_ref = msg->next;
talloc_free(msg);
}
ares = talloc_zero(ac->store, struct ldb_reply);
if (ares == NULL) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
num_ctrls = 2;
i = 0;
if (ac->store->controls != NULL) {
ares->controls = ac->store->controls;
while (ares->controls[i]) i++; /* counting */
ares->controls = talloc_move(ares, &ac->store->controls);
num_ctrls += i;
}
ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
if (ares->controls == NULL) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->controls[i] = talloc(ares->controls, struct ldb_control);
if (ares->controls[i] == NULL) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
if (ares->controls[i]->oid == NULL) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->controls[i]->critical = 0;
ares->controls[i + 1] = NULL;
paged = talloc(ares->controls[i], struct ldb_paged_control);
if (paged == NULL) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->controls[i]->data = paged;
if (ac->size > 0) {
paged->size = 0;
paged->cookie = NULL;
paged->cookie_len = 0;
} else {
paged->size = ac->store->num_entries;
paged->cookie = talloc_strdup(paged, ac->store->cookie);
paged->cookie_len = strlen(paged->cookie) + 1;
}
ares->type = LDB_REPLY_DONE;
ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
handle->status = ret;
return ret;
}
static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
struct paged_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
handle->state = LDB_ASYNC_PENDING;
ac = talloc_get_type(handle->private_data, struct paged_context);
if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
/* if lower level is finished we do not need to call it anymore */
/* return all we have until size == 0 or we empty storage */
ret = paged_results(handle);
/* we are done, if num_entries is zero free the storage
* as that mean we delivered the last batch */
if (ac->store->num_entries == 0) {
talloc_free(ac->store);
}
return ret;
}
if (type == LDB_WAIT_ALL) {
while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
ret = ldb_wait(ac->store->req->handle, type);
if (ret != LDB_SUCCESS) {
handle->state = LDB_ASYNC_DONE;
handle->status = ret;
return ret;
}
}
ret = paged_results(handle);
/* we are done, if num_entries is zero free the storage
* as that mean we delivered the last batch */
if (ac->store->num_entries == 0) {
talloc_free(ac->store);
}
return ret;
}
ret = ldb_wait(ac->store->req->handle, type);
if (ret != LDB_SUCCESS) {
handle->state = LDB_ASYNC_DONE;
handle->status = ret;
return ret;
}
handle->status = ret;
if (ac->store->num_entries >= ac->size ||
ac->store->req->handle->state == LDB_ASYNC_DONE) {
ret = paged_results(handle);
/* we are done, if num_entries is zero free the storage
* as that mean we delivered the last batch */
if (ac->store->num_entries == 0) {
talloc_free(ac->store);
}
}
return ret;
}
static int paged_request_init(struct ldb_module *module)
{
struct private_data *data;
struct ldb_request *req;
int ret;
data = talloc(module, struct private_data);
if (data == NULL) {
return LDB_ERR_OTHER;
}
data->next_free_id = 1;
data->store = NULL;
module->private_data = data;
req = talloc(module, struct ldb_request);
if (req == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
req->operation = LDB_REQ_REGISTER_CONTROL;
req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
req->controls = NULL;
ret = ldb_request(module->ldb, req);
if (ret != LDB_SUCCESS) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "paged_request: Unable to register control with rootdse!\n");
}
talloc_free(req);
return ldb_next_init(module);
}
static const struct ldb_module_ops paged_ops = {
.name = "paged_results",
.search = paged_search,
.wait = paged_wait,
.init_context = paged_request_init
};
int ldb_paged_results_init(void)
{
return ldb_register_module(&paged_ops);
}
@@ -0,0 +1,468 @@
/*
ldb database library
Copyright (C) Simo Sorce 2005-2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: paged_searches
*
* Component: ldb paged searches module
*
* Description: this module detects if the remote ldap server supports
* paged results and use them to transparently access all objects
*
* Author: Simo Sorce
*/
#include "includes.h"
#include "ldb/include/includes.h"
#define PS_DEFAULT_PAGE_SIZE 500
/* 500 objects per query seem to be a decent compromise
* the default AD limit per request is 1000 entries */
struct private_data {
bool paged_supported;
};
struct ps_context {
struct ldb_module *module;
void *up_context;
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
struct ldb_request *orig_req;
struct ldb_request *new_req;
bool pending;
char **saved_referrals;
int num_referrals;
};
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
void *context,
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
{
struct ps_context *ac;
struct ldb_handle *h;
h = talloc_zero(mem_ctx, struct ldb_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct ps_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, "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->up_context = context;
ac->up_callback = callback;
ac->pending = False;
ac->saved_referrals = NULL;
ac->num_referrals = 0;
return h;
}
static int check_ps_continuation(struct ldb_reply *ares, struct ps_context *ac)
{
struct ldb_paged_control *rep_control, *req_control;
/* look up our paged control */
if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) {
/* something wrong here */
return LDB_ERR_OPERATIONS_ERROR;
}
rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control);
if (rep_control->cookie_len == 0) {
/* we are done */
ac->pending = False;
return LDB_SUCCESS;
}
/* more processing required */
/* let's fill in the request control with the new cookie */
/* if there's a reply control we must find a request
* control matching it */
if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ac->new_req->controls[0]->oid) != 0) {
/* something wrong here */
return LDB_ERR_OPERATIONS_ERROR;
}
req_control = talloc_get_type(ac->new_req->controls[0]->data, struct ldb_paged_control);
if (req_control->cookie) {
talloc_free(req_control->cookie);
}
req_control->cookie = talloc_memdup(req_control,
rep_control->cookie,
rep_control->cookie_len);
req_control->cookie_len = rep_control->cookie_len;
ac->pending = True;
return LDB_SUCCESS;
}
static int store_referral(char *referral, struct ps_context *ac)
{
ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
if (!ac->saved_referrals) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
if (!ac->saved_referrals[ac->num_referrals]) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->num_referrals++;
ac->saved_referrals[ac->num_referrals] = NULL;
return LDB_SUCCESS;
}
static int send_referrals(struct ldb_context *ldb, struct ps_context *ac)
{
struct ldb_reply *ares;
int i;
for (i = 0; i < ac->num_referrals; i++) {
ares = talloc_zero(ac, struct ldb_reply);
if (!ares) {
return LDB_ERR_OPERATIONS_ERROR;
}
ares->type = LDB_REPLY_REFERRAL;
ares->referral = ac->saved_referrals[i];
ac->up_callback(ldb, ac->up_context, ares);
}
return LDB_SUCCESS;
}
static int ps_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct ps_context *ac = NULL;
int ret = LDB_ERR_OPERATIONS_ERROR;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct ps_context);
switch (ares->type) {
case LDB_REPLY_ENTRY:
ac->up_callback(ldb, ac->up_context, ares);
break;
case LDB_REPLY_REFERRAL:
ret = store_referral(ares->referral, ac);
if (ret != LDB_SUCCESS) {
goto error;
}
break;
case LDB_REPLY_DONE:
ret = check_ps_continuation(ares, ac);
if (ret != LDB_SUCCESS) {
goto error;
}
if (!ac->pending) {
/* send referrals */
ret = send_referrals(ldb, ac);
if (ret != LDB_SUCCESS) {
goto error;
}
/* send REPLY_DONE */
ac->up_callback(ldb, ac->up_context, ares);
}
break;
default:
goto error;
}
return LDB_SUCCESS;
error:
talloc_free(ares);
return ret;
}
static int ps_search(struct ldb_module *module, struct ldb_request *req)
{
struct private_data *private_data;
struct ldb_paged_control *control;
struct ps_context *ac;
struct ldb_handle *h;
private_data = talloc_get_type(module->private_data, struct private_data);
/* check if paging is supported and if there is a any control */
if (!private_data || !private_data->paged_supported || req->controls) {
/* do not touch this request paged controls not
* supported or explicit controls have been set or we
* are just not setup yet */
return ldb_next_request(module, req);
}
if (!req->callback || !req->context) {
ldb_set_errstring(module->ldb,
"Async interface called with NULL callback function or NULL context");
return LDB_ERR_OPERATIONS_ERROR;
}
h = init_handle(req, module, req->context, req->callback);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct ps_context);
ac->new_req = talloc(ac, struct ldb_request);
if (!ac->new_req) return LDB_ERR_OPERATIONS_ERROR;
ac->new_req->controls = talloc_array(ac->new_req, struct ldb_control *, 2);
if (!ac->new_req->controls) return LDB_ERR_OPERATIONS_ERROR;
ac->new_req->controls[0] = talloc(ac->new_req->controls, struct ldb_control);
if (!ac->new_req->controls[0]) return LDB_ERR_OPERATIONS_ERROR;
control = talloc(ac->new_req->controls[0], struct ldb_paged_control);
if (!control) return LDB_ERR_OPERATIONS_ERROR;
control->size = PS_DEFAULT_PAGE_SIZE;
control->cookie = NULL;
control->cookie_len = 0;
ac->new_req->controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
ac->new_req->controls[0]->critical = 1;
ac->new_req->controls[0]->data = control;
ac->new_req->controls[1] = NULL;
ac->new_req->operation = req->operation;
ac->new_req->op.search.base = req->op.search.base;
ac->new_req->op.search.scope = req->op.search.scope;
ac->new_req->op.search.tree = req->op.search.tree;
ac->new_req->op.search.attrs = req->op.search.attrs;
ac->new_req->context = ac;
ac->new_req->callback = ps_callback;
ldb_set_timeout_from_prev_req(module->ldb, req, ac->new_req);
req->handle = h;
return ldb_next_request(module, ac->new_req);
}
static int ps_continuation(struct ldb_handle *handle)
{
struct ps_context *ac;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(handle->private_data, struct ps_context);
/* reset the requests handle */
ac->new_req->handle = NULL;
return ldb_next_request(handle->module, ac->new_req);
}
static int ps_wait_none(struct ldb_handle *handle)
{
struct ps_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
handle->state = LDB_ASYNC_PENDING;
handle->status = LDB_SUCCESS;
ac = talloc_get_type(handle->private_data, struct ps_context);
ret = ldb_wait(ac->new_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->new_req->handle->status != LDB_SUCCESS) {
handle->status = ac->new_req->handle->status;
goto done;
}
if (ac->new_req->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* see if we need to send another request for the next batch */
if (ac->pending) {
ret = ps_continuation(handle);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
/* continue the search with the next request */
return LDB_SUCCESS;
}
ret = LDB_SUCCESS;
done:
handle->state = LDB_ASYNC_DONE;
return ret;
}
static int ps_wait_all(struct ldb_handle *handle)
{
int ret;
while (handle->state != LDB_ASYNC_DONE) {
ret = ps_wait_none(handle);
if (ret != LDB_SUCCESS) {
return ret;
}
}
return handle->status;
}
static int ps_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
if (type == LDB_WAIT_ALL) {
return ps_wait_all(handle);
} else {
return ps_wait_none(handle);
}
}
static int check_supported_paged(struct ldb_context *ldb, void *context,
struct ldb_reply *ares)
{
struct private_data *data;
data = talloc_get_type(context,
struct private_data);
if (ares->type == LDB_REPLY_ENTRY) {
if (ldb_msg_check_string_attribute(ares->message,
"supportedControl",
LDB_CONTROL_PAGED_RESULTS_OID)) {
data->paged_supported = True;
}
}
return LDB_SUCCESS;
}
static int ps_init(struct ldb_module *module)
{
static const char *attrs[] = { "supportedControl", NULL };
struct private_data *data;
int ret;
struct ldb_request *req;
data = talloc(module, struct private_data);
if (data == NULL) {
return LDB_ERR_OTHER;
}
module->private_data = data;
data->paged_supported = False;
req = talloc(module, struct ldb_request);
if (req == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return LDB_ERR_OPERATIONS_ERROR;
}
req->operation = LDB_SEARCH;
req->op.search.base = ldb_dn_new(req, module->ldb, NULL);
req->op.search.scope = LDB_SCOPE_BASE;
req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
if (req->op.search.tree == NULL) {
ldb_set_errstring(module->ldb, "Unable to parse search expression");
talloc_free(req);
return LDB_ERR_OPERATIONS_ERROR;
}
req->op.search.attrs = attrs;
req->controls = NULL;
req->context = data;
req->callback = check_supported_paged;
ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
ret = ldb_next_request(module, req);
if (ret == LDB_SUCCESS) {
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
talloc_free(req);
if (ret != LDB_SUCCESS) {
return ret;
}
return ldb_next_init(module);
}
static const struct ldb_module_ops ps_ops = {
.name = "paged_searches",
.search = ps_search,
.wait = ps_wait,
.init_context = ps_init
};
int ldb_paged_searches_init(void)
{
return ldb_register_module(&ps_ops);
}
+343
View File
@@ -0,0 +1,343 @@
/*
ldb database library
Copyright (C) Andrew Bartlet 2005
Copyright (C) Simo Sorce 2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: rdb_name
*
* Component: ldb rdn name module
*
* Description: keep a consistent name attribute on objects manpulations
*
* Author: Andrew Bartlet
*
* Modifications:
* - made the module async
* Simo Sorce Mar 2006
*/
#include "includes.h"
#include "ldb/include/includes.h"
static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
{
int i;
for (i = 0; i < msg->num_elements; i++) {
if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
return &msg->elements[i];
}
}
return NULL;
}
static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_request *down_req;
struct ldb_message *msg;
struct ldb_message_element *attribute;
const char *rdn_name;
struct ldb_val rdn_val;
int i, ret;
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
/* do not manipulate our control entries */
if (ldb_dn_is_special(req->op.add.message->dn)) {
return ldb_next_request(module, req);
}
down_req = talloc(req, struct ldb_request);
if (down_req == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
*down_req = *req;
down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
if (msg == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
rdn_name = ldb_dn_get_rdn_name(msg->dn);
if (rdn_name == NULL) {
talloc_free(down_req);
return LDB_ERR_OPERATIONS_ERROR;
}
rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(msg->dn));
/* Perhaps someone above us tried to set this? */
if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
attribute->num_values = 0;
}
if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
talloc_free(down_req);
return LDB_ERR_OPERATIONS_ERROR;
}
attribute = rdn_name_find_attribute(msg, rdn_name);
if (!attribute) {
if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
talloc_free(down_req);
return LDB_ERR_OPERATIONS_ERROR;
}
} else {
const struct ldb_attrib_handler *handler = ldb_attrib_handler(module->ldb, rdn_name);
for (i = 0; i < attribute->num_values; i++) {
if (handler->comparison_fn(module->ldb, msg, &rdn_val, &attribute->values[i]) == 0) {
/* overwrite so it matches in case */
attribute->values[i] = rdn_val;
break;
}
}
if (i == attribute->num_values) {
ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
"RDN mismatch on %s: %s (%s)",
ldb_dn_get_linearized(msg->dn), rdn_name, rdn_val.data);
talloc_free(down_req);
return LDB_ERR_OPERATIONS_ERROR;
}
}
/* go on with the call chain */
ret = ldb_next_request(module, down_req);
/* do not free down_req as the call results may be linked to it,
* it will be freed when the upper level request get freed */
if (ret == LDB_SUCCESS) {
req->handle = down_req->handle;
}
return ret;
}
struct rename_context {
enum {RENAME_RENAME, RENAME_MODIFY} step;
struct ldb_request *orig_req;
struct ldb_request *down_req;
struct ldb_request *mod_req;
};
static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_handle *h;
struct rename_context *ac;
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n");
/* do not manipulate our control entries */
if (ldb_dn_is_special(req->op.rename.newdn)) {
return ldb_next_request(module, req);
}
h = talloc_zero(req, struct ldb_handle);
if (h == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
h->module = module;
ac = talloc_zero(h, struct rename_context);
if (ac == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
h->private_data = (void *)ac;
h->state = LDB_ASYNC_INIT;
h->status = LDB_SUCCESS;
ac->orig_req = req;
ac->down_req = talloc(req, struct ldb_request);
if (ac->down_req == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
*(ac->down_req) = *req;
ac->step = RENAME_RENAME;
req->handle = h;
/* rename first, modify "name" if rename is ok */
return ldb_next_request(module, ac->down_req);
}
static int rdn_name_rename_do_mod(struct ldb_handle *h) {
struct rename_context *ac;
const char *rdn_name;
struct ldb_val rdn_val;
struct ldb_message *msg;
ac = talloc_get_type(h->private_data, struct rename_context);
ac->mod_req = talloc_zero(ac, struct ldb_request);
ac->mod_req->operation = LDB_MODIFY;
ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
if (msg == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
msg->dn = ldb_dn_copy(msg, ac->orig_req->op.rename.newdn);
if (msg->dn == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
rdn_name = ldb_dn_get_rdn_name(ac->orig_req->op.rename.newdn);
if (rdn_name == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(ac->orig_req->op.rename.newdn));
if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
ldb_set_timeout_from_prev_req(h->module->ldb, ac->orig_req, ac->mod_req);
ac->step = RENAME_MODIFY;
/* do the mod call */
return ldb_request(h->module->ldb, ac->mod_req);
}
static int rename_wait(struct ldb_handle *handle)
{
struct rename_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
if (handle->state == LDB_ASYNC_DONE) {
return handle->status;
}
handle->state = LDB_ASYNC_PENDING;
handle->status = LDB_SUCCESS;
ac = talloc_get_type(handle->private_data, struct rename_context);
switch(ac->step) {
case RENAME_RENAME:
ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->down_req->handle->status != LDB_SUCCESS) {
handle->status = ac->down_req->handle->status;
goto done;
}
if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
/* rename operation done */
return rdn_name_rename_do_mod(handle);
case RENAME_MODIFY:
ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
if (ac->mod_req->handle->status != LDB_SUCCESS) {
handle->status = ac->mod_req->handle->status;
goto done;
}
if (ac->mod_req->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 rename_wait_all(struct ldb_handle *handle) {
int ret;
while (handle->state != LDB_ASYNC_DONE) {
ret = rename_wait(handle);
if (ret != LDB_SUCCESS) {
return ret;
}
}
return handle->status;
}
static int rdn_name_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
if (type == LDB_WAIT_ALL) {
return rename_wait_all(handle);
} else {
return rename_wait(handle);
}
}
static const struct ldb_module_ops rdn_name_ops = {
.name = "rdn_name",
.add = rdn_name_add,
.rename = rdn_name_rename,
.wait = rdn_name_wait
};
int ldb_rdn_name_init(void)
{
return ldb_register_module(&rdn_name_ops);
}
+137
View File
@@ -0,0 +1,137 @@
/*
ldb database library
Copyright (C) Simo Sorce 2004
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: ldb skel module
*
* Description: example module
*
* Author: Simo Sorce
*/
#include "includes.h"
#include "ldb/include/includes.h"
struct private_data {
char *some_private_data;
};
/* search */
static int skel_search(struct ldb_module *module, struct ldb_request *req)
{
return ldb_next_request(module, req);
}
/* add */
static int skel_add(struct ldb_module *module, struct ldb_request *req){
return ldb_next_request(module, req);
}
/* modify */
static int skel_modify(struct ldb_module *module, struct ldb_request *req)
{
return ldb_next_request(module, req);
}
/* delete */
static int skel_delete(struct ldb_module *module, struct ldb_request *req)
{
return ldb_next_request(module, req);
}
/* rename */
static int skel_rename(struct ldb_module *module, struct ldb_request *req)
{
return ldb_next_request(module, req);
}
/* start a transaction */
static int skel_start_trans(struct ldb_module *module)
{
return ldb_next_start_trans(module);
}
/* end a transaction */
static int skel_end_trans(struct ldb_module *module)
{
return ldb_next_end_trans(module);
}
/* delete a transaction */
static int skel_del_trans(struct ldb_module *module)
{
return ldb_next_del_trans(module);
}
static int skel_destructor(struct ldb_module *ctx)
{
struct private_data *data = talloc_get_type(ctx->private_data, struct private_data);
/* put your clean-up functions here */
if (data->some_private_data) talloc_free(data->some_private_data);
return 0;
}
static int skel_request(struct ldb_module *module, struct ldb_request *req)
{
return ldb_next_request(module, req);
}
static int skel_init(struct ldb_module *ctx)
{
struct private_data *data;
data = talloc(ctx, struct private_data);
if (data == NULL) {
return 1;
}
data->some_private_data = NULL;
ctx->private_data = data;
talloc_set_destructor (ctx, skel_destructor);
return ldb_next_init(ctx);
}
static const struct ldb_module_ops skel_ops = {
.name = "skel",
.init_context = skel_init,
.search = skel_search,
.add = skel_add,
.modify = skel_modify,
.del = skel_delete,
.rename = skel_rename,
.request = skel_request,
.start_transaction = skel_start_trans,
.end_transaction = skel_end_trans,
.del_transaction = skel_del_trans,
};
int ldb_skel_init(void)
{
return ldb_register_module(&skel_ops);
}
+443
View File
@@ -0,0 +1,443 @@
/*
ldb database library
Copyright (C) Simo Sorce 2005
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: ldb server side sort control module
*
* Description: this module sorts the results of a search
*
* Author: Simo Sorce
*/
#include "includes.h"
#include "ldb/include/includes.h"
struct opaque {
struct ldb_context *ldb;
const struct ldb_attrib_handler *h;
const char *attribute;
int reverse;
int result;
};
struct sort_context {
struct ldb_module *module;
void *up_context;
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
char *attributeName;
char *orderingRule;
int reverse;
struct ldb_request *req;
struct ldb_message **msgs;
char **referrals;
struct ldb_control **controls;
int num_msgs;
int num_refs;
const struct ldb_attrib_handler *h;
int sort_result;
};
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
void *context,
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
{
struct sort_context *ac;
struct ldb_handle *h;
h = talloc_zero(mem_ctx, struct ldb_handle);
if (h == NULL) {
ldb_set_errstring(module->ldb, "Out of Memory");
return NULL;
}
h->module = module;
ac = talloc_zero(h, struct sort_context);
if (ac == NULL) {
ldb_set_errstring(module->ldb, "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->up_context = context;
ac->up_callback = callback;
return h;
}
static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
{
struct ldb_control **controls;
struct ldb_sort_resp_control *resp;
int i;
if (*ctrls) {
controls = *ctrls;
for (i = 0; controls[i]; i++);
controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
} else {
i = 0;
controls = talloc_array(mem_ctx, struct ldb_control *, 2);
}
if (! controls )
return LDB_ERR_OPERATIONS_ERROR;
*ctrls = controls;
controls[i+1] = NULL;
controls[i] = talloc(controls, struct ldb_control);
if (! controls[i] )
return LDB_ERR_OPERATIONS_ERROR;
controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
controls[i]->critical = 0;
resp = talloc(controls[i], struct ldb_sort_resp_control);
if (! resp )
return LDB_ERR_OPERATIONS_ERROR;
resp->result = result;
resp->attr_desc = talloc_strdup(resp, desc);
if (! resp->attr_desc )
return LDB_ERR_OPERATIONS_ERROR;
controls[i]->data = resp;
return LDB_SUCCESS;
}
static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
{
struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
struct ldb_message_element *el1, *el2;
if (ac->sort_result != 0) {
/* an error occurred previously,
* let's exit the sorting by returning always 0 */
return 0;
}
el1 = ldb_msg_find_element(*msg1, ac->attributeName);
el2 = ldb_msg_find_element(*msg2, ac->attributeName);
if (!el1 || !el2) {
/* the attribute was not found return and
* set an error */
ac->sort_result = 53;
return 0;
}
if (ac->reverse)
return ac->h->comparison_fn(ac->module->ldb, ac, &el2->values[0], &el1->values[0]);
return ac->h->comparison_fn(ac->module->ldb, ac, &el1->values[0], &el2->values[0]);
}
static int server_sort_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
struct sort_context *ac = NULL;
if (!context || !ares) {
ldb_set_errstring(ldb, "NULL Context or Result in callback");
goto error;
}
ac = talloc_get_type(context, struct sort_context);
if (ares->type == LDB_REPLY_ENTRY) {
ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
if (! ac->msgs) {
goto error;
}
ac->msgs[ac->num_msgs + 1] = NULL;
ac->msgs[ac->num_msgs] = talloc_move(ac->msgs, &ares->message);
ac->num_msgs++;
}
if (ares->type == LDB_REPLY_REFERRAL) {
ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
if (! ac->referrals) {
goto error;
}
ac->referrals[ac->num_refs + 1] = NULL;
ac->referrals[ac->num_refs] = talloc_move(ac->referrals, &ares->referral);
ac->num_refs++;
}
if (ares->type == LDB_REPLY_DONE) {
ac->controls = talloc_move(ac, &ares->controls);
}
talloc_free(ares);
return LDB_SUCCESS;
error:
talloc_free(ares);
return LDB_ERR_OPERATIONS_ERROR;
}
static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_control *control;
struct ldb_server_sort_control **sort_ctrls;
struct ldb_control **saved_controls;
struct sort_context *ac;
struct ldb_handle *h;
int ret;
/* check if there's a paged request control */
control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
if (control == NULL) {
/* not found go on */
return ldb_next_request(module, req);
}
req->handle = NULL;
if (!req->callback || !req->context) {
ldb_set_errstring(module->ldb,
"Async interface called with NULL callback function or NULL context");
return LDB_ERR_OPERATIONS_ERROR;
}
h = init_handle(req, module, req->context, req->callback);
if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(h->private_data, struct sort_context);
sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
if (!sort_ctrls) {
return LDB_ERR_PROTOCOL_ERROR;
}
/* FIXME: we do not support more than one attribute for sorting right now */
/* FIXME: we need to check if the attribute type exist or return an error */
if (sort_ctrls[1] != NULL) {
if (control->critical) {
struct ldb_reply *ares;
ares = talloc_zero(req, struct ldb_reply);
if (!ares)
return LDB_ERR_OPERATIONS_ERROR;
/* 53 = unwilling to perform */
ares->type = LDB_REPLY_DONE;
if ((ret = build_response(ares, &ares->controls, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
return ret;
}
h->status = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
h->state = LDB_ASYNC_DONE;
ret = ac->up_callback(module->ldb, ac->up_context, ares);
return ret;
} else {
/* just pass the call down and don't do any sorting */
ldb_next_request(module, req);
}
}
ac->attributeName = sort_ctrls[0]->attributeName;
ac->orderingRule = sort_ctrls[0]->orderingRule;
ac->reverse = sort_ctrls[0]->reverse;
ac->req = talloc(req, struct ldb_request);
if (!ac->req)
return LDB_ERR_OPERATIONS_ERROR;
ac->req->operation = req->operation;
ac->req->op.search.base = req->op.search.base;
ac->req->op.search.scope = req->op.search.scope;
ac->req->op.search.tree = req->op.search.tree;
ac->req->op.search.attrs = req->op.search.attrs;
ac->req->controls = req->controls;
/* save it locally and remove it from the list */
/* we do not need to replace them later as we
* are keeping the original req intact */
if (!save_controls(control, ac->req, &saved_controls)) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac->req->context = ac;
ac->req->callback = server_sort_search_callback;
ldb_set_timeout_from_prev_req(module->ldb, req, ac->req);
req->handle = h;
return ldb_next_request(module, ac->req);
}
static int server_sort_results(struct ldb_handle *handle)
{
struct sort_context *ac;
struct ldb_reply *ares;
int i, ret;
ac = talloc_get_type(handle->private_data, struct sort_context);
ac->h = ldb_attrib_handler(ac->module->ldb, ac->attributeName);
ac->sort_result = 0;
ldb_qsort(ac->msgs, ac->num_msgs,
sizeof(struct ldb_message *),
ac, (ldb_qsort_cmp_fn_t)sort_compare);
for (i = 0; i < ac->num_msgs; i++) {
ares = talloc_zero(ac, struct ldb_reply);
if (!ares) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->type = LDB_REPLY_ENTRY;
ares->message = talloc_move(ares, &ac->msgs[i]);
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
if (handle->status != LDB_SUCCESS) {
return handle->status;
}
}
for (i = 0; i < ac->num_refs; i++) {
ares = talloc_zero(ac, struct ldb_reply);
if (!ares) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->type = LDB_REPLY_REFERRAL;
ares->referral = talloc_move(ares, &ac->referrals[i]);
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
if (handle->status != LDB_SUCCESS) {
return handle->status;
}
}
ares = talloc_zero(ac, struct ldb_reply);
if (!ares) {
handle->status = LDB_ERR_OPERATIONS_ERROR;
return handle->status;
}
ares->type = LDB_REPLY_DONE;
ares->controls = talloc_move(ares, &ac->controls);
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
if (handle->status != LDB_SUCCESS) {
return handle->status;
}
if ((ret = build_response(ac, &ac->controls, ac->sort_result, "sort control is not complete yet")) != LDB_SUCCESS) {
return ret;
}
return LDB_SUCCESS;
}
static int server_sort_wait(struct ldb_handle *handle, enum ldb_wait_type type)
{
struct sort_context *ac;
int ret;
if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
ac = talloc_get_type(handle->private_data, struct sort_context);
ret = ldb_wait(ac->req->handle, type);
if (ret != LDB_SUCCESS) {
handle->status = ret;
return ret;
}
handle->state = ac->req->handle->state;
handle->status = ac->req->handle->status;
if (handle->status != LDB_SUCCESS) {
return handle->status;
}
if (handle->state == LDB_ASYNC_DONE) {
ret = server_sort_results(handle);
}
return ret;
}
static int server_sort_init(struct ldb_module *module)
{
struct ldb_request *req;
int ret;
req = talloc(module, struct ldb_request);
if (req == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
req->operation = LDB_REQ_REGISTER_CONTROL;
req->op.reg_control.oid = LDB_CONTROL_SERVER_SORT_OID;
req->controls = NULL;
ret = ldb_request(module->ldb, req);
if (ret != LDB_SUCCESS) {
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "server_sort: Unable to register control with rootdse!\n");
}
talloc_free(req);
return ldb_next_init(module);
}
static const struct ldb_module_ops server_sort_ops = {
.name = "server_sort",
.search = server_sort_search,
.wait = server_sort_wait,
.init_context = server_sort_init
};
int ldb_sort_init(void)
{
return ldb_register_module(&server_sort_ops);
}