wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -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
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user