Fix the mess with ldb includes.
[metze/samba/wip.git] / source4 / lib / ldb / modules / asq.c
index 2975f4d832d464517cb26554a9e8416257e25101..475b609e412693d324fd4ea33bf1294fc77c1325 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2005
+   Copyright (C) Simo Sorce  2005-2008
 
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
@@ -10,7 +10,7 @@
    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.
+   version 3 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
@@ -18,8 +18,7 @@
    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
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
  *  Author: Simo Sorce
  */
 
-#include "includes.h"
-#include "ldb/include/includes.h"
+#include "ldb_module.h"
 
-#define ASQ_CTRL_SUCCESS                       0
-#define ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX      21
-#define ASQ_CTRL_UNWILLING_TO_PERFORM          53
-#define ASQ_CTRL_AFFECTS_MULTIPLE_DSA          71
+struct asq_context {
 
-static int build_response(struct ldb_result *res, int result)
+       enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
+
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+       struct ldb_asq_control *asq_ctrl;
+
+       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_reply *base_res;
+
+       struct ldb_request **reqs;
+       int num_reqs;
+       int cur_req;
+
+       struct ldb_control **controls;
+};
+
+static struct asq_context *asq_context_init(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_context *ldb;
+       struct asq_context *ac;
+
+       ldb = ldb_module_get_ctx(module);
+
+       ac = talloc_zero(req, struct asq_context);
+       if (ac == NULL) {
+               ldb_oom(ldb);
+               return NULL;
+       }
+
+       ac->module = module;
+       ac->req = req;
+
+       return ac;
+}
+
+static int asq_search_continue(struct asq_context *ac);
+
+static int asq_search_terminate(struct asq_context *ac)
 {
        struct ldb_asq_control *asq;
        int i;
 
-       if (res->controls) {
-               for (i = 0; res->controls[i]; i++);
-               res->controls = talloc_realloc(res, res->controls, struct ldb_control *, i + 2);
+       if (ac->controls) {
+               for (i = 0; ac->controls[i]; i++) /* count em */ ;
        } else {
                i = 0;
-               res->controls = talloc_array(res, struct ldb_control *, 2);
        }
-       if (res->controls == NULL)
+
+       ac->controls = talloc_realloc(ac, ac->controls, struct ldb_control *, i + 2);
+
+       if (ac->controls == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-       res->controls[i] = talloc(res->controls, struct ldb_control);
-       if (res->controls[i] == NULL)
+       ac->controls[i] = talloc(ac->controls, struct ldb_control);
+       if (ac->controls[i] == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-       res->controls[i]->oid = LDB_CONTROL_ASQ_OID;
-       res->controls[i]->critical = 0;
+       ac->controls[i]->oid = LDB_CONTROL_ASQ_OID;
+       ac->controls[i]->critical = 0;
 
-       asq = talloc_zero(res->controls[i], struct ldb_asq_control);
+       asq = talloc_zero(ac->controls[i], struct ldb_asq_control);
        if (asq == NULL)
                return LDB_ERR_OPERATIONS_ERROR;
 
-       asq->result = result;
-       
-       res->controls[i]->data = asq;
+       asq->result = ac->asq_ret;
 
-       res->controls[i + 1] = NULL;
+       ac->controls[i]->data = asq;
 
-       return LDB_SUCCESS;
+       ac->controls[i + 1] = NULL;
+
+       return ldb_module_done(ac->req, ac->controls, NULL, LDB_SUCCESS);
 }
 
-/* search */
-static int asq_search(struct ldb_module *module, struct ldb_request *req)
+static int asq_base_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       struct ldb_control *control;
-       struct ldb_asq_control *asq_ctrl;
-       struct ldb_request *base_req;
-       struct ldb_message_element *el;
-       struct ldb_result *res;
-       char **base_attrs;
-       int i, c, ret;
+       struct asq_context *ac;
+       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);
+       ac = talloc_get_type(req->context, struct asq_context);
+
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       /* pre-allocate a clean result structure */
-       req->op.search.res = res = talloc_zero(req, struct ldb_result);
-       if (res == NULL)
-               return LDB_ERR_OPERATIONS_ERROR;
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               ac->base_res = talloc_move(ac, &ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore referrals */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
+
+               talloc_free(ares);
+
+               /* next step */
+               ret = asq_search_continue(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               break;
 
-       /* check the search is well formed */
-       if (req->op.search.scope != LDB_SCOPE_BASE) {
-               return build_response(res, ASQ_CTRL_UNWILLING_TO_PERFORM);
        }
+       return LDB_SUCCESS;
+}
 
-       asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
-       if (!asq_ctrl) {
-               return LDB_ERR_PROTOCOL_ERROR;
+static int asq_reqs_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct asq_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct asq_context);
+
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       /* get the object to retrieve the DNs to search */
-       base_req = talloc_zero(req, struct ldb_request);
-       if (base_req == NULL)
-               return LDB_ERR_OPERATIONS_ERROR;
-       base_req->operation = LDB_REQ_SEARCH;
-       base_req->op.search.base = req->op.search.base;
-       base_req->op.search.scope = LDB_SCOPE_BASE;
-       base_req->op.search.tree = req->op.search.tree;
-       base_attrs = talloc_array(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)
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* pass the message up to the original callback as we
+                * do not have to elaborate on it any further */
+               ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore referrals */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
+
+               talloc_free(ares);
+
+               ret = asq_search_continue(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               break;
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int asq_build_first_request(struct asq_context *ac, struct ldb_request **base_req)
+{
+       struct ldb_context *ldb;
+       const char **base_attrs;
+       int ret;
+
+       ldb = ldb_module_get_ctx(ac->module);
+
+       ac->req_attrs = ac->req->op.search.attrs;
+       ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute);
+       if (ac->req_attribute == NULL)
                return LDB_ERR_OPERATIONS_ERROR;
-       base_attrs[1] = NULL;
-       base_req->op.search.attrs = (const char * const *)base_attrs;
-       base_req->creds = req->creds;
 
-       ret = ldb_request(module->ldb, base_req);
+       base_attrs = talloc_array(ac, const char *, 2);
+       if (base_attrs == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+       base_attrs[0] = talloc_strdup(base_attrs, ac->asq_ctrl->source_attribute);
+       if (base_attrs[0] == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+       base_attrs[1] = NULL;
 
+       ret = ldb_build_search_req(base_req, ldb, ac,
+                                       ac->req->op.search.base,
+                                       LDB_SCOPE_BASE,
+                                       NULL,
+                                       (const char * const *)base_attrs,
+                                       NULL,
+                                       ac, asq_base_callback,
+                                       ac->req);
        if (ret != LDB_SUCCESS) {
-               talloc_free(base_req);
-               return ret;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (base_req->op.search.res->count == 0) {
-               talloc_free(base_req);
-               return build_response(res, ASQ_CTRL_SUCCESS);
+       return LDB_SUCCESS;
+}
+
+static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated)
+{
+       struct ldb_context *ldb;
+       struct ldb_control **saved_controls;
+       struct ldb_control *control;
+       struct ldb_dn *dn;
+       struct ldb_message_element *el;
+       int ret, i;
+
+       if (ac->base_res == NULL) {
+               return LDB_ERR_NO_SUCH_OBJECT;
        }
-       
-       /* look up the DNs */
-       el = ldb_msg_find_element(base_req->op.search.res->msgs[0],
-                                 asq_ctrl->source_attribute);
+
+       ldb = ldb_module_get_ctx(ac->module);
+
+       el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
        /* no values found */
        if (el == NULL) {
-               talloc_free(base_req);
-               return build_response(res, ASQ_CTRL_SUCCESS);
+               ac->asq_ret = ASQ_CTRL_SUCCESS;
+               *terminated = true;
+               return asq_search_terminate(ac);
+       }
+
+       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, c = 0; i < el->num_values; i++) {
-               struct ldb_request *exp_req;
+       for (i = 0; i < el->num_values; i++) {
 
-               exp_req = talloc_zero(req, struct ldb_request);
-               if (exp_req == NULL)
+               dn = ldb_dn_new(ac, ldb,
+                               (const char *)el->values[i].data);
+               if ( ! ldb_dn_validate(dn)) {
+                       ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
+                       *terminated = true;
+                       return asq_search_terminate(ac);
+               }
+
+               ret = ldb_build_search_req_ex(&ac->reqs[i],
+                                               ldb, ac,
+                                               dn, LDB_SCOPE_BASE,
+                                               ac->req->op.search.tree,
+                                               ac->req_attrs,
+                                               ac->req->controls,
+                                               ac, asq_reqs_callback,
+                                               ac->req);
+               if (ret != LDB_SUCCESS) {
                        return LDB_ERR_OPERATIONS_ERROR;
-               exp_req->operation = LDB_REQ_SEARCH;
-               exp_req->op.search.base = ldb_dn_explode(exp_req, (const char *)el->values[i].data);
-               if (exp_req->op.search.base == NULL) {
-                       return build_response(res, ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX);
                }
-               exp_req->op.search.scope = LDB_SCOPE_BASE;
-               exp_req->op.search.tree = req->op.search.tree;
-               exp_req->op.search.attrs = req->op.search.attrs;
-               exp_req->creds = req->creds;
 
-               ret = ldb_request(module->ldb, exp_req);
+               /* remove the ASQ control itself */
+               control = ldb_request_get_control(ac->req, LDB_CONTROL_ASQ_OID);
+               if (!save_controls(control, ac->reqs[i], &saved_controls)) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       }
 
-               if (ret != LDB_SUCCESS)
-                       return ret;
+       return LDB_SUCCESS;
+}
+
+static int asq_search_continue(struct asq_context *ac)
+{
+       struct ldb_context *ldb;
+       bool terminated = false;
+       int ret;
 
-               if (exp_req->op.search.res && exp_req->op.search.res->count != 0) {
-                       if (res->msgs == NULL) {
-                               res->msgs = talloc_array(res,
-                                               struct ldb_message *, 2);
-                       } else {
-                               res->msgs = talloc_realloc(res, res->msgs,
-                                               struct ldb_message *, c + 2);
-                       }
-                       if (res->msgs == NULL)
-                               return LDB_ERR_OPERATIONS_ERROR;
-
-                       res->msgs[c] = talloc_steal(res->msgs, exp_req->op.search.res->msgs[0]);
-                       c++;
+       ldb = ldb_module_get_ctx(ac->module);
+
+       switch (ac->step) {
+       case ASQ_SEARCH_BASE:
+
+               /* build up the requests call chain */
+               ret = asq_build_multiple_requests(ac, &terminated);
+               if (ret != LDB_SUCCESS || terminated) {
+                       return ret;
                }
 
-               if (res->msgs) {
-                       res->msgs[c] = NULL;
-                       res->count = c;
+               ac->step = ASQ_SEARCH_MULTI;
+
+               return ldb_request(ldb, ac->reqs[ac->cur_req]);
+
+       case ASQ_SEARCH_MULTI:
+
+               ac->cur_req++;
+
+               if (ac->cur_req == ac->num_reqs) {
+                       /* done */
+                       return asq_search_terminate(ac);
                }
 
-               talloc_free(exp_req);
+               return ldb_request(ldb, ac->reqs[ac->cur_req]);
        }
 
-       talloc_free(base_req);
-
-       return build_response(res, ASQ_CTRL_SUCCESS);
+       return LDB_ERR_OPERATIONS_ERROR;
 }
 
-static int asq(struct ldb_module *module, struct ldb_request *req)
+static int asq_search(struct ldb_module *module, struct ldb_request *req)
 {
-       switch (req->operation) {
+       struct ldb_context *ldb;
+       struct ldb_request *base_req;
+       struct ldb_control *control;
+       struct asq_context *ac;
+       int ret;
 
-       case LDB_REQ_SEARCH:
-               return asq_search(module, req);
+       ldb = ldb_module_get_ctx(module);
 
-       default:
+       /* check if there's a paged request control */
+       control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID);
+       if (control == NULL) {
+               /* not found go on */
                return ldb_next_request(module, req);
+       }
 
+       ac = asq_context_init(module, req);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-}
 
-static int asq_init_2(struct ldb_module *module)
-{
-       struct ldb_request request;
-       int ret;
+       /* check the search is well formed */
+       if (req->op.search.scope != LDB_SCOPE_BASE) {
+               ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
+               return asq_search_terminate(ac);
+       }
 
-       request.operation = LDB_REQ_REGISTER;
-       request.op.reg.oid = LDB_CONTROL_ASQ_OID;
-       request.controls = NULL;
+       ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
+       if (!ac->asq_ctrl) {
+               return LDB_ERR_PROTOCOL_ERROR;
+       }
 
-       ret = ldb_request(module->ldb, &request);
+       ret = asq_build_first_request(ac, &base_req);
        if (ret != LDB_SUCCESS) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Unable to register control with rootdse!\n");
-               return LDB_ERR_OTHER;
+               return ret;
        }
 
-       return ldb_next_second_stage_init(module);
-}
-
+       ac->step = ASQ_SEARCH_BASE;
 
-static const struct ldb_module_ops asq_ops = {
-       .name              = "asq",
-       .request           = asq,
-       .second_stage_init = asq_init_2
-};
+       return ldb_request(ldb, base_req);
+}
 
-struct ldb_module *asq_module_init(struct ldb_context *ldb, const char *options[])
+static int asq_init(struct ldb_module *module)
 {
-       struct ldb_module *ctx;
+       struct ldb_context *ldb;
+       int ret;
 
-       ctx = talloc(ldb, struct ldb_module);
-       if (!ctx)
-               return NULL;
+       ldb = ldb_module_get_ctx(module);
 
-       ctx->ldb = ldb;
-       ctx->prev = ctx->next = NULL;
-       ctx->ops = &asq_ops;
-       ctx->private_data = NULL;
+       ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID);
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n");
+       }
 
-       return ctx;
+       return ldb_next_init(module);
 }
+
+const struct ldb_module_ops ldb_asq_module_ops = {
+       .name              = "asq",
+       .search            = asq_search,
+       .init_context      = asq_init
+};