LDB ASYNC: samba4 modules
authorSimo Sorce <idra@samba.org>
Thu, 11 Sep 2008 22:36:28 +0000 (18:36 -0400)
committerStefan Metzmacher <metze@samba.org>
Mon, 29 Sep 2008 02:22:20 +0000 (04:22 +0200)
22 files changed:
source4/dsdb/samdb/ldb_modules/anr.c
source4/dsdb/samdb/ldb_modules/extended_dn.c
source4/dsdb/samdb/ldb_modules/instancetype.c
source4/dsdb/samdb/ldb_modules/kludge_acl.c
source4/dsdb/samdb/ldb_modules/linked_attributes.c
source4/dsdb/samdb/ldb_modules/local_password.c
source4/dsdb/samdb/ldb_modules/normalise.c
source4/dsdb/samdb/ldb_modules/objectclass.c
source4/dsdb/samdb/ldb_modules/objectguid.c
source4/dsdb/samdb/ldb_modules/partition.c
source4/dsdb/samdb/ldb_modules/password_hash.c
source4/dsdb/samdb/ldb_modules/proxy.c
source4/dsdb/samdb/ldb_modules/ranged_results.c
source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/samdb/ldb_modules/rootdse.c
source4/dsdb/samdb/ldb_modules/samldb.c
source4/dsdb/samdb/ldb_modules/schema_fsmo.c
source4/dsdb/samdb/ldb_modules/show_deleted.c
source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
source4/dsdb/samdb/ldb_modules/subtree_delete.c
source4/dsdb/samdb/ldb_modules/subtree_rename.c
source4/dsdb/samdb/ldb_modules/update_keytab.c

index 4e2c527fe9c31335590511f6cd6c8a4b3b7f2198..028df588d6bddb44fb874e773c156189a6e533c0 100644 (file)
@@ -1,7 +1,8 @@
 /* 
    ldb database library
 
-   Copyright (C) Amdrew Bartlett <abartlet@samba.org> 2007
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+   Copyright (C) Simo Sorce <idra@samba.org> 2008
    Copyright (C) Andrew Tridgell  2004
     
    This program is free software; you can redistribute it and/or modify
@@ -99,6 +100,7 @@ struct ldb_parse_tree *make_match_tree(struct ldb_module *module,
 struct anr_context {
        bool found_anr;
        struct ldb_module *module;
+       struct ldb_request *req;
 };
 
 /**
@@ -106,39 +108,35 @@ struct anr_context {
  * parse tree with an 'or' of all the anr attributes in the schema.  
  */
 
-typedef struct ldb_parse_tree *(*anr_parse_tree_callback_t)(TALLOC_CTX *mem_ctx,
-                                                          const struct ldb_val *match,
-                                                          void *context);
-
-
 /**
- * Callback function to do the heavy lifting for the for the parse tree walker 
+ * Callback function to do the heavy lifting for the parse tree walker
  */
-struct ldb_parse_tree *anr_replace_callback(TALLOC_CTX *mem_ctx,
-                                           const struct ldb_val *match,
-                                           void *context)
+static int anr_replace_value(struct anr_context *ac,
+                            TALLOC_CTX *mem_ctx,
+                            const struct ldb_val *match,
+                            struct ldb_parse_tree **ntree)
 {
        struct ldb_parse_tree *tree = NULL;
-       struct anr_context *anr_context = talloc_get_type(context, struct anr_context);
-       struct ldb_module *module = anr_context->module;
+       struct ldb_module *module = ac->module;
        struct ldb_parse_tree *match_tree;
-       uint8_t *p;
-       enum ldb_parse_op op;
        struct dsdb_attribute *cur;
        const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
+       uint8_t *p;
+       enum ldb_parse_op op;
+
        if (!schema) {
                ldb_asprintf_errstring(module->ldb, "no schema with which to construct anr filter");
-               return NULL;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       anr_context->found_anr = true;
+       ac->found_anr = true;
 
        if (match->length > 1 && match->data[0] == '=') {
-               DATA_BLOB *match2 = talloc(tree, DATA_BLOB);
+               DATA_BLOB *match2 = talloc(mem_ctx, DATA_BLOB);
                *match2 = data_blob_const(match->data+1, match->length - 1);
                if (match2 == NULL){
                        ldb_oom(module->ldb);
-                       return NULL;
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
                match = match2;
                op = LDB_OP_EQUALITY;
@@ -154,7 +152,7 @@ struct ldb_parse_tree *anr_replace_callback(TALLOC_CTX *mem_ctx,
                        tree = make_parse_list(module, mem_ctx,  LDB_OP_OR, tree, match_tree);
                        if (tree == NULL) {
                                ldb_oom(module->ldb);
-                               return NULL;
+                               return LDB_ERR_OPERATIONS_ERROR;
                        }
                } else {
                        tree = match_tree;
@@ -173,7 +171,7 @@ struct ldb_parse_tree *anr_replace_callback(TALLOC_CTX *mem_ctx,
                DATA_BLOB *second_match = talloc(tree, DATA_BLOB);
                if (!first_match || !second_match) {
                        ldb_oom(module->ldb);
-                       return NULL;
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
                *first_match = data_blob_const(match->data, p-match->data);
                *second_match = data_blob_const(p+1, match->length - (p-match->data) - 1);
@@ -183,26 +181,26 @@ struct ldb_parse_tree *anr_replace_callback(TALLOC_CTX *mem_ctx,
                match_tree_1 = make_match_tree(module, mem_ctx, op, "givenName", first_match);
                match_tree_2 = make_match_tree(module, mem_ctx, op, "sn", second_match);
 
-               first_split_filter = make_parse_list(module, context,  LDB_OP_AND, match_tree_1, match_tree_2);
+               first_split_filter = make_parse_list(module, ac,  LDB_OP_AND, match_tree_1, match_tree_2);
                if (first_split_filter == NULL){
                        ldb_oom(module->ldb);
-                       return NULL;
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
                
                match_tree_1 = make_match_tree(module, mem_ctx, op, "sn", first_match);
                match_tree_2 = make_match_tree(module, mem_ctx, op, "givenName", second_match);
 
-               second_split_filter = make_parse_list(module, context,  LDB_OP_AND, match_tree_1, match_tree_2);
+               second_split_filter = make_parse_list(module, ac,  LDB_OP_AND, match_tree_1, match_tree_2);
                if (second_split_filter == NULL){
                        ldb_oom(module->ldb);
-                       return NULL;
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
 
                split_filters = make_parse_list(module, mem_ctx,  LDB_OP_OR, 
                                                first_split_filter, second_split_filter);
                if (split_filters == NULL) {
                        ldb_oom(module->ldb);
-                       return NULL;
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
 
                if (tree) {
@@ -212,38 +210,46 @@ struct ldb_parse_tree *anr_replace_callback(TALLOC_CTX *mem_ctx,
                        tree = split_filters;
                }
        }
-       return tree;
+       *ntree = tree;
+       return LDB_SUCCESS;
 }
 
 /*
   replace any occurances of an attribute with a new, generated attribute tree
 */
-struct ldb_parse_tree *anr_replace_subtrees(struct ldb_parse_tree *tree, 
-                                           const char *attr, 
-                                           anr_parse_tree_callback_t callback,
-                                           void *context)
+static int anr_replace_subtrees(struct anr_context *ac,
+                               struct ldb_parse_tree *tree,
+                               const char *attr,
+                               struct ldb_parse_tree **ntree)
 {
+       int ret;
        int i;
-       struct ldb_parse_tree *tmp;
 
        switch (tree->operation) {
        case LDB_OP_AND:
        case LDB_OP_OR:
                for (i=0;i<tree->u.list.num_elements;i++) {
-                       tmp = anr_replace_subtrees(tree->u.list.elements[i],
-                                                  attr, callback, context);
-                       if (tmp) tree->u.list.elements[i] = tmp;
+                       ret = anr_replace_subtrees(ac, tree->u.list.elements[i],
+                                                  attr, &tree->u.list.elements[i]);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+                       *ntree = tree;
                }
                break;
        case LDB_OP_NOT:
-               tmp = anr_replace_subtrees(tree->u.isnot.child, attr, callback, context);
-               if (tmp) tree->u.isnot.child = tmp;
+               ret = anr_replace_subtrees(ac, tree->u.isnot.child, attr, &tree->u.isnot.child);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+               *ntree = tree;
                break;
        case LDB_OP_EQUALITY:
                if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
-                       tmp = callback(tree, &tree->u.equality.value, 
-                                       context);
-                       if (tmp) tree = tmp;
+                       ret = anr_replace_value(ac, tree, &tree->u.equality.value, ntree);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
                }
                break;
        case LDB_OP_SUBSTRING:
@@ -252,53 +258,97 @@ struct ldb_parse_tree *anr_replace_subtrees(struct ldb_parse_tree *tree,
                            tree->u.substring.end_with_wildcard == 1 && 
                            tree->u.substring.chunks[0] != NULL && 
                            tree->u.substring.chunks[1] == NULL) {
-                               tmp = callback(tree, tree->u.substring.chunks[0], context);
-                               if (tmp) tree = tmp;
+                               ret = anr_replace_value(ac, tree, tree->u.substring.chunks[0], ntree);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
                        }
                }
                break;
+       default:
+               break;
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int anr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct anr_context *ac;
+
+       ac = talloc_get_type(req->context, struct anr_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);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               return ldb_module_send_entry(ac->req, ares->message);
+
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(ac->req, ares->referral);
+
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
+
        }
-       return tree;
+       return LDB_SUCCESS;
 }
 
 /* search */
 static int anr_search(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_parse_tree *anr_tree;
-       struct anr_context *context = talloc(req, struct anr_context);
-       if (!context) {
+       struct ldb_request *down_req;
+       struct anr_context *ac;
+       int ret;
+
+       ac = talloc(req, struct anr_context);
+       if (!ac) {
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       context->module = module;
-       context->found_anr = false;
+       ac->module = module;
+       ac->req = req;
+       ac->found_anr = false;
 
 #if 0
        printf("oldanr : %s\n", ldb_filter_from_tree (0, req->op.search.tree));
 #endif
 
-       /* Yes, this is a problem with req->op.search.tree being const... */
-       anr_tree = anr_replace_subtrees(req->op.search.tree, "anr", anr_replace_callback, context);
-       if (!anr_tree) {
-               talloc_free(context);
+       ret = anr_replace_subtrees(ac, req->op.search.tree, "anr", &anr_tree);
+       if (ret != LDB_SUCCESS) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (context->found_anr) {
-               /* The above function modifies the tree if it finds "anr", so no
-                * point just setting this on the down_req */
-#if 0
-               printf("newtree: %s\n", ldb_filter_from_tree (0, anr_tree));
-#endif
-               req->op.search.tree = talloc_steal(req, anr_tree);
-       } else {
-               if (anr_tree != req->op.search.tree) {
-                       talloc_free(anr_tree);
-               }
-               talloc_free(context);
+       if (!ac->found_anr) {
+               talloc_free(ac);
+               return ldb_next_request(module, req);
        }
-       return ldb_next_request(module, req);
+
+       ret = ldb_build_search_req_ex(&down_req,
+                                       module->ldb, ac,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       anr_tree,
+                                       req->op.search.attrs,
+                                       req->controls,
+                                       ac, anr_search_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       talloc_steal(down_req, anr_tree);
+
+       return ldb_next_request(module, down_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_anr_module_ops = {
index 84bf5e484352c635b9dd1791638e255128969eb6..e40190e86f8ccccbe49ee80e20e99bedf48e4da6 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
@@ -95,7 +95,7 @@ static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
        return true;
 }
 
-static bool inject_extended_dn(struct ldb_message *msg,
+static int inject_extended_dn(struct ldb_message *msg,
                                struct ldb_context *ldb,
                                int type,
                                bool remove_guid,
@@ -113,8 +113,9 @@ static bool inject_extended_dn(struct ldb_message *msg,
        guid_blob = ldb_msg_find_ldb_val(msg, "objectGUID");
        sid_blob = ldb_msg_find_ldb_val(msg, "objectSID");
 
-       if (!guid_blob)
-               return false;
+       if (!guid_blob) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        switch (type) {
                case 0:
@@ -123,7 +124,7 @@ static bool inject_extended_dn(struct ldb_message *msg,
                                const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
                                const char *lower_sid_hex = strlower_talloc(msg, data_blob_hex_string(msg, sid_blob));
                                if (!lower_guid_hex || !lower_sid_hex) {
-                                       return false;
+                                       return LDB_ERR_OPERATIONS_ERROR;
                                }
                                new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
                                                         lower_guid_hex, 
@@ -132,7 +133,7 @@ static bool inject_extended_dn(struct ldb_message *msg,
                        } else {
                                const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
                                if (!lower_guid_hex) {
-                                       return false;
+                                       return LDB_ERR_OPERATIONS_ERROR;
                                }
                                new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
                                                         lower_guid_hex, 
@@ -151,8 +152,8 @@ static bool inject_extended_dn(struct ldb_message *msg,
                        if (sid) {
                                object_sid = dom_sid_string(msg, sid);
                                if (!object_sid)
-                                       return false;
-                               
+                                       return LDB_ERR_OPERATIONS_ERROR;
+
                        }
                        
                        /* Normal, sane format */
@@ -167,11 +168,11 @@ static bool inject_extended_dn(struct ldb_message *msg,
                        }
                        break;
                default:
-                       return false;
+                       return LDB_ERR_OPERATIONS_ERROR;
        }
 
        if (!new_dn) {
-               return false;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
        if (remove_guid) {
@@ -184,52 +185,70 @@ static bool inject_extended_dn(struct ldb_message *msg,
 
        msg->dn = ldb_dn_new(msg, ldb, new_dn);
        if (! ldb_dn_validate(msg->dn))
-               return false;
+               return LDB_ERR_OPERATIONS_ERROR;
 
        val = ldb_msg_find_ldb_val(msg, "distinguishedName");
        if (val) {
                ldb_msg_remove_attr(msg, "distinguishedName");
                if (ldb_msg_add_steal_string(msg, "distinguishedName", new_dn))
-                       return false;
+                       return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       return true;
+       return LDB_SUCCESS;
 }
 
 /* search */
 struct extended_context {
 
        struct ldb_module *module;
-       void *up_context;
-       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+       struct ldb_request *req;
 
-       const char * const *attrs;
        bool remove_guid;
        bool remove_sid;
        int extended_type;
 };
 
-static int extended_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct extended_context *ac;
+       int ret;
 
-       ac = talloc_get_type(context, struct extended_context);
+       ac = talloc_get_type(req->context, struct extended_context);
 
-       if (ares->type == LDB_REPLY_ENTRY) {
+       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);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
                /* for each record returned post-process to add any derived
                   attributes that have been asked for */
-               if (!inject_extended_dn(ares->message, ldb, ac->extended_type, ac->remove_guid, ac->remove_sid)) {
-                       goto error;
+               ret = inject_extended_dn(ares->message, ac->module->ldb,
+                                        ac->extended_type, ac->remove_guid,
+                                        ac->remove_sid);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
                }
-       }
 
-       return ac->up_callback(ldb, ac->up_context, ares);
+               return ldb_module_send_entry(ac->req, ares->message);
 
-error:
-       talloc_free(ares);
-       return LDB_ERR_OPERATIONS_ERROR;
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(ac->req, ares->referral);
+
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
+
+       }
+       return LDB_SUCCESS;
 }
 
+
 static int extended_search(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_control *control;
@@ -237,6 +256,7 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
        struct ldb_control **saved_controls;
        struct extended_context *ac;
        struct ldb_request *down_req;
+       const char * const *cast_attrs = NULL;
        char **new_attrs;
        int ret;
 
@@ -261,9 +281,7 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
        }
 
        ac->module = module;
-       ac->up_context = req->context;
-       ac->up_callback = req->callback;
-       ac->attrs = req->op.search.attrs;
+       ac->req = req;
        ac->remove_guid = false;
        ac->remove_sid = false;
        if (extended_ctrl) {
@@ -272,17 +290,6 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
                ac->extended_type = 0;
        }
 
-       down_req = talloc_zero(req, struct ldb_request);
-       if (down_req == NULL) {
-               ldb_oom(module->ldb);
-               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;
-
        /* check if attrs only is specified, in that case check wether we need to modify them */
        if (req->op.search.attrs) {
                if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
@@ -292,26 +299,38 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
                        ac->remove_sid = true;
                }
                if (ac->remove_guid || ac->remove_sid) {
-                       new_attrs = copy_attrs(down_req, req->op.search.attrs);
+                       new_attrs = copy_attrs(ac, req->op.search.attrs);
                        if (new_attrs == NULL) {
                                ldb_oom(module->ldb);
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
                        
                        if (ac->remove_guid) {
-                               if (!add_attrs(down_req, &new_attrs, "objectGUID"))
+                               if (!add_attrs(ac, &new_attrs, "objectGUID"))
                                        return LDB_ERR_OPERATIONS_ERROR;
                        }
                        if (ac->remove_sid) {
-                               if (!add_attrs(down_req, &new_attrs, "objectSID"))
+                               if (!add_attrs(ac, &new_attrs, "objectSID"))
                                        return LDB_ERR_OPERATIONS_ERROR;
                        }
-
-                       down_req->op.search.attrs = (const char * const *)new_attrs;
+                       cast_attrs = (const char * const *)new_attrs;
+               } else {
+                       cast_attrs = req->op.search.attrs;
                }
        }
 
-       down_req->controls = req->controls;
+       ret = ldb_build_search_req_ex(&down_req,
+                                       module->ldb, ac,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       req->op.search.tree,
+                                       cast_attrs,
+                                       req->controls,
+                                       ac, extended_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        /* save it locally and remove it from the list */
        /* we do not need to replace them later as we
@@ -320,45 +339,21 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       down_req->context = ac;
-       down_req->callback = extended_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;
+       return ldb_next_request(module, down_req);
 }
 
 static int extended_init(struct ldb_module *module)
 {
-       struct ldb_request *req;
        int ret;
 
-       req = talloc(module, struct ldb_request);
-       if (req == NULL) {
-               ldb_oom(module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       req->operation = LDB_REQ_REGISTER_CONTROL;
-       req->op.reg_control.oid = LDB_CONTROL_EXTENDED_DN_OID;
-       req->controls = NULL;
-
-       ret = ldb_request(module->ldb, req);
+       ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
        if (ret != LDB_SUCCESS) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "extended_dn: Unable to register control with rootdse!\n");
-               talloc_free(req);
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                       "extended_dn: Unable to register control with rootdse!\n");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       talloc_free(req);
        return ldb_next_init(module);
 }
 
index fd5aa5e18aea56291ee9163c3b23de9fb9bb083d..492ef1c92b6b591733f1eff9410c31aaf06b9f5f 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2004-2006
+   Copyright (C) Simo Sorce  2004-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
    Copyright (C) Andrew Tridgell 2005
    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/flags.h"
 
+struct it_context {
+       struct ldb_module *module;
+       struct ldb_request *req;
+};
+
+static int it_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct it_context *ac;
+
+       ac = talloc_get_type(req->context, struct it_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(req->handle->ldb, "Invalid reply type!");
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+}
+
 /* add_record: add instancetype attribute */
 static int instancetype_add(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_request *down_req;
        struct ldb_message *msg;
+       struct it_context *ac;
        uint32_t instance_type;
        int ret;
        const struct ldb_control *partition_ctrl;
@@ -70,18 +102,16 @@ static int instancetype_add(struct ldb_module *module, struct ldb_request *req)
                                    struct dsdb_control_current_partition);
        SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
 
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
-               ldb_oom(module->ldb);
+       ac = talloc(req, struct it_context);
+       if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-
-       *down_req = *req;
+       ac->module = module;
+       ac->req = req;
 
        /* we have to copy the message as the caller might have it as a const */
-       down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+       msg = ldb_msg_copy_shallow(ac, req->op.add.message);
        if (msg == NULL) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -99,23 +129,21 @@ static int instancetype_add(struct ldb_module *module, struct ldb_request *req)
 
        ret = ldb_msg_add_fmt(msg, "instanceType", "%u", instance_type);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-       /* 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;
+       ret = ldb_build_add_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, it_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       return ret;
+       /* go on with the call chain */
+       return ldb_next_request(module, down_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_instancetype_module_ops = {
index 6836f95873ef81b61a0e8f7f41d2b0913157865b..24527c36c95641aa9556744cb5b62f013b563c63 100644 (file)
@@ -2,7 +2,7 @@
    ldb database library
 
    Copyright (C) Andrew Bartlett 2005
-   Copyright (C) Simo Sorce 2006
+   Copyright (C) Simo Sorce 2006-2008
 
     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
@@ -74,15 +74,14 @@ static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
 struct kludge_acl_context {
 
        struct ldb_module *module;
-       void *up_context;
-       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+       struct ldb_request *req;
 
        enum security_user_level user_type;
        bool allowedAttributes;
        bool allowedAttributesEffective;
        bool allowedChildClasses;
        bool allowedChildClassesEffective;
-       const char **attrs;
+       const char * const *attrs;
 };
 
 /* read all objectClasses */
@@ -191,6 +190,7 @@ static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *
                      (comparison_fn_t)data_blob_cmp);
        
                for (i=1 ; i < allowedClasses->num_values; i++) {
+
                        struct ldb_val *val1 = &allowedClasses->values[i-1];
                        struct ldb_val *val2 = &allowedClasses->values[i];
                        if (data_blob_cmp(val1, val2) == 0) {
@@ -207,80 +207,111 @@ static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *
 
 /* find all attributes allowed by all these objectClasses */
 
-static int kludge_acl_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct kludge_acl_context *ac;
        struct kludge_private_data *data;
        int i, ret;
 
-       ac = talloc_get_type(context, struct kludge_acl_context);
+       ac = talloc_get_type(req->context, struct kludge_acl_context);
        data = talloc_get_type(ac->module->private_data, struct kludge_private_data);
 
-       if (ares->type != LDB_REPLY_ENTRY) {
-               return ac->up_callback(ldb, ac->up_context, ares);
+       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);
        }
 
-       if (ac->allowedAttributes) {
-               ret = kludge_acl_allowedAttributes(ldb, ares->message, "allowedAttributes");
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               if (ac->allowedAttributes) {
+                       ret = kludge_acl_allowedAttributes(ac->module->ldb,
+                                                  ares->message,
+                                                  "allowedAttributes");
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                       }
                }
-       }
-       if (ac->allowedChildClasses) {
-               ret = kludge_acl_childClasses(ldb, ares->message, "allowedChildClasses");
-               if (ret != LDB_SUCCESS) {
-                       return ret;
+               if (ac->allowedChildClasses) {
+                       ret = kludge_acl_childClasses(ac->module->ldb,
+                                               ares->message,
+                                               "allowedChildClasses");
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                       }
                }
-       }
 
-       if (data && data->password_attrs) /* if we are not initialized just get through */
-       {
-               switch (ac->user_type) {
-               case SECURITY_SYSTEM:
-                       if (ac->allowedAttributesEffective) {
-                               ret = kludge_acl_allowedAttributes(ldb, ares->message, "allowedAttributesEffective");
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
+               if (data && data->password_attrs) /* if we are not initialized just get through */
+               {
+                       switch (ac->user_type) {
+                       case SECURITY_SYSTEM:
+                               if (ac->allowedAttributesEffective) {
+                                       ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
+                                                                       "allowedClassesAttributesEffective");
+                                       if (ret != LDB_SUCCESS) {
+                                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                                       }
                                }
-                       }
-                       if (ac->allowedChildClassesEffective) {
-                               ret = kludge_acl_childClasses(ldb, ares->message, "allowedChildClassesEffective");
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
+                               if (ac->allowedChildClassesEffective) {
+                                       ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
+                                                                       "allowedClassesChildClassesEffective");
+                                       if (ret != LDB_SUCCESS) {
+                                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                                       }
                                }
-                       }
-                       break;
-               case SECURITY_ADMINISTRATOR:
-                       if (ac->allowedAttributesEffective) {
-                               ret = kludge_acl_allowedAttributes(ldb, ares->message, "allowedAttributesEffective");
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
+                               break;
+
+                       case SECURITY_ADMINISTRATOR:
+                               if (ac->allowedAttributesEffective) {
+                                       ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
+                                                                       "allowedClassesAttributesEffective");
+                                       if (ret != LDB_SUCCESS) {
+                                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                                       }
                                }
-                       }
-                       if (ac->allowedChildClassesEffective) {
-                               ret = kludge_acl_childClasses(ldb, ares->message, "allowedChildClassesEffective");
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
+                               if (ac->allowedChildClassesEffective) {
+                                       ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
+                                                                       "allowedClassesChildClassesEffective");
+                                       if (ret != LDB_SUCCESS) {
+                                               return ldb_module_done(ac->req, NULL, NULL, ret);
+                                       }
+                               }
+                               /* fall through */
+                       default:
+                               /* remove password attributes */
+                               for (i = 0; data->password_attrs[i]; i++) {
+                                       ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
                                }
                        }
-                       /* fall though */
-               default:
-                       /* remove password attributes */
-                       for (i = 0; data->password_attrs[i]; i++) {
-                               ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
+               }
+
+               if (ac->allowedAttributes ||
+                   ac->allowedAttributesEffective ||
+                   ac->allowedChildClasses ||
+                   ac->allowedChildClassesEffective) {
+
+                       if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
+                           !ldb_attr_in_list(ac->attrs, "*")) {
+
+                               ldb_msg_remove_attr(ares->message,
+                                                   "objectClass");
                        }
                }
-       }
 
-       if ((ac->allowedAttributes || ac->allowedAttributesEffective
-            || ac->allowedChildClasses || ac->allowedChildClassesEffective) && 
-           (!ldb_attr_in_list(ac->attrs, "objectClass") && 
-            !ldb_attr_in_list(ac->attrs, "*"))) {
-               ldb_msg_remove_attr(ares->message, "objectClass");
-       }
+               return ldb_module_send_entry(ac->req, ares->message);
+
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(ac->req, ares->referral);
 
-       return ac->up_callback(ldb, ac->up_context, ares);
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
+
+       }
+       return LDB_SUCCESS;
 }
 
 static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
@@ -288,10 +319,9 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
        struct kludge_acl_context *ac;
        struct ldb_request *down_req;
        struct kludge_private_data *data;
+       const char * const *attrs;
        int ret, i;
 
-       req->handle = NULL;
-
        ac = talloc(req, struct kludge_acl_context);
        if (ac == NULL) {
                ldb_oom(module->ldb);
@@ -301,23 +331,10 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
        data = talloc_get_type(module->private_data, struct kludge_private_data);
 
        ac->module = module;
-       ac->up_context = req->context;
-       ac->up_callback = req->callback;
+       ac->req = req;
        ac->user_type = what_is_user(module);
        ac->attrs = req->op.search.attrs;
 
-       down_req = talloc_zero(req, struct ldb_request);
-       if (down_req == NULL) {
-               ldb_oom(module->ldb);
-               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;
-       down_req->op.search.attrs = req->op.search.attrs;
-
        ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
 
        ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
@@ -327,12 +344,11 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
        ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
 
        if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
-               down_req->op.search.attrs
-                       = ldb_attr_list_copy_add(down_req, down_req->op.search.attrs, "objectClass");
+               attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
+       } else {
+               attrs = req->op.search.attrs;
        }
 
-       /*  FIXME: I hink we should copy the tree and keep the original
-        *  unmodified. SSS */
        /* replace any attributes in the parse tree that are private,
           so we don't allow a search for 'userPassword=penguin',
           just as we would not allow that attribute to be returned */
@@ -340,30 +356,34 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
        case SECURITY_SYSTEM:
                break;
        default:
+       /* FIXME: We should copy the tree and keep the original unmodified. */
                /* remove password attributes */
-               for (i = 0; data && data->password_attrs && data->password_attrs[i]; i++) {
-                       ldb_parse_tree_attr_replace(down_req->op.search.tree, 
+
+               if (!data || !data->password_attrs) {
+                       break;
+               }
+               for (i = 0; data->password_attrs[i]; i++) {
+                       ldb_parse_tree_attr_replace(req->op.search.tree,
                                                    data->password_attrs[i],
                                                    "kludgeACLredactedattribute");
                }
        }
 
-       down_req->controls = req->controls;
-
-       down_req->context = ac;
-       down_req->callback = kludge_acl_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;
+       ret = ldb_build_search_req_ex(&down_req,
+                                       module->ldb, ac,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       req->op.search.tree,
+                                       attrs,
+                                       req->controls,
+                                       ac, kludge_acl_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       return ret;
+       /* perform the search */
+       return ldb_next_request(module, down_req);
 }
 
 /* ANY change type */
index e64472432d21d2293b1bfd173144a1942a16169e..3b389afffbb853a33f4c71a0f8389069eb0f72e0 100644 (file)
@@ -2,6 +2,7 @@
    ldb database library
 
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+   Copyright (C) Simo Sorce <idra@samba.org> 2008
 
    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
 #include "ldb/include/ldb_private.h"
 #include "dsdb/samdb/samdb.h"
 
-struct linked_attributes_context {
-       enum la_step {LA_SEARCH, LA_DO_OPS, LA_DO_ORIG} step;
-       struct ldb_module *module;
-       struct ldb_handle *handle;
-       struct ldb_request *orig_req;
-
-       struct ldb_request *search_req;
-       struct ldb_request **down_req;
-       struct ldb_request *orig_down_req;
-
-       int num_requests;
-       int finished_requests;
-
-       const char **linked_attrs;
+struct la_op_store {
+       struct la_op_store *next;
+       enum la_op {LA_OP_ADD, LA_OP_DEL} op;
+       struct ldb_dn *dn;
+       char *name;
+       char *value;
 };
 
 struct replace_context {
-       struct linked_attributes_context *ac;
+       struct la_context *ac;
+       unsigned int num_elements;
        struct ldb_message_element *el;
 };
 
-static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares);
-
-static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
-                                                                struct ldb_module *module)
-{
-       struct linked_attributes_context *ac;
-       struct ldb_handle *h;
+struct la_context {
+       const struct dsdb_schema *schema;
+       struct ldb_module *module;
+       struct ldb_request *req;
 
-       h = talloc_zero(req, struct ldb_handle);
-       if (h == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               return NULL;
-       }
+       struct replace_context *rc;
+       struct la_op_store *ops;
+       struct la_op_store *cur;
+};
 
-       h->module = module;
+static struct la_context *linked_attributes_init(struct ldb_module *module,
+                                                struct ldb_request *req)
+{
+       struct la_context *ac;
 
-       ac = talloc_zero(h, struct linked_attributes_context);
+       ac = talloc_zero(req, struct la_context);
        if (ac == NULL) {
                ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
                return NULL;
        }
 
-       h->private_data = ac;
-
+       ac->schema = dsdb_get_schema(module->ldb);
        ac->module = module;
-       ac->handle = h;
-       ac->orig_req = req;
-       
-       ac->orig_down_req = talloc(ac, struct ldb_request);
-       if (!ac->orig_down_req) {
-               ldb_oom(ac->module->ldb);
-               return NULL;
-       }
-
-       *ac->orig_down_req = *req;
-
-       req->handle = h;
+       ac->req = req;
 
        return ac;
 }
 
 /* Common routine to handle reading the attributes and creating a
  * series of modify requests */
-
-static int setup_modifies(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
-                         struct linked_attributes_context *ac,
-                         const struct ldb_message *msg, 
-                         struct ldb_dn *olddn, struct ldb_dn *newdn) 
+static int la_store_op(struct la_context *ac,
+                       enum la_op op, char *dn,
+                       const char *name, const char *value)
 {
-       int i, j, ret = LDB_SUCCESS;
-       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
-       /* Look up each of the returned attributes */
-       /* Find their schema */
-       /* And it is an actual entry: now create a series of modify requests */
-       for (i=0; i < msg->num_elements; i++) {
-               int otherid;
-               const struct dsdb_attribute *target_attr;
-               const struct ldb_message_element *el = &msg->elements[i];
-               const struct dsdb_attribute *schema_attr
-                       = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
-               if (!schema_attr) {
-                       ldb_asprintf_errstring(ldb, 
-                                              "attribute %s is not a valid attribute in schema", el->name);
-                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
-               }
-               /* We have a valid attribute, but if it's not linked they maybe we just got an extra return on our search... */
-               if (schema_attr->linkID == 0) {
-                       continue;
-               }
-               
-               /* Depending on which direction this link is in, we need to find it's partner */
-               if ((schema_attr->linkID & 1) == 1) {
-                       otherid = schema_attr->linkID - 1;
-               } else {
-                       otherid = schema_attr->linkID + 1;
-               }
-               
-               /* Now find the target attribute */
-               target_attr = dsdb_attribute_by_linkID(schema, otherid);
-               if (!target_attr) {
-                       ldb_asprintf_errstring(ldb, 
-                                              "attribute %s does not have valid link target", el->name);
-                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
-               }
-               
-               /* For each value being moded, we need to setup the modify */
-               for (j=0; j < el->num_values; j++) {
-                       struct ldb_message_element *ret_el;
-                       struct ldb_request *new_req;
-                       struct ldb_message *new_msg;
-
-                       /* Create a spot in the list for the requests */
-                       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                                     struct ldb_request *, ac->num_requests + 1);
-                       if (!ac->down_req) {
-                               ldb_oom(ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
+       struct la_op_store *os, *tmp;
+       struct ldb_dn *op_dn;
 
-                       /* Create the modify request */
-                       new_msg = ldb_msg_new(ac->down_req);
-                       if (!new_msg) {
-                               ldb_oom(ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       new_msg->dn = ldb_dn_from_ldb_val(new_msg, ldb, &el->values[j]);
-                       if (!new_msg->dn) {
-                               ldb_asprintf_errstring(ldb, 
-                                                      "attribute %s value %s was not a valid DN", msg->elements[i].name,
-                                                      el->values[j].data);
-                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
-                       }
-                       
-                       if (olddn) {
-                               ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
-                                                       LDB_FLAG_MOD_DELETE, &ret_el);
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
-                               }       
-                               ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
-                               if (!ret_el->values) {
-                                       ldb_oom(ldb);
-                                       return LDB_ERR_OPERATIONS_ERROR;
-                               }
-                               ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(olddn));
-                               ret_el->num_values = 1;
-                       }
-                       
-                       if (newdn) {
-                               ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
-                                                       LDB_FLAG_MOD_ADD, &ret_el);
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
-                               }       
-                               ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
-                               if (!ret_el->values) {
-                                       ldb_oom(ldb);
-                                       return LDB_ERR_OPERATIONS_ERROR;
+       op_dn = ldb_dn_new(ac, ac->module->ldb, dn);
+       if (!op_dn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* optimize out del - add operations that would end up
+        * with no changes */
+       if (ac->ops && op == LA_OP_DEL) {
+               /* do a linear search to find out if there is
+                * an equivalent add */
+               os = ac->ops;
+               while (os->next) {
+
+                       tmp = os->next;
+                       if (tmp->op == LA_OP_ADD) {
+
+                               if ((strcmp(name, tmp->name) == 0) &&
+                                   (strcmp(value, tmp->value) == 0) &&
+                                   (ldb_dn_compare(op_dn, tmp->dn) == 0)) {
+
+                                       break;
                                }
-                               ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(newdn));
-                               ret_el->num_values = 1;
                        }
+                       os = os->next;
+               }
+               if (os->next) {
+                       /* pair found, remove it and return */
+                       os->next = tmp->next;
+                       talloc_free(tmp);
+                       talloc_free(op_dn);
+                       return LDB_SUCCESS;
+               }
+       }
 
-                       ret = ldb_build_mod_req(&new_req, ldb, ac->down_req,
-                                               new_msg,
-                                               NULL,
-                                               NULL,
-                                               NULL);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       talloc_steal(new_req, new_msg);
-                       
-                       ldb_set_timeout_from_prev_req(ldb, ac->orig_req, new_req);
-                       
-                       ac->down_req[ac->num_requests] = new_req;
-                       ac->num_requests++;
-                       
-
-                       /* Run the new request */
-                       ret = ldb_next_request(ac->module, new_req);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
+       os = talloc_zero(ac, struct la_op_store);
+       if (!os) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       os->op = op;
+
+       os->dn = talloc_steal(os, op_dn);
+       if (!os->dn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       os->name = talloc_strdup(os, name);
+       if (!os->name) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if ((op != LA_OP_DEL) && (value == NULL)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (value) {
+               os->value = talloc_strdup(os, value);
+               if (!os->value) {
+                       return LDB_ERR_OPERATIONS_ERROR;
                }
        }
-       return ret;
+
+       if (ac->ops) {
+               ac->cur->next = os;
+       } else {
+               ac->ops = os;
+       }
+       ac->cur = os;
+
+       return LDB_SUCCESS;
 }
 
+static int la_op_search_callback(struct ldb_request *req,
+                                struct ldb_reply *ares);
+static int la_do_mod_request(struct la_context *ac);
+static int la_mod_callback(struct ldb_request *req,
+                          struct ldb_reply *ares);
+static int la_down_req(struct la_context *ac);
+static int la_down_callback(struct ldb_request *req,
+                           struct ldb_reply *ares);
+
+
+
 /* add */
 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
 {
-       int i;
-       struct linked_attributes_context *ac;
-
-       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               /* without schema, this doesn't make any sense */
-               return ldb_next_request(module, req);
-       }
+       const struct dsdb_attribute *target_attr;
+       struct la_context *ac;
+       const char *attr_name;
+       const char *attr_val;
+       int ret;
+       int i, j;
 
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
                /* do not manipulate our control entries */
                return ldb_next_request(module, req);
        }
 
-
-       ac = linked_attributes_init_handle(req, module);
+       ac = linked_attributes_init(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       ac->step = LA_DO_OPS;
-       
+
+       if (!ac->schema) {
+               /* without schema, this doesn't make any sense */
+               talloc_free(ac);
+               return ldb_next_request(module, req);
+       }
+
        /* Need to ensure we only have forward links being specified */
        for (i=0; i < req->op.add.message->num_elements; i++) {
                const struct ldb_message_element *el = &req->op.add.message->elements[i];
                const struct dsdb_attribute *schema_attr
-                       = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+                       = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
                if (!schema_attr) {
                        ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
+                                              "attribute %s is not a valid attribute in schema", el->name);
                        return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
                }
-               /* We have a valid attribute, not find out if it is linked */
+               /* We have a valid attribute, now find out if it is linked */
                if (schema_attr->linkID == 0) {
                        continue;
                }
@@ -268,160 +211,134 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request *
                if ((schema_attr->linkID & 1) == 1) {
                        /* Odd is for the target.  Illigal to modify */
                        ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
+                                              "attribute %s must not be modified directly, it is a linked attribute", el->name);
                        return LDB_ERR_UNWILLING_TO_PERFORM;
                }
                
                /* Even link IDs are for the originating attribute */
+               target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+               attr_name = target_attr->lDAPDisplayName;
+               attr_val = ldb_dn_get_linearized(ac->req->op.add.message->dn);
+
+               for (j = 0; j < el->num_values; j++) {
+                       ret = la_store_op(ac, LA_OP_ADD,
+                                         (char *)el->values[j].data,
+                                         attr_name, attr_val);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
        }
 
-       /* Now call the common routine to setup the modifies across all the attributes */
-       return setup_modifies(module->ldb, ac, ac, req->op.add.message, NULL, req->op.add.message->dn);
-}
-
-struct merge {
-       struct ldb_dn *dn;
-       bool add;
-       bool ignore;
-};
-
-static int merge_cmp(struct merge *merge1, struct merge *merge2) {
-       int ret;
-       ret = ldb_dn_compare(merge1->dn, merge2->dn);
-       if (ret == 0) {
-               if (merge1->add == merge2->add) {
-                       return 0;
-               }
-               if (merge1->add == true) {
-                       return 1;
-               }
-               return -1;
+       /* if no linked attributes are present continue */
+       if (ac->ops == NULL) {
+               talloc_free(ac);
+               return ldb_next_request(module, req);
        }
-       return ret;
+
+       /* start with the first one */
+       return la_do_mod_request(ac);
 }
 
-static int linked_attributes_mod_replace_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       struct replace_context *ac2 = talloc_get_type(context, struct replace_context);
-       struct linked_attributes_context *ac = ac2->ac;
-    
-       /* OK, we have one search result here: */
+       const struct dsdb_attribute *schema_attr;
+       const struct dsdb_attribute *target_attr;
+       struct ldb_message_element *search_el;
+       struct replace_context *rc;
+       struct la_context *ac;
+       const char *attr_name;
+       const char *dn;
+       int i, j;
+       int ret = LDB_SUCCESS;
 
-       /* Only entries are interesting, and we only want the olddn */
-       if (ares->type == LDB_REPLY_ENTRY
-           && ldb_dn_compare(ares->message->dn, ac->orig_req->op.mod.message->dn) == 0) {
-               /* only bother at all if there were some linked attributes found */
-               struct ldb_message_element *search_el
-                       = ldb_msg_find_element(ares->message,
-                                              ac2->el->name);
-               
-               /* See if this element already exists */
-               if (search_el) {
+       ac = talloc_get_type(req->context, struct la_context);
+       rc = ac->rc;
 
-                       struct merge *merged_list = NULL;
+       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);
+       }
 
-                       int ret, size = 0, i;
-                       struct ldb_message *msg = ldb_msg_new(ac);
-                       if (!msg) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
+       /* Only entries are interesting, and we only want the olddn */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
+                       /* Guh?  We only asked for this DN */
+                       ldb_oom(ac->module->ldb);
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
 
-                       /* Add all the existing elements, marking as 'proposed for delete' by setting .add = false */
-                       for (i=0; i < search_el->num_values; i++) {
-                               merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
-                               merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &search_el->values[i]);
-                               merged_list[size].add = false;
-                               merged_list[size].ignore = false;
-                               size++;
-                       }
+               dn = ldb_dn_get_linearized(ac->req->op.add.message->dn);
 
-                       /* Add all the new replacement elements, marking as 'proposed for add' by setting .add = true */
-                       for (i=0; i < ac2->el->num_values; i++) {
-                               merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
-                               merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &ac2->el->values[i]);
-                               merged_list[size].add = true;
-                               merged_list[size].ignore = false;
-                               size++;
-                       }
+               for (i = 0; i < rc->num_elements; i++) {
 
-                       /* Sort the list, so we can pick out an add and delete for the same DN, and eliminate them */
-                       qsort(merged_list, size,
-                             sizeof(*merged_list),
-                             (comparison_fn_t)merge_cmp);
-
-                       /* Now things are sorted, it is trivial to mark pairs of DNs as 'ignore' */
-                       for (i=0; i + 1 < size; i++) {
-                               if (ldb_dn_compare(merged_list[i].dn, 
-                                                  merged_list[i+1].dn) == 0 
-                                   /* Fortunetly the sort also sorts 'add == false' first */
-                                   && merged_list[i].add == false
-                                   && merged_list[i+1].add == true) {
-
-                                       /* Mark as ignore, so we include neither in the actual operations */
-                                       merged_list[i].ignore = true;
-                                       merged_list[i+1].ignore = true;
-                               }
+                       schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
+                       if (!schema_attr) {
+                               ldb_asprintf_errstring(ac->module->ldb,
+                                       "attribute %s is not a valid attribute in schema",
+                                       rc->el[i].name);
+                               talloc_free(ares);
+                               return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OBJECT_CLASS_VIOLATION);
                        }
 
-                       /* Arrange to delete anything the search found that we don't re-add */
-                       for (i=0; i < size; i++) {
-                               if (merged_list[i].ignore == false
-                                   && merged_list[i].add == false) {
-                                       ldb_msg_add_steal_string(msg, search_el->name, 
-                                                                ldb_dn_get_linearized(merged_list[i].dn));
-                               }
-                       }
+                       search_el = ldb_msg_find_element(ares->message,
+                                                        rc->el[i].name);
 
-                       /* The DN to set on the linked attributes is the original DN of the modify message */
-                       msg->dn = ac->orig_req->op.mod.message->dn;
-                       
-                       ret = setup_modifies(ac->module->ldb, ac2, ac, msg, ares->message->dn, NULL);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
+                       /* See if this element already exists */
+                       /* otherwise just ignore as
+                        * the add has already been scheduled */
+                       if ( ! search_el) {
+                               continue;
                        }
 
-                       /* Now add links for all the actually new elements */
-                       for (i=0; i < size; i++) {
-                               if (merged_list[i].ignore == false && merged_list[i].add == true) {
-                                       ldb_msg_add_steal_string(msg, search_el->name, 
-                                                                ldb_dn_get_linearized(merged_list[i].dn));
+                       target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+                       attr_name = target_attr->lDAPDisplayName;
+
+                       /* make sure we manage each value */
+                       for (j = 0; j < search_el->num_values; j++) {
+                               ret = la_store_op(ac, LA_OP_DEL,
+                                         (char *)search_el->values[j].data,
+                                                 attr_name, dn);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(ares);
+                                       return ldb_module_done(ac->req,
+                                                              NULL, NULL, ret);
                                }
                        }
+               }
 
-                       ret = setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ares->message->dn);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       talloc_free(merged_list);
+               break;
 
-               } else {
-                       /* Looks like it doesn't exist, process like an 'add' */
-                       struct ldb_message *msg = ldb_msg_new(ac);
-                       if (!msg) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       msg->num_elements = 1;
-                       msg->elements = ac2->el;
-                       msg->dn = ac->orig_req->op.mod.message->dn;
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               break;
 
-                       return setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ac->orig_req->op.mod.message->dn);
-               }
-               talloc_free(ares);
-               return LDB_SUCCESS;
-       } else if (ares->type == LDB_REPLY_ENTRY) {
-               /* Guh?  We only asked for this DN */
-               return LDB_ERR_OPERATIONS_ERROR;
+       case LDB_REPLY_DONE:
 
-       } else {
                talloc_free(ares);
+
+               /* All mods set up, start with the first one */
+               ret = la_do_mod_request(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
                return LDB_SUCCESS;
        }
-       
-       
+
+       talloc_free(ares);
+       return ret;
 }
+
+
 /* modify */
 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
 {
@@ -431,523 +348,545 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques
        /* Apply the modify to the linked entry */
 
        int i, j;
-       struct linked_attributes_context *ac;
-
-       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               /* without schema, this doesn't make any sense */
-               return ldb_next_request(module, req);
-       }
+       struct la_context *ac;
+       struct ldb_request *search_req;
+       int ret;
 
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
                /* do not manipulate our control entries */
                return ldb_next_request(module, req);
        }
 
-
-       ac = linked_attributes_init_handle(req, module);
+       ac = linked_attributes_init(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       /* prepare the first operation */
-       ac->step = LA_DO_OPS;
+
+       if (!ac->schema) {
+               /* without schema, this doesn't make any sense */
+               return ldb_next_request(module, req);
+       }
+
+       ac->rc = NULL;
 
        for (i=0; i < req->op.mod.message->num_elements; i++) {
-               int ret;
-               struct ldb_request *new_req;
+               bool store_el = false;
+               const char *attr_name;
+               const char *attr_val;
                const struct dsdb_attribute *target_attr;
                const struct ldb_message_element *el = &req->op.mod.message->elements[i];
                const struct dsdb_attribute *schema_attr
-                       = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+                       = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
                if (!schema_attr) {
                        ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s is not a valid attribute in schema", req->op.mod.message->elements[i].name);
+                                              "attribute %s is not a valid attribute in schema", el->name);
                        return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
                }
-               /* We have a valid attribute, not find out if it is linked */
+               /* We have a valid attribute, now find out if it is linked */
                if (schema_attr->linkID == 0) {
                        continue;
                }
                
                if ((schema_attr->linkID & 1) == 1) {
-                       /* Odd is for the target.  Illigal to modify */
+                       /* Odd is for the target.  Illegal to modify */
                        ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s must not be modified directly, it is a linked attribute", req->op.mod.message->elements[i].name);
+                                              "attribute %s must not be modified directly, it is a linked attribute", el->name);
                        return LDB_ERR_UNWILLING_TO_PERFORM;
                }
                
                /* Even link IDs are for the originating attribute */
                
                /* Now find the target attribute */
-               target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
+               target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
                if (!target_attr) {
                        ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s does not have valid link target", req->op.mod.message->elements[i].name);
+                                              "attribute %s does not have valid link target", el->name);
                        return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
                }
 
-               /* Replace with new set of values */
-               if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)
-                   && el->num_values > 0) {
-                       struct replace_context *ac2 = talloc(ac, struct replace_context);
-                       const char **attrs = talloc_array(ac, const char *, 2);
-                       if (!attrs || !ac2) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       attrs[0] = el->name;
-                       attrs[1] = NULL;
-
-                       ac2->ac = ac;
-                       ac2->el = el;
-
-                       /* We need to setup a search, compare with the list, and then setup add/del as required */
-                       
-                       /* The callback does all the hard work here */
-                       ret = ldb_build_search_req(&new_req, module->ldb, req,
-                                                  req->op.mod.message->dn, 
-                                                  LDB_SCOPE_BASE,
-                                                  "(objectClass=*)",
-                                                  attrs,
-                                                  NULL, 
-                                                  ac2, 
-                                                  linked_attributes_mod_replace_search_callback);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       talloc_steal(new_req, attrs);
-                       
-                       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
+               attr_name = target_attr->lDAPDisplayName;
+               attr_val = ldb_dn_get_linearized(ac->req->op.mod.message->dn);
 
-                       /* Create a spot in the list for the requests */
-                       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                                     struct ldb_request *, ac->num_requests + 1);
-                       if (!ac->down_req) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
+               switch (el->flags & LDB_FLAG_MOD_MASK) {
+               case LDB_FLAG_MOD_REPLACE:
+                       /* treat as just a normal add the delete part is handled by the callback */
+                       store_el = true;
 
-                       ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
-                       ac->num_requests++;
+                       /* break intentionally missing */
 
-                       ret = ldb_next_request(module, new_req);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       continue;
+               case LDB_FLAG_MOD_ADD:
 
-                       /* Delete all values case */
-               } else if (((el->flags & LDB_FLAG_MOD_MASK) & (LDB_FLAG_MOD_DELETE|LDB_FLAG_MOD_REPLACE)) 
-                          && el->num_values == 0) {
-                       const char **attrs = talloc_array(ac, const char *, 2);
-                       if (!attrs) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
+                       /* For each value being added, we need to setup the adds */
+                       for (j = 0; j < el->num_values; j++) {
+                               ret = la_store_op(ac, LA_OP_ADD,
+                                         (char *)el->values[j].data,
+                                                 attr_name, attr_val);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
                        }
-                       attrs[0] = el->name;
-                       attrs[1] = NULL;
-
-                       /* We need to setup a search, and then setup del as required */
-                       
-                       /* The callback does all the hard work here, acting identically to if we had delted the whole entry */
-                       ret = ldb_build_search_req(&new_req, module->ldb, req,
-                                                  req->op.mod.message->dn, 
-                                                  LDB_SCOPE_BASE,
-                                                  "(objectClass=*)",
-                                                  attrs,
-                                                  NULL, 
-                                                  ac, 
-                                                  linked_attributes_rename_del_search_callback);
+                       break;
 
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       talloc_steal(new_req, attrs);
-                       
-                       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
+               case LDB_FLAG_MOD_DELETE:
 
-                       /* Create a spot in the list for the requests */
-                       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                                     struct ldb_request *, ac->num_requests + 1);
-                       if (!ac->down_req) {
-                               ldb_oom(ac->module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
+                       if (el->num_values) {
+                               /* For each value being deleted, we need to setup the delete */
+                               for (j = 0; j < el->num_values; j++) {
+                                       ret = la_store_op(ac, LA_OP_DEL,
+                                                 (char *)el->values[j].data,
+                                                         attr_name, attr_val);
+                                       if (ret != LDB_SUCCESS) {
+                                               return ret;
+                                       }
+                               }
+                       } else {
+                               store_el = true;
                        }
 
-                       ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
-                       ac->num_requests++;
-                       
-                       ret = ldb_next_request(module, new_req);
-               
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       continue;
+                       break;
                }
 
-               /* Prepare the modify (mod element) on the targets, for a normal modify request */
-
-               /* For each value being moded, we need to setup the modify */
-               for (j=0; j < el->num_values; j++) {
-                       /* Create the modify request */
-                       struct ldb_message *new_msg = ldb_msg_new(ac);
-                       if (!new_msg) {
-                               ldb_oom(module->ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       new_msg->dn = ldb_dn_from_ldb_val(new_msg, module->ldb, &el->values[j]);
-                       if (!new_msg->dn) {
-                               ldb_asprintf_errstring(module->ldb, 
-                                              "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
-                                                      el->values[j].data);
-                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
-                       }
+               if (store_el) {
+                       struct ldb_message_element *search_el;
 
-                       ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
-                                               el->flags & LDB_FLAG_MOD_MASK, NULL);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
-                                                ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
+                       if (!ac->rc) {
+                               ac->rc = talloc_zero(ac, struct replace_context);
+                               if (!ac->rc) {
+                                       ldb_oom(module->ldb);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
                        }
 
-                       ret = ldb_build_mod_req(&new_req, module->ldb, ac,
-                                               new_msg,
-                                               NULL,
-                                               NULL,
-                                               NULL);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       talloc_steal(new_req, new_msg);
-                       
-                       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
-                       
-                       /* Now add it to the list */
-                       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                                     struct ldb_request *, ac->num_requests + 1);
-                       if (!ac->down_req) {
-                               ldb_oom(ac->module->ldb);
+                       search_el = talloc_realloc(ac->rc, ac->rc->el,
+                                                  struct ldb_message_element,
+                                                  ac->rc->num_elements +1);
+                       if (!search_el) {
+                               ldb_oom(module->ldb);
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
-                       ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
-                       ac->num_requests++;
+                       ac->rc->el = search_el;
 
-                       /* Run the new request */
-                       ret = ldb_next_request(module, new_req);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
-                       }
+                       ac->rc->el[ac->rc->num_elements] = *el;
+                       ac->rc->num_elements++;
                }
        }
-       return LDB_SUCCESS;
-}
 
-static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
-{
-       struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
-       struct ldb_dn *olddn, *newdn;
-    
-       switch (ac->orig_req->operation) {
-       case LDB_DELETE:
-       {
-               olddn = ac->orig_req->op.del.dn;
-               newdn = NULL;
-               break;
-       } 
-       /* This isn't the general modify case, just the modify when we are asked to delete all values */
-       case LDB_MODIFY:
-       {
-               olddn = ac->orig_req->op.mod.message->dn;
-               newdn = NULL;
-               break;
-       } 
-       case LDB_RENAME:
-       {
-               olddn = ac->orig_req->op.rename.olddn;
-               newdn = ac->orig_req->op.rename.newdn;
-               break;
-       }       
-       default:
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       
+       /* both replace and delete without values are handled in the callback
+        * after the search on the entry to be modified is performed */
+       if (ac->rc) {
+               const char **attrs;
+
+               attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements +1);
+               if (!attrs) {
+                       ldb_oom(module->ldb);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               for (i = 0; i < ac->rc->num_elements; i++) {
+                       attrs[i] = ac->rc->el[i].name;
+               }
+               attrs[i] = NULL;
 
-       /* OK, we have one search result here: */
+               /* The callback does all the hard work here */
+               ret = ldb_build_search_req(&search_req, module->ldb, ac,
+                                          req->op.mod.message->dn,
+                                          LDB_SCOPE_BASE,
+                                          "(objectClass=*)", attrs,
+                                          NULL,
+                                          ac, la_mod_search_callback,
+                                          req);
 
-       /* Only entries are interesting, and we only want the olddn */
-       if (ares->type == LDB_REPLY_ENTRY
-           && ldb_dn_compare(ares->message->dn, olddn) == 0) {
-               /* only bother at all if there were some linked attributes found */
-               if (ares->message->num_elements > 0) {
-                       return setup_modifies(ldb, ac, ac,
-                                             ares->message, olddn, newdn);
+               if (ret == LDB_SUCCESS) {
+                       talloc_steal(search_req, attrs);
+
+                       ret = ldb_next_request(module, search_req);
                }
-               talloc_free(ares);
-               return LDB_SUCCESS;
-       } else if (ares->type == LDB_REPLY_ENTRY) {
-               /* Guh?  We only asked for this DN */
-               return LDB_ERR_OPERATIONS_ERROR;
 
        } else {
-               talloc_free(ares);
-               return LDB_SUCCESS;
+               if (ac->ops) {
+                       /* start the mod requests chain */
+                       ret = la_do_mod_request(ac);
+               } else {
+                       /* nothing to do for this module, proceed */
+                       talloc_free(ac);
+                       ret = ldb_next_request(module, req);
+               }
        }
-       
-       
+
+       return ret;
 }
-/* rename */
-static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
+
+/* delete, rename */
+static int linked_attributes_op(struct ldb_module *module, struct ldb_request *req)
 {
-       /* Look up list of linked attributes */
+       struct ldb_request *search_req;
+       struct ldb_dn *base_dn;
+       struct la_context *ac;
        const char **attrs;
        WERROR werr;
        int ret;
-       struct linked_attributes_context *ac;
-       struct ldb_request *new_req;
-       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               /* without schema, this doesn't make any sense */
-               return ldb_next_request(module, req);
-       }
 
        /* This gets complex:  We need to:
-          - Do a search for the entry 
+          - Do a search for the entry
           - Wait for these result to appear
-          - In the callback for the result, issue a modify request based on the linked attributes found
+          - In the callback for the result, issue a modify
+               request based on the linked attributes found
           - Wait for each modify result
-          - Regain our sainity 
+          - Regain our sainity
        */
 
-       ac = linked_attributes_init_handle(req, module);
+       switch (req->operation) {
+       case LDB_RENAME:
+               base_dn = req->op.rename.olddn;
+               break;
+       case LDB_DELETE:
+               base_dn = req->op.del.dn;
+               break;
+       default:
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = linked_attributes_init(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
+
+       if (!ac->schema) {
+               /* without schema, this doesn't make any sense */
+               return ldb_next_request(module, req);
+       }
+
+       werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
        if (!W_ERROR_IS_OK(werr)) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       ret = ldb_build_search_req(&new_req, module->ldb, req,
-                                  req->op.rename.olddn, 
-                                  LDB_SCOPE_BASE,
-                                  "(objectClass=*)",
-                                  attrs,
-                                  NULL, 
-                                  ac, 
-                                  linked_attributes_rename_del_search_callback);
+
+       ret = ldb_build_search_req(&search_req, module->ldb, req,
+                                  base_dn, LDB_SCOPE_BASE,
+                                  "(objectClass=*)", attrs,
+                                  NULL,
+                                  ac, la_op_search_callback,
+                                  req);
 
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       talloc_steal(new_req, attrs);
+       talloc_steal(search_req, attrs);
+
+       return ldb_next_request(module, search_req);
+}
 
-       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
+static int la_op_search_callback(struct ldb_request *req,
+                                struct ldb_reply *ares)
+{
+       struct la_context *ac;
+       const struct dsdb_attribute *schema_attr;
+       const struct dsdb_attribute *target_attr;
+       const struct ldb_message_element *el;
+       const char *attr_name;
+       const char *deldn;
+       const char *adddn;
+       int i, j;
+       int ret;
 
-       if (ret != LDB_SUCCESS) {
-               return ret;
+       ac = talloc_get_type(req->context, struct la_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);
+       }
+
+       /* Only entries are interesting, and we only want the olddn */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
+               if (ret != 0) {
+                       /* Guh?  We only asked for this DN */
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+               if (ares->message->num_elements == 0) {
+                       /* only bother at all if there were some
+                        * linked attributes found */
+                       talloc_free(ares);
+                       return LDB_SUCCESS;
+               }
+
+               switch (ac->req->operation) {
+               case LDB_DELETE:
+                       deldn = ldb_dn_get_linearized(ac->req->op.del.dn);
+                       adddn = NULL;
+                       break;
+               case LDB_RENAME:
+                       deldn = ldb_dn_get_linearized(ac->req->op.rename.olddn);
+                       adddn = ldb_dn_get_linearized(ac->req->op.rename.newdn);
+                       break;
+               default:
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               for (i = 0; i < ares->message->num_elements; i++) {
+                       el = &ares->message->elements[i];
+
+                       schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+                       if (!schema_attr) {
+                               ldb_asprintf_errstring(ac->module->ldb,
+                                       "attribute %s is not a valid attribute"
+                                       " in schema", el->name);
+                               talloc_free(ares);
+                               return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OBJECT_CLASS_VIOLATION);
+                       }
+
+                       /* Valid attribute, now find out if it is linked */
+                       if (schema_attr->linkID == 0) {
+                               /* Not a linked attribute, skip */
+                               continue;
+                       }
+
+                       if ((schema_attr->linkID & 1) == 0) {
+                               /* Odd is for the target. */
+                               target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+                               attr_name = target_attr->lDAPDisplayName;
+                       } else {
+                               target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
+                               attr_name = target_attr->lDAPDisplayName;
+                       }
+                       for (j = 0; j < el->num_values; j++) {
+                               ret = la_store_op(ac, LA_OP_DEL,
+                                         (char *)el->values[j].data,
+                                                 attr_name, deldn);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(ares);
+                                       return ldb_module_done(ac->req,
+                                                              NULL, NULL, ret);
+                               }
+                               if (!adddn) continue;
+                               ret = la_store_op(ac, LA_OP_ADD,
+                                         (char *)el->values[j].data,
+                                                 attr_name, adddn);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(ares);
+                                       return ldb_module_done(ac->req,
+                                                              NULL, NULL, ret);
+                               }
+                       }
+               }
+
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               break;
+
+       case LDB_REPLY_DONE:
+
+               talloc_free(ares);
+
+               if (ac->ops) {
+                       /* start the mod requests chain */
+                       ret = la_do_mod_request(ac);
+               } else {
+                       ret = la_down_req(ac);
+               }
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               return LDB_SUCCESS;
        }
 
-       ac->search_req = new_req;
-       ac->step = LA_SEARCH;
-       return ldb_next_request(module, new_req);
+       talloc_free(ares);
+       return LDB_SUCCESS;
 }
 
-/* delete */
-static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
+/* do a linked attributes modify request */
+static int la_do_mod_request(struct la_context *ac)
 {
-       /* Look up list of linked attributes */
-       const char **attrs;
-       WERROR werr;
+       struct ldb_message_element *ret_el;
+       struct ldb_request *mod_req;
+       struct ldb_message *new_msg;
+       struct ldb_context *ldb;
        int ret;
-       struct ldb_request *new_req;
-       struct linked_attributes_context *ac;
-       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               /* without schema, this doesn't make any sense */
-               return ldb_next_request(module, req);
-       }
 
-       /* This gets complex:  We need to:
-          - Do a search for the entry 
-          - Wait for these result to appear
-          - In the callback for the result, issue a modify request based on the linked attributes found
-          - Wait for each modify result
-          - Regain our sainity 
-       */
+       ldb = ac->module->ldb;
 
-       ac = linked_attributes_init_handle(req, module);
-       if (!ac) {
+       /* Create the modify request */
+       new_msg = ldb_msg_new(ac);
+       if (!new_msg) {
+               ldb_oom(ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
-       if (!W_ERROR_IS_OK(werr)) {
+       new_msg->dn = ldb_dn_copy(new_msg, ac->ops->dn);
+       if (!new_msg->dn) {
                return LDB_ERR_OPERATIONS_ERROR;
-       };
-       
-       ret = ldb_build_search_req(&new_req, module->ldb, req,
-                                  req->op.del.dn, 
-                                  LDB_SCOPE_BASE,
-                                  "(objectClass=*)",
-                                  attrs,
-                                  NULL, 
-                                  ac, 
-                                  linked_attributes_rename_del_search_callback);
+       }
 
+       if (ac->ops->op == LA_OP_ADD) {
+               ret = ldb_msg_add_empty(new_msg, ac->ops->name,
+                                       LDB_FLAG_MOD_ADD, &ret_el);
+       } else {
+               ret = ldb_msg_add_empty(new_msg, ac->ops->name,
+                                       LDB_FLAG_MOD_DELETE, &ret_el);
+       }
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-
-       talloc_steal(new_req, attrs);
-
-       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-
+       ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
+       if (!ret_el->values) {
+               ldb_oom(ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret_el->values[0] = data_blob_string_const(ac->ops->value);
+       ret_el->num_values = 1;
+
+       /* use ac->ops as the mem_ctx so that the request will be freed
+        * in the callback as soon as completed */
+       ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
+                               new_msg,
+                               NULL,
+                               ac, la_mod_callback,
+                               ac->req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
+       talloc_steal(mod_req, new_msg);
 
-       ac->search_req = new_req;
-       ac->step = LA_SEARCH;
-       return ldb_next_request(module, new_req);
+       /* Run the new request */
+       return ldb_next_request(ac->module, mod_req);
 }
 
+static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct la_context *ac;
+       struct la_op_store *os;
+       int ret;
 
-static int linked_attributes_wait_none(struct ldb_handle *handle) {
-       struct linked_attributes_context *ac;
-       int i, ret = LDB_ERR_OPERATIONS_ERROR;
-       if (!handle || !handle->private_data) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       ac = talloc_get_type(req->context, struct la_context);
 
-       if (handle->state == LDB_ASYNC_DONE) {
-               return handle->status;
+       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);
        }
 
-       handle->state = LDB_ASYNC_PENDING;
-       handle->status = LDB_SUCCESS;
-
-       ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
-
-       switch (ac->step) {
-       case LA_SEARCH:
-               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;
-               }
-               ac->step = LA_DO_OPS;
-               return LDB_SUCCESS;
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb,
+                                 "invalid ldb_reply_type in callback");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       case LA_DO_OPS:
-               for (i=0; i < ac->num_requests; i++) {
-                       ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
-                       
-                       if (ret != LDB_SUCCESS) {
-                               handle->status = ret;
-                               goto done;
-                       }
-                       if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
-                               handle->status = ac->down_req[i]->handle->status;
-                               goto done;
-                       }
-                       
-                       if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
-                               return LDB_SUCCESS;
-                       }
-               }
+       talloc_free(ares);
 
-               /* Now run the original request */
-               ac->step = LA_DO_ORIG;
-               return ldb_next_request(ac->module, ac->orig_down_req);
+       if (ac->ops) {
+               os = ac->ops;
+               ac->ops = os->next;
 
-       case LA_DO_ORIG:
-               ret = ldb_wait(ac->orig_down_req->handle, LDB_WAIT_NONE);
-               
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->orig_down_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->orig_down_req->handle->status;
-                       goto done;
-               }
-               
-               if (ac->orig_down_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-               ret = LDB_SUCCESS;
+               /* this frees the request too
+                * DO NOT access 'req' after this point */
+               talloc_free(os);
        }
 
-done:
-       handle->state = LDB_ASYNC_DONE;
-       return ret;
+       /* as last op run the original request */
+       if (ac->ops) {
+               ret = la_do_mod_request(ac);
+       } else {
+               ret = la_down_req(ac);
+       }
 
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+       return LDB_SUCCESS;
 }
 
-static int linked_attributes_wait_all(struct ldb_handle *handle) {
-
+static int la_down_req(struct la_context *ac)
+{
+       struct ldb_request *down_req;
        int ret;
 
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = linked_attributes_wait_none(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
+       switch (ac->req->operation) {
+       case LDB_ADD:
+               ret = ldb_build_add_req(&down_req, ac->module->ldb, ac,
+                                       ac->req->op.add.message,
+                                       ac->req->controls,
+                                       ac, la_down_callback,
+                                       ac->req);
+               break;
+       case LDB_MODIFY:
+               ret = ldb_build_mod_req(&down_req, ac->module->ldb, ac,
+                                       ac->req->op.mod.message,
+                                       ac->req->controls,
+                                       ac, la_down_callback,
+                                       ac->req);
+               break;
+       case LDB_DELETE:
+               ret = ldb_build_del_req(&down_req, ac->module->ldb, ac,
+                                       ac->req->op.del.dn,
+                                       ac->req->controls,
+                                       ac, la_down_callback,
+                                       ac->req);
+               break;
+       case LDB_RENAME:
+               ret = ldb_build_rename_req(&down_req, ac->module->ldb, ac,
+                                          ac->req->op.rename.olddn,
+                                          ac->req->op.rename.newdn,
+                                          ac->req->controls,
+                                          ac, la_down_callback,
+                                          ac->req);
+               break;
+       default:
+               ret = LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       return handle->status;
+       return ldb_next_request(ac->module, down_req);
 }
 
-static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+static int la_down_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       if (type == LDB_WAIT_ALL) {
-               return linked_attributes_wait_all(handle);
-       } else {
-               return linked_attributes_wait_none(handle);
+       struct la_context *ac;
+
+       ac = talloc_get_type(req->context, struct la_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb,
+                                 "invalid ldb_reply_type in callback");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
+
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, ares->error);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
        .name              = "linked_attributes",
        .add               = linked_attributes_add,
        .modify            = linked_attributes_modify,
-       .del               = linked_attributes_delete,
-       .rename            = linked_attributes_rename,
-       .wait              = linked_attributes_wait,
+       .del               = linked_attributes_op,
+       .rename            = linked_attributes_op,
 };
index a411c015133df8165ef457f27a9ac074f14dfc7c..622e44416634e6301aa730a0cda1d3ae4ca2e7c8 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database module
 
-   Copyright (C) Simo Sorce  2004-2006
+   Copyright (C) Simo Sorce  2004-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
    Copyright (C) Andrew Tridgell 2004
 
@@ -44,7 +44,7 @@
    This allows the password database to be syncronised in a multi-master
    fashion, seperate to the more difficult concerns of the main
    database.  (With passwords, the last writer always wins)
-   
+
    Each incoming add/modify is split into a remote, and a local request, done in that order.
 
    We maintain a list of attributes that are kept locally:
@@ -62,73 +62,89 @@ static const char * const password_attrs[] = {
 
 /* And we merge them back into search requests when asked to do so */
 
-struct lpdb_context {
+struct lpdb_reply {
+       struct lpdb_reply *next;
+       struct ldb_reply *remote;
+       struct ldb_dn *local_dn;
+};
 
-       enum lpdb_type {LPDB_ADD, LPDB_MOD, LPDB_SEARCH} type;
-       enum lpdb_step {LPDB_ADD_REMOTE, LPDB_MOD_REMOTE, LPDB_MOD_SEARCH_SELF, LPDB_LOCAL, LPDB_SEARCH_REMOTE} step;
+struct lpdb_context {
 
        struct ldb_module *module;
-       struct ldb_request *orig_req;
-       struct ldb_request *remote_req;
-       struct ldb_request *search_req;
-       struct ldb_request *local_req;
+       struct ldb_request *req;
 
        struct ldb_message *local_message;
 
+       struct lpdb_reply *list;
+       struct lpdb_reply *current;
+       struct ldb_reply *remote_done;
+       struct ldb_reply *remote;
+
        bool added_objectGUID;
        bool added_objectClass;
 
-       struct ldb_reply *search_res;
-};
-
-struct lpdb_local_search_context {
-       struct lpdb_context *ac;
-       struct ldb_reply *remote_res;
-       struct ldb_reply *local_res;
 };
 
-static struct ldb_handle *lpdb_init_handle(struct ldb_request *req, struct ldb_module *module, enum lpdb_type type)
+static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
+                                             struct ldb_request *req)
 {
        struct lpdb_context *ac;
-       struct ldb_handle *h;
 
-       h = talloc_zero(req, struct ldb_handle);
-       if (h == NULL) {
+       ac = talloc_zero(req, struct lpdb_context);
+       if (ac == NULL) {
                ldb_set_errstring(module->ldb, "Out of Memory");
                return NULL;
        }
 
-       h->module = module;
+       ac->module = module;
+       ac->req = req;
 
-       ac = talloc_zero(h, struct lpdb_context);
-       if (ac == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
-               return NULL;
-       }
+       return ac;
+}
 
-       h->private_data = (void *)ac;
+static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct lpdb_context *ac;
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
+       ac = talloc_get_type(req->context, struct lpdb_context);
 
-       ac->type = type;
-       ac->module = module;
-       ac->orig_req = req;
+       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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       return h;
+       talloc_free(ares);
+       return ldb_module_done(ac->req,
+                               ac->remote_done->controls,
+                               ac->remote_done->response,
+                               ac->remote_done->error);
 }
 
-/* Add a record, splitting password attributes from the user's main
- * record */
+/*****************************************************************************
+ * ADD
+ ****************************************************************************/
+
+static int lpdb_add_callback(struct ldb_request *req,
+                               struct ldb_reply *ares);
 
 static int local_password_add(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_handle *h;
-       struct lpdb_context *ac;
        struct ldb_message *remote_message;
-       struct ldb_message *local_message;
+       struct ldb_request *remote_req;
+       struct lpdb_context *ac;
        struct GUID objectGUID;
+       int ret;
        int i;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_add\n");
@@ -163,22 +179,12 @@ static int local_password_add(struct ldb_module *module, struct ldb_request *req
        }
 
        /* From here, we assume we have password attributes to split off */
-       h = lpdb_init_handle(req, module, LPDB_ADD);
-       if (!h) {
+       ac = lpdb_init_context(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
 
-       ac->orig_req = req;
-
-       ac->remote_req = talloc(ac, struct ldb_request);
-       if (ac->remote_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *(ac->remote_req) = *(ac->orig_req);
-
-       remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.add.message);
+       remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
        if (remote_message == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -188,81 +194,113 @@ static int local_password_add(struct ldb_module *module, struct ldb_request *req
                ldb_msg_remove_attr(remote_message, password_attrs[i]);
        }
 
-       ac->remote_req->op.add.message = remote_message;
-
-       ac->remote_req->context = NULL;
-       ac->remote_req->callback = NULL;
-
-       ac->local_req = talloc(ac, struct ldb_request);
-       if (ac->local_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       /* Find the objectGUID to use as the key */
+       objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
 
-       *(ac->local_req) = *(ac->orig_req);
-       local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.add.message);
-       if (local_message == NULL) {
+       ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
+       if (ac->local_message == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* Remove anything seen in the remote message from the local
         * message (leaving only password attributes) */
-       for (i=0;i<ac->remote_req->op.add.message->num_elements;i++) {
-               ldb_msg_remove_attr(local_message, ac->remote_req->op.add.message->elements[i].name);
+       for (i=0; i < remote_message->num_elements; i++) {
+               ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
        }
 
        /* We must have an objectGUID already, or we don't know where
         * to add the password.  This may be changed to an 'add and
         * search', to allow the directory to create the objectGUID */
-       if (ldb_msg_find_ldb_val(ac->orig_req->op.add.message, "objectGUID") == NULL) {
-               ldb_set_errstring(module->ldb, 
-                                 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
+       if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) {
+               ldb_set_errstring(module->ldb,
+                                 "no objectGUID found in search: "
+                                 "local_password module must be "
+                                 "onfigured below objectGUID module!\n");
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       /* Find the objectGUID to use as the key */
-       objectGUID = samdb_result_guid(ac->orig_req->op.add.message, "objectGUID");
-       
-       local_message->dn = ldb_dn_new(local_message, module->ldb, LOCAL_BASE);
-       ldb_dn_add_child_fmt(local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(local_message, &objectGUID));
-
-       ac->local_req->op.add.message = local_message;
-
-       ac->local_req->context = NULL;
-       ac->local_req->callback = NULL;
-
-       ac->step = LPDB_ADD_REMOTE;
+       ac->local_message->dn = ldb_dn_new(ac->local_message,
+                                          module->ldb, LOCAL_BASE);
+       if ((ac->local_message->dn == NULL) ||
+           ( ! ldb_dn_add_child_fmt(ac->local_message->dn,
+                                    PASSWORD_GUID_ATTR "=%s",
+                                    GUID_string(ac->local_message,
+                                                       &objectGUID)))) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-       /* Return our own handle do deal with this call */
-       req->handle = h;
+       ret = ldb_build_add_req(&remote_req, module->ldb, ac,
+                               remote_message,
+                               req->controls,
+                               ac, lpdb_add_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       return ldb_next_request(module, ac->remote_req);
+       return ldb_next_request(module, remote_req);
 }
 
-/* After adding the remote entry, add the local one */
-static int local_password_add_local(struct ldb_handle *h) {
-
+/* Add a record, splitting password attributes from the user's main
+ * record */
+static int lpdb_add_callback(struct ldb_request *req,
+                               struct ldb_reply *ares)
+{
+       struct ldb_request *local_req;
        struct lpdb_context *ac;
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
+       int ret;
+
+       ac = talloc_get_type(req->context, struct lpdb_context);
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
+       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);
+       }
 
-       ac->step = LPDB_LOCAL;
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+       ac->remote_done = talloc_steal(ac, ares);
 
-       /* perform the local add */
-       return ldb_next_request(ac->module, ac->local_req);
+       ret = ldb_build_add_req(&local_req, ac->module->ldb, ac,
+                               ac->local_message,
+                               NULL,
+                               ac, lpdb_local_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
+       ret = ldb_next_request(ac->module, local_req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+       return LDB_SUCCESS;
 }
 
-static int local_password_mod_search_self(struct ldb_handle *h);
+/*****************************************************************************
+ * MODIFY
+ ****************************************************************************/
+
+static int lpdb_modify_callabck(struct ldb_request *req,
+                               struct ldb_reply *ares);
+static int lpdb_mod_search_callback(struct ldb_request *req,
+                                   struct ldb_reply *ares);
 
 static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_handle *h;
        struct lpdb_context *ac;
        struct ldb_message *remote_message;
-       struct ldb_message *local_message;
+       struct ldb_request *remote_req;
+       int ret;
        int i;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
@@ -289,243 +327,565 @@ static int local_password_modify(struct ldb_module *module, struct ldb_request *
        }
 
        /* From here, we assume we have password attributes to split off */
-       h = lpdb_init_handle(req, module, LPDB_MOD);
-       if (!h) {
+       ac = lpdb_init_context(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
 
-       ac->orig_req = req;
-
-       ac->remote_req = talloc(ac, struct ldb_request);
-       if (ac->remote_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *(ac->remote_req) = *(ac->orig_req);
-       remote_message = ldb_msg_copy_shallow(ac->remote_req, ac->orig_req->op.mod.message);
+       remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
        if (remote_message == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
+
        /* Remove any password attributes from the remote message */
        for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
                ldb_msg_remove_attr(remote_message, password_attrs[i]);
        }
 
-       ac->remote_req->op.mod.message = remote_message;
-
-       ac->remote_req->context = NULL;
-       ac->remote_req->callback = NULL;
-
-       ac->local_req = talloc(ac, struct ldb_request);
-       if (ac->local_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *(ac->local_req) = *(ac->orig_req);
-       local_message = ldb_msg_copy_shallow(ac->local_req, ac->orig_req->op.mod.message);
-       if (local_message == NULL) {
+       ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
+       if (ac->local_message == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* Remove anything seen in the remote message from the local
         * message (leaving only password attributes) */
-       for (i=0;i<ac->remote_req->op.mod.message->num_elements;i++) {
-               ldb_msg_remove_attr(local_message, ac->remote_req->op.mod.message->elements[i].name);
+       for (i=0; i < remote_message->num_elements;i++) {
+               ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
        }
 
-       ac->local_req->op.mod.message = local_message;
-       ac->local_message = local_message;
+       ret = ldb_build_mod_req(&remote_req, module->ldb, ac,
+                               remote_message,
+                               req->controls,
+                               ac, lpdb_modify_callabck,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       ac->local_req->context = NULL;
-       ac->local_req->callback = NULL;
+       return ldb_next_request(module, remote_req);
+}
 
-       ac->step = LPDB_MOD_REMOTE;
+/* On a modify, we don't have the objectGUID handy, so we need to
+ * search our DN for it */
+static int lpdb_modify_callabck(struct ldb_request *req,
+                               struct ldb_reply *ares)
+{
+       static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+       struct ldb_request *search_req;
+       struct lpdb_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct lpdb_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ac->remote_done = talloc_steal(ac, ares);
 
-       /* Return our own handle do deal with this call */
-       req->handle = h;
+       /* prepare the search operation */
+       ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
+                                  ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
+                                  "(objectclass=*)", attrs,
+                                  NULL,
+                                  ac, lpdb_mod_search_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       return ldb_next_request(module, ac->remote_req);
+       ret = ldb_next_request(ac->module, search_req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       return LDB_SUCCESS;
 }
 
-/* Called when we search for our oen entry.  Stores the one entry we
+/* Called when we search for our own entry.  Stores the one entry we
  * expect (as it is a base search) on the context pointer */
-static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int lpdb_mod_search_callback(struct ldb_request *req,
+                                   struct ldb_reply *ares)
 {
+       struct ldb_request *local_req;
        struct lpdb_context *ac;
+       struct ldb_dn *local_dn;
+       struct GUID objectGUID;
+       int ret = LDB_SUCCESS;
 
-       ac = talloc_get_type(context, struct lpdb_context);
+       ac = talloc_get_type(req->context, struct lpdb_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);
+       }
 
-       /* 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");
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               if (ac->remote != NULL) {
+                       ldb_set_errstring(ac->module->ldb, "Too many results");
                        talloc_free(ares);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
 
-               ac->search_res = talloc_steal(ac, ares);
-       } else {
+               ac->remote = talloc_steal(ac, ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+
+               /* ignore */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
+               /* After we find out the objectGUID for the entry, modify the local
+                * password database as required */
+
                talloc_free(ares);
+
+               /* if it is not an entry of type person this is an error */
+               /* TODO: remove this when sambaPassword will be in schema */
+               if (ac->remote == NULL) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "entry just modified (%s) not found!",
+                               ldb_dn_get_linearized(req->op.search.base));
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+               if (!ldb_msg_check_string_attribute(ac->remote->message,
+                                                   "objectClass", "person")) {
+                       /* Not relevent to us */
+                       return ldb_module_done(ac->req,
+                                               ac->remote_done->controls,
+                                               ac->remote_done->response,
+                                               ac->remote_done->error);
+               }
+
+               if (ldb_msg_find_ldb_val(ac->remote->message,
+                                        "objectGUID") == NULL) {
+                       ldb_set_errstring(ac->module->ldb,
+                                         "no objectGUID found in search: "
+                                         "local_password module must be "
+                                         "configured below objectGUID "
+                                         "module!\n");
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OBJECT_CLASS_VIOLATION);
+               }
+
+               objectGUID = samdb_result_guid(ac->remote->message,
+                                               "objectGUID");
+
+               local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
+               if ((local_dn == NULL) ||
+                   ( ! ldb_dn_add_child_fmt(local_dn,
+                                           PASSWORD_GUID_ATTR "=%s",
+                                           GUID_string(ac, &objectGUID)))) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+               ac->local_message->dn = local_dn;
+
+               ret = ldb_build_mod_req(&local_req, ac->module->ldb, ac,
+                                       ac->local_message,
+                                       NULL,
+                                       ac, lpdb_local_callback,
+                                       ac->req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+
+               /* perform the local update */
+               ret = ldb_next_request(ac->module, local_req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
        }
 
        return LDB_SUCCESS;
 }
 
-/* On a modify, we don't have the objectGUID handy, so we need to
- * search our DN for it */
-static int local_password_mod_search_self(struct ldb_handle *h) {
+/*****************************************************************************
+ * DELETE
+ ****************************************************************************/
 
+static int lpdb_delete_callabck(struct ldb_request *req,
+                               struct ldb_reply *ares);
+static int lpdb_del_search_callback(struct ldb_request *req,
+                                   struct ldb_reply *ares);
+
+static int local_password_delete(struct ldb_module *module,
+                                struct ldb_request *req)
+{
+       struct ldb_request *remote_req;
        struct lpdb_context *ac;
-       static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+       int ret;
 
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
 
-       /* 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;
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.mod.message->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* If the caller is manipulating the local passwords directly,
+        * let them pass */
+       if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
+                               req->op.del.dn) == 0) {
+               return ldb_next_request(module, req);
        }
 
-       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->orig_req, NULL);
-       if (ac->search_req->op.search.tree == NULL) {
-               ldb_set_errstring(ac->module->ldb, "Invalid search filter");
+       /* From here, we assume we have password attributes to split off */
+       ac = lpdb_init_context(module, req);
+       if (!ac) {
                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 = LPDB_MOD_SEARCH_SELF;
+       ret = ldb_build_del_req(&remote_req, module->ldb, ac,
+                               req->op.del.dn,
+                               req->controls,
+                               ac, lpdb_delete_callabck,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       return ldb_next_request(ac->module, ac->search_req);
+       return ldb_next_request(module, remote_req);
 }
 
-/* After we find out the objectGUID for the entry, modify the local
- * password database as required */
-static int local_password_mod_local(struct ldb_handle *h) {
+/* On a modify, we don't have the objectGUID handy, so we need to
+ * search our DN for it */
+static int lpdb_delete_callabck(struct ldb_request *req,
+                               struct ldb_reply *ares)
+{
+       static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+       struct ldb_request *search_req;
+       struct lpdb_context *ac;
+       int ret;
 
+       ac = talloc_get_type(req->context, struct lpdb_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Unexpected reply type");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ac->remote_done = talloc_steal(ac, ares);
+
+       /* prepare the search operation */
+       ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
+                                  ac->req->op.del.dn, LDB_SCOPE_BASE,
+                                  "(objectclass=*)", attrs,
+                                  NULL,
+                                  ac, lpdb_del_search_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ret = ldb_next_request(ac->module, search_req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       return LDB_SUCCESS;
+}
+
+/* Called when we search for our own entry.  Stores the one entry we
+ * expect (as it is a base search) on the context pointer */
+static int lpdb_del_search_callback(struct ldb_request *req,
+                                   struct ldb_reply *ares)
+{
+       struct ldb_request *local_req;
        struct lpdb_context *ac;
+       struct ldb_dn *local_dn;
        struct GUID objectGUID;
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
-
-       /* if it is not an entry of type person this is an error */
-       /* TODO: remove this when these things are checked in the schema */
-       if (!ac->search_res) {
-               ldb_asprintf_errstring(ac->module->ldb, 
-                                       "entry just modified (%s) not found!",
-                                       ldb_dn_get_linearized(ac->remote_req->op.mod.message->dn));
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       if (!ldb_msg_check_string_attribute(ac->search_res->message, "objectClass", "person")) {
-               /* Not relevent to us */
-               return LDB_SUCCESS;
+       int ret = LDB_SUCCESS;
+
+       ac = talloc_get_type(req->context, struct lpdb_context);
+
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-       
-       if (ldb_msg_find_ldb_val(ac->search_res->message, "objectGUID") == NULL) {
-               ldb_set_errstring(ac->module->ldb, 
-                                 "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
-               return LDB_ERR_OBJECT_CLASS_VIOLATION;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
-       
-       objectGUID = samdb_result_guid(ac->search_res->message, "objectGUID");
 
-       ac->local_message->dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
-       ldb_dn_add_child_fmt(ac->local_message->dn, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID));
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               if (ac->remote != NULL) {
+                       ldb_set_errstring(ac->module->ldb, "Too many results");
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               ac->remote = talloc_steal(ac, ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
+               /* ignore */
+               talloc_free(ares);
+               break;
 
-       ac->step = LPDB_LOCAL;
+       case LDB_REPLY_DONE:
+               /* After we find out the objectGUID for the entry, modify the local
+                * password database as required */
 
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+               talloc_free(ares);
+
+               /* if it is not an entry of type person this is NOT an error */
+               /* TODO: remove this when sambaPassword will be in schema */
+               if (ac->remote == NULL) {
+                       return ldb_module_done(ac->req,
+                                               ac->remote_done->controls,
+                                               ac->remote_done->response,
+                                               ac->remote_done->error);
+               }
+               if (!ldb_msg_check_string_attribute(ac->remote->message,
+                                                   "objectClass", "person")) {
+                       /* Not relevent to us */
+                       return ldb_module_done(ac->req,
+                                               ac->remote_done->controls,
+                                               ac->remote_done->response,
+                                               ac->remote_done->error);
+               }
+
+               if (ldb_msg_find_ldb_val(ac->remote->message,
+                                        "objectGUID") == NULL) {
+                       ldb_set_errstring(ac->module->ldb,
+                                         "no objectGUID found in search: "
+                                         "local_password module must be "
+                                         "configured below objectGUID "
+                                         "module!\n");
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OBJECT_CLASS_VIOLATION);
+               }
+
+               objectGUID = samdb_result_guid(ac->remote->message,
+                                               "objectGUID");
+
+               local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
+               if ((local_dn == NULL) ||
+                   ( ! ldb_dn_add_child_fmt(local_dn,
+                                           PASSWORD_GUID_ATTR "=%s",
+                                           GUID_string(ac, &objectGUID)))) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               ret = ldb_build_del_req(&local_req, ac->module->ldb, ac,
+                                       local_dn,
+                                       NULL,
+                                       ac, lpdb_local_callback,
+                                       ac->req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
 
-       /* perform the local update */
-       return ldb_next_request(ac->module, ac->local_req);
+               /* perform the local update */
+               ret = ldb_next_request(ac->module, local_req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+       }
+
+       return LDB_SUCCESS;
 }
 
 
-static int lpdb_local_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+/*****************************************************************************
+ * SEARCH
+ ****************************************************************************/
+
+static int lpdb_local_search_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares);
+
+static int lpdb_local_search(struct lpdb_context *ac)
+{
+       struct ldb_request *local_req;
+       int ret;
+
+       ret = ldb_build_search_req(&local_req, ac->module->ldb, ac,
+                                  ac->current->local_dn,
+                                  LDB_SCOPE_BASE,
+                                  "(objectclass=*)",
+                                  ac->req->op.search.attrs,
+                                  NULL,
+                                  ac, lpdb_local_search_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return ldb_next_request(ac->module, local_req);
+}
+
+static int lpdb_local_search_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
 {
-       struct lpdb_local_search_context *local_context;
+       struct lpdb_context *ac;
+       struct ldb_reply *merge;
+       struct lpdb_reply *lr;
+       int ret;
+       int i;
+
+       ac = talloc_get_type(req->context, struct lpdb_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);
+       }
 
-       local_context = talloc_get_type(context, struct lpdb_local_search_context);
+       lr = ac->current;
 
-       /* we are interested only in the single reply (base search) we receive here */
+       /* we are interested only in a single reply (base search) */
        switch (ares->type) {
        case LDB_REPLY_ENTRY:
-       {
-               int i;
-               if (local_context->local_res != NULL) {
-                       ldb_set_errstring(ldb, "Too many results to base search for password entry!");
+
+               if (lr->remote == NULL) {
+                       ldb_set_errstring(ac->module->ldb,
+                               "Too many results for password entry search!");
                        talloc_free(ares);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
-               
-               local_context->local_res = ares;
 
-               /* Make sure never to return the internal key attribute to the caller */
+               merge = lr->remote;
+               lr->remote = NULL;
+
+               /* steal the local results on the remote results to be
+                * returned all together */
+               talloc_steal(merge, ares->message->elements);
+
+               /* Make sure never to return the internal key attribute */
                ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
 
-               talloc_steal(local_context->remote_res->message->elements, ares->message->elements);
                for (i=0; i < ares->message->num_elements; i++) {
                        struct ldb_message_element *el;
                        
-                       el = ldb_msg_find_element(local_context->remote_res->message, 
+                       el = ldb_msg_find_element(merge->message,
                                                  ares->message->elements[i].name);
                        if (!el) {
-                               if (ldb_msg_add_empty(local_context->remote_res->message, 
-                                                     ares->message->elements[i].name, 0, &el) != LDB_SUCCESS) {
+                               ret = ldb_msg_add_empty(merge->message,
+                                                       ares->message->elements[i].name,
+                                                       0, &el);
+                               if (ret != LDB_SUCCESS) {
                                        talloc_free(ares);
-                                       return LDB_ERR_OPERATIONS_ERROR;
+                                       return ldb_module_done(ac->req,
+                                                               NULL, NULL,
+                                                               LDB_ERR_OPERATIONS_ERROR);
                                }
                                *el = ares->message->elements[i];
                        }
                }
-               return local_context->ac->orig_req->callback(ldb, 
-                                                                  local_context->ac->orig_req->context,
-                                                                  local_context->remote_res);
-       } 
-       case LDB_REPLY_DONE:
-       {
-               /* Fire off the callback if there was no local entry, so we get the rest returned */
-               if (local_context->local_res == NULL) {
-                       return local_context->ac->orig_req->callback(ldb, 
-                                                                          local_context->ac->orig_req->context,
-                                                                          local_context->remote_res);
-               }
-               return LDB_SUCCESS;
+
+               /* free the rest */
+               talloc_free(ares);
+
+               return ldb_module_send_entry(ac->req, merge->message);
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
                break;
-       }
-       default:
-       {
+
+       case LDB_REPLY_DONE:
+
                talloc_free(ares);
-               ldb_set_errstring(ldb, "Unexpected result type in base search for password entry!");
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+
+               /* if this entry was not returned yet, return it now */
+               if (lr->remote) {
+                       ret = ldb_module_send_entry(ac->req, ac->remote->message);
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req,
+                                                       NULL, NULL, ret);
+                       }
+                       lr->remote = NULL;
+               }
+
+               if (lr->next->remote->type == LDB_REPLY_DONE) {
+                       /* this was the last one */
+                       return ldb_module_done(ac->req,
+                                               lr->next->remote->controls,
+                                               lr->next->remote->response,
+                                               lr->next->remote->error);
+               } else {
+                       /* next one */
+                       ac->current = lr->next;
+                       talloc_free(lr);
+
+                       ret = lpdb_local_search(ac);
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req,
+                                                       NULL, NULL, ret);
+                       }
+               }
        }
+
+       return LDB_SUCCESS;
 }
 
 /* For each entry returned in a remote search, do a local base search,
  * based on the objectGUID we asked for as an additional attribute */
-static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int lpdb_remote_search_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
 {
        struct lpdb_context *ac;
+       struct ldb_dn *local_dn;
+       struct GUID objectGUID;
+       struct lpdb_reply *lr;
+       int ret;
 
-       ac = talloc_get_type(context, struct lpdb_context);
+       ac = talloc_get_type(req->context, struct lpdb_context);
 
-       if (ares->type == LDB_REPLY_ENTRY) {
-               struct ldb_request *req;
-               struct lpdb_local_search_context *local_context;
-               struct GUID objectGUID;
+       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);
+       }
 
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
                /* No point searching further if it's not a 'person' entry */
                if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
 
@@ -538,13 +898,14 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s
                                ldb_msg_remove_attr(ares->message, "objectClass");
                        }
                        
-                       return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+                       return ldb_module_send_entry(ac->req, ares->message);
                }
 
                if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
                        ldb_set_errstring(ac->module->ldb, 
                                          "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
        
                objectGUID = samdb_result_guid(ares->message, "objectGUID");
@@ -557,44 +918,63 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s
                        ldb_msg_remove_attr(ares->message, "objectClass");
                }
 
-               req = talloc_zero(ac, struct ldb_request);
-               if (!req) {
-                       return LDB_ERR_OPERATIONS_ERROR;
+               local_dn = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
+               if ((local_dn == NULL) ||
+                   (! ldb_dn_add_child_fmt(local_dn,
+                                           PASSWORD_GUID_ATTR "=%s",
+                                           GUID_string(ac, &objectGUID)))) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               lr = talloc_zero(ac, struct lpdb_reply);
+               if (lr == NULL) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
+               lr->local_dn = talloc_steal(lr, local_dn);
+               lr->remote = talloc_steal(lr, ares);
 
-               local_context = talloc(ac, struct lpdb_local_search_context);
-               if (!local_context) {
-                       return LDB_ERR_OPERATIONS_ERROR;
+               if (ac->list) {
+                       ac->current->next = lr;
+               } else {
+                       ac->list = lr;
                }
-               local_context->ac = ac;
-               local_context->remote_res = ares;
-               local_context->local_res = NULL;
+               ac->current= lr;
+
+               break;
 
-               req->op.search.base = ldb_dn_new(ac, ac->module->ldb, LOCAL_BASE);
-               if ( ! ldb_dn_add_child_fmt(req->op.search.base, PASSWORD_GUID_ATTR "=%s", GUID_string(ac, &objectGUID))) {
-                       return LDB_ERR_OPERATIONS_ERROR;
+       case LDB_REPLY_REFERRAL:
+
+               return ldb_module_send_referral(ac->req, ares->referral);
+
+       case LDB_REPLY_DONE:
+
+               if (ac->list == NULL) {
+                       /* found nothing */
+                       return ldb_module_done(ac->req, ares->controls,
+                                               ares->response, ares->error);
                }
-               req->operation = LDB_SEARCH;
-               req->op.search.scope = LDB_SCOPE_BASE;
-               req->op.search.tree = ldb_parse_tree(req, NULL);
-               if (req->op.search.tree == NULL) {
-                       ldb_set_errstring(ac->module->ldb, "Out of Memory");
-                       return LDB_ERR_OPERATIONS_ERROR;
+
+               lr = talloc_zero(ac, struct lpdb_reply);
+               if (lr == NULL) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
-               req->op.search.attrs = ac->orig_req->op.search.attrs;
-               req->controls = NULL;
-               req->context = ac;
-               req->callback = get_self_callback;
+               lr->remote = talloc_steal(lr, ares);
 
-               ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
-               
-               req->context = local_context;
-               req->callback = lpdb_local_search_callback;
+               ac->current->next = lr;
 
-               return ldb_next_request(ac->module, req);
-       } else {
-               return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+               /* rewind current and start local searches */
+               ac->current= ac->list;
+
+               ret = lpdb_local_search(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
        }
+
+       return LDB_SUCCESS;
 }
 
 /* Search for passwords and other attributes.  The passwords are
@@ -603,7 +983,7 @@ static int lpdb_remote_search_callback(struct ldb_context *ldb, void *context, s
 
 static int local_password_search(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_handle *h;
+       struct ldb_request *remote_req;
        struct lpdb_context *ac;
        int i;
        int ret;
@@ -615,6 +995,8 @@ static int local_password_search(struct ldb_module *module, struct ldb_request *
                return ldb_next_request(module, req);
        }
 
+       search_attrs = NULL;
+
        /* If the caller is searching for the local passwords directly, let them pass */
        if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE),
                                req->op.search.base) == 0) {
@@ -634,32 +1016,15 @@ static int local_password_search(struct ldb_module *module, struct ldb_request *
                }
        }
 
-       h = lpdb_init_handle(req, module, LPDB_SEARCH);
-       if (!h) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       
-       ac = talloc_get_type(h->private_data, struct lpdb_context);
-
-       ac->orig_req = req;
-
-       ac->remote_req = talloc(ac, struct ldb_request);
-       if (ac->remote_req == NULL) {
+       ac = lpdb_init_context(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
-       *(ac->remote_req) = *(ac->orig_req);
-
-       /* Return our own handle do deal with this call */
-       ac->remote_req->handle = h;
-       
-       ac->remote_req->context = ac;
-       ac->remote_req->callback = lpdb_remote_search_callback;
-
        if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
                if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
-                       search_attrs = ldb_attr_list_copy_add(req, req->op.search.attrs, "objectGUID");
+                       search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
                        ac->added_objectGUID = true;
                        if (!search_attrs) {
                                return LDB_ERR_OPERATIONS_ERROR;
@@ -668,7 +1033,7 @@ static int local_password_search(struct ldb_module *module, struct ldb_request *
                        search_attrs = req->op.search.attrs;
                }
                if (!ldb_attr_in_list(search_attrs, "objectClass")) {
-                       search_attrs = ldb_attr_list_copy_add(req, search_attrs, "objectClass");
+                       search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
                        ac->added_objectClass = true;
                        if (!search_attrs) {
                                return LDB_ERR_OPERATIONS_ERROR;
@@ -678,175 +1043,26 @@ static int local_password_search(struct ldb_module *module, struct ldb_request *
                search_attrs = req->op.search.attrs;
        }
 
-       ac->remote_req->op.search.attrs = search_attrs;
-
-       ldb_set_timeout_from_prev_req(module->ldb, ac->orig_req, ac->remote_req);
-
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = LPDB_SEARCH_REMOTE;
-
-       /* perform the search */
-       ret = ldb_next_request(module, ac->remote_req);
-
-       if (ret == LDB_SUCCESS) {
-               req->handle = ac->remote_req->handle;
-       }
-
-       return ret;
-}
-
-static int lpdb_wait(struct ldb_handle *handle) {
-       struct lpdb_context *ac;
-       int ret;
-    
-       if (!handle || !handle->private_data) {
+       ret = ldb_build_search_req_ex(&remote_req, module->ldb, ac,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       req->op.search.tree,
+                                       search_attrs,
+                                       req->controls,
+                                       ac, lpdb_remote_search_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
                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 lpdb_context);
-
-       switch (ac->step) {
-       case LPDB_ADD_REMOTE:
-               ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->remote_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->remote_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->remote_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* original request done, go on */
-               return local_password_add_local(handle);
-               
-       case LPDB_MOD_REMOTE:
-               ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->remote_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->remote_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->remote_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* original request done, go on */
-               return local_password_mod_search_self(handle);
-               
-       case LPDB_MOD_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;
-               }
-
-               /* original request done, go on */
-               return local_password_mod_local(handle);
-               
-       case LPDB_LOCAL:
-               ret = ldb_wait(ac->local_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->local_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->local_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->local_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               break;
-               
-       case LPDB_SEARCH_REMOTE:
-               ret = ldb_wait(ac->remote_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->remote_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->remote_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->remote_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 lpdb_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = lpdb_wait(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-       }
-
-       return handle->status;
-}
-
-static int local_password_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return lpdb_wait_all(handle);
-       } else {
-               return lpdb_wait(handle);
-       }
+       /* perform the search */
+       return ldb_next_request(module, remote_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_local_password_module_ops = {
        .name          = "local_password",
        .add           = local_password_add,
        .modify        = local_password_modify,
-       .search        = local_password_search,
-       .wait          = local_password_wait
+       .del           = local_password_delete,
+       .search        = local_password_search
 };
index 3306fd3c33b9f8cba6b1bd76d674465c3160d83c..70513bd644cc189f554c8c893e9fce90f99e96bf 100644 (file)
     CN=Admins,CN=Users,DC=samba,DC=example,DC=com
    
  */
+
+struct norm_context {
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+       const struct dsdb_schema *schema;
+};
+
 static int fix_dn(struct ldb_dn *dn) 
 {
        int i, ret;
@@ -69,93 +77,117 @@ static int fix_dn(struct ldb_dn *dn)
        return LDB_SUCCESS;
 }
 
-static int normalise_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+static int normalize_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
-       struct ldb_request *orig_req = talloc_get_type(context, struct ldb_request);
-       TALLOC_CTX *mem_ctx;
+       struct ldb_message *msg;
+       struct norm_context *ac;
        int i, j, ret;
 
-       /* Only entries are interesting, and we handle the case of the parent seperatly */
-       if (ares->type != LDB_REPLY_ENTRY) {
-               return orig_req->callback(ldb, orig_req->context, ares);
-       }
+       ac = talloc_get_type(req->context, struct norm_context);
 
-       if (!schema) {
-               return orig_req->callback(ldb, orig_req->context, ares);
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-
-       mem_ctx = talloc_new(ares);
-       if (!mem_ctx) {
-               ldb_oom(ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       /* OK, we have one of *many* search results passing by here,
-        * but we should get them one at a time */
+       /* Only entries are interesting, and we handle the case of the parent seperatly */
 
-       ret = fix_dn(ares->message->dn);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(mem_ctx);
-               return ret;
-       }
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
 
-       for (i = 0; i < ares->message->num_elements; i++) {
-               const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, ares->message->elements[i].name);
-               if (!attribute) {
-                       continue;
-               }
-               /* Look to see if this attributeSyntax is a DN */
-               if (!((strcmp(attribute->attributeSyntax_oid, "2.5.5.1") == 0) ||
-                     (strcmp(attribute->attributeSyntax_oid, "2.5.5.7") == 0))) {
-                       continue;
+               /* OK, we have one of *many* search results passing by here,
+                * but we should get them one at a time */
+               msg = ares->message;
+
+               ret = fix_dn(msg->dn);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
                }
-               for (j = 0; j < ares->message->elements[i].num_values; j++) {
-                       const char *dn_str;
-                       struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, ldb, &ares->message->elements[i].values[j]);
-                       if (!dn) {
-                               talloc_free(mem_ctx);
-                               return LDB_ERR_OPERATIONS_ERROR;
+
+               for (i = 0; i < msg->num_elements; i++) {
+                       const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
+                       if (!attribute) {
+                               continue;
                        }
-                       ret = fix_dn(dn);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(mem_ctx);
-                               return ret;
+                       /* Look to see if this attributeSyntax is a DN */
+                       if (!((strcmp(attribute->attributeSyntax_oid, "2.5.5.1") == 0) ||
+                             (strcmp(attribute->attributeSyntax_oid, "2.5.5.7") == 0))) {
+                               continue;
+                       }
+                       for (j = 0; j < msg->elements[i].num_values; j++) {
+                               const char *dn_str;
+                               struct ldb_dn *dn = ldb_dn_new(ac, ac->module->ldb, (const char *)msg->elements[i].values[j].data);
+                               if (!dn) {
+                                       return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+                               }
+                               ret = fix_dn(dn);
+                               if (ret != LDB_SUCCESS) {
+                                       return ldb_module_done(ac->req, NULL, NULL, ret);
+                               }
+                               dn_str = talloc_steal(msg->elements[i].values, ldb_dn_get_linearized(dn));
+                               msg->elements[i].values[j] = data_blob_string_const(dn_str);
+                               talloc_free(dn);
                        }
-                       dn_str = talloc_steal(ares->message->elements[i].values, ldb_dn_get_linearized(dn));
-                       ares->message->elements[i].values[j] = data_blob_string_const(dn_str);
-                       talloc_free(dn);
                }
+
+               return ldb_module_send_entry(ac->req, msg);
+
+       case LDB_REPLY_REFERRAL:
+
+               return ldb_module_send_referral(ac->req, ares->referral);
+
+       case LDB_REPLY_DONE:
+
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
-       talloc_free(mem_ctx);
-       return orig_req->callback(ldb, orig_req->context, ares);
+
+       return LDB_SUCCESS;
 }
 
 /* search */
 static int normalise_search(struct ldb_module *module, struct ldb_request *req)
 {
+       struct ldb_request *down_req;
+       struct norm_context *ac;
        int ret;
-       struct ldb_request *down_req = talloc(req, struct ldb_request);
-       if (!down_req) {
-               ldb_oom(module->ldb);
+
+       ac = talloc(req, struct norm_context);
+       if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       *down_req = *req;
-       down_req->context = req;
-       down_req->callback = normalise_search_callback;
 
-       ret = ldb_next_request(module, down_req);
+       ac->module = module;
+       ac->req = 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;
+       /* if schema not yet present just skip over */
+       ac->schema = dsdb_get_schema(ac->module->ldb);
+       if (ac->schema == NULL) {
+               talloc_free(ac);
+               return ldb_next_request(module, req);
        }
-       return ret;
+
+       ret = ldb_build_search_req_ex(&down_req, module->ldb, ac,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       req->op.search.tree,
+                                       req->op.search.attrs,
+                                       req->controls,
+                                       ac, normalize_search_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return ldb_next_request(module, down_req);
 }
 
 
+
 _PUBLIC_ const struct ldb_module_ops ldb_normalise_module_ops = {
        .name              = "normalise",
        .search            = normalise_search,
index b048a8d8e1e2aa06a7fa05101f5db4fdea9cc84c..2c242d47c68e6db5e5468f9bc9f212aea11a2bff 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2006
+   Copyright (C) Simo Sorce  2006-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
 
    This program is free software; you can redistribute it and/or modify
 
 struct oc_context {
 
-       enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD, 
-                     OC_SEARCH_ADD_PARENT, OC_DO_ADD, 
-                     OC_SEARCH_RENAME_PARENT, OC_DO_RENAME} step;
-
        struct ldb_module *module;
-       struct ldb_request *orig_req;
-
-       struct ldb_request *down_req;
+       struct ldb_request *req;
 
-       struct ldb_request *search_req;
        struct ldb_reply *search_res;
 
-       struct ldb_request *add_req;
-       struct ldb_request *mod_req;
-       struct ldb_request *rename_req;
+       int (*step_fn)(struct oc_context *);
 };
 
 struct class_list {
@@ -69,46 +60,31 @@ struct class_list {
        const struct dsdb_class *objectclass;
 };
 
-static int objectclass_do_add(struct ldb_handle *h);
-
-static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
+static struct oc_context *oc_init_context(struct ldb_module *module,
+                                         struct ldb_request *req)
 {
        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);
+       ac = talloc_zero(req, 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;
+       ac->req = req;
 
-       return h;
+       return ac;
 }
 
+static int objectclass_do_add(struct oc_context *ac);
+
 /* Sort objectClasses into correct order, and validate that all
  * objectClasses specified actually exist in the schema
  */
 
 static int objectclass_sort(struct ldb_module *module,
                            const struct dsdb_schema *schema,
-                           struct ldb_message *msg, /* so that when we create new elements, we put it on the right parent */
                            TALLOC_CTX *mem_ctx,
                            struct ldb_message_element *objectclass_element,
                            struct class_list **sorted_out) 
@@ -117,6 +93,7 @@ static int objectclass_sort(struct ldb_module *module,
        int layer;
        struct class_list *sorted = NULL, *parent_class = NULL,
                *subclass = NULL, *unsorted = NULL, *current, *poss_subclass, *poss_parent, *new_parent;
+
        /* DESIGN:
         *
         * We work on 4 different 'bins' (implemented here as linked lists):
@@ -150,8 +127,7 @@ static int objectclass_sort(struct ldb_module *module,
        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);
+                       ldb_oom(module->ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                current->objectclass = dsdb_class_by_lDAPDisplayName(schema, (const char *)objectclass_element->values[i].data);
@@ -258,7 +234,7 @@ static DATA_BLOB *get_sd(struct ldb_module *module, TALLOC_CTX *mem_ctx,
        struct auth_session_info *session_info
                = ldb_get_opaque(module->ldb, "sessionInfo");
        struct security_descriptor *sd;
-       struct dom_sid *domain_sid = samdb_domain_sid(module->ldb);
+       const struct dom_sid *domain_sid = samdb_domain_sid(module->ldb);
 
        if (!objectclass->defaultSecurityDescriptor || !domain_sid) {
                return NULL;
@@ -292,28 +268,77 @@ static DATA_BLOB *get_sd(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 
 }
 
-static int get_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct oc_context *ac;
+       int ret;
 
-       ac = talloc_get_type(context, struct oc_context);
+       ac = talloc_get_type(req->context, struct oc_context);
 
-       /* we are interested only in the single reply (base search) we receive here */
-       if (ares->type == LDB_REPLY_ENTRY) {
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS &&
+           ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
                if (ac->search_res != NULL) {
-                       ldb_set_errstring(ldb, "Too many results");
+                       ldb_set_errstring(ac->module->ldb, "Too many results");
                        talloc_free(ares);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
 
-               ac->search_res = talloc_move(ac, &ares);
-       } else {
+               ac->search_res = talloc_steal(ac, ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
                talloc_free(ares);
+               ret = ac->step_fn(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               break;
        }
 
        return LDB_SUCCESS;
 }
 
+static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct oc_context *ac;
+
+       ac = talloc_get_type(req->context, struct oc_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, ares->error);
+}
+
 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
 
    This should mean that if the parent is:
@@ -367,12 +392,11 @@ static int fix_attributes(struct ldb_context *ldb, const struct dsdb_schema *sch
        return LDB_SUCCESS;
 }
 
+static int objectclass_do_add(struct oc_context *ac);
+
 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
 {
-
-       static const char * const attrs[] = { NULL };
-
-       struct ldb_handle *h;
+       struct ldb_request *search_req;
        struct oc_context *ac;
        struct ldb_dn *parent_dn;
        int ret;
@@ -384,61 +408,56 @@ static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       /* Need to object to this, but cn=rootdse doesn't hae an objectClass... */
+       /* Need to object to this, but cn=rootdse doesn't have an objectClass... */
        if (ldb_msg_find_element(req->op.add.message, 
                                 "objectClass") == NULL) {
                return ldb_next_request(module, req);
        }
 
-       h = oc_init_handle(req, module);
-       if (!h) {
+       ac = oc_init_context(module, req);
+       if (ac == NULL) {
                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;
 
        /* If there isn't a parent, just go on to the add processing */
-       if (ldb_dn_get_comp_num(ac->orig_req->op.add.message->dn) == 1) {
-               return objectclass_do_add(h);
+       if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
+               return objectclass_do_add(ac);
        }
 
-       parent_dn = ldb_dn_get_parent(ac, ac->orig_req->op.add.message->dn);
+       /* get copy of parent DN */
+       parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
        if (parent_dn == NULL) {
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_build_search_req(&ac->search_req, module->ldb,
+       ret = ldb_build_search_req(&search_req, module->ldb,
                                   ac, parent_dn, LDB_SCOPE_BASE,
-                                  "(objectClass=*)",
-                                  attrs, NULL, 
-                                  ac, get_search_callback);
+                                  "(objectClass=*)", NULL,
+                                  NULL,
+                                  ac, get_search_callback,
+                                  req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
+       talloc_steal(search_req, parent_dn);
 
-       talloc_steal(ac->search_req, parent_dn);
-
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
+       ac->step_fn = objectclass_do_add;
 
-       ac->step = OC_SEARCH_ADD_PARENT;
-
-       return ldb_next_request(ac->module, ac->search_req);
+       return ldb_next_request(ac->module, search_req);
 }
 
-static int objectclass_do_add(struct ldb_handle *h) 
+static int objectclass_do_add(struct oc_context *ac)
 {
        const struct dsdb_schema *schema;
-       struct oc_context *ac;
+       struct ldb_request *add_req;
+       char *value;
        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);
+
        schema = dsdb_get_schema(ac->module->ldb);
 
        mem_ctx = talloc_new(ac);
@@ -446,41 +465,34 @@ static int objectclass_do_add(struct ldb_handle *h)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ac->add_req = talloc(ac, struct ldb_request);
-       if (ac->add_req == NULL) {
-               talloc_free(mem_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
 
-       *ac->add_req = *ac->orig_req;
-
-       ac->add_req->op.add.message = msg = ldb_msg_copy_shallow(ac->add_req, ac->orig_req->op.add.message);
-
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->add_req);
-       
        /* Check we have a valid parent */
        if (ac->search_res == NULL) {
-               if (ldb_dn_compare(ldb_get_root_basedn(ac->module->ldb), ac->orig_req->op.add.message->dn) == 0) {
+               if (ldb_dn_compare(ldb_get_root_basedn(ac->module->ldb),
+                                                               msg->dn) == 0) {
                        /* Allow the tree to be started */
                        
                        /* but don't keep any error string, it's meaningless */
                        ldb_set_errstring(ac->module->ldb, NULL);
                } else {
                        ldb_asprintf_errstring(ac->module->ldb, "objectclass: Cannot add %s, parent does not exist!", 
-                                              ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
+                                              ldb_dn_get_linearized(msg->dn));
+                       talloc_free(mem_ctx);
                        return LDB_ERR_UNWILLING_TO_PERFORM;
                }
        } else {
                
                /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
                ret = fix_dn(msg, 
-                            ac->orig_req->op.add.message->dn,
+                            ac->req->op.add.message->dn,
                             ac->search_res->message->dn,
                             &msg->dn);
 
                if (ret != LDB_SUCCESS) {
                        ldb_asprintf_errstring(ac->module->ldb, "Could not munge DN %s into normal form", 
-                                              ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
+                                              ldb_dn_get_linearized(ac->req->op.add.message->dn));
+                       talloc_free(mem_ctx);
                        return ret;
                }
 
@@ -505,7 +517,7 @@ static int objectclass_do_add(struct ldb_handle *h)
                        talloc_free(mem_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               ret = objectclass_sort(ac->module, schema, msg, mem_ctx, objectclass_element, &sorted);
+               ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
                if (ret != LDB_SUCCESS) {
                        talloc_free(mem_ctx);
                        return ret;
@@ -524,7 +536,13 @@ static int objectclass_do_add(struct ldb_handle *h)
                
                /* 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->lDAPDisplayName);
+                       value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
+                       if (value == NULL) {
+                               ldb_oom(ac->module->ldb);
+                               talloc_free(mem_ctx);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       ret = ldb_msg_add_string(msg, "objectClass", value);
                        if (ret != LDB_SUCCESS) {
                                ldb_set_errstring(ac->module->ldb, 
                                                  "objectclass: could not re-add sorted "
@@ -537,8 +555,13 @@ static int objectclass_do_add(struct ldb_handle *h)
                                struct ldb_message_element *el;
                                int32_t systemFlags = 0;
                                if (!ldb_msg_find_element(msg, "objectCategory")) {
-                                       ldb_msg_add_string(msg, "objectCategory", 
-                                                          current->objectclass->defaultObjectCategory);
+                                       value = talloc_strdup(msg, current->objectclass->defaultObjectCategory);
+                                       if (value == NULL) {
+                                               ldb_oom(ac->module->ldb);
+                                               talloc_free(mem_ctx);
+                                               return LDB_ERR_OPERATIONS_ERROR;
+                                       }
+                                       ldb_msg_add_string(msg, "objectCategory", value);
                                }
                                if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (current->objectclass->defaultHidingValue == true)) {
                                        ldb_msg_add_string(msg, "showInAdvancedViewOnly", 
@@ -597,20 +620,33 @@ static int objectclass_do_add(struct ldb_handle *h)
                return ret;
        }
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = OC_DO_ADD;
+       ret = ldb_build_add_req(&add_req, ac->module->ldb, ac,
+                               msg,
+                               ac->req->controls,
+                               ac, oc_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        /* perform the add */
-       return ldb_next_request(ac->module, ac->add_req);
+       return ldb_next_request(ac->module, add_req);
 }
 
+static int oc_modify_callback(struct ldb_request *req,
+                               struct ldb_reply *ares);
+static int objectclass_do_mod(struct oc_context *ac);
+
 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_message_element *objectclass_element;
        struct ldb_message *msg;
        const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
+       struct class_list *sorted, *current;
+       struct ldb_request *down_req;
+       struct oc_context *ac;
+       TALLOC_CTX *mem_ctx;
+       char *value;
        int ret;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
@@ -626,23 +662,19 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
        }
        objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
 
+       ac = oc_init_context(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
        /* 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 with just fixing the attribute case */
        if (!objectclass_element) {
-               struct ldb_request *down_req = talloc(req, struct ldb_request);
-               if (down_req == NULL) {
-                       ldb_set_errstring(module->ldb, "Out of memory!");
-                       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.mod.message == NULL) {
+               msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+               if (msg == NULL) {
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                
@@ -651,15 +683,17 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
                        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;
+               ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                                       msg,
+                                       req->controls,
+                                       ac, oc_op_callback,
+                                       req);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
-               return ret;
+
+               /* go on with the call chain */
+               return ldb_next_request(module, down_req);
        }
 
        switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
@@ -668,41 +702,28 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
                        return LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED;
                }
                break;
+
        case LDB_FLAG_MOD_REPLACE:
-       {
-               struct ldb_request *down_req;
-               struct class_list *sorted, *current;
-               TALLOC_CTX *mem_ctx;
-               mem_ctx = talloc_new(req);
+               mem_ctx = talloc_new(ac);
                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!");
+               msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+               if (msg == NULL) {
                        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.mod.message == NULL) {
-                       talloc_free(mem_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               
+
                ret = fix_attributes(module->ldb, schema, msg);
                if (ret != LDB_SUCCESS) {
                        talloc_free(mem_ctx);
                        return ret;
                }
 
-               ret = objectclass_sort(module, schema, msg, mem_ctx, objectclass_element, &sorted);
+               ret = objectclass_sort(module, schema, mem_ctx, objectclass_element, &sorted);
                if (ret != LDB_SUCCESS) {
+                       talloc_free(mem_ctx);
                        return ret;
                }
 
@@ -719,9 +740,21 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
 
                /* 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->lDAPDisplayName);
+                       /* copy the value as this string is on the schema
+                        * context and we can't rely on it not changing
+                        * before the operation is over */
+                       value = talloc_strdup(msg,
+                                       current->objectclass->lDAPDisplayName);
+                       if (value == NULL) {
+                               ldb_oom(module->ldb);
+                               talloc_free(mem_ctx);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       ret = ldb_msg_add_string(msg, "objectClass", value);
                        if (ret != LDB_SUCCESS) {
-                               ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+                               ldb_set_errstring(module->ldb,
+                                       "objectclass: could not re-add sorted "
+                                       "objectclass to modify msg");
                                talloc_free(mem_ctx);
                                return ret;
                        }
@@ -731,130 +764,121 @@ static int objectclass_modify(struct ldb_module *module, struct ldb_request *req
 
                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;
+
+               ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                                       msg,
+                                       req->controls,
+                                       ac, oc_op_callback,
+                                       req);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
-               return ret;
-       }
+
+               /* go on with the call chain */
+               return ldb_next_request(module, down_req);
        }
 
        /* This isn't the default branch of the switch, but a 'in any
         * other case'.  When a delete isn't for all objectClasses for
         * example
         */
-       {
-               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_oom(ac->module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               
-               *(ac->down_req) = *req; /* copy the request */
-               
-               ac->down_req->op.mod.message = msg = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
-               
-               if (ac->down_req->op.mod.message == NULL) {
-                       ldb_oom(ac->module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               
-               ret = fix_attributes(ac->module->ldb, schema, msg);
-               if (ret != LDB_SUCCESS) {
-                       ldb_oom(ac->module->ldb);
-                       return ret;
-               }
 
-               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;
+       msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+       if (msg == NULL) {
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-               return ldb_next_request(module, ac->down_req);
+       ret = fix_attributes(module->ldb, schema, msg);
+       if (ret != LDB_SUCCESS) {
+               ldb_oom(ac->module->ldb);
+               return ret;
+       }
+
+       ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, oc_modify_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
+
+       return ldb_next_request(module, down_req);
 }
 
-static int objectclass_search_self(struct ldb_handle *h) 
+static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       int ret;
-       struct oc_context *ac;
        static const char * const attrs[] = { "objectClass", NULL };
+       struct ldb_request *search_req;
+       struct oc_context *ac;
+       int ret;
 
-       ac = talloc_get_type(h->private_data, struct oc_context);
+       ac = talloc_get_type(req->context, struct oc_context);
 
-       ret = ldb_build_search_req(&ac->search_req, ac->module->ldb,
-                                  ac, ac->orig_req->op.mod.message->dn, LDB_SCOPE_BASE,
+       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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
+                                  ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
                                   "(objectClass=*)",
                                   attrs, NULL, 
-                                  ac, get_search_callback);
-
+                                  ac, get_search_callback,
+                                  ac->req);
        if (ret != LDB_SUCCESS) {
-               return ret;
+               return ldb_module_done(ac->req, NULL, NULL, ret);
        }
 
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
-
-       ac->step = OC_SEARCH_SELF;
+       ac->step_fn = objectclass_do_mod;
 
-       return ldb_next_request(ac->module, ac->search_req);
+       ret = ldb_next_request(ac->module, search_req);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+       return LDB_SUCCESS;
 }
 
-static int objectclass_do_mod(struct ldb_handle *h) {
+static int objectclass_do_mod(struct oc_context *ac)
+{
 
        const struct dsdb_schema *schema;
-       struct oc_context *ac;
+       struct ldb_request *mod_req;
+       char *value;
        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);
-       schema = dsdb_get_schema(ac->module->ldb);
 
-       mem_ctx = talloc_new(ac);
-       if (mem_ctx == NULL) {
+       if (ac->search_res == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
+       schema = dsdb_get_schema(ac->module->ldb);
 
-       ac->mod_req = talloc(ac, struct ldb_request);
-       if (ac->mod_req == NULL) {
-               talloc_free(mem_ctx);
+       mem_ctx = talloc_new(ac);
+       if (mem_ctx == NULL) {
                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);
+       msg = ldb_msg_new(ac);
        if (msg == NULL) {
-               ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg");
+               ldb_set_errstring(ac->module->ldb,
+                       "objectclass: could not create new modify msg");
                talloc_free(mem_ctx);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -869,9 +893,9 @@ static int objectclass_do_mod(struct ldb_handle *h) {
        }
        
        /* modify dn */
-       msg->dn = ac->orig_req->op.mod.message->dn;
+       msg->dn = ac->req->op.mod.message->dn;
 
-       ret = objectclass_sort(ac->module, schema, msg, mem_ctx, objectclass_element, &sorted);
+       ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -889,7 +913,12 @@ static int objectclass_do_mod(struct ldb_handle *h) {
        
        /* 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->lDAPDisplayName);
+               value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
+               if (value == NULL) {
+                       ldb_oom(ac->module->ldb);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               ret = ldb_msg_add_string(msg, "objectClass", value);
                if (ret != LDB_SUCCESS) {
                        ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
                        talloc_free(mem_ctx);
@@ -903,33 +932,38 @@ static int objectclass_do_mod(struct ldb_handle *h) {
                return ret;
        }
 
-
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = OC_DO_MOD;
+       ret = ldb_build_mod_req(&mod_req, ac->module->ldb, ac,
+                               msg,
+                               ac->req->controls,
+                               ac, oc_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(mem_ctx);
+               return ret;
+       }
 
        talloc_free(mem_ctx);
-       /* perform the search */
-       return ldb_next_request(ac->module, ac->mod_req);
+       /* perform the modify */
+       return ldb_next_request(ac->module, mod_req);
 }
 
+static int objectclass_do_rename(struct oc_context *ac);
+
 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
 {
-
        static const char * const attrs[] = { NULL };
 
-       struct ldb_handle *h;
+       struct ldb_request *search_req;
        struct oc_context *ac;
        struct ldb_dn *parent_dn;
        int ret;
-       
+
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
 
        if (ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
                return ldb_next_request(module, req);
        }
-       
+
        /* Firstly ensure we are not trying to rename it to be a child of itself */
        if ((ldb_dn_compare_base(req->op.rename.olddn, req->op.rename.newdn) == 0) 
            && (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) != 0)) {
@@ -938,65 +972,50 @@ static int objectclass_rename(struct ldb_module *module, struct ldb_request *req
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
-       h = oc_init_handle(req, module);
-       if (!h) {
+       ac = oc_init_context(module, req);
+       if (ac == NULL) {
                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;
 
-       parent_dn = ldb_dn_get_parent(ac, ac->orig_req->op.rename.newdn);
+       parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
        if (parent_dn == NULL) {
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ret = ldb_build_search_req(&ac->search_req, module->ldb,
+       ret = ldb_build_search_req(&search_req, module->ldb,
                                   ac, parent_dn, LDB_SCOPE_BASE,
                                   "(objectClass=*)",
                                   attrs, NULL, 
-                                  ac, get_search_callback);
+                                  ac, get_search_callback,
+                                  req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-       talloc_steal(ac->search_req, parent_dn);
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
 
-       ac->step = OC_SEARCH_RENAME_PARENT;
+       ac->step_fn = objectclass_do_rename;
 
-       return ldb_next_request(ac->module, ac->search_req);
+       return ldb_next_request(ac->module, search_req);
 }
 
-static int objectclass_do_rename(struct ldb_handle *h) 
+static int objectclass_do_rename(struct oc_context *ac)
 {
-       struct oc_context *ac;
+       struct ldb_request *rename_req;
+       struct ldb_dn *fixed_dn;
        int ret;
-      
-       ac = talloc_get_type(h->private_data, struct oc_context);
 
-       ac->rename_req = talloc(ac, struct ldb_request);
-       if (ac->rename_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *ac->rename_req = *ac->orig_req;
-
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->rename_req);
-       
        /* Check we have a valid parent */
        if (ac->search_res == NULL) {
                ldb_asprintf_errstring(ac->module->ldb, "objectclass: Cannot rename %s, parent does not exist!", 
-                                      ldb_dn_get_linearized(ac->orig_req->op.rename.newdn));
+                                      ldb_dn_get_linearized(ac->req->op.rename.newdn));
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
        
-       /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
-       ret = fix_dn(ac->rename_req, 
-                    ac->orig_req->op.rename.newdn, 
-                    ac->search_res->message->dn, 
-                    &ac->rename_req->op.rename.newdn);
-
+       /* Fix up the DN to be in the standard form,
+        * taking particular care to match the parent DN */
+       ret = fix_dn(ac,
+                    ac->req->op.rename.newdn,
+                    ac->search_res->message->dn,
+                    &fixed_dn);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -1005,197 +1024,17 @@ static int objectclass_do_rename(struct ldb_handle *h)
         * by reading the allowedChildClasses and
         * allowedChildClasssesEffective attributes */
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = OC_DO_RENAME;
-
-       /* perform the rename */
-       return ldb_next_request(ac->module, ac->rename_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;
-               
-       case OC_SEARCH_ADD_PARENT:
-               ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->search_req->handle->status != LDB_SUCCESS
-                   && ac->search_req->handle->status != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ac->search_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* parent search done, go on */
-               return objectclass_do_add(handle);
-
-       case OC_DO_ADD:
-               ret = ldb_wait(ac->add_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->add_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->add_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->add_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               break;
-               
-       case OC_SEARCH_RENAME_PARENT:
-               ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->search_req->handle->status != LDB_SUCCESS && ac->search_req->handle->status != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ac->search_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* parent search done, go on */
-               return objectclass_do_rename(handle);
-
-       case OC_DO_RENAME:
-               ret = ldb_wait(ac->rename_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->rename_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->rename_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->rename_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;
-               }
+       ret = ldb_build_rename_req(&rename_req, ac->module->ldb, ac,
+                                  ac->req->op.rename.olddn, fixed_dn,
+                                  ac->req->controls,
+                                  ac, oc_op_callback,
+                                  ac->req);
+       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);
-       }
+       /* perform the rename */
+       return ldb_next_request(ac->module, rename_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_objectclass_module_ops = {
@@ -1203,5 +1042,4 @@ _PUBLIC_ const struct ldb_module_ops ldb_objectclass_module_ops = {
        .add           = objectclass_add,
        .modify        = objectclass_modify,
        .rename        = objectclass_rename,
-       .wait          = objectclass_wait
 };
index f62839389d2c6b70274da08e281dfb3af9ada880..054905992deeee44a46596a2e6ccbf199d9cda4e 100644 (file)
@@ -1,9 +1,9 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2004-2006
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
    Copyright (C) Andrew Tridgell 2005
+   Copyright (C) Simo Sorce  2004-2008
 
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
@@ -103,6 +103,36 @@ static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_
        return 0;
 }
 
+struct og_context {
+       struct ldb_module *module;
+       struct ldb_request *req;
+};
+
+static int og_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct og_context *ac;
+
+       ac = talloc_get_type(req->context, struct og_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, ares->error);
+}
+
 /* add_record: add objectGUID attribute */
 static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
 {
@@ -115,6 +145,7 @@ static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
        enum ndr_err_code ndr_err;
        int ret;
        time_t t = time(NULL);
+       struct og_context *ac;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
 
@@ -127,15 +158,15 @@ static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
+       ac = talloc(req, struct og_context);
+       if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-
-       *down_req = *req;
+       ac->module = module;
+       ac->req = req;
 
        /* we have to copy the message as the caller might have it as a const */
-       down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+       msg = ldb_msg_copy_shallow(ac, req->op.add.message);
        if (msg == NULL) {
                talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
@@ -149,44 +180,41 @@ static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
                                       &guid,
                                       (ndr_push_flags_fn_t)ndr_push_GUID);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
        if (ret) {
-               talloc_free(down_req);
                return ret;
        }
        
        if (add_time_element(msg, "whenCreated", t) != 0 ||
            add_time_element(msg, "whenChanged", t) != 0) {
-               talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* Get a sequence number from the backend */
+       /* FIXME: ldb_sequence_number is still SYNC now, when this changes,
+        * make sure this function is split and a callback is used */
        ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
        if (ret == LDB_SUCCESS) {
                if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
                    add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
-                       talloc_free(down_req);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
        }
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-       /* 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;
+       ret = ldb_build_add_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, og_op_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       return ret;
+       /* go on with the call chain */
+       return ldb_next_request(module, down_req);
 }
 
 /* modify_record: update timestamps */
@@ -197,6 +225,7 @@ static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
        int ret;
        time_t t = time(NULL);
        uint64_t seq_num;
+       struct og_context *ac;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
 
@@ -205,22 +234,20 @@ static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
+       ac = talloc(req, struct og_context);
+       if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-
-       *down_req = *req;
+       ac->module = module;
+       ac->req = req;
 
        /* we have to copy the message as the caller might have it as a const */
-       down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
+       msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
        if (msg == NULL) {
-               talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        if (add_time_element(msg, "whenChanged", t) != 0) {
-               talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -228,23 +255,21 @@ static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
        ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
        if (ret == LDB_SUCCESS) {
                if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
-                       talloc_free(down_req);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
        }
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-       /* 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;
+       ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, og_op_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       return ret;
+       /* go on with the call chain */
+       return ldb_next_request(module, down_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_objectguid_module_ops = {
index 0cd0baf6255750d96228abf994cb83d4fe659fbf..ad2901c308bf4671edaa883bf282110622fcc955 100644 (file)
@@ -1,4 +1,3 @@
-
 /* 
    Partitions ldb module
 
@@ -43,61 +42,98 @@ struct partition_private_data {
        struct ldb_dn **replicate;
 };
 
+struct part_request {
+       struct ldb_module *module;
+       struct ldb_request *req;
+};
+
 struct partition_context {
        struct ldb_module *module;
-       struct ldb_request *orig_req;
+       struct ldb_request *req;
 
-       struct ldb_request **down_req;
+       struct part_request *part_req;
        int num_requests;
        int finished_requests;
 };
 
-static struct partition_context *partition_init_handle(struct ldb_request *req, struct ldb_module *module)
+static struct partition_context *partition_init_ctx(struct ldb_module *module, struct ldb_request *req)
 {
        struct partition_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 partition_context);
+       ac = talloc_zero(req, struct partition_context);
        if (ac == NULL) {
                ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
                return NULL;
        }
 
-       h->private_data = ac;
-
        ac->module = module;
-       ac->orig_req = req;
-
-       req->handle = h;
+       ac->req = req;
 
        return ac;
 }
 
-static struct ldb_module *make_module_for_next_request(TALLOC_CTX *mem_ctx, 
-                                                      struct ldb_context *ldb,
-                                                      struct ldb_module *module)
+#define PARTITION_FIND_OP(module, op) do { \
+       struct ldb_context *ldbctx = module->ldb; \
+        while (module && module->ops->op == NULL) module = module->next; \
+        if (module == NULL) { \
+                ldb_asprintf_errstring(ldbctx, \
+                       "Unable to find backend operation for " #op ); \
+                return LDB_ERR_OPERATIONS_ERROR; \
+        } \
+} while (0)
+
+/*
+ *    helper functions to call the next module in chain
+ *    */
+
+int partition_request(struct ldb_module *module, struct ldb_request *request)
 {
-       struct ldb_module *current;
-       static const struct ldb_module_ops ops; /* zero */
-       current = talloc_zero(mem_ctx, struct ldb_module);
-       if (current == NULL) {
-               return module;
+       int ret;
+       switch (request->operation) {
+       case LDB_SEARCH:
+               PARTITION_FIND_OP(module, search);
+               ret = module->ops->search(module, request);
+               break;
+       case LDB_ADD:
+               PARTITION_FIND_OP(module, add);
+               ret = module->ops->add(module, request);
+               break;
+       case LDB_MODIFY:
+               PARTITION_FIND_OP(module, modify);
+               ret = module->ops->modify(module, request);
+               break;
+       case LDB_DELETE:
+               PARTITION_FIND_OP(module, del);
+               ret = module->ops->del(module, request);
+               break;
+       case LDB_RENAME:
+               PARTITION_FIND_OP(module, rename);
+               ret = module->ops->rename(module, request);
+               break;
+       case LDB_EXTENDED:
+               PARTITION_FIND_OP(module, extended);
+               ret = module->ops->extended(module, request);
+               break;
+       case LDB_SEQUENCE_NUMBER:
+               PARTITION_FIND_OP(module, sequence_number);
+               ret = module->ops->sequence_number(module, request);
+               break;
+       default:
+               PARTITION_FIND_OP(module, request);
+               ret = module->ops->request(module, request);
+               break;
        }
-       
-       current->ldb = ldb;
-       current->ops = &ops;
-       current->prev = NULL;
-       current->next = module;
-       return current;
+       if (ret == LDB_SUCCESS) {
+               return ret;
+       }
+       if (!ldb_errstring(module->ldb)) {
+               /* Set a default error string, to place the blame somewhere */
+               ldb_asprintf_errstring(module->ldb,
+                                       "error in module %s: %s (%d)",
+                                       module->ops->name,
+                                       ldb_strerror(ret), ret);
+       }
+       return ret;
 }
 
 static struct dsdb_control_current_partition *find_partition(struct partition_private_data *data,
@@ -120,127 +156,201 @@ static struct dsdb_control_current_partition *find_partition(struct partition_pr
 /**
  * fire the caller's callback for every entry, but only send 'done' once.
  */
-static int partition_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int partition_req_callback(struct ldb_request *req,
+                                 struct ldb_reply *ares)
 {
        struct partition_context *ac;
+       struct ldb_module *module;
+       struct ldb_request *nreq;
+       int ret;
 
-       ac = talloc_get_type(context, struct partition_context);
+       ac = talloc_get_type(req->context, struct partition_context);
 
-       if (ares->type == LDB_REPLY_ENTRY) {
-               return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
-       } else {
-               ac->finished_requests++;
-               if (ac->finished_requests == ac->num_requests) {
-                       return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
-               } else {
-                       talloc_free(ares);
-                       return LDB_SUCCESS;
-               }
+       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);
        }
-}
-
-/**
- * only fire the 'last' callback, and only for START-TLS for now 
- */
-static int partition_other_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
-{
-       struct partition_context *ac;
 
-       ac = talloc_get_type(context, struct partition_context);
+       switch (ares->type) {
+       case LDB_REPLY_REFERRAL:
+               /* ignore referrals for now */
+               break;
 
-       if (!ac->orig_req->callback) {
-               talloc_free(ares);
-               return LDB_SUCCESS;
-       }
+       case LDB_REPLY_ENTRY:
+               if (ac->req->operation != LDB_SEARCH) {
+                       ldb_set_errstring(ac->module->ldb,
+                               "partition_req_callback:"
+                               " Unsupported reply type for this request");
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+               return ldb_module_send_entry(ac->req, ares->message);
+
+       case LDB_REPLY_DONE:
+               if (ac->req->operation == LDB_EXTENDED) {
+                       /* FIXME: check for ares->response, replmd does not fill it ! */
+                       if (ares->response) {
+                               if (strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID) != 0) {
+                                       ldb_set_errstring(ac->module->ldb,
+                                                         "partition_req_callback:"
+                                                         " Unknown extended reply, "
+                                                         "only supports START_TLS");
+                                       talloc_free(ares);
+                                       return ldb_module_done(ac->req, NULL, NULL,
+                                                               LDB_ERR_OPERATIONS_ERROR);
+                               }
+                       }
+               }
 
-       if (!ares 
-           || (ares->type == LDB_REPLY_EXTENDED 
-               && strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID))) {
                ac->finished_requests++;
                if (ac->finished_requests == ac->num_requests) {
-                       return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+                       /* this was the last one, call callback */
+                       return ldb_module_done(ac->req, ares->controls,
+                                               ares->response, ares->error);
                }
-               talloc_free(ares);
-               return LDB_SUCCESS;
+
+               /* not the last, now call the next one */
+               module = ac->part_req[ac->finished_requests].module;
+               nreq = ac->part_req[ac->finished_requests].req;
+
+               ret = partition_request(module, nreq);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+
+               break;
        }
-       ldb_set_errstring(ldb, "partition_other_callback: Unknown reply type, only supports START_TLS");
+
        talloc_free(ares);
-       return LDB_ERR_OPERATIONS_ERROR;
+       return LDB_SUCCESS;
 }
 
-
-static int partition_send_request(struct partition_context *ac, 
+static int partition_prep_request(struct partition_context *ac,
                                  struct dsdb_control_current_partition *partition)
 {
        int ret;
-       struct ldb_module *backend;
        struct ldb_request *req;
 
-       if (partition) {
-               backend = make_module_for_next_request(ac, ac->module->ldb, partition->module);
-       } else {
-               backend = ac->module;
-       }
-
-       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                       struct ldb_request *, ac->num_requests + 1);
-       if (!ac->down_req) {
+       ac->part_req = talloc_realloc(ac, ac->part_req,
+                                       struct part_request,
+                                       ac->num_requests + 1);
+       if (ac->part_req == NULL) {
                ldb_oom(ac->module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       req = ac->down_req[ac->num_requests] = talloc(ac, struct ldb_request);
-       if (req == NULL) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+
+       switch (ac->req->operation) {
+       case LDB_SEARCH:
+               ret = ldb_build_search_req_ex(&req, ac->module->ldb,
+                                       ac->part_req,
+                                       ac->req->op.search.base,
+                                       ac->req->op.search.scope,
+                                       ac->req->op.search.tree,
+                                       ac->req->op.search.attrs,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       case LDB_ADD:
+               ret = ldb_build_add_req(&req, ac->module->ldb, ac->part_req,
+                                       ac->req->op.add.message,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       case LDB_MODIFY:
+               ret = ldb_build_mod_req(&req, ac->module->ldb, ac->part_req,
+                                       ac->req->op.mod.message,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       case LDB_DELETE:
+               ret = ldb_build_del_req(&req, ac->module->ldb, ac->part_req,
+                                       ac->req->op.del.dn,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       case LDB_RENAME:
+               ret = ldb_build_rename_req(&req, ac->module->ldb, ac->part_req,
+                                       ac->req->op.rename.olddn,
+                                       ac->req->op.rename.newdn,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       case LDB_EXTENDED:
+               ret = ldb_build_extended_req(&req, ac->module->ldb,
+                                       ac->part_req,
+                                       ac->req->op.extended.oid,
+                                       ac->req->op.extended.data,
+                                       ac->req->controls,
+                                       ac, partition_req_callback,
+                                       ac->req);
+               break;
+       default:
+               ldb_set_errstring(ac->module->ldb,
+                                 "Unsupported request type!");
+               ret = LDB_ERR_UNWILLING_TO_PERFORM;
        }
-       
-       *req = *ac->orig_req; /* copy the request */
 
-       if (req->controls) {
-               req->controls
-                       = talloc_memdup(req,
-                                       ac->orig_req->controls, talloc_get_size(ac->orig_req->controls));
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ac->part_req[ac->num_requests].req = req;
+
+       if (ac->req->controls) {
+               req->controls = talloc_memdup(req, ac->req->controls,
+                                       talloc_get_size(ac->req->controls));
                if (req->controls == NULL) {
                        ldb_oom(ac->module->ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
        }
 
-       if (req->operation == LDB_SEARCH) {
-               /* If the search is for 'more' than this partition,
-                * then change the basedn, so a remote LDAP server
-                * doesn't object */
-               if (partition) {
-                       if (ldb_dn_compare_base(partition->dn, req->op.search.base) != 0) {
-                               req->op.search.base = partition->dn;
-                       }
-               } else {
-                       req->op.search.base = NULL;
-               }
-               req->callback = partition_search_callback;
-               req->context = ac;
-       } else {
-               req->callback = partition_other_callback;
-               req->context = ac;
-       }
-
        if (partition) {
-               ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition);
+               ac->part_req[ac->num_requests].module = partition->module;
+
+               ret = ldb_request_add_control(req,
+                                       DSDB_CONTROL_CURRENT_PARTITION_OID,
+                                       false, partition);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
-       }
 
-       /* Spray off search requests the backend */
-       ret = ldb_next_request(backend, req);
-       if (ret != LDB_SUCCESS) {
-               return ret;
+               if (req->operation == LDB_SEARCH) {
+                       /* If the search is for 'more' than this partition,
+                        * then change the basedn, so a remote LDAP server
+                        * doesn't object */
+                       if (ldb_dn_compare_base(partition->dn,
+                                               req->op.search.base) != 0) {
+                               req->op.search.base = partition->dn;
+                       }
+               }
+
+       } else {
+               /* make sure you put the NEXT module here, or
+                * partition_request() will simply loop forever on itself */
+               ac->part_req[ac->num_requests].module = ac->module->next;
        }
 
        ac->num_requests++;
+
        return LDB_SUCCESS;
 }
 
+static int partition_call_first(struct partition_context *ac)
+{
+       return partition_request(ac->part_req[0].module, ac->part_req[0].req);
+}
+
 /**
  * Send a request down to all the partitions
  */
@@ -251,17 +361,19 @@ static int partition_send_all(struct ldb_module *module,
        int i;
        struct partition_private_data *data = talloc_get_type(module->private_data, 
                                                              struct partition_private_data);
-       int ret = partition_send_request(ac, NULL);
+       int ret = partition_prep_request(ac, NULL);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
        for (i=0; data && data->partitions && data->partitions[i]; i++) {
-               ret = partition_send_request(ac, data->partitions[i]);
+               ret = partition_prep_request(ac, data->partitions[i]);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
        }
-       return LDB_SUCCESS;
+
+       /* fire the first one */
+       return partition_call_first(ac);
 }
 
 /**
@@ -270,21 +382,23 @@ static int partition_send_all(struct ldb_module *module,
  */
 static int partition_replicate(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn) 
 {
+       struct partition_context *ac;
        unsigned i;
        int ret;
        struct dsdb_control_current_partition *partition;
-       struct ldb_module *backend;
        struct partition_private_data *data = talloc_get_type(module->private_data, 
                                                              struct partition_private_data);
+       if (!data || !data->partitions) {
+               return ldb_next_request(module, req);
+       }
        
        if (req->operation != LDB_SEARCH) {
                /* Is this a special DN, we need to replicate to every backend? */
                for (i=0; data->replicate && data->replicate[i]; i++) {
                        if (ldb_dn_compare(data->replicate[i], 
                                           dn) == 0) {
-                               struct partition_context *ac;
                                
-                               ac = partition_init_handle(req, module);
+                               ac = partition_init_ctx(module, req);
                                if (!ac) {
                                        return LDB_ERR_OPERATIONS_ERROR;
                                }
@@ -310,18 +424,19 @@ static int partition_replicate(struct ldb_module *module, struct ldb_request *re
                return ldb_next_request(module, req);
        }
 
-       backend = make_module_for_next_request(req, module->ldb, partition->module);
-       if (!backend) {
+       ac = partition_init_ctx(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition);
+       /* we need to add a control but we never touch the original request */
+       ret = partition_prep_request(ac, partition);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       /* issue request */
-       return ldb_next_request(backend, req);
+       /* fire the first one */
+       return partition_call_first(ac);
 }
 
 /* search */
@@ -336,6 +451,7 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
 
        /* (later) consider if we should be searching multiple
         * partitions (for 'invisible' partition behaviour */
+
        struct ldb_control *search_control = ldb_request_get_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID);
        struct ldb_control *domain_scope_control = ldb_request_get_control(req, LDB_CONTROL_DOMAIN_SCOPE_OID);
        
@@ -366,7 +482,7 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
                                return LDB_ERR_OPERATIONS_ERROR;
                        }
                }
-               ac = partition_init_handle(req, module);
+               ac = partition_init_ctx(module, req);
                if (!ac) {
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -378,7 +494,7 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
                for (i=0; data && data->partitions && data->partitions[i]; i++) {
                        /* Find all partitions under the search base */
                        if (ldb_dn_compare_base(req->op.search.base, data->partitions[i]->dn) == 0) {
-                               ret = partition_send_request(ac, data->partitions[i]);
+                               ret = partition_prep_request(ac, data->partitions[i]);
                                if (ret != LDB_SUCCESS) {
                                        return ret;
                                }
@@ -390,8 +506,10 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
                        talloc_free(ac);
                        return ldb_next_request(module, req);
                }
-               
-               return LDB_SUCCESS;
+
+               /* fire the first one */
+               return partition_call_first(ac);
+
        } else {
                /* Handle this like all other requests */
                if (search_control && (search_options->search_options & ~LDB_SEARCH_OPTION_PHANTOM_ROOT) == 0) {
@@ -436,7 +554,7 @@ static int partition_rename(struct ldb_module *module, struct ldb_request *req)
        struct partition_private_data *data = talloc_get_type(module->private_data, 
                                                              struct partition_private_data);
 
-       /* Skip the lot if 'data' isn't here yet (initialistion) */
+       /* Skip the lot if 'data' isn't here yet (initialization) */
        if (!data) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -486,24 +604,26 @@ static int partition_start_trans(struct ldb_module *module)
                                                              struct partition_private_data);
        /* Look at base DN */
        /* Figure out which partition it is under */
-       /* Skip the lot if 'data' isn't here yet (initialistion) */
+       /* Skip the lot if 'data' isn't here yet (initialization) */
        ret = ldb_next_start_trans(module);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
        for (i=0; data && data->partitions && data->partitions[i]; i++) {
-               struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
+               struct ldb_module *next = data->partitions[i]->module;
+               PARTITION_FIND_OP(next, start_transaction);
 
-               ret = ldb_next_start_trans(next);
-               talloc_free(next);
+               ret = next->ops->start_transaction(next);
                if (ret != LDB_SUCCESS) {
                        /* Back it out, if it fails on one */
                        for (i--; i >= 0; i--) {
-                               next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
-                               ldb_next_del_trans(next);
-                               talloc_free(next);
+                               next = data->partitions[i]->module;
+                               PARTITION_FIND_OP(next, del_transaction);
+
+                               next->ops->del_transaction(next);
                        }
+                       ldb_next_del_trans(module);
                        return ret;
                }
        }
@@ -513,7 +633,7 @@ static int partition_start_trans(struct ldb_module *module)
 /* end a transaction */
 static int partition_end_trans(struct ldb_module *module)
 {
-       int i, ret, ret2 = LDB_SUCCESS;
+       int i, ret;
        struct partition_private_data *data = talloc_get_type(module->private_data, 
                                                              struct partition_private_data);
        ret = ldb_next_end_trans(module);
@@ -525,24 +645,24 @@ static int partition_end_trans(struct ldb_module *module)
        /* Figure out which partition it is under */
        /* Skip the lot if 'data' isn't here yet (initialistion) */
        for (i=0; data && data->partitions && data->partitions[i]; i++) {
-               struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
-               
-               ret = ldb_next_end_trans(next);
-               talloc_free(next);
+               struct ldb_module *next = data->partitions[i]->module;
+               PARTITION_FIND_OP(next, end_transaction);
+
+               ret = next->ops->end_transaction(next);
                if (ret != LDB_SUCCESS) {
-                       ret2 = ret;
-               }
-       }
+                       /* Back it out, if it fails on one */
+                       for (i--; i >= 0; i--) {
+                               next = data->partitions[i]->module;
+                               PARTITION_FIND_OP(next, del_transaction);
 
-       if (ret != LDB_SUCCESS) {
-               /* Back it out, if it fails on one */
-               for (i=0; data && data->partitions && data->partitions[i]; i++) {
-                       struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
-                       ldb_next_del_trans(next);
-                       talloc_free(next);
+                               next->ops->del_transaction(next);
+                       }
+                       ldb_next_del_trans(module);
+                       return ret;
                }
        }
-       return ret;
+
+       return LDB_SUCCESS;
 }
 
 /* delete a transaction */
@@ -560,10 +680,10 @@ static int partition_del_trans(struct ldb_module *module)
        /* Figure out which partition it is under */
        /* Skip the lot if 'data' isn't here yet (initialistion) */
        for (i=0; data && data->partitions && data->partitions[i]; i++) {
-               struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
-               
-               ret = ldb_next_del_trans(next);
-               talloc_free(next);
+               struct ldb_module *next = data->partitions[i]->module;
+               PARTITION_FIND_OP(next, del_transaction);
+
+               ret = next->ops->del_transaction(next);
                if (ret != LDB_SUCCESS) {
                        ret2 = ret;
                }
@@ -571,6 +691,9 @@ static int partition_del_trans(struct ldb_module *module)
        return ret2;
 }
 
+/* NOTE: ldb_sequence_number is still a completely SYNCHRONOUS call
+ * implemented only in ldb_rdb. It does not require ldb_wait() to be
+ * called after a request is made */
 static int partition_sequence_number(struct ldb_module *module, struct ldb_request *req)
 {
        int i, ret;
@@ -593,19 +716,29 @@ static int partition_sequence_number(struct ldb_module *module, struct ldb_reque
                        seq_number = seq_number + req->op.seq_num.seq_num;
                }
 
+               /* gross hack part1 */
+               ret = ldb_request_add_control(req,
+                                       DSDB_CONTROL_CURRENT_PARTITION_OID,
+                                       false, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+
                /* Look at base DN */
                /* Figure out which partition it is under */
                /* Skip the lot if 'data' isn't here yet (initialistion) */
                for (i=0; data && data->partitions && data->partitions[i]; i++) {
-                       struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
-                       
-                       ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, data->partitions[i]);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
+
+                       /* gross hack part2 */
+                       int j;
+                       for (j=0; req->controls[j]; j++) {
+                               if (strcmp(req->controls[j]->oid, DSDB_CONTROL_CURRENT_PARTITION_OID) == 0) {
+                                       req->controls[j]->data = data->partitions[i];
+                                       break;
+                               }
                        }
 
-                       ret = ldb_next_request(next, req);
-                       talloc_free(next);
+                       ret = partition_request(data->partitions[i]->module, req);
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
@@ -630,15 +763,13 @@ static int partition_sequence_number(struct ldb_module *module, struct ldb_reque
                        return ret;
                }
                timestamp = date_req->op.seq_num.seq_num;
-               
+
                /* Look at base DN */
                /* Figure out which partition it is under */
                /* Skip the lot if 'data' isn't here yet (initialistion) */
                for (i=0; data && data->partitions && data->partitions[i]; i++) {
-                       struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
-                       
-                       ret = ldb_next_request(next, date_req);
-                       talloc_free(next);
+
+                       ret = partition_request(data->partitions[i]->module, req);
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
@@ -651,7 +782,7 @@ static int partition_sequence_number(struct ldb_module *module, struct ldb_reque
        switch (req->op.seq_num.flags) {
        case LDB_SEQ_NEXT:
        case LDB_SEQ_HIGHEST_SEQ:
-               
+
                req->op.seq_num.flags = 0;
 
                /* Has someone above set a timebase sequence? */
@@ -705,7 +836,6 @@ static int partition_extended_schema_update_now(struct ldb_module *module, struc
        struct partition_private_data *data;
        struct ldb_dn *schema_dn;
        struct partition_context *ac;
-       struct ldb_module *backend;
        int ret;
 
        schema_dn = talloc_get_type(req->op.extended.data, struct ldb_dn);
@@ -724,30 +854,33 @@ static int partition_extended_schema_update_now(struct ldb_module *module, struc
                return ldb_next_request(module, req);
        }
 
-       ac = partition_init_handle(req, module);
+       ac = partition_init_ctx(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       backend = make_module_for_next_request(req, module->ldb, partition->module);
-       if (!backend) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition);
+       /* we need to add a control but we never touch the original request */
+       ret = partition_prep_request(ac, partition);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       return ldb_next_request(backend, req);
+       /* fire the first one */
+       return partition_call_first(ac);
 }
 
 
 /* extended */
 static int partition_extended(struct ldb_module *module, struct ldb_request *req)
 {
+       struct partition_private_data *data;
        struct partition_context *ac;
 
+       data = talloc_get_type(module->private_data, struct partition_private_data);
+       if (!data || !data->partitions) {
+               return ldb_next_request(module, req);
+       }
+
        if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
                return partition_extended_replicated_objects(module, req);
        }
@@ -762,27 +895,23 @@ static int partition_extended(struct ldb_module *module, struct ldb_request *req
         * we need to send it to all partitions
         */
 
-       ac = partition_init_handle(req, module);
+       ac = partition_init_ctx(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-                       
+
        return partition_send_all(module, ac, req);
 }
 
-static int sort_compare(void *void1,
-                       void *void2, void *opaque)
+static int partition_sort_compare(const void *v1, const void *v2)
 {
-       struct dsdb_control_current_partition **pp1 = 
-               (struct dsdb_control_current_partition **)void1;
-       struct dsdb_control_current_partition **pp2 = 
-               (struct dsdb_control_current_partition **)void2;
-       struct dsdb_control_current_partition *partition1 = talloc_get_type(*pp1,
-                                                           struct dsdb_control_current_partition);
-       struct dsdb_control_current_partition *partition2 = talloc_get_type(*pp2,
-                                                           struct dsdb_control_current_partition);
-
-       return ldb_dn_compare(partition1->dn, partition2->dn);
+       struct dsdb_control_current_partition *p1;
+       struct dsdb_control_current_partition *p2;
+
+       p1 = *((struct dsdb_control_current_partition **)v1);
+       p2 = *((struct dsdb_control_current_partition **)v2);
+
+       return ldb_dn_compare(p1->dn, p2->dn);
 }
 
 static int partition_init(struct ldb_module *module)
@@ -888,8 +1017,8 @@ static int partition_init(struct ldb_module *module)
        data->partitions[i] = NULL;
 
        /* sort these into order, most to least specific */
-       ldb_qsort(data->partitions, partition_attributes->num_values, sizeof(*data->partitions), 
-                 module->ldb, sort_compare);
+       qsort(data->partitions, partition_attributes->num_values,
+             sizeof(*data->partitions), partition_sort_compare);
 
        for (i=0; data->partitions[i]; i++) {
                struct ldb_request *req;
@@ -902,8 +1031,19 @@ static int partition_init(struct ldb_module *module)
                
                req->operation = LDB_REQ_REGISTER_PARTITION;
                req->op.reg_partition.dn = data->partitions[i]->dn;
+               req->callback = ldb_op_default_callback;
+
+               ldb_set_timeout(module->ldb, req, 0);
+
+               req->handle = ldb_handle_new(req, module->ldb);
+               if (req->handle == NULL) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
                
                ret = ldb_request(module->ldb, req);
+               if (ret == LDB_SUCCESS) {
+                       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+               }
                if (ret != LDB_SUCCESS) {
                        ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Unable to register partition with rootdse!\n");
                        talloc_free(mem_ctx);
@@ -921,7 +1061,7 @@ static int partition_init(struct ldb_module *module)
                        talloc_free(mem_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               
+
                for (i=0; i < replicate_attributes->num_values; i++) {
                        data->replicate[i] = ldb_dn_from_ldb_val(data->replicate, module->ldb, &replicate_attributes->values[i]);
                        if (!ldb_dn_validate(data->replicate[i])) {
@@ -1016,72 +1156,6 @@ static int partition_init(struct ldb_module *module)
        return ldb_next_init(module);
 }
 
-static int partition_wait_none(struct ldb_handle *handle) {
-       struct partition_context *ac;
-       int ret;
-       int i;
-    
-       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 partition_context);
-
-       for (i=0; i < ac->num_requests; i++) {
-               ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
-               
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->down_req[i]->handle->status;
-                       goto done;
-               }
-               
-               if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-       }
-
-       ret = LDB_SUCCESS;
-
-done:
-       handle->state = LDB_ASYNC_DONE;
-       return ret;
-}
-
-
-static int partition_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = partition_wait_none(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-       }
-
-       return handle->status;
-}
-
-static int partition_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return partition_wait_all(handle);
-       } else {
-               return partition_wait_none(handle);
-       }
-}
-
 _PUBLIC_ const struct ldb_module_ops ldb_partition_module_ops = {
        .name              = "partition",
        .init_context      = partition_init,
@@ -1095,5 +1169,4 @@ _PUBLIC_ const struct ldb_module_ops ldb_partition_module_ops = {
        .start_transaction = partition_start_trans,
        .end_transaction   = partition_end_trans,
        .del_transaction   = partition_del_trans,
-       .wait              = partition_wait
 };
index 6faef63c3d7d6813f22044b4143792150de08203..e36de3c5c408e0f2b7851ba5bc7ce720f03c4188 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database module
 
-   Copyright (C) Simo Sorce  2004-2006
+   Copyright (C) Simo Sorce  2004-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
    Copyright (C) Andrew Tridgell 2004
    Copyright (C) Stefan Metzmacher 2007
 
 struct ph_context {
 
-       enum ph_type {PH_ADD, PH_MOD} type;
-       enum ph_step {PH_ADD_SEARCH_DOM, PH_ADD_DO_ADD, PH_MOD_DO_REQ, PH_MOD_SEARCH_SELF, PH_MOD_SEARCH_DOM, PH_MOD_DO_MOD} step;
-
        struct ldb_module *module;
-       struct ldb_request *orig_req;
+       struct ldb_request *req;
 
        struct ldb_request *dom_req;
        struct ldb_reply *dom_res;
 
-       struct ldb_request *down_req;
-
-       struct ldb_request *search_req;
        struct ldb_reply *search_res;
 
-       struct ldb_request *mod_req;
-
        struct dom_sid *domain_sid;
+       struct domain_data *domain;
 };
 
 struct domain_data {
@@ -1378,168 +1371,193 @@ static int setup_password_fields(struct setup_password_fields_io *io)
        return LDB_SUCCESS;
 }
 
-static struct ldb_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type)
+static struct ph_context *ph_init_context(struct ldb_module *module,
+                                         struct ldb_request *req)
 {
        struct ph_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 ph_context);
+       ac = talloc_zero(req, struct ph_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->type = type;
        ac->module = module;
-       ac->orig_req = req;
+       ac->req = req;
 
-       return h;
+       return ac;
 }
 
-static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ph_context *ac;
 
-       ac = talloc_get_type(context, struct ph_context);
+       ac = talloc_get_type(req->context, struct ph_context);
 
-       /* we are interested only in the single reply (base search) we receive here */
-       if (ares->type == LDB_REPLY_ENTRY) {
-               if (ac->dom_res != NULL) {
-                       ldb_set_errstring(ldb, "Too many results");
-                       talloc_free(ares);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               ac->dom_res = talloc_steal(ac, ares);
-       } else {
-               talloc_free(ares);
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-
-       return LDB_SUCCESS;
-}
-
-static int build_domain_data_request(struct ph_context *ac)
-{
-       /* attrs[] is returned from this function in
-          ac->dom_req->op.search.attrs, so it must be static, as
-          otherwise the compiler can put it on the stack */
-       static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", NULL };
-       char *filter;
-
-       ac->dom_req = talloc_zero(ac, struct ldb_request);
-       if (ac->dom_req == NULL) {
-               ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
-       ac->dom_req->operation = LDB_SEARCH;
-       ac->dom_req->op.search.base = ldb_get_default_basedn(ac->module->ldb);
-       ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE;
 
-       filter = talloc_asprintf(ac->dom_req,
-                                "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))", 
-                                ldap_encode_ndr_dom_sid(ac->dom_req, ac->domain_sid));
-       if (filter == NULL) {
-               ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
-               talloc_free(ac->dom_req);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ac->dom_req->op.search.tree = ldb_parse_tree(ac->dom_req, filter);
-       if (ac->dom_req->op.search.tree == NULL) {
-               ldb_set_errstring(ac->module->ldb, "Invalid search filter");
-               talloc_free(ac->dom_req);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-       ac->dom_req->op.search.attrs = attrs;
-       ac->dom_req->controls = NULL;
-       ac->dom_req->context = ac;
-       ac->dom_req->callback = get_domain_data_callback;
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->dom_req);
 
-       return LDB_SUCCESS;
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, ares->error);
 }
 
-static struct domain_data *get_domain_data(struct ldb_module *module, void *ctx, struct ldb_reply *res)
+static int password_hash_add_do_add(struct ph_context *ac);
+static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
+static int password_hash_mod_search_self(struct ph_context *ac);
+static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
+static int password_hash_mod_do_mod(struct ph_context *ac);
+
+static int get_domain_data_callback(struct ldb_request *req,
+                                   struct ldb_reply *ares)
 {
        struct domain_data *data;
-       const char *tmp;
        struct ph_context *ac;
+       int ret;
+       char *tmp;
        char *p;
 
-       ac = talloc_get_type(ctx, struct ph_context);
+       ac = talloc_get_type(req->context, struct ph_context);
 
-       data = talloc_zero(ac, struct domain_data);
-       if (data == NULL) {
-               return NULL;
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-
-       if (res == NULL) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Could not find this user's domain: %s!\n", dom_sid_string(data, ac->domain_sid));
-               talloc_free(data);
-               return NULL;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       data->pwdProperties= samdb_result_uint(res->message, "pwdProperties", 0);
-       data->store_cleartext = data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
-       data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0);
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               if (ac->domain != NULL) {
+                       ldb_set_errstring(ac->module->ldb, "Too many results");
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
 
-       /* For a domain DN, this puts things in dotted notation */
-       /* For builtin domains, this will give details for the host,
-        * but that doesn't really matter, as it's just used for salt
-        * and kerberos principals, which don't exist here */
+               data = talloc_zero(ac, struct domain_data);
+               if (data == NULL) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
 
-       tmp = ldb_dn_canonical_string(ctx, res->message->dn);
-       if (!tmp) {
-               return NULL;
-       }
-       
-       /* But it puts a trailing (or just before 'builtin') / on things, so kill that */
-       p = strchr(tmp, '/');
-       if (p) {
-               p[0] = '\0';
-       }
+               data->pwdProperties = samdb_result_uint(ares->message, "pwdProperties", 0);
+               data->store_cleartext = data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
+               data->pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", 0);
+
+               /* For a domain DN, this puts things in dotted notation */
+               /* For builtin domains, this will give details for the host,
+                * but that doesn't really matter, as it's just used for salt
+                * and kerberos principals, which don't exist here */
+
+               tmp = ldb_dn_canonical_string(data, ares->message->dn);
+               if (!tmp) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               /* But it puts a trailing (or just before 'builtin') / on things, so kill that */
+               p = strchr(tmp, '/');
+               if (p) {
+                       p[0] = '\0';
+               }
 
-       if (tmp != NULL) {
                data->dns_domain = strlower_talloc(data, tmp);
                if (data->dns_domain == NULL) {
-                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
-                       return NULL;
+                       ldb_oom(ac->module->ldb);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
                data->realm = strupper_talloc(data, tmp);
                if (data->realm == NULL) {
-                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
-                       return NULL;
+                       ldb_oom(ac->module->ldb);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
+               /* FIXME: NetbIOS name is *always* the first domain component ?? -SSS */
                p = strchr(tmp, '.');
                if (p) {
                        p[0] = '\0';
                }
                data->netbios_domain = strupper_talloc(data, tmp);
                if (data->netbios_domain == NULL) {
-                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n");
-                       return NULL;
+                       ldb_oom(ac->module->ldb);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               talloc_free(tmp);
+               ac->domain = data;
+               break;
+
+       case LDB_REPLY_DONE:
+
+               /* call the next step */
+               switch (ac->req->operation) {
+               case LDB_ADD:
+                       ret = password_hash_add_do_add(ac);
+                       break;
+
+               case LDB_MODIFY:
+                       ret = password_hash_mod_do_mod(ac);
+                       break;
+
+               default:
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
+               }
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
                }
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               break;
        }
 
-       return data;
+       talloc_free(ares);
+       return LDB_SUCCESS;
+}
+
+static int build_domain_data_request(struct ph_context *ac)
+{
+       /* attrs[] is returned from this function in
+          ac->dom_req->op.search.attrs, so it must be static, as
+          otherwise the compiler can put it on the stack */
+       static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", NULL };
+       char *filter;
+
+       filter = talloc_asprintf(ac,
+                               "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))",
+                                ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+       if (filter == NULL) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return ldb_build_search_req(&ac->dom_req, ac->module->ldb, ac,
+                                   ldb_get_default_basedn(ac->module->ldb),
+                                   LDB_SCOPE_SUBTREE,
+                                   filter, attrs,
+                                   NULL,
+                                   ac, get_domain_data_callback,
+                                   ac->req);
 }
 
 static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_handle *h;
        struct ph_context *ac;
        struct ldb_message_element *sambaAttr;
        struct ldb_message_element *ntAttr;
@@ -1558,7 +1576,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       /* nobody must touch this fields */
+       /* nobody must touch these fields */
        if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
@@ -1617,16 +1635,16 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       h = ph_init_handle(req, module, PH_ADD);
-       if (!h) {
+       ac = ph_init_context(module, req);
+       if (ac == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct ph_context);
 
        /* get user domain data */
        ac->domain_sid = samdb_result_sid_prefix(ac, req->op.add.message, "objectSid");
        if (ac->domain_sid == NULL) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                         "can't handle entry with missing objectSid!\n");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -1635,51 +1653,33 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return ret;
        }
 
-       ac->step = PH_ADD_SEARCH_DOM;
-
-       req->handle = h;
-
        return ldb_next_request(module, ac->dom_req);
 }
 
-static int password_hash_add_do_add(struct ldb_handle *h) {
+static int password_hash_add_do_add(struct ph_context *ac) {
 
-       struct ph_context *ac;
-       struct domain_data *domain;
+       struct ldb_request *down_req;
        struct smb_krb5_context *smb_krb5_context;
        struct ldb_message *msg;
        struct setup_password_fields_io io;
        int ret;
 
-       ac = talloc_get_type(h->private_data, struct ph_context);
-
-       domain = get_domain_data(ac->module, ac, ac->dom_res);
-       if (domain == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ac->down_req = talloc(ac, struct ldb_request);
-       if (ac->down_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *(ac->down_req) = *(ac->orig_req);
-       ac->down_req->op.add.message = msg = ldb_msg_copy_shallow(ac->down_req, ac->orig_req->op.add.message);
-       if (ac->down_req->op.add.message == NULL) {
+       msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
+       if (msg == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* Some operations below require kerberos contexts */
-       if (smb_krb5_init_context(ac->down_req, 
-                                 ldb_get_opaque(h->module->ldb, "EventContext"), 
-                                 (struct loadparm_context *)ldb_get_opaque(h->module->ldb, "loadparm"), 
+       if (smb_krb5_init_context(ac,
+                                 ldb_get_event_context(ac->module->ldb),
+                                 (struct loadparm_context *)ldb_get_opaque(ac->module->ldb, "loadparm"),
                                  &smb_krb5_context) != 0) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        ZERO_STRUCT(io);
        io.ac                           = ac;
-       io.domain                       = domain;
+       io.domain                       = ac->domain;
        io.smb_krb5_context             = smb_krb5_context;
 
        io.u.user_account_control       = samdb_result_uint(msg, "userAccountControl", 0);
@@ -1756,27 +1756,27 @@ static int password_hash_add_do_add(struct ldb_handle *h) {
                return ret;
        }
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = PH_ADD_DO_ADD;
-
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
+       ret = ldb_build_add_req(&down_req, ac->module->ldb, ac,
+                               msg,
+                               ac->req->controls,
+                               ac, ph_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       /* perform the operation */
-       return ldb_next_request(ac->module, ac->down_req);
+       return ldb_next_request(ac->module, down_req);
 }
 
-static int password_hash_mod_search_self(struct ldb_handle *h);
-
 static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ldb_handle *h;
        struct ph_context *ac;
        struct ldb_message_element *sambaAttr;
        struct ldb_message_element *ntAttr;
        struct ldb_message_element *lmAttr;
        struct ldb_message *msg;
+       struct ldb_request *down_req;
+       int ret;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
 
@@ -1824,83 +1824,143 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       h = ph_init_handle(req, module, PH_MOD);
-       if (!h) {
+       ac = ph_init_context(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       ac = talloc_get_type(h->private_data, struct ph_context);
 
-       /* return or own handle to deal with this call */
-       req->handle = h;
-
-       /* prepare the first operation */
-       ac->down_req = talloc_zero(ac, struct ldb_request);
-       if (ac->down_req == NULL) {
-               ldb_set_errstring(module->ldb, "Out of memory!");
+       /* use a new message structure so that we can modify it */
+       msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+       if (msg == NULL) {
+               ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       *(ac->down_req) = *req; /* copy the request */
-
-       /* use a new message structure so that we can modify it */
-       ac->down_req->op.mod.message = msg = ldb_msg_copy_shallow(ac->down_req, req->op.mod.message);
-
-       /* - remove any imodification to the password from the first commit
+       /* - remove any modification to the password from the first commit
         *   we will make the real modification later */
        if (sambaAttr) ldb_msg_remove_attr(msg, "userPassword");
        if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
        if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
 
-       /* if there was nothing else to be modify skip to next step */
+       /* if there was nothing else to be modified skip to next step */
        if (msg->num_elements == 0) {
-               talloc_free(ac->down_req);
-               ac->down_req = NULL;
-               return password_hash_mod_search_self(h);
+               return password_hash_mod_search_self(ac);
        }
-       
-       ac->down_req->context = NULL;
-       ac->down_req->callback = NULL;
 
-       ac->step = PH_MOD_DO_REQ;
+       ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, ph_modify_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
+       return ldb_next_request(module, down_req);
+}
 
-       return ldb_next_request(module, ac->down_req);
+static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct ph_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct ph_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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ret = password_hash_mod_search_self(ac);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
+       talloc_free(ares);
+       return LDB_SUCCESS;
 }
 
-static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ph_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct ph_context);
 
-       ac = talloc_get_type(context, struct ph_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);
+       }
+
+       /* we are interested only in the single reply (base search) */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
 
-       /* 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");
+                       ldb_set_errstring(ac->module->ldb, "Too many results");
                        talloc_free(ares);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
 
                /* if it is not an entry of type person this is an error */
-               /* TODO: remove this when userPassword will be in schema */
+               /* TODO: remove this when sambaPassword will be in schema */
                if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
-                       ldb_set_errstring(ldb, "Object class violation");
+                       ldb_set_errstring(ac->module->ldb, "Object class violation");
                        talloc_free(ares);
-                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OBJECT_CLASS_VIOLATION);
                }
 
                ac->search_res = talloc_steal(ac, ares);
-       } else {
-               talloc_free(ares);
+               return LDB_SUCCESS;
+
+       case LDB_REPLY_DONE:
+
+               /* get object domain sid */
+               ac->domain_sid = samdb_result_sid_prefix(ac,
+                                                       ac->search_res->message,
+                                                       "objectSid");
+               if (ac->domain_sid == NULL) {
+                       ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR,
+                                 "can't handle entry without objectSid!\n");
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               /* get user domain data */
+               ret = build_domain_data_request(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL,ret);
+               }
+
+               return ldb_next_request(ac->module, ac->dom_req);
+
+       case LDB_REPLY_REFERRAL:
+               /*ignore anything else for now */
+               break;
        }
 
+       talloc_free(ares);
        return LDB_SUCCESS;
 }
 
-static int password_hash_mod_search_self(struct ldb_handle *h) {
+static int password_hash_mod_search_self(struct ph_context *ac) {
 
-       struct ph_context *ac;
        static const char * const attrs[] = { "userAccountControl", "lmPwdHistory", 
                                              "ntPwdHistory", 
                                              "objectSid", "msDS-KeyVersionNumber", 
@@ -1909,64 +1969,28 @@ static int password_hash_mod_search_self(struct ldb_handle *h) {
                                              "dBCSPwd", "unicodePwd",
                                              "supplementalCredentials",
                                              NULL };
-
-       ac = talloc_get_type(h->private_data, struct ph_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, "Invalid search filter");
-               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 = PH_MOD_SEARCH_SELF;
-
-       return ldb_next_request(ac->module, ac->search_req);
-}
-
-static int password_hash_mod_search_dom(struct ldb_handle *h) {
-
-       struct ph_context *ac;
+       struct ldb_request *search_req;
        int ret;
 
-       ac = talloc_get_type(h->private_data, struct ph_context);
+       ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
+                                  ac->req->op.mod.message->dn,
+                                  LDB_SCOPE_BASE,
+                                  "(objectclass=*)",
+                                  attrs,
+                                  NULL,
+                                  ac, ph_mod_search_callback,
+                                  ac->req);
 
-       /* get object domain sid */
-       ac->domain_sid = samdb_result_sid_prefix(ac, ac->search_res->message, "objectSid");
-       if (ac->domain_sid == NULL) {
-               ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n");
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* get user domain data */
-       ret = build_domain_data_request(ac);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       ac->step = PH_MOD_SEARCH_DOM;
-
-       return ldb_next_request(ac->module, ac->dom_req);
+       return ldb_next_request(ac->module, search_req);
 }
 
-static int password_hash_mod_do_mod(struct ldb_handle *h) {
+static int password_hash_mod_do_mod(struct ph_context *ac) {
 
-       struct ph_context *ac;
-       struct domain_data *domain;
+       struct ldb_request *mod_req;
        struct smb_krb5_context *smb_krb5_context;
        struct ldb_message *msg;
        struct ldb_message *orig_msg;
@@ -1974,43 +1998,29 @@ static int password_hash_mod_do_mod(struct ldb_handle *h) {
        struct setup_password_fields_io io;
        int ret;
 
-       ac = talloc_get_type(h->private_data, struct ph_context);
-
-       domain = get_domain_data(ac->module, ac, ac->dom_res);
-       if (domain == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ac->mod_req = talloc(ac, struct ldb_request);
-       if (ac->mod_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       *(ac->mod_req) = *(ac->orig_req);
-       
        /* use a new message structure so that we can modify it */
-       ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+       msg = ldb_msg_new(ac);
        if (msg == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* modify dn */
-       msg->dn = ac->orig_req->op.mod.message->dn;
+       msg->dn = ac->req->op.mod.message->dn;
 
        /* Some operations below require kerberos contexts */
-       if (smb_krb5_init_context(ac->mod_req, 
-                                 ldb_get_opaque(h->module->ldb, "EventContext"), 
-                                 (struct loadparm_context *)ldb_get_opaque(h->module->ldb, "loadparm"), 
+       if (smb_krb5_init_context(ac,
+                                 ldb_get_event_context(ac->module->ldb),
+                                 (struct loadparm_context *)ldb_get_opaque(ac->module->ldb, "loadparm"),
                                  &smb_krb5_context) != 0) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       orig_msg        = discard_const(ac->orig_req->op.mod.message);
+       orig_msg        = discard_const(ac->req->op.mod.message);
        searched_msg    = ac->search_res->message;
 
        ZERO_STRUCT(io);
        io.ac                           = ac;
-       io.domain                       = domain;
+       io.domain                       = ac->domain;
        io.smb_krb5_context             = smb_krb5_context;
 
        io.u.user_account_control       = samdb_result_uint(searched_msg, "userAccountControl", 0);
@@ -2093,189 +2103,20 @@ static int password_hash_mod_do_mod(struct ldb_handle *h) {
                return ret;
        }
 
-       h->state = LDB_ASYNC_INIT;
-       h->status = LDB_SUCCESS;
-
-       ac->step = PH_MOD_DO_MOD;
-
-       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
-
-       /* perform the search */
-       return ldb_next_request(ac->module, ac->mod_req);
-}
-
-static int ph_wait(struct ldb_handle *handle) {
-       struct ph_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 ph_context);
-
-       switch (ac->step) {
-       case PH_ADD_SEARCH_DOM:
-               ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->dom_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->dom_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* domain search done, go on */
-               return password_hash_add_do_add(handle);
-
-       case PH_ADD_DO_ADD:
-               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;
-               }
-
-               break;
-               
-       case PH_MOD_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;
-               }
-
-               /* non-password mods done, go on */
-               return password_hash_mod_search_self(handle);
-               
-       case PH_MOD_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;
-               }
-
-               if (ac->search_res == NULL) {
-                       return LDB_ERR_NO_SUCH_OBJECT;
-               }
-
-               /* self search done, go on */
-               return password_hash_mod_search_dom(handle);
-               
-       case PH_MOD_SEARCH_DOM:
-               ret = ldb_wait(ac->dom_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->dom_req->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->dom_req->handle->status;
-                       goto done;
-               }
-
-               if (ac->dom_req->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-
-               /* domain search done, go on */
-               return password_hash_mod_do_mod(handle);
-
-       case PH_MOD_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 ph_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = ph_wait(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
+       ret = ldb_build_mod_req(&mod_req, ac->module->ldb, ac,
+                               msg,
+                               ac->req->controls,
+                               ac, ph_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       return handle->status;
-}
-
-static int password_hash_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return ph_wait_all(handle);
-       } else {
-               return ph_wait(handle);
-       }
+       return ldb_next_request(ac->module, mod_req);
 }
 
 _PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
        .name          = "password_hash",
        .add           = password_hash_add,
        .modify        = password_hash_modify,
-       .wait          = password_hash_wait
 };
index d0a315e45a444d59359b157cdc16a0e331224f34..171832bbb46a9f8abb9a481baac9245e93d2721f 100644 (file)
@@ -50,6 +50,14 @@ struct proxy_data {
        const char **newstr;
 };
 
+struct proxy_ctx {
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+#ifdef DEBUG_PROXY
+       int count;
+#endif
+};
 
 /*
   load the @PROXYINFO record
@@ -62,7 +70,6 @@ static int load_proxy_info(struct ldb_module *module)
        int ret;
        const char *olddn, *newdn, *url, *username, *password, *oldstr, *newstr;
        struct cli_credentials *creds;
-       
 
        /* see if we have already loaded it */
        if (proxy->upstream != NULL) {
@@ -105,7 +112,7 @@ static int load_proxy_info(struct ldb_module *module)
                goto failed;
        }
 
-       proxy->upstream = ldb_init(proxy);
+       proxy->upstream = ldb_init(proxy, ldb_get_event_context(ldb));
        if (proxy->upstream == NULL) {
                ldb_oom(module->ldb);
                goto failed;
@@ -180,10 +187,10 @@ static void proxy_convert_blob(TALLOC_CTX *mem_ctx, struct ldb_val *v,
 /*
   convert a returned value
 */
-static void proxy_convert_value(struct ldb_module *module, struct ldb_message *msg, struct ldb_val *v)
+static void proxy_convert_value(struct proxy_data *proxy, struct ldb_message *msg, struct ldb_val *v)
 {
-       struct proxy_data *proxy = talloc_get_type(module->private_data, struct proxy_data);
        int i;
+
        for (i=0;proxy->oldstr[i];i++) {
                char *p = strcasestr((char *)v->data, proxy->oldstr[i]);
                if (p == NULL) continue;
@@ -195,20 +202,21 @@ static void proxy_convert_value(struct ldb_module *module, struct ldb_message *m
 /*
   convert a returned value
 */
-static struct ldb_parse_tree *proxy_convert_tree(struct ldb_module *module, 
+static struct ldb_parse_tree *proxy_convert_tree(TALLOC_CTX *mem_ctx,
+                                                struct proxy_data *proxy,
                                                 struct ldb_parse_tree *tree)
 {
-       struct proxy_data *proxy = talloc_get_type(module->private_data, struct proxy_data);
        int i;
-       char *expression = ldb_filter_from_tree(module, tree);
+       char *expression = ldb_filter_from_tree(mem_ctx, tree);
+
        for (i=0;proxy->newstr[i];i++) {
                struct ldb_val v;
                char *p = strcasestr(expression, proxy->newstr[i]);
                if (p == NULL) continue;
                v.data = (uint8_t *)expression;
                v.length = strlen(expression)+1;
-               proxy_convert_blob(module, &v, proxy->newstr[i], proxy->oldstr[i]);
-               return ldb_parse_tree(module, (const char *)v.data);
+               proxy_convert_blob(mem_ctx, &v, proxy->newstr[i], proxy->oldstr[i]);
+               return ldb_parse_tree(mem_ctx, (const char *)v.data);
        }
        return tree;
 }
@@ -218,13 +226,14 @@ static struct ldb_parse_tree *proxy_convert_tree(struct ldb_module *module,
 /*
   convert a returned record
 */
-static void proxy_convert_record(struct ldb_module *module, struct ldb_message *msg)
+static void proxy_convert_record(struct ldb_context *ldb,
+                                struct proxy_data *proxy,
+                                struct ldb_message *msg)
 {
-       struct proxy_data *proxy = talloc_get_type(module->private_data, struct proxy_data);
        int attr, v;
-       
+
        /* fix the message DN */
-       if (ldb_dn_compare_base(module->ldb, proxy->olddn, msg->dn) == 0) {
+       if (ldb_dn_compare_base(ldb, proxy->olddn, msg->dn) == 0) {
                ldb_dn_remove_base_components(msg->dn, ldb_dn_get_comp_num(proxy->olddn));
                ldb_dn_add_base(msg->dn, proxy->newdn);
        }
@@ -232,21 +241,71 @@ static void proxy_convert_record(struct ldb_module *module, struct ldb_message *
        /* fix any attributes */
        for (attr=0;attr<msg->num_elements;attr++) {
                for (v=0;v<msg->elements[attr].num_values;v++) {
-                       proxy_convert_value(module, msg, &msg->elements[attr].values[v]);
+                       proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
                }
        }
 
        /* fix any DN components */
        for (attr=0;attr<msg->num_elements;attr++) {
                for (v=0;v<msg->elements[attr].num_values;v++) {
-                       proxy_convert_value(module, msg, &msg->elements[attr].values[v]);
+                       proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
                }
        }
 }
 
+static int proxy_search_callback(struct ldb_request *req,
+                                 struct ldb_reply *ares)
+{
+       struct proxy_data *proxy;
+       struct proxy_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct proxy_ctx);
+       proxy = talloc_get_type(ac->module->private_data, struct proxy_data);
+
+       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);
+       }
+
+       /* Only entries are interesting, and we only want the olddn */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+#ifdef DEBUG_PROXY
+               ac->count++;
+#endif
+               proxy_convert_record(ac->module->ldb, proxy, ares->message);
+               ret = ldb_module_send_entry(ac->req, ares->message);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+
+               /* ignore remote referrals */
+               break;
+
+       case LDB_REPLY_DONE:
+
+#ifdef DEBUG_PROXY
+               printf("# record %d\n", ac->count+1);
+#endif
+
+               return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
+       }
+
+       talloc_free(ares);
+       return ret;
+}
+
 /* search */
 static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *req)
 {
+       struct proxy_ctx *ac;
+       struct ldb_parse_tree *newtree;
        struct proxy_data *proxy = talloc_get_type(module->private_data, struct proxy_data);
        struct ldb_request *newreq;
        struct ldb_dn *base;
@@ -259,7 +318,7 @@ static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *re
        }
 
        if (load_proxy_info(module) != 0) {
-               return -1;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
        /* see if the dn is within olddn */
@@ -267,47 +326,50 @@ static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *re
                goto passthru;
        }
 
-       newreq = talloc(module, struct ldb_request);
-       if (newreq == NULL) {
-               return -1;
+       ac = talloc(req, struct proxy_ctx);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       newreq->op.search.tree = proxy_convert_tree(module, req->op.search.tree);
+       ac->module = module;
+       ac->req = req;
+#ifdef DEBUG_PROXY
+       ac->count = 0;
+#endif
+
+       newtree = proxy_convert_tree(ac, proxy, req->op.search.tree);
 
        /* convert the basedn of this search */
-       base = ldb_dn_copy(proxy, req->op.search.base);
+       base = ldb_dn_copy(ac, req->op.search.base);
        if (base == NULL) {
-               talloc_free(newreq);
                goto failed;
        }
        ldb_dn_remove_base_components(base, ldb_dn_get_comp_num(proxy->newdn));
        ldb_dn_add_base(base, proxy->olddn);
 
        ldb_debug(module->ldb, LDB_DEBUG_FATAL, "proxying: '%s' with dn '%s' \n", 
-                 ldb_filter_from_tree(proxy, newreq->op.search.tree), ldb_dn_get_linearized(newreq->op.search.base));
+                 ldb_filter_from_tree(ac, newreq->op.search.tree), ldb_dn_get_linearized(newreq->op.search.base));
        for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
                ldb_debug(module->ldb, LDB_DEBUG_FATAL, "attr: '%s'\n", req->op.search.attrs[i]);
        }
 
-       newreq->op.search.base = base;
-       newreq->op.search.scope = req->op.search.scope;
-       newreq->op.search.attrs = req->op.search.attrs;
-       newreq->op.search.res = req->op.search.res;
-       newreq->controls = req->controls;
+       ret = ldb_build_search_req_ex(&newreq, module->ldb, ac,
+                                     base, req->op.search.scope,
+                                     newtree, req->op.search.attrs,
+                                     req->controls,
+                                     ac, proxy_search_callback,
+                                     req);
+
+       /* FIXME: warning, need a real event system hooked up for this to work properly,
+        *        for now this makes the module *not* ASYNC */
        ret = ldb_request(proxy->upstream, newreq);
        if (ret != LDB_SUCCESS) {
                ldb_set_errstring(module->ldb, ldb_errstring(proxy->upstream));
-               talloc_free(newreq);
-               return -1;
        }
-
-       for (i = 0; i < newreq->op.search.res->count; i++) {
-               printf("# record %d\n", i+1);
-               
-               proxy_convert_record(module, newreq->op.search.res->msgs[i]);
+       ret = ldb_wait(newreq->handle, LDB_WAIT_ALL);
+       if (ret != LDB_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldb_errstring(proxy->upstream));
        }
-
-       talloc_free(newreq);
        return ret;
 
 failed:
index c6ebea1044e8ecd5597837e38a85ecdafd5cd012..b8e43a7e886000bcb8fc4922fc9d6736a2d649d3 100644 (file)
 #include "ldb_includes.h"
 
 struct rr_context {
-       struct ldb_request *orig_req;
-       struct ldb_request *down_req;
+       struct ldb_module *module;
+       struct ldb_request *req;
 };
 
-static int rr_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+static struct rr_context *rr_init_context(struct ldb_module *module,
+                                         struct ldb_request *req)
 {
-       struct rr_context *rr_context = talloc_get_type(context, struct rr_context);
-       struct ldb_request *orig_req = rr_context->orig_req;
+       struct rr_context *ac;
+
+       ac = talloc_zero(req, struct rr_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       ac->module = module;
+       ac->req = req;
+
+       return ac;
+}
+
+static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct rr_context *ac;
        int i, j;
-       
-       if (ares->type != LDB_REPLY_ENTRY) {
-               return rr_context->orig_req->callback(ldb, rr_context->orig_req->context, ares);
+
+       ac = talloc_get_type(req->context, struct rr_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);
+       }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               return ldb_module_send_referral(ac->req, ares->referral);
        }
 
+       if (ares->type == LDB_REPLY_DONE) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       /* LDB_REPLY_ENTRY */
+
        /* Find those that are range requests from the attribute list */
-       for (i = 0; orig_req->op.search.attrs[i]; i++) {
+       for (i = 0; ac->req->op.search.attrs[i]; i++) {
                char *p, *new_attr;
                const char *end_str;
                unsigned int start, end, orig_num_values;
                struct ldb_message_element *el;
                struct ldb_val *orig_values;
-               p = strchr(orig_req->op.search.attrs[i], ';');
+               p = strchr(ac->req->op.search.attrs[i], ';');
                if (!p) {
                        continue;
                }
                if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
                        continue;
                }
-               if (sscanf(p, ";range=%u-%u", &start, &end) == 2) {
-               } else if (sscanf(p, ";range=%u-*", &start) == 1) {
-                       end = (unsigned int)-1;
-               } else {
-                       continue;
+               if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
+                       if (sscanf(p, ";range=%u-*", &start) == 1) {
+                               end = (unsigned int)-1;
+                       } else {
+                               continue;
+                       }
                }
-               new_attr = talloc_strndup(orig_req, 
-                                         orig_req->op.search.attrs[i],
-                                         (unsigned int)(p-orig_req->op.search.attrs[i]));
+               new_attr = talloc_strndup(ac->req,
+                                         ac->req->op.search.attrs[i],
+                                         (size_t)(p - ac->req->op.search.attrs[i]));
 
                if (!new_attr) {
-                       ldb_oom(ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       ldb_oom(ac->module->ldb);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
                el = ldb_msg_find_element(ares->message, new_attr);
                talloc_free(new_attr);
                if (!el) {
                        continue;
                }
-               if (start > end) {
-                       ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
-                       return LDB_ERR_UNWILLING_TO_PERFORM;
-               }
                if (end >= (el->num_values - 1)) {
                        /* Need to leave the requested attribute in
                         * there (so add an empty one to match) */
@@ -91,11 +123,12 @@ static int rr_search_callback(struct ldb_context *ldb, void *context, struct ldb
                } else {
                        end_str = talloc_asprintf(el, "%u", end);
                        if (!end_str) {
-                               ldb_oom(ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
+                               ldb_oom(ac->module->ldb);
+                               return ldb_module_done(ac->req, NULL, NULL,
+                                                       LDB_ERR_OPERATIONS_ERROR);
                        }
                }
-               /* If start is greater then where we noe find the end to be */
+               /* If start is greater then where we are find the end to be */
                if (start > end) {
                        el->num_values = 0;
                        el->values = NULL;
@@ -104,16 +137,19 @@ static int rr_search_callback(struct ldb_context *ldb, void *context, struct ldb
                        orig_num_values = el->num_values;
                        
                        if ((start + end < start) || (start + end < end)) {
-                               ldb_asprintf_errstring(ldb, "range request error: start or end would overflow!");
-                               return LDB_ERR_UNWILLING_TO_PERFORM;
+                               ldb_asprintf_errstring(ac->module->ldb,
+                                       "range request error: start or end would overflow!");
+                               return ldb_module_done(ac->req, NULL, NULL,
+                                                       LDB_ERR_UNWILLING_TO_PERFORM);
                        }
                        
                        el->num_values = 0;
                        
                        el->values = talloc_array(el, struct ldb_val, (end - start) + 1);
                        if (!el->values) {
-                               ldb_oom(ldb);
-                               return LDB_ERR_OPERATIONS_ERROR;
+                               ldb_oom(ac->module->ldb);
+                               return ldb_module_done(ac->req, NULL, NULL,
+                                                       LDB_ERR_OPERATIONS_ERROR);
                        }
                        for (j=start; j <= end; j++) {
                                el->values[el->num_values] = orig_values[j];
@@ -122,13 +158,13 @@ static int rr_search_callback(struct ldb_context *ldb, void *context, struct ldb
                }
                el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str);
                if (!el->name) {
-                       ldb_oom(ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       ldb_oom(ac->module->ldb);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
        }
 
-       return rr_context->orig_req->callback(ldb, rr_context->orig_req->context, ares);
-
+       return ldb_module_send_entry(ac->req, ares->message);
 }
 
 /* search */
@@ -137,8 +173,10 @@ static int rr_search(struct ldb_module *module, struct ldb_request *req)
        int i;
        unsigned int start, end;
        const char **new_attrs = NULL;
-       struct rr_context *context;
        bool found_rr = false;
+       struct ldb_request *down_req;
+       struct rr_context *ac;
+       int ret;
 
        /* Strip the range request from the attribute */
        for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
@@ -146,19 +184,21 @@ static int rr_search(struct ldb_module *module, struct ldb_request *req)
                new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
                new_attrs[i] = req->op.search.attrs[i];
                new_attrs[i+1] = NULL;
-               p = strchr(req->op.search.attrs[i], ';');
+               p = strchr(new_attrs[i], ';');
                if (!p) {
                        continue;
                }
                if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
                        continue;
                }
-               if (sscanf(p, ";range=%u-%u", &start, &end) == 2) {
-               } else if (sscanf(p, ";range=%u-*", &start) == 1) {
-                       end = (unsigned int)-1;
-               } else {
-                       ldb_asprintf_errstring(module->ldb, "range request error: range requst malformed");
-                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               end = (unsigned int)-1;
+               if (sscanf(p, ";range=%u-*", &start) != 1) {
+                       if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
+                               ldb_asprintf_errstring(module->ldb,
+                                       "range request error: "
+                                       "range request malformed");
+                               return LDB_ERR_UNWILLING_TO_PERFORM;
+                       }
                }
                if (start > end) {
                        ldb_asprintf_errstring(module->ldb, "range request error: start must not be greater than end");
@@ -166,9 +206,8 @@ static int rr_search(struct ldb_module *module, struct ldb_request *req)
                }
 
                found_rr = true;
-               new_attrs[i] = talloc_strndup(new_attrs, 
-                                             req->op.search.attrs[i],
-                                             (unsigned int)(p-req->op.search.attrs[i]));
+               new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
+                                             (size_t)(p - new_attrs[i]));
 
                if (!new_attrs[i]) {
                        ldb_oom(module->ldb);
@@ -177,27 +216,27 @@ static int rr_search(struct ldb_module *module, struct ldb_request *req)
        }
 
        if (found_rr) {
-               int ret;
-               context = talloc(req, struct rr_context);
-               context->orig_req = req;
-               context->down_req = talloc(context, struct ldb_request);
-               *context->down_req = *req;
-               
-               context->down_req->op.search.attrs = new_attrs;
-               
-               context->down_req->callback = rr_search_callback;
-               context->down_req->context = context;
-
-               ret = ldb_next_request(module, context->down_req);
-               
-               /* We don't need to implement our own 'wait' function, so pass the handle along */
-               if (ret == LDB_SUCCESS) {
-                       req->handle = context->down_req->handle;
+               ac = rr_init_context(module, req);
+               if (!ac) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ret = ldb_build_search_req_ex(&down_req, module->ldb, ac,
+                                             req->op.search.base,
+                                             req->op.search.scope,
+                                             req->op.search.tree,
+                                             new_attrs,
+                                             req->controls,
+                                             ac, rr_search_callback,
+                                             req);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
-               return ret;
+               return ldb_next_request(module, down_req);
        }
 
        /* No change, just run the original request as if we were never here */
+       talloc_free(new_attrs);
        return ldb_next_request(module, req);
 }
 
index fbaf461a3fa11d0da109c52e1a687b88324c0374..13a979b6f808fe5a2bbd95abd7ff0efd538b3c6a 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2004-2006
+   Copyright (C) Simo Sorce  2004-2008
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
    Copyright (C) Andrew Tridgell 2005
    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
@@ -51,8 +51,7 @@
 
 struct replmd_replicated_request {
        struct ldb_module *module;
-       struct ldb_handle *handle;
-       struct ldb_request *orig_req;
+       struct ldb_request *req;
 
        const struct dsdb_schema *schema;
 
@@ -63,59 +62,23 @@ struct replmd_replicated_request {
 
        uint32_t index_current;
 
-       struct {
-               TALLOC_CTX *mem_ctx;
-               struct ldb_request *search_req;
-               struct ldb_message *search_msg;
-               int search_ret;
-               struct ldb_request *change_req;
-               int change_ret;
-       } sub;
+       struct ldb_message *search_msg;
 };
 
-static struct replmd_replicated_request *replmd_replicated_init_handle(struct ldb_module *module,
-                                                                      struct ldb_request *req,
-                                                                      struct dsdb_extended_replicated_objects *objs)
+static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
+                                         struct ldb_request *req)
 {
-       struct replmd_replicated_request *ar;
-       struct ldb_handle *h;
-       const struct dsdb_schema *schema;
-
-       schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
-                             "replmd_replicated_init_handle: no loaded schema found\n");
-               return NULL;
-       }
+       struct replmd_replicated_request *ac;
 
-       h = talloc_zero(req, struct ldb_handle);
-       if (h == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               return NULL;
-       }
-
-       h->module       = module;
-       h->state        = LDB_ASYNC_PENDING;
-       h->status       = LDB_SUCCESS;
-
-       ar = talloc_zero(h, struct replmd_replicated_request);
-       if (ar == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
+       ac = talloc_zero(req, struct replmd_replicated_request);
+       if (ac == NULL) {
+               ldb_oom(module->ldb);
                return NULL;
        }
 
-       h->private_data = ar;
-
-       ar->module      = module;
-       ar->handle      = h;
-       ar->orig_req    = req;
-       ar->schema      = schema;
-       ar->objs        = objs;
-
-       req->handle = h;
-
-       return ar;
+       ac->module = module;
+       ac->req = req;
+       return ac;
 }
 
 /*
@@ -239,34 +202,37 @@ static void replmd_ldb_message_sort(struct ldb_message *msg,
                  discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
 }
 
-static int replmd_prepare_originating(struct ldb_module *module, struct ldb_request *req,
-                                     struct ldb_dn *dn, const char *fn_name,
-                                     int (*fn)(struct ldb_module *,
-                                               struct ldb_request *,
-                                               const struct dsdb_schema *))
+static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       const struct dsdb_schema *schema;
-       /* do not manipulate our control entries */
-       if (ldb_dn_is_special(dn)) {
-               return ldb_next_request(module, req);
+       struct replmd_replicated_request *ac;
+
+       ac = talloc_get_type(req->context, struct replmd_replicated_request);
+
+       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);
        }
 
-       schema = dsdb_get_schema(module->ldb);
-       if (!schema) {
-               ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
-                             "%s: no dsdb_schema loaded",
-                             fn_name);
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb,
+                                 "invalid ldb_reply_type in callback");
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
 
-       return fn(module, req, schema);
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, LDB_SUCCESS);
 }
 
-static int replmd_add_originating(struct ldb_module *module,
-                                 struct ldb_request *req,
-                                 const struct dsdb_schema *schema)
+static int replmd_add(struct ldb_module *module, struct ldb_request *req)
 {
+       struct replmd_replicated_request *ac;
+       const struct dsdb_schema *schema;
        enum ndr_err_code ndr_err;
        struct ldb_request *down_req;
        struct ldb_message *msg;
@@ -283,11 +249,30 @@ static int replmd_add_originating(struct ldb_module *module,
        int ret;
        uint32_t i, ni=0;
 
-       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.add.message->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add\n");
+
+       schema = dsdb_get_schema(module->ldb);
+       if (!schema) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
+                             "replmd_modify: no dsdb_schema loaded");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       ac = replmd_ctx_init(module, req);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->schema = schema;
 
        if (ldb_msg_find_element(req->op.add.message, "objectGUID")) {
                ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
-                             "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
+                             "replmd_add: it's not allowed to add an object with objectGUID\n");
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
@@ -304,22 +289,13 @@ static int replmd_add_originating(struct ldb_module *module,
        our_invocation_id = samdb_ntds_invocation_id(module->ldb);
        if (!our_invocation_id) {
                ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
-                             "replmd_add_originating: unable to find invocationId\n");
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* create a copy of the request */
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
-               ldb_oom(module->ldb);
+                             "replmd_add: unable to find invocationId\n");
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       *down_req = *req;
 
        /* we have to copy the message as the caller might have it as a const */
-       down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+       msg = ldb_msg_copy_shallow(ac, req->op.add.message);
        if (msg == NULL) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -328,7 +304,6 @@ static int replmd_add_originating(struct ldb_module *module,
        unix_to_nt_time(&now, t);
        time_str = ldb_timestring(msg, t);
        if (!time_str) {
-               talloc_free(down_req);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -346,7 +321,6 @@ static int replmd_add_originating(struct ldb_module *module,
         */
        ret = ldb_msg_add_string(msg, "whenCreated", time_str);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -359,7 +333,6 @@ static int replmd_add_originating(struct ldb_module *module,
                                               struct replPropertyMetaData1,
                                               nmd.ctr.ctr1.count);
        if (!nmd.ctr.ctr1.array) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -374,9 +347,8 @@ static int replmd_add_originating(struct ldb_module *module,
                sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
                if (!sa) {
                        ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
-                                     "replmd_add_originating: attribute '%s' not defined in schema\n",
+                                     "replmd_add: attribute '%s' not defined in schema\n",
                                      e->name);
-                       talloc_free(down_req);
                        return LDB_ERR_NO_SUCH_ATTRIBUTE;
                }
 
@@ -422,7 +394,6 @@ static int replmd_add_originating(struct ldb_module *module,
                                       &nmd,
                                       (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -432,31 +403,26 @@ static int replmd_add_originating(struct ldb_module *module,
         */
        ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ret = ldb_msg_add_string(msg, "whenChanged", time_str);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
        if (ret != LDB_SUCCESS) {
-               talloc_free(down_req);
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -466,49 +432,54 @@ static int replmd_add_originating(struct ldb_module *module,
         */
        replmd_ldb_message_sort(msg, schema);
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-       /* 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;
+       ret = ldb_build_add_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, replmd_op_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       return ret;
-}
-
-static int replmd_add(struct ldb_module *module, struct ldb_request *req)
-{
-       return replmd_prepare_originating(module, req, req->op.add.message->dn,
-                                         "replmd_add", replmd_add_originating);
+       /* go on with the call chain */
+       return ldb_next_request(module, down_req);
 }
 
-static int replmd_modify_originating(struct ldb_module *module,
-                                    struct ldb_request *req,
-                                    const struct dsdb_schema *schema)
+static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
 {
+       struct replmd_replicated_request *ac;
+       const struct dsdb_schema *schema;
        struct ldb_request *down_req;
        struct ldb_message *msg;
        int ret;
        time_t t = time(NULL);
        uint64_t seq_num;
 
-       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.mod.message->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
+
+       schema = dsdb_get_schema(module->ldb);
+       if (!schema) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
+                             "replmd_modify: no dsdb_schema loaded");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
 
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
+       ac = replmd_ctx_init(module, req);
+       if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       *down_req = *req;
+       ac->schema = schema;
 
        /* we have to copy the message as the caller might have it as a const */
-       down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
+       msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
        if (msg == NULL) {
-               talloc_free(down_req);
+               talloc_free(ac);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -525,7 +496,7 @@ static int replmd_modify_originating(struct ldb_module *module,
         */
 
        if (add_time_element(msg, "whenChanged", t) != 0) {
-               talloc_free(down_req);
+               talloc_free(ac);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -533,7 +504,7 @@ static int replmd_modify_originating(struct ldb_module *module,
        ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
        if (ret == LDB_SUCCESS) {
                if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
-                       talloc_free(down_req);
+                       talloc_free(ac);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
        }
@@ -543,96 +514,71 @@ static int replmd_modify_originating(struct ldb_module *module,
         * - replace the old object with the newly constructed one
         */
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-       /* 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 replmd_modify(struct ldb_module *module, struct ldb_request *req)
-{
-       return replmd_prepare_originating(module, req, req->op.mod.message->dn,
-                                         "replmd_modify", replmd_modify_originating);
-}
-
-static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
-{
-       struct ldb_reply *ares = NULL;
-
-       ar->handle->status = ret;
-       ar->handle->state = LDB_ASYNC_DONE;
-
-       if (!ar->orig_req->callback) {
-               return LDB_SUCCESS;
-       }
-       
-       /* we're done and need to report the success to the caller */
-       ares = talloc_zero(ar, struct ldb_reply);
-       if (!ares) {
-               ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
-               ar->handle->state = LDB_ASYNC_DONE;
-               return LDB_ERR_OPERATIONS_ERROR;
+       ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                               msg,
+                               req->controls,
+                               ac, replmd_op_callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
+       talloc_steal(down_req, msg);
 
-       ares->type      = LDB_REPLY_EXTENDED;
-       ares->response  = NULL;
-
-       return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
-}
-
-static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
-{
-       return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
+       /* go on with the call chain */
+       return ldb_next_request(module, down_req);
 }
 
 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
 {
-       return replmd_replicated_request_reply_helper(ar, ret);
+       return ret;
 }
 
 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
 {
        int ret = LDB_ERR_OTHER;
        /* TODO: do some error mapping */
-       return replmd_replicated_request_reply_helper(ar, ret);
+       return ret;
 }
 
 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
 
-static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
-                                               void *private_data,
+static int replmd_replicated_apply_add_callback(struct ldb_request *req,
                                                struct ldb_reply *ares)
 {
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+       struct replmd_replicated_request *ar = talloc_get_type(req->context,
                                               struct replmd_replicated_request);
+       int ret;
 
-       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
+
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ar->module->ldb, "Invalid reply type\n!");
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
+       talloc_free(ares);
        ar->index_current++;
 
-       return replmd_replicated_apply_next(ar);
-#else
+       ret = replmd_replicated_apply_next(ar);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, NULL, NULL, ret);
+       }
+
        return LDB_SUCCESS;
-#endif
 }
 
 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
 {
+       struct ldb_request *change_req;
        enum ndr_err_code ndr_err;
        struct ldb_message *msg;
        struct replPropertyMetaDataBlob *md;
@@ -699,39 +645,17 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
 
        replmd_ldb_message_sort(msg, ar->schema);
 
-       ret = ldb_build_add_req(&ar->sub.change_req,
+       ret = ldb_build_add_req(&change_req,
                                ar->module->ldb,
-                               ar->sub.mem_ctx,
+                               ar,
                                msg,
                                ar->controls,
                                ar,
-                               replmd_replicated_apply_add_callback);
+                               replmd_replicated_apply_add_callback,
+                               ar->req);
        if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       return ldb_next_request(ar->module, ar->sub.change_req);
-#else
-       ret = ldb_next_request(ar->module, ar->sub.change_req);
-       if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ar->module->ldb, "Failed to add replicated object %s: %s", ldb_dn_get_linearized(ar->sub.change_req->op.add.message->dn), 
-                                      ldb_errstring(ar->module->ldb));
-               return replmd_replicated_request_error(ar, ret);
-       }
-
-       ar->sub.change_ret = ldb_wait(ar->sub.change_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ar->module->ldb, "Failed while waiting on add replicated object %s: %s", ldb_dn_get_linearized(ar->sub.change_req->op.add.message->dn), 
-                                      ldb_errstring(ar->module->ldb));
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
-       }
-
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
-
-       ar->index_current++;
-
-       return LDB_SUCCESS;
-#endif
+       return ldb_next_request(ar->module, change_req);
 }
 
 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
@@ -755,35 +679,42 @@ static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMeta
        return m1->originating_usn - m2->originating_usn;
 }
 
-static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
-                                                 void *private_data,
+static int replmd_replicated_apply_merge_callback(struct ldb_request *req,
                                                  struct ldb_reply *ares)
 {
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+       struct replmd_replicated_request *ar = talloc_get_type(req->context,
                                               struct replmd_replicated_request);
+       int ret;
 
-       ret = ldb_next_request(ar->module, ar->sub.change_req);
-       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
-
-       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ar->module->ldb, "Invalid reply type\n!");
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
+       talloc_free(ares);
        ar->index_current++;
 
+       ret = replmd_replicated_apply_next(ar);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, NULL, NULL, ret);
+       }
+
        return LDB_SUCCESS;
-#else
-       return LDB_SUCCESS;
-#endif
 }
 
 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
 {
+       struct ldb_request *change_req;
        enum ndr_err_code ndr_err;
        struct ldb_message *msg;
        struct replPropertyMetaDataBlob *rmd;
@@ -804,11 +735,11 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        /*
         * TODO: add rename conflict handling
         */
-       if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
+       if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
                ldb_debug_set(ar->module->ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
                              ar->index_current);
                ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
-                         ldb_dn_get_linearized(ar->sub.search_msg->dn),
+                         ldb_dn_get_linearized(ar->search_msg->dn),
                          ldb_dn_get_linearized(msg->dn));
                return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
        }
@@ -819,9 +750,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        }
 
        /* find existing meta data */
-       omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replPropertyMetaData");
+       omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
        if (omd_value) {
-               ndr_err = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, 
+               ndr_err = ndr_pull_struct_blob(omd_value, ar,
                                               lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), &omd,
                                               (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -837,7 +768,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        ZERO_STRUCT(nmd);
        nmd.version = 1;
        nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
-       nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
+       nmd.ctr.ctr1.array = talloc_array(ar,
                                          struct replPropertyMetaData1,
                                          nmd.ctr.ctr1.count);
        if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
@@ -920,14 +851,16 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        if (msg->num_elements == 0) {
                ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
                          ar->index_current);
-               goto next_object;
+
+               ar->index_current++;
+               return replmd_replicated_apply_next(ar);
        }
 
        ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
                  ar->index_current, msg->num_elements);
 
        /*
-        * when we now that we'll modify the record, add the whenChanged, uSNChanged
+        * when we know that we'll modify the record, add the whenChanged, uSNChanged
         * and replPopertyMetaData attributes
         */
        ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
@@ -950,150 +883,123 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
        }
 
-       ret = ldb_build_mod_req(&ar->sub.change_req,
+       ret = ldb_build_mod_req(&change_req,
                                ar->module->ldb,
-                               ar->sub.mem_ctx,
+                               ar,
                                msg,
                                ar->controls,
                                ar,
-                               replmd_replicated_apply_merge_callback);
+                               replmd_replicated_apply_merge_callback,
+                               ar->req);
        if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       return ldb_next_request(ar->module, ar->sub.change_req);
-#else
-       ret = ldb_next_request(ar->module, ar->sub.change_req);
-       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
-
-       ar->sub.change_ret = ldb_wait(ar->sub.change_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
-       }
-
-next_object:
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
-
-       ar->index_current++;
-
-       return LDB_SUCCESS;
-#endif
+       return ldb_next_request(ar->module, change_req);
 }
 
-static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
-                                                  void *private_data,
+static int replmd_replicated_apply_search_callback(struct ldb_request *req,
                                                   struct ldb_reply *ares)
 {
-       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+       struct replmd_replicated_request *ar = talloc_get_type(req->context,
                                               struct replmd_replicated_request);
-       bool is_done = false;
+       int ret;
+
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS &&
+           ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
+       }
 
        switch (ares->type) {
        case LDB_REPLY_ENTRY:
-               ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
+               ar->search_msg = talloc_steal(ar, ares->message);
                break;
+
        case LDB_REPLY_REFERRAL:
                /* we ignore referrals */
                break;
-       case LDB_REPLY_EXTENDED:
-       case LDB_REPLY_DONE:
-               is_done = true;
-       }
 
-       talloc_free(ares);
-
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       if (is_done) {
-               ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-               if (ar->sub.search_ret != LDB_SUCCESS) {
-                       return replmd_replicated_request_error(ar, ar->sub.search_ret);
+       case LDB_REPLY_DONE:
+               if (ar->search_msg != NULL) {
+                       ret = replmd_replicated_apply_merge(ar);
+               } else {
+                       ret = replmd_replicated_apply_add(ar);
                }
-               if (ar->sub.search_msg) {
-                       return replmd_replicated_apply_merge(ar);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ar->req, NULL, NULL, ret);
                }
-               return replmd_replicated_apply_add(ar);
        }
-#endif
+
+       talloc_free(ares);
        return LDB_SUCCESS;
 }
 
-static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
+static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
+
+static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
 {
        int ret;
        char *tmp_str;
        char *filter;
+       struct ldb_request *search_req;
 
-       tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
+       if (ar->index_current >= ar->objs->num_objects) {
+               /* done with it, go to the last op */
+               return replmd_replicated_uptodate_vector(ar);
+       }
+
+       ar->search_msg = NULL;
+
+       tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
        if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
 
-       filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
+       filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
        if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
        talloc_free(tmp_str);
 
-       ret = ldb_build_search_req(&ar->sub.search_req,
+       ret = ldb_build_search_req(&search_req,
                                   ar->module->ldb,
-                                  ar->sub.mem_ctx,
+                                  ar,
                                   ar->objs->partition_dn,
                                   LDB_SCOPE_SUBTREE,
                                   filter,
                                   NULL,
                                   NULL,
                                   ar,
-                                  replmd_replicated_apply_search_callback);
-       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
-
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       return ldb_next_request(ar->module, ar->sub.search_req);
-#else
-       ret = ldb_next_request(ar->module, ar->sub.search_req);
+                                  replmd_replicated_apply_search_callback,
+                                  ar->req);
        if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
-       ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.search_ret != LDB_SUCCESS && ar->sub.search_ret != LDB_ERR_NO_SUCH_OBJECT) {
-               return replmd_replicated_request_error(ar, ar->sub.search_ret);
-       }
-       if (ar->sub.search_msg) {
-               return replmd_replicated_apply_merge(ar);
-       }
-
-       return replmd_replicated_apply_add(ar);
-#endif
+       return ldb_next_request(ar->module, search_req);
 }
 
-static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
-{
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       if (ar->index_current >= ar->objs->num_objects) {
-               return replmd_replicated_uptodate_vector(ar);
-       }
-#endif
-
-       ar->sub.mem_ctx = talloc_new(ar);
-       if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-
-       return replmd_replicated_apply_search(ar);
-}
-
-static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
-                                                     void *private_data,
+static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
                                                      struct ldb_reply *ares)
 {
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+       struct replmd_replicated_request *ar = talloc_get_type(req->context,
                                               struct replmd_replicated_request);
 
-       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ar->module->ldb, "Invalid reply type\n!");
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       return replmd_replicated_request_done(ar);
-#else
-       return LDB_SUCCESS;
-#endif
+       talloc_free(ares);
+
+       return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
 }
 
 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
@@ -1104,6 +1010,7 @@ static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplic
 
 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
 {
+       struct ldb_request *change_req;
        enum ndr_err_code ndr_err;
        struct ldb_message *msg;
        struct replUpToDateVectorBlob ouv;
@@ -1145,9 +1052,9 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        /*
         * first create the new replUpToDateVector
         */
-       ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
+       ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
        if (ouv_value) {
-               ndr_err = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx, 
+               ndr_err = ndr_pull_struct_blob(ouv_value, ar,
                                               lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), &ouv,
                                               (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1168,7 +1075,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
         */
        nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
        if (ruv) nuv.ctr.ctr2.count += ruv->count;
-       nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
+       nuv.ctr.ctr2.cursors = talloc_array(ar,
                                            struct drsuapi_DsReplicaCursor2,
                                            nuv.ctr.ctr2.count);
        if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
@@ -1269,9 +1176,9 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        /*
         * create the change ldb_message
         */
-       msg = ldb_msg_new(ar->sub.mem_ctx);
+       msg = ldb_msg_new(ar);
        if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-       msg->dn = ar->sub.search_msg->dn;
+       msg->dn = ar->search_msg->dn;
 
        ndr_err = ndr_push_struct_blob(&nuv_value, msg, 
                                       lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), 
@@ -1304,12 +1211,12 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
         * first see if we already have a repsFrom value for the current source dsa
         * if so we'll later replace this value
         */
-       orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
+       orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
        if (orf_el) {
                for (i=0; i < orf_el->num_values; i++) {
                        struct repsFromToBlob *trf;
 
-                       trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
+                       trf = talloc(ar, struct repsFromToBlob);
                        if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
 
                        ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), trf,
@@ -1378,72 +1285,62 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        nrf_el->flags = LDB_FLAG_MOD_REPLACE;
 
        /* prepare the ldb_modify() request */
-       ret = ldb_build_mod_req(&ar->sub.change_req,
+       ret = ldb_build_mod_req(&change_req,
                                ar->module->ldb,
-                               ar->sub.mem_ctx,
+                               ar,
                                msg,
                                ar->controls,
                                ar,
-                               replmd_replicated_uptodate_modify_callback);
+                               replmd_replicated_uptodate_modify_callback,
+                               ar->req);
        if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       return ldb_next_request(ar->module, ar->sub.change_req);
-#else
-       ret = ldb_next_request(ar->module, ar->sub.change_req);
-       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
-
-       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.change_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.change_ret);
-       }
-
-       talloc_free(ar->sub.mem_ctx);
-       ZERO_STRUCT(ar->sub);
-
-       return replmd_replicated_request_done(ar);
-#endif
+       return ldb_next_request(ar->module, change_req);
 }
 
-static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
-                                                     void *private_data,
+static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
                                                      struct ldb_reply *ares)
 {
-       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+       struct replmd_replicated_request *ar = talloc_get_type(req->context,
                                               struct replmd_replicated_request);
-       bool is_done = false;
+       int ret;
+
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS &&
+           ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
+       }
 
        switch (ares->type) {
        case LDB_REPLY_ENTRY:
-               ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
+               ar->search_msg = talloc_steal(ar, ares->message);
                break;
+
        case LDB_REPLY_REFERRAL:
                /* we ignore referrals */
                break;
-       case LDB_REPLY_EXTENDED:
-       case LDB_REPLY_DONE:
-               is_done = true;
-       }
-
-       talloc_free(ares);
 
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       if (is_done) {
-               ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-               if (ar->sub.search_ret != LDB_SUCCESS) {
-                       return replmd_replicated_request_error(ar, ar->sub.search_ret);
+       case LDB_REPLY_DONE:
+               if (ar->search_msg == NULL) {
+                       ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+               } else {
+                       ret = replmd_replicated_uptodate_modify(ar);
                }
-               if (!ar->sub.search_msg) {
-                       return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ar->req, NULL, NULL, ret);
                }
-
-               return replmd_replicated_uptodate_modify(ar);
        }
-#endif
+
+       talloc_free(ares);
        return LDB_SUCCESS;
 }
 
-static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
+
+static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
 {
        int ret;
        static const char *attrs[] = {
@@ -1451,43 +1348,24 @@ static int replmd_replicated_uptodate_search(struct replmd_replicated_request *a
                "repsFrom",
                NULL
        };
+       struct ldb_request *search_req;
+
+       ar->search_msg = NULL;
 
-       ret = ldb_build_search_req(&ar->sub.search_req,
+       ret = ldb_build_search_req(&search_req,
                                   ar->module->ldb,
-                                  ar->sub.mem_ctx,
+                                  ar,
                                   ar->objs->partition_dn,
                                   LDB_SCOPE_BASE,
                                   "(objectClass=*)",
                                   attrs,
                                   NULL,
                                   ar,
-                                  replmd_replicated_uptodate_search_callback);
-       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
-
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
-       return ldb_next_request(ar->module, ar->sub.search_req);
-#else
-       ret = ldb_next_request(ar->module, ar->sub.search_req);
+                                  replmd_replicated_uptodate_search_callback,
+                                  ar->req);
        if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
-       ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
-       if (ar->sub.search_ret != LDB_SUCCESS) {
-               return replmd_replicated_request_error(ar, ar->sub.search_ret);
-       }
-       if (!ar->sub.search_msg) {
-               return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
-       }
-
-       return replmd_replicated_uptodate_modify(ar);
-#endif
-}
-
-static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
-{
-       ar->sub.mem_ctx = talloc_new(ar);
-       if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-
-       return replmd_replicated_uptodate_search(ar);
+       return ldb_next_request(ar->module, search_req);
 }
 
 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
@@ -1511,9 +1389,16 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
                return LDB_ERR_PROTOCOL_ERROR;
        }
 
-       ar = replmd_replicated_init_handle(module, req, objs);
-       if (!ar) {
+       ar = replmd_ctx_init(module, req);
+       if (!ar)
                return LDB_ERR_OPERATIONS_ERROR;
+
+       ar->objs = objs;
+       ar->schema = dsdb_get_schema(module->ldb);
+       if (!ar->schema) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
+               talloc_free(ar);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
        ctrls = req->controls;
@@ -1532,20 +1417,7 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
        ar->controls = req->controls;
        req->controls = ctrls;
 
-#ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */ 
        return replmd_replicated_apply_next(ar);
-#else
-       while (ar->index_current < ar->objs->num_objects &&
-              req->handle->state != LDB_ASYNC_DONE) { 
-               replmd_replicated_apply_next(ar);
-       }
-
-       if (req->handle->state != LDB_ASYNC_DONE) {
-               replmd_replicated_uptodate_vector(ar);
-       }
-
-       return LDB_SUCCESS;
-#endif
 }
 
 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
@@ -1557,53 +1429,9 @@ static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
        return ldb_next_request(module, req);
 }
 
-static int replmd_wait_none(struct ldb_handle *handle) {
-       struct replmd_replicated_request *ar;
-    
-       if (!handle || !handle->private_data) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
-       if (!ar) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* we do only sync calls */
-       if (handle->state != LDB_ASYNC_DONE) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       return handle->status;
-}
-
-static int replmd_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = replmd_wait_none(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-       }
-
-       return handle->status;
-}
-
-static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return replmd_wait_all(handle);
-       } else {
-               return replmd_wait_none(handle);
-       }
-}
-
 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
        .name          = "repl_meta_data",
        .add           = replmd_add,
        .modify        = replmd_modify,
        .extended      = replmd_extended,
-       .wait          = replmd_wait
 };
index ebc90d4cf3c009761ae097057640dffa99fb7aec..0d14a54464c686e481593c0c1b8b79c14b2575bb 100644 (file)
@@ -4,7 +4,7 @@
    rootDSE ldb module
 
    Copyright (C) Andrew Tridgell 2005
-   Copyright (C) Simo Sorce 2005
+   Copyright (C) Simo Sorce 2005-2008
    
    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
@@ -221,40 +221,77 @@ failed:
 
 struct rootdse_context {
        struct ldb_module *module;
-       void *up_context;
-       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
-
-       const char * const * attrs;
+       struct ldb_request *req;
 };
 
-static int rootdse_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static struct rootdse_context *rootdse_init_context(struct ldb_module *module,
+                                                   struct ldb_request *req)
+{
+       struct rootdse_context *ac;
+
+       ac = talloc_zero(req, struct rootdse_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       ac->module = module;
+       ac->req = req;
+
+       return ac;
+}
+
+static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct rootdse_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct rootdse_context);
 
-       ac = talloc_get_type(context, struct rootdse_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);
+       }
 
-       if (ares->type == LDB_REPLY_ENTRY) {
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
                /*
                 * if the client explicit asks for the 'netlogon' attribute
                 * the reply_entry needs to be skipped
                 */
-               if (ac->attrs && ldb_attr_in_list(ac->attrs, "netlogon")) {
+               if (ac->req->op.search.attrs &&
+                   ldb_attr_in_list(ac->req->op.search.attrs, "netlogon")) {
                        talloc_free(ares);
                        return LDB_SUCCESS;
                }
 
                /* for each record returned post-process to add any dynamic
                   attributes that have been asked for */
-               if (rootdse_add_dynamic(ac->module, ares->message, ac->attrs) != LDB_SUCCESS) {
-                       goto error;
+               ret = rootdse_add_dynamic(ac->module, ares->message,
+                                         ac->req->op.search.attrs);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ares);
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
                }
-       }
 
-       return ac->up_callback(ldb, ac->up_context, ares);
+               return ldb_module_send_entry(ac->req, ares->message);
+
+       case LDB_REPLY_REFERRAL:
+               /* should we allow the backend to return referrals in this case
+                * ?? */
+               break;
+
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
 
-error:
        talloc_free(ares);
-       return LDB_ERR_OPERATIONS_ERROR;
+       return LDB_SUCCESS;
 }
 
 static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
@@ -270,48 +307,25 @@ static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       ac = talloc(req, struct rootdse_context);
+       ac = rootdse_init_context(module, req);
        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;
        /* in our db we store the rootDSE with a DN of @ROOTDSE */
-       down_req->op.search.base = ldb_dn_new(down_req, module->ldb, "@ROOTDSE");
-       down_req->op.search.scope = LDB_SCOPE_BASE;
-       down_req->op.search.tree = ldb_parse_tree(down_req, NULL);
-       if (down_req->op.search.base == NULL || down_req->op.search.tree == NULL) {
-               ldb_oom(module->ldb);
-               talloc_free(down_req);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       down_req->op.search.attrs = req->op.search.attrs;
-       down_req->controls = req->controls;
-
-       down_req->context = ac;
-       down_req->callback = rootdse_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;
+       ret = ldb_build_search_req(&down_req, module->ldb, ac,
+                                       ldb_dn_new(ac, module->ldb, "@ROOTDSE"),
+                                       LDB_SCOPE_BASE,
+                                       NULL,
+                                       req->op.search.attrs,
+                                       req->controls,
+                                       ac, rootdse_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       return ret;
+       return ldb_next_request(module, down_req);
 }
 
 static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
@@ -332,9 +346,9 @@ static int rootdse_register_control(struct ldb_module *module, struct ldb_reques
        priv->num_controls += 1;
        priv->controls = list;
 
-       return LDB_SUCCESS;
+       return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
 }
+
 static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
 {
        struct private_data *priv = talloc_get_type(module->private_data, struct private_data);
@@ -353,9 +367,9 @@ static int rootdse_register_partition(struct ldb_module *module, struct ldb_requ
        priv->num_partitions += 1;
        priv->partitions = list;
 
-       return LDB_SUCCESS;
+       return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
 }
+
 
 static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
 {
index cf720669b94f99616e41049f9eb92c2e0eb303ba..e4fc4908a4a90321ab5d8b015a731e2b507730ce 100644 (file)
@@ -1,8 +1,8 @@
-/* 
+/*
    SAM ldb module
 
-   Copyright (C) Simo Sorce  2004
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+   Copyright (C) Simo Sorce  2004-2008
 
    * NOTICE: this module is NOT released under the GNU LGPL license as
    * other ldb code. This module is release under the GNU GPL v3 or
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
 */
 #include "lib/ldb/include/ldb_errors.h"
 #include "lib/ldb/include/ldb.h"
 #include "lib/ldb/include/ldb_private.h"
+#include "lib/events/events.h"
 #include "dsdb/samdb/samdb.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
 #include "util/util_ldb.h"
+#include "ldb_wrap.h"
+
+struct samldb_ctx;
+
+typedef int (*samldb_step_fn_t)(struct samldb_ctx *);
+
+struct samldb_step {
+       struct samldb_step *next;
+       samldb_step_fn_t fn;
+};
+
+struct samldb_ctx {
+       struct ldb_module *module;
+       struct ldb_request *req;
 
-int samldb_notice_sid(struct ldb_module *module, 
-                     TALLOC_CTX *mem_ctx, const struct dom_sid *sid);
+       /* the resulting message */
+       struct ldb_message *msg;
+
+       /* used to apply templates */
+       const char *type;
+
+       /* used to find parent domain */
+       struct ldb_dn *check_dn;
+       struct ldb_dn *domain_dn;
+       struct dom_sid *domain_sid;
+       uint32_t next_rid;
+
+       /* generic storage, remember to zero it before use */
+       struct ldb_reply *ares;
+
+       /* holds the entry SID */
+       struct dom_sid *sid;
+
+       /* all the async steps necessary to complete the operation */
+       struct samldb_step *steps;
+       struct samldb_step *curstep;
+};
 
-static bool samldb_msg_add_sid(struct ldb_module *module, struct ldb_message *msg, const char *name, const struct dom_sid *sid)
+struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
+                                  struct ldb_request *req)
 {
-       struct ldb_val v;
-       enum ndr_err_code ndr_err;
+       struct samldb_ctx *ac;
 
-       ndr_err = ndr_push_struct_blob(&v, msg, NULL, sid,
-                                      (ndr_push_flags_fn_t)ndr_push_dom_sid);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               return false;
+       ac = talloc_zero(req, struct samldb_ctx);
+       if (ac == NULL) {
+               ldb_oom(module->ldb);
+               return NULL;
        }
-       return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
+
+       ac->module = module;
+       ac->req = req;
+
+       return ac;
 }
 
-/*
-  allocate a new id, attempting to do it atomically
-  return 0 on failure, the id on success
-*/
-static int samldb_set_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
-                              struct ldb_dn *dn, uint32_t old_id, uint32_t new_id)
+static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
 {
-       struct ldb_message msg;
-       int ret;
-       struct ldb_val vals[2];
-       struct ldb_message_element els[2];
+       struct samldb_step *step;
 
-       if (new_id == 0) {
-               /* out of IDs ! */
-               ldb_set_errstring(ldb, "Are we out of valid IDs ?\n");
+       step = talloc_zero(ac, struct samldb_step);
+       if (step == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* we do a delete and add as a single operation. That prevents
-          a race, in case we are not actually on a transaction db */
-       ZERO_STRUCT(msg);
-       msg.dn = ldb_dn_copy(mem_ctx, dn);
-       if (!msg.dn) {
-               ldb_oom(ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ac->steps == NULL) {
+               ac->steps = step;
+               ac->curstep = step;
+       } else {
+               ac->curstep->next = step;
+               ac->curstep = step;
        }
-       msg.num_elements = 2;
-       msg.elements = els;
 
-       els[0].num_values = 1;
-       els[0].values = &vals[0];
-       els[0].flags = LDB_FLAG_MOD_DELETE;
-       els[0].name = talloc_strdup(mem_ctx, "nextRid");
-       if (!els[0].name) {
-               ldb_oom(ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       step->fn = fn;
 
-       els[1].num_values = 1;
-       els[1].values = &vals[1];
-       els[1].flags = LDB_FLAG_MOD_ADD;
-       els[1].name = els[0].name;
+       return LDB_SUCCESS;
+}
 
-       vals[0].data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", old_id);
-       if (!vals[0].data) {
-               ldb_oom(ldb);
+static int samldb_first_step(struct samldb_ctx *ac)
+{
+       if (ac->steps == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       vals[0].length = strlen((char *)vals[0].data);
 
-       vals[1].data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", new_id);
-       if (!vals[1].data) {
-               ldb_oom(ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       ac->curstep = ac->steps;
+       return ac->curstep->fn(ac);
+}
+
+static int samldb_next_step(struct samldb_ctx *ac)
+{
+       if (ac->curstep->next) {
+               ac->curstep = ac->curstep->next;
+               return ac->curstep->fn(ac);
        }
-       vals[1].length = strlen((char *)vals[1].data);
 
-       ret = ldb_modify(ldb, &msg);
-       return ret;
+       /* it is an error if the last step does not properly
+        * return to the upper module by itself */
+       return LDB_ERR_OPERATIONS_ERROR;
 }
 
-/*
-  allocate a new id, attempting to do it atomically
-  return 0 on failure, the id on success
-*/
-static int samldb_find_next_rid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
-                               struct ldb_dn *dn, uint32_t *old_rid)
+static int samldb_search_template_callback(struct ldb_request *req,
+                                          struct ldb_reply *ares)
 {
-       const char * const attrs[2] = { "nextRid", NULL };
-       struct ldb_result *res = NULL;
+       struct samldb_ctx *ac;
        int ret;
-       const char *str;
 
-       ret = ldb_search(module->ldb, mem_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "nextRid=*");
-       if (ret != LDB_SUCCESS) {
-               return ret;
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
        }
-       if (res->count != 1) {
-               talloc_free(res);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ares->error != LDB_SUCCESS) {
+#ifdef REAL_EVENT_SYSTEM_HOOKED_UP
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+#else
+               return ldb_request_done(req, ares->error);
+#endif
        }
 
-       str = ldb_msg_find_attr_as_string(res->msgs[0], "nextRid", NULL);
-       if (str == NULL) {
-               ldb_asprintf_errstring(module->ldb,
-                                       "attribute nextRid not found in %s\n",
-                                       ldb_dn_get_linearized(dn));
-               talloc_free(res);
-               return LDB_ERR_OPERATIONS_ERROR;
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* save entry */
+               if (ac->ares != NULL) {
+                       /* one too many! */
+                       ldb_set_errstring(ac->module->ldb,
+                               "Invalid number of results while searching "
+                               "for template objects");
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       goto done;
+               }
+
+               ac->ares = talloc_steal(ac, ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_DONE:
+
+               talloc_free(ares);
+#ifdef REAL_EVENT_SYSTEM_HOOKED_UP
+               ret = samldb_next_step(ac);
+#else
+               return ldb_request_done(req, LDB_SUCCESS);
+#endif
+               break;
+       }
+
+done:
+       if (ret != LDB_SUCCESS) {
+#ifdef REAL_EVENT_SYSTEM_HOOKED_UP
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+#else
+               return ldb_request_done(req, ret);
+#endif
        }
 
-       *old_rid = strtol(str, NULL, 0);
-       talloc_free(res);
        return LDB_SUCCESS;
 }
 
-static int samldb_allocate_next_rid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
-                                   struct ldb_dn *dn, const struct dom_sid *dom_sid, 
-                                   struct dom_sid **new_sid)
+static int samldb_search_template(struct samldb_ctx *ac)
 {
-       struct dom_sid *obj_sid;
-       uint32_t old_rid;
+       struct event_context *ev;
+       struct loadparm_context *lparm_ctx;
+       struct ldb_context *templates_ldb;
+       char *templates_ldb_path;
+       struct ldb_request *req;
+       struct ldb_dn *basedn;
+       void *opaque;
        int ret;
-       
-       ret = samldb_find_next_rid(module, mem_ctx, dn, &old_rid);      
-       if (ret) {
-               return ret;
+
+       opaque = ldb_get_opaque(ac->module->ldb, "loadparm");
+       lparm_ctx = talloc_get_type(opaque, struct loadparm_context);
+       if (lparm_ctx == NULL) {
+               ldb_set_errstring(ac->module->ldb,
+                       "Unable to find loadparm context\n");
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-               
-       /* return the new object sid */
-       obj_sid = dom_sid_add_rid(mem_ctx, dom_sid, old_rid);
-               
-       *new_sid = dom_sid_add_rid(mem_ctx, dom_sid, old_rid + 1);
-       if (!*new_sid) {
+
+       opaque = ldb_get_opaque(ac->module->ldb, "templates_ldb");
+       templates_ldb = talloc_get_type(opaque, struct ldb_context);
+
+       /* make sure we have the templates ldb */
+       if (!templates_ldb) {
+               templates_ldb_path = samdb_relative_path(ac->module->ldb, ac,
+                                                        "templates.ldb");
+               if (!templates_ldb_path) {
+                       ldb_set_errstring(ac->module->ldb,
+                                       "samldb_init_template: ERROR: Failed "
+                                       "to contruct path for template db");
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               /* NOTE: this is a request on a different database!
+                *
+                *       Therefore we need to do a bloody sync call
+                *       otherwise the fake event queue will never call it
+                *       as it runs on the main ldb context and knows
+                *       nothing about the templates_ldb one */
+#ifdef REAL_EVENT_SYSTEM_HOOKED_UP
+               ev = ldb_get_event_context(ac->module->ldb);
+#else
+               ev = event_context_init(NULL);
+#endif
+               if (!talloc_reference(templates_ldb, ev)) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               templates_ldb = ldb_wrap_connect(ac->module->ldb, ev,
+                                               lparm_ctx, templates_ldb_path,
+                                               NULL, NULL, 0, NULL);
+               talloc_free(templates_ldb_path);
+
+               if (!templates_ldb) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ret = ldb_set_opaque(ac->module->ldb,
+                                       "templates_ldb", templates_ldb);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       /* search template */
+       basedn = ldb_dn_new_fmt(ac, templates_ldb,
+                           "cn=Template%s,cn=Templates", ac->type);
+       if (basedn == NULL) {
+               ldb_set_errstring(ac->module->ldb,
+                       "samldb_init_template: ERROR: Failed "
+                       "to contruct DN for template");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = samldb_notice_sid(module, mem_ctx, *new_sid);
-       if (ret != 0) {
-               /* gah, there are conflicting sids.
-                * This is a critical situation it means that someone messed up with
-                * the DB and nextRid is not returning free RIDs, report an error
-                * and refuse to create any user until the problem is fixed */
-               ldb_asprintf_errstring(module->ldb,
-                                       "Critical Error: unconsistent DB, unable to retireve an unique RID to generate a new SID: %s",
-                                       ldb_errstring(module->ldb));
+       /* pull the template record */
+       ret = ldb_build_search_req(&req, templates_ldb, ac,
+                                  basedn, LDB_SCOPE_BASE,
+                                 "(distinguishedName=*)", NULL,
+                                 NULL,
+                                 ac, samldb_search_template_callback,
+                                 ac->req);
+       if (ret != LDB_SUCCESS) {
                return ret;
        }
-       return ret;
+
+       talloc_steal(req, basedn);
+       ac->ares = NULL;
+
+       /* NOTE: this is a request on a different database!
+        *       Therefore we need to do a bloody sync call
+        *       otherwise the fake event queue will never call it
+        *       as it runs on the main ldb context and knows
+        *       nothing about the templates_ldb one */
+#ifdef REAL_EVENT_SYSTEM_HOOKED_UP
+       return ldb_request(templates_ldb, req);
+#else
+       ret = ldb_request(templates_ldb, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return samldb_next_step(ac);
+#endif
 }
 
-/* search the domain related to the provided dn
-   allocate a new RID for the domain
-   return the new sid string
-*/
-static int samldb_get_new_sid(struct ldb_module *module, 
-                             TALLOC_CTX *mem_ctx, struct ldb_dn *obj_dn,
-                             struct ldb_dn *dom_dn, 
-                             struct dom_sid **sid)
+static int samldb_apply_template(struct samldb_ctx *ac)
+{
+       struct ldb_message_element *el;
+       struct ldb_message *msg;
+       int i, j;
+       int ret;
+
+       msg = ac->ares->message;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               el = &msg->elements[i];
+               /* some elements should not be copied */
+               if (ldb_attr_cmp(el->name, "cn") == 0 ||
+                   ldb_attr_cmp(el->name, "name") == 0 ||
+                   ldb_attr_cmp(el->name, "objectClass") == 0 ||
+                   ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+                   ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+                   ldb_attr_cmp(el->name, "distinguishedName") == 0 ||
+                   ldb_attr_cmp(el->name, "objectGUID") == 0) {
+                       continue;
+               }
+               for (j = 0; j < el->num_values; j++) {
+                       ret = samdb_find_or_add_attribute(
+                                       ac->module->ldb, ac->msg, el->name,
+                                       (char *)el->values[j].data);
+                       if (ret != LDB_SUCCESS) {
+                               ldb_set_errstring(ac->module->ldb,
+                                         "Failed adding template attribute\n");
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+               }
+       }
+
+       return samldb_next_step(ac);
+}
+
+static int samldb_get_parent_domain(struct samldb_ctx *ac);
+
+static int samldb_get_parent_domain_callback(struct ldb_request *req,
+                                            struct ldb_reply *ares)
 {
-       const char * const attrs[2] = { "objectSid", NULL };
-       struct ldb_result *res = NULL;
+       struct samldb_ctx *ac;
+       const char *nextRid;
        int ret;
-       struct dom_sid *dom_sid;
 
-       /* get the domain component part of the provided dn */
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* save entry */
+               if (ac->domain_dn != NULL) {
+                       /* one too many! */
+                       ldb_set_errstring(ac->module->ldb,
+                               "Invalid number of results while searching "
+                               "for domain object");
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
+               }
+
+               nextRid = ldb_msg_find_attr_as_string(ares->message,
+                                                       "nextRid", NULL);
+               if (nextRid == NULL) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "attribute nextRid not found in %s\n",
+                               ldb_dn_get_linearized(ares->message->dn));
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;;
+               }
+
+               ac->next_rid = strtol(nextRid, NULL, 0);
+
+               ac->domain_sid = samdb_result_dom_sid(ac, ares->message,
+                                                               "objectSid");
+               if (ac->domain_sid == NULL) {
+                       ldb_set_errstring(ac->module->ldb,
+                               "error retrieving parent domain domain sid!\n");
+                       ret = LDB_ERR_CONSTRAINT_VIOLATION;
+                       break;
+               }
+               ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+               talloc_free(ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_DONE:
 
-       /* find the domain sid */
+               talloc_free(ares);
+               if (ac->domain_dn == NULL) {
+                       /* search again */
+                       ret = samldb_get_parent_domain(ac);
+               } else {
+                       /* found, go on */
+                       ret = samldb_next_step(ac);
+               }
+               break;
+       }
 
-       ret = ldb_search(module->ldb, mem_ctx, &res, dom_dn, LDB_SCOPE_BASE, attrs, "objectSid=*");
+done:
        if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(module->ldb,
-                                       "samldb_get_new_sid: error retrieving domain sid from %s: %s!\n",
-                                       ldb_dn_get_linearized(dom_dn),
-                                       ldb_errstring(module->ldb));
-               talloc_free(res);
-               return ret;
+               return ldb_module_done(ac->req, NULL, NULL, ret);
        }
 
-       if (res->count != 1) {
-               ldb_asprintf_errstring(module->ldb,
-                                       "samldb_get_new_sid: error retrieving domain sid from %s: not found!\n",
-                                       ldb_dn_get_linearized(dom_dn));
-               talloc_free(res);
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+       return LDB_SUCCESS;
+}
+
+/* Find a domain object in the parents of a particular DN.  */
+static int samldb_get_parent_domain(struct samldb_ctx *ac)
+{
+       static const char * const attrs[3] = { "objectSid", "nextRid", NULL };
+       struct ldb_request *req;
+       struct ldb_dn *dn;
+       int ret;
+
+       if (ac->check_dn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       dom_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
-       if (dom_sid == NULL) {
-               ldb_set_errstring(module->ldb, "samldb_get_new_sid: error parsing domain sid!\n");
-               talloc_free(res);
+       dn = ldb_dn_get_parent(ac, ac->check_dn);
+       if (dn == NULL) {
+               ldb_set_errstring(ac->module->ldb,
+                       "Unable to find parent domain object");
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       /* allocate a new Rid for the domain */
-       ret = samldb_allocate_next_rid(module, mem_ctx, dom_dn, dom_sid, sid);
-       if (ret != 0) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s: %s\n", ldb_dn_get_linearized(dom_dn), ldb_errstring(module->ldb));
-               talloc_free(res);
+       ac->check_dn = dn;
+
+       ret = ldb_build_search_req(&req, ac->module->ldb, ac,
+                                  dn, LDB_SCOPE_BASE,
+                                  "(|(objectClass=domain)"
+                                    "(objectClass=builtinDomain)"
+                                    "(objectClass=samba4LocalDomain))",
+                                  attrs,
+                                  NULL,
+                                  ac, samldb_get_parent_domain_callback,
+                                  ac->req);
+
+       if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       talloc_free(res);
+       return ldb_next_request(ac->module, req);
+}
 
-       return ret;
+static int samldb_generate_samAccountName(struct ldb_message *msg)
+{
+       char *name;
+
+       /* Format: $000000-000000000000 */
+
+       name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
+                               (unsigned int)generate_random(),
+                               (unsigned int)generate_random(),
+                               (unsigned int)generate_random());
+       if (name == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       return ldb_msg_add_steal_string(msg, "samAccountName", name);
 }
 
-/* If we are adding new users/groups, we need to update the nextRid
- * attribute to be 'above' all incoming users RIDs.  This tries to
- * avoid clashes in future */
+static int samldb_check_samAccountName_callback(struct ldb_request *req,
+                                               struct ldb_reply *ares)
+{
+       struct samldb_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               /* if we get an entry it means this samAccountName
+                * already exists */
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_ENTRY_ALREADY_EXISTS);
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_DONE:
+
+               /* not found, go on */
+               talloc_free(ares);
+               ret = samldb_next_step(ac);
+               break;
+       }
+
+done:
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
 
-int samldb_notice_sid(struct ldb_module *module, 
-                     TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
+       return LDB_SUCCESS;
+}
+
+static int samldb_check_samAccountName(struct samldb_ctx *ac)
 {
+       struct ldb_request *req;
+       const char *name;
+       char *filter;
        int ret;
-       struct ldb_dn *dom_dn;
-       struct dom_sid *dom_sid;
-       const char *attrs[] = { NULL };
-       struct ldb_result *dom_res;
-       struct ldb_result *res;
-       uint32_t old_rid;
-
-       /* find if this SID already exists */
-       ret = ldb_search(module->ldb, mem_ctx, &res,
-                                NULL, LDB_SCOPE_SUBTREE, attrs,
-                                "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
-       if (ret == LDB_SUCCESS) {
-               if (res->count > 0) {
-                       talloc_free(res);
-                       ldb_asprintf_errstring(module->ldb,
-                                               "Attempt to add record with SID %s rejected,"
-                                               " because this SID is already in the database",
-                                               dom_sid_string(mem_ctx, sid));
-                       /* We have a duplicate SID, we must reject the add */
-                       return LDB_ERR_CONSTRAINT_VIOLATION;
+
+       if (ldb_msg_find_element(ac->msg, "samAccountName") == NULL) {
+               ret = samldb_generate_samAccountName(ac->msg);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
-               talloc_free(res);
-       } else {
-               ldb_asprintf_errstring(module->ldb,
-                                       "samldb_notice_sid: error searching to see if sid %s is in use: %s\n", 
-                                       dom_sid_string(mem_ctx, sid), 
-                                       ldb_errstring(module->ldb));
-               return ret;
        }
 
-       dom_sid = dom_sid_dup(mem_ctx, sid);
-       if (!dom_sid) {
+       name = ldb_msg_find_attr_as_string(ac->msg, "samAccountName", NULL);
+       if (name == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       filter = talloc_asprintf(ac, "samAccountName=%s", name);
+       if (filter == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       /* get the domain component part of the provided SID */
-       dom_sid->num_auths--;
-
-       /* find the domain DN */
-       ret = ldb_search(module->ldb, mem_ctx, &dom_res,
-                                NULL, LDB_SCOPE_SUBTREE, attrs,
-                                "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))", 
-                                ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
-       if (ret == LDB_SUCCESS) {
-               if (dom_res->count == 0) {
-                       talloc_free(dom_res);
-                       /* This isn't an operation on a domain we know about, so nothing to update */
-                       return LDB_SUCCESS;
-               }
 
-               if (dom_res->count > 1) {
-                       talloc_free(dom_res);
-                       ldb_asprintf_errstring(module->ldb,
-                                       "samldb_notice_sid: error retrieving domain from sid: duplicate (found %d) domain: %s!\n", 
-                                       dom_res->count, dom_sid_string(dom_res, dom_sid));
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-       } else {
-               ldb_asprintf_errstring(module->ldb,
-                                       "samldb_notice_sid: error retrieving domain from sid: %s: %s\n", 
-                                       dom_sid_string(dom_res, dom_sid), 
-                                       ldb_errstring(module->ldb));
+       ret = ldb_build_search_req(&req, ac->module->ldb, ac,
+                               ac->domain_dn, LDB_SCOPE_SUBTREE,
+                               filter, NULL,
+                               NULL,
+                               ac, samldb_check_samAccountName_callback,
+                               ac->req);
+       talloc_free(filter);
+       if (ret != LDB_SUCCESS) {
                return ret;
        }
+       ac->ares = NULL;
+       return ldb_next_request(ac->module, req);
+}
 
-       dom_dn = dom_res->msgs[0]->dn;
+static int samldb_check_samAccountType(struct samldb_ctx *ac)
+{
+       unsigned int account_type;
+       unsigned int group_type;
+       unsigned int uac;
+       int ret;
 
-       ret = samldb_find_next_rid(module, mem_ctx, 
-                                  dom_dn, &old_rid);
-       if (ret) {
-               talloc_free(dom_res);
-               return ret;
+       /* make sure sAMAccountType is not specified */
+       if (ldb_msg_find_element(ac->msg, "sAMAccountType") != NULL) {
+               ldb_asprintf_errstring(ac->module->ldb,
+                                       "sAMAccountType must not be specified");
+               return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
-       if (old_rid <= sid->sub_auths[sid->num_auths - 1]) {
-               ret = samldb_set_next_rid(module->ldb, mem_ctx, dom_dn, old_rid, 
-                                         sid->sub_auths[sid->num_auths - 1] + 1);
+       if (strcmp("user", ac->type) == 0) {
+               uac = samdb_result_uint(ac->msg, "userAccountControl", 0);
+               if (uac == 0) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                                               "userAccountControl invalid");
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               } else {
+                       account_type = samdb_uf2atype(uac);
+                       ret = samdb_msg_add_uint(ac->module->ldb,
+                                                ac->msg, ac->msg,
+                                                "sAMAccountType",
+                                                account_type);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
+       } else
+       if (strcmp("group", ac->type) == 0) {
+
+               group_type = samdb_result_uint(ac->msg, "groupType", 0);
+               if (group_type == 0) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                                               "groupType invalid");
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               } else {
+                       account_type = samdb_gtype2atype(group_type);
+                       ret = samdb_msg_add_uint(ac->module->ldb,
+                                                ac->msg, ac->msg,
+                                                "sAMAccountType",
+                                                account_type);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
        }
-       talloc_free(dom_res);
-       return ret;
+
+       return samldb_next_step(ac);
 }
 
-static int samldb_handle_sid(struct ldb_module *module, 
-                            TALLOC_CTX *mem_ctx, struct ldb_message *msg2,
-                            struct ldb_dn *parent_dn)
+static int samldb_get_sid_domain_callback(struct ldb_request *req,
+                                         struct ldb_reply *ares)
 {
+       struct samldb_ctx *ac;
+       const char *nextRid;
        int ret;
-       
-       struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg2, "objectSid");
-       if (sid == NULL) { 
-               ret = samldb_get_new_sid(module, msg2, msg2->dn, parent_dn, &sid);
-               if (ret != 0) {
-                       return ret;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* save entry */
+               if (ac->next_rid != 0) {
+                       /* one too many! */
+                       ldb_set_errstring(ac->module->ldb,
+                               "Invalid number of results while searching "
+                               "for domain object");
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
                }
 
-               if ( ! samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
-                       talloc_free(sid);
-                       return LDB_ERR_OPERATIONS_ERROR;
+               nextRid = ldb_msg_find_attr_as_string(ares->message,
+                                                       "nextRid", NULL);
+               if (nextRid == NULL) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "attribute nextRid not found in %s\n",
+                               ldb_dn_get_linearized(ares->message->dn));
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
                }
-               talloc_free(sid);
+
+               ac->next_rid = strtol(nextRid, NULL, 0);
+
+               ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+               talloc_free(ares);
                ret = LDB_SUCCESS;
-       } else {
-               ret = samldb_notice_sid(module, msg2, sid);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               ret = LDB_SUCCESS;
+               break;
+
+       case LDB_REPLY_DONE:
+
+               if (ac->next_rid == 0) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "Unable to get nextRid from domain entry\n");
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
+               }
+
+               /* found, go on */
+               ret = samldb_next_step(ac);
+               break;
+       }
+
+done:
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
        }
-       return ret;
+
+       return LDB_SUCCESS;
 }
 
-static int samldb_generate_samAccountName(struct ldb_module *module, TALLOC_CTX *mem_ctx, 
-                                         struct ldb_dn *dom_dn, char **name) 
+/* Find a domain object in the parents of a particular DN.  */
+static int samldb_get_sid_domain(struct samldb_ctx *ac)
 {
-       const char *attrs[] = { NULL };
-       struct ldb_result *res;
+       static const char * const attrs[2] = { "nextRid", NULL };
+       struct ldb_request *req;
+       char *filter;
        int ret;
-       
-       /* Format: $000000-000000000000 */
-       
-       do {
-               *name = talloc_asprintf(mem_ctx, "$%.6X-%.6X%.6X", (unsigned int)generate_random(), (unsigned int)generate_random(), (unsigned int)generate_random());
-               /* TODO: Figure out exactly what this is meant to conflict with */
-               ret = ldb_search(module->ldb,
-                                        mem_ctx, &res, dom_dn, LDB_SCOPE_SUBTREE, attrs,
-                                        "samAccountName=%s",
-                                        ldb_binary_encode_string(mem_ctx, *name));
-               if (ret != LDB_SUCCESS) {
-                       ldb_asprintf_errstring(module->ldb, "samldb: Failure searching to determine if samAccountName %s is unique: %s",
-                                              *name, ldb_errstring(module->ldb));
-                       return ret;
-               }
 
-               if (res->count == 0) {
-                       talloc_free(res);
-                       /* Great. There are no conflicting users/groups/etc */
-                       return LDB_SUCCESS;
-               } else {
-                       talloc_free(*name);
-                        /* gah, there is a conflicting name, lets move around the loop again... */
-               }
-       } while (1);
+       if (ac->sid == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->domain_sid = dom_sid_dup(ac, ac->sid);
+       if (!ac->domain_sid) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       /* get the domain component part of the provided SID */
+       ac->domain_sid->num_auths--;
+
+       filter = talloc_asprintf(ac, "(&(objectSid=%s)"
+                                      "(|(objectClass=domain)"
+                                        "(objectClass=builtinDomain)"
+                                        "(objectClass=samba4LocalDomain)))",
+                                ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+       if (filter == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldb_build_search_req(&req, ac->module->ldb, ac,
+                                  ldb_get_default_basedn(ac->module->ldb),
+                                  LDB_SCOPE_SUBTREE,
+                                  filter, attrs,
+                                  NULL,
+                                  ac, samldb_get_sid_domain_callback,
+                                  ac->req);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ac->next_rid = 0;
+       return ldb_next_request(ac->module, req);
 }
 
-static int samldb_fill_group_object(struct ldb_module *module, const struct ldb_message *msg,
-                                                   struct ldb_message **ret_msg)
+static bool samldb_msg_add_sid(struct ldb_message *msg,
+                               const char *name,
+                               const struct dom_sid *sid)
 {
-       int ret;
-       unsigned int group_type;
-       char *name;
-       struct ldb_message *msg2;
-       struct ldb_dn *dom_dn;
-       const char *rdn_name;
-       TALLOC_CTX *mem_ctx = talloc_new(msg);
-       const char *errstr;
-       if (!mem_ctx) {
+       struct ldb_val v;
+       enum ndr_err_code ndr_err;
+
+       ndr_err = ndr_push_struct_blob(&v, msg, NULL, sid,
+                                      (ndr_push_flags_fn_t)ndr_push_dom_sid);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return false;
+       }
+       return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
+}
+
+static int samldb_new_sid(struct samldb_ctx *ac)
+{
+
+       if (ac->domain_sid == NULL || ac->next_rid == 0) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* build the new msg */
-       msg2 = ldb_msg_copy(mem_ctx, msg);
-       if (!msg2) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
-               talloc_free(mem_ctx);
+       ac->sid = dom_sid_add_rid(ac, ac->domain_sid, ac->next_rid + 1);
+       if (ac->sid == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = samdb_copy_template(module->ldb, msg2, 
-                                 "group",
-                                 &errstr);
-       if (ret != 0) {
-               
-               talloc_free(mem_ctx);
-               return ret;
+       if ( ! samldb_msg_add_sid(ac->msg, "objectSid", ac->sid)) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       rdn_name = ldb_dn_get_rdn_name(msg2->dn);
+       return samldb_next_step(ac);
+}
 
-       if (strcasecmp(rdn_name, "cn") != 0) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad RDN (%s) for group!\n", rdn_name);
-               talloc_free(mem_ctx);
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+static int samldb_check_sid_callback(struct ldb_request *req,
+                                    struct ldb_reply *ares)
+{
+       struct samldb_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               /* if we get an entry it means an object with the
+                * requested sid exists */
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_CONSTRAINT_VIOLATION);
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
+
+               /* not found, go on */
+               talloc_free(ares);
+               ret = samldb_next_step(ac);
+               break;
        }
 
-       ret = samdb_search_for_parent_domain(module->ldb, mem_ctx, msg2->dn, &dom_dn, &errstr);
+done:
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int samldb_check_sid(struct samldb_ctx *ac)
+{
+       const char *const attrs[2] = { "objectSid", NULL };
+       struct ldb_request *req;
+       char *filter;
+       int ret;
+
+       if (ac->sid == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       filter = talloc_asprintf(ac, "(objectSid=%s)",
+                                ldap_encode_ndr_dom_sid(ac, ac->sid));
+       if (filter == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldb_build_search_req(&req, ac->module->ldb, ac,
+                                  ldb_get_default_basedn(ac->module->ldb),
+                                  LDB_SCOPE_SUBTREE,
+                                  filter, attrs,
+                                  NULL,
+                                  ac, samldb_check_sid_callback,
+                                  ac->req);
+
        if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(module->ldb,
-                                      "samldb_fill_group_object: %s", errstr);
                return ret;
        }
 
-       /* Generate a random name, if no samAccountName was supplied */
-       if (ldb_msg_find_element(msg2, "samAccountName") == NULL) {
-               ret = samldb_generate_samAccountName(module, mem_ctx, dom_dn, &name);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
-               ret = samdb_find_or_add_attribute(module->ldb, msg2, "sAMAccountName", name);
-               if (ret) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
+       return ldb_next_request(ac->module, req);
+}
+
+static int samldb_notice_sid_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
+{
+       struct samldb_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
        }
-       
-       if (ldb_msg_find_element(msg2, "sAMAccountType") != NULL) {
-               ldb_asprintf_errstring(module->ldb, "sAMAccountType must not be specified");
-               talloc_free(mem_ctx);
-               return LDB_ERR_UNWILLING_TO_PERFORM;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
-       group_type = samdb_result_uint(msg2, "groupType", 0);
-       if (group_type == 0) {
-               ldb_asprintf_errstring(module->ldb, "groupType invalid");
-               talloc_free(mem_ctx);
-               return LDB_ERR_UNWILLING_TO_PERFORM;
-       } else {
-               unsigned int account_type = samdb_gtype2atype(group_type);
-               ret = samdb_msg_add_uint(module->ldb, msg2, msg2,
-                                        "sAMAccountType",
-                                        account_type);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb,
+                       "Invalid reply type!\n");
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
        }
 
-       /* Manage SID allocation, conflicts etc */
-       ret = samldb_handle_sid(module, mem_ctx, msg2, dom_dn); 
+       ret = samldb_next_step(ac);
 
-       if (ret == LDB_SUCCESS) {
-               talloc_steal(msg, msg2);
-               *ret_msg = msg2;
+done:
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
        }
-       talloc_free(mem_ctx);
-       return ret;
+
+       return LDB_SUCCESS;
 }
 
-static int samldb_fill_user_or_computer_object(struct ldb_module *module, const struct ldb_message *msg, struct ldb_message **ret_msg)
+/* If we are adding new users/groups, we need to update the nextRid
+ * attribute to be 'above' the new/incoming RID. Attempt to do it
+ *atomically. */
+static int samldb_notice_sid(struct samldb_ctx *ac)
 {
+       uint32_t old_id, new_id;
+       struct ldb_request *req;
+       struct ldb_message *msg;
+       struct ldb_message_element *els;
+       struct ldb_val *vals;
        int ret;
-       char *name;
-       struct ldb_message *msg2;
-       struct ldb_dn *dom_dn;
-       const char *rdn_name;
-       TALLOC_CTX *mem_ctx = talloc_new(msg);
-       const char *errstr;
-       unsigned int user_account_control;
-       if (!mem_ctx) {
+
+       old_id = ac->next_rid;
+       new_id = ac->sid->sub_auths[ac->sid->num_auths - 1];
+
+       if (old_id >= new_id) {
+               /* no need to update the domain nextRid attribute */
+               return samldb_next_step(ac);
+       }
+
+       /* we do a delete and add as a single operation. That prevents
+          a race, in case we are not actually on a transaction db */
+       msg = talloc_zero(ac, struct ldb_message);
+       if (msg == NULL) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       els = talloc_array(msg, struct ldb_message_element, 2);
+       if (els == NULL) {
+               ldb_oom(ac->module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
+       vals = talloc_array(msg, struct ldb_val, 2);
+       if (vals == NULL) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       msg->dn = ac->domain_dn;
+       msg->num_elements = 2;
+       msg->elements = els;
 
-       /* build the new msg */
-       msg2 = ldb_msg_copy(mem_ctx, msg);
-       if (!msg2) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: ldb_msg_copy failed!\n");
-               talloc_free(mem_ctx);
+       els[0].num_values = 1;
+       els[0].values = &vals[0];
+       els[0].flags = LDB_FLAG_MOD_DELETE;
+       els[0].name = talloc_strdup(msg, "nextRid");
+       if (!els[0].name) {
+               ldb_oom(ac->module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = samdb_copy_template(module->ldb, msg2, 
-                                 "user",
-                                 &errstr);
-       if (ret) {
-               ldb_asprintf_errstring(module->ldb, 
-                                      "samldb_fill_user_or_computer_object: Error copying user template: %s\n",
-                                      errstr);
-               talloc_free(mem_ctx);
+       els[1].num_values = 1;
+       els[1].values = &vals[1];
+       els[1].flags = LDB_FLAG_MOD_ADD;
+       els[1].name = els[0].name;
+
+       vals[0].data = (uint8_t *)talloc_asprintf(vals, "%u", old_id);
+       if (!vals[0].data) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       vals[0].length = strlen((char *)vals[0].data);
+
+       vals[1].data = (uint8_t *)talloc_asprintf(vals, "%u", new_id);
+       if (!vals[1].data) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       vals[1].length = strlen((char *)vals[1].data);
+
+       ret = ldb_build_mod_req(&req, ac->module->ldb, ac,
+                               msg, NULL,
+                               ac, samldb_notice_sid_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       rdn_name = ldb_dn_get_rdn_name(msg2->dn);
+       return ldb_next_request(ac->module, req);
+}
 
-       if (strcasecmp(rdn_name, "cn") != 0) {
-               ldb_asprintf_errstring(module->ldb, "Bad RDN (%s=) for user/computer, should be CN=!\n", rdn_name);
-               talloc_free(mem_ctx);
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+static int samldb_add_entry_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
+{
+       struct samldb_ctx *ac;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       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);
        }
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb,
+                       "Invalid reply type!\n");
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       /* we exit the samldb module here */
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, LDB_SUCCESS);
+}
 
-       ret = samdb_search_for_parent_domain(module->ldb, mem_ctx, msg2->dn, &dom_dn, &errstr);
+static int samldb_add_entry(struct samldb_ctx *ac)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_build_add_req(&req, ac->module->ldb, ac,
+                               ac->msg,
+                               ac->req->controls,
+                               ac, samldb_add_entry_callback,
+                               ac->req);
        if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(module->ldb,
-                                      "samldb_fill_user_or_computer_object: %s", errstr);
                return ret;
        }
 
-       if (ldb_msg_find_element(msg2, "samAccountName") == NULL) {
-               ret = samldb_generate_samAccountName(module, mem_ctx, dom_dn, &name);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
-               ret = samdb_find_or_add_attribute(module->ldb, msg2, "sAMAccountName", name);
-               if (ret) {
-                       talloc_free(mem_ctx);
-                       return ret;
-               }
+       return ldb_next_request(ac->module, req);
+}
+
+static int samldb_fill_object(struct samldb_ctx *ac, const char *type)
+{
+       int ret;
+
+       /* first look for the template */
+       ac->type = type;
+       ret = samldb_add_step(ac, samldb_search_template);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* then apply it */
+       ret = samldb_add_step(ac, samldb_apply_template);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* search for a parent domain objet */
+       ac->check_dn = ac->req->op.add.message->dn;
+       ret = samldb_add_step(ac, samldb_get_parent_domain);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* check if we have a valid samAccountName */
+       ret = samldb_add_step(ac, samldb_check_samAccountName);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* check account_type/group_type */
+       ret = samldb_add_step(ac, samldb_check_samAccountType);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* check if we have a valid SID */
+       ac->sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
+       if ( ! ac->sid) {
+               ret = samldb_add_step(ac, samldb_new_sid);
+               if (ret != LDB_SUCCESS) return ret;
+       } else {
+               ret = samldb_add_step(ac, samldb_get_sid_domain);
+               if (ret != LDB_SUCCESS) return ret;
        }
 
-       if (ldb_msg_find_element(msg2, "sAMAccountType") != NULL) {
-               ldb_asprintf_errstring(module->ldb, "sAMAccountType must not be specified");
-               talloc_free(mem_ctx);
-               return LDB_ERR_UNWILLING_TO_PERFORM;
+       ret = samldb_add_step(ac, samldb_check_sid);
+       if (ret != LDB_SUCCESS) return ret;
+
+       ret = samldb_add_step(ac, samldb_notice_sid);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* finally proceed with adding the entry */
+       ret = samldb_add_step(ac, samldb_add_entry);
+       if (ret != LDB_SUCCESS) return ret;
+
+       return samldb_first_step(ac);
+
+       /* TODO: userAccountControl, badPwdCount, codePage,
+        *       countryCode, badPasswordTime, lastLogoff, lastLogon,
+        *       pwdLastSet, primaryGroupID, accountExpires, logonCount */
+
+}
+
+static int samldb_foreign_notice_sid_callback(struct ldb_request *req,
+                                               struct ldb_reply *ares)
+{
+       struct samldb_ctx *ac;
+       const char *nextRid;
+       const char *name;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct samldb_ctx);
+
+       if (!ares) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
        }
-       user_account_control = samdb_result_uint(msg2, "userAccountControl", 0);
-       if (user_account_control == 0) {
-               ldb_asprintf_errstring(module->ldb, "userAccountControl invalid");
-               talloc_free(mem_ctx);
-               return LDB_ERR_UNWILLING_TO_PERFORM;
-       } else {
-               unsigned int account_type = samdb_uf2atype(user_account_control);
-               ret = samdb_msg_add_uint(module->ldb, msg2, msg2,
-                                        "sAMAccountType",
-                                        account_type);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       /* Manage SID allocation, conflicts etc */
-       ret = samldb_handle_sid(module, mem_ctx, msg2, dom_dn); 
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* save entry */
+               if (ac->next_rid != 0) {
+                       /* one too many! */
+                       ldb_set_errstring(ac->module->ldb,
+                               "Invalid number of results while searching "
+                               "for domain object");
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
+               }
+
+               nextRid = ldb_msg_find_attr_as_string(ares->message,
+                                                       "nextRid", NULL);
+               if (nextRid == NULL) {
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "attribute nextRid not found in %s\n",
+                               ldb_dn_get_linearized(ares->message->dn));
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       break;
+               }
+
+               ac->next_rid = strtol(nextRid, NULL, 0);
+
+               ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+               name = samdb_result_string(ares->message, "name", NULL);
+               ldb_debug(ac->module->ldb, LDB_DEBUG_TRACE,
+                        "NOTE (strange but valid): Adding foreign SID "
+                        "record with SID %s, but this domain (%s) is "
+                        "not foreign in the database",
+                        dom_sid_string(ares, ac->sid), name);
+
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               talloc_free(ares);
+               break;
 
-       /* TODO: userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
+       case LDB_REPLY_DONE:
 
-       if (ret == 0) {
-               *ret_msg = msg2;
-               talloc_steal(msg, msg2);
+               /* if this is a fake foreign SID, notice the SID */
+               if (ac->domain_dn) {
+                       ret = samldb_notice_sid(ac);
+                       break;
+               }
+
+               /* found, go on */
+               ret = samldb_next_step(ac);
+               break;
        }
-       talloc_free(mem_ctx);
-       return ret;
+
+done:
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
+       return LDB_SUCCESS;
 }
-       
-static int samldb_fill_foreignSecurityPrincipal_object(struct ldb_module *module, const struct ldb_message *msg, 
-                                                      struct ldb_message **ret_msg)
+
+/* Find a domain object in the parents of a particular DN. */
+static int samldb_foreign_notice_sid(struct samldb_ctx *ac)
 {
-       struct ldb_message *msg2;
-       const char *rdn_name;
-       struct dom_sid *dom_sid;
-       struct dom_sid *sid;
-       const char *dom_attrs[] = { "name", NULL };
-       struct ldb_message **dom_msgs;
-       const char *errstr;
+       static const char * const attrs[3] = { "nextRid", "name", NULL };
+       struct ldb_request *req;
+       char *filter;
        int ret;
 
-       TALLOC_CTX *mem_ctx = talloc_new(msg);
-       if (!mem_ctx) {
+       if (ac->sid == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* build the new msg */
-       msg2 = ldb_msg_copy(mem_ctx, msg);
-       if (!msg2) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_foreignSecurityPrincipal_object: ldb_msg_copy failed!\n");
-               talloc_free(mem_ctx);
+       ac->domain_sid = dom_sid_dup(ac, ac->sid);
+       if (!ac->domain_sid) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
+       /* get the domain component part of the provided SID */
+       ac->domain_sid->num_auths--;
 
-       ret = samdb_copy_template(module->ldb, msg2, 
-                                 "ForeignSecurityPrincipal",
-                                 &errstr);
-       if (ret != 0) {
-               ldb_asprintf_errstring(module->ldb, 
-                                      "samldb_fill_foreignSecurityPrincipal_object: "
-                                      "Error copying template: %s",
-                                   errstr);
-               talloc_free(mem_ctx);
-               return ret;
+       filter = talloc_asprintf(ac, "(&(objectSid=%s)(objectclass=domain))",
+                                ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+       if (filter == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       rdn_name = ldb_dn_get_rdn_name(msg2->dn);
+       ret = ldb_build_search_req(&req, ac->module->ldb, ac,
+                                  ldb_get_default_basedn(ac->module->ldb),
+                                  LDB_SCOPE_SUBTREE,
+                                  filter, attrs,
+                                  NULL,
+                                  ac, samldb_foreign_notice_sid_callback,
+                                  ac->req);
 
-       if (strcasecmp(rdn_name, "cn") != 0) {
-               ldb_asprintf_errstring(module->ldb, "Bad RDN (%s=) for ForeignSecurityPrincipal, should be CN=!", rdn_name);
-               talloc_free(mem_ctx);
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       sid = samdb_result_dom_sid(msg2, msg, "objectSid");
-       if (!sid) {
-               /* Slightly different for the foreign sids.  We don't want
-                * domain SIDs ending up there, it would cause all sorts of
-                * pain */
-
-               sid = dom_sid_parse_talloc(msg2, (const char *)ldb_dn_get_rdn_val(msg2->dn)->data);
-               if (!sid) {
-                       ldb_set_errstring(module->ldb, "No valid found SID in ForeignSecurityPrincipal CN!");
-                       talloc_free(mem_ctx);
-                       return LDB_ERR_CONSTRAINT_VIOLATION;
-               }
+       ac->next_rid = 0;
+       return ldb_next_request(ac->module, req);
+}
 
-               if ( ! samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
-                       talloc_free(sid);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
+static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
+{
+       int ret;
 
-               dom_sid = dom_sid_dup(mem_ctx, sid);
-               if (!dom_sid) {
-                       talloc_free(mem_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
+       ac->sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
+       if (ac->sid == NULL) {
+               ac->sid = dom_sid_parse_talloc(ac->msg,
+                          (const char *)ldb_dn_get_rdn_val(ac->msg->dn)->data);
+               if (!ac->sid) {
+                       ldb_set_errstring(ac->module->ldb,
+                                       "No valid found SID in "
+                                       "ForeignSecurityPrincipal CN!");
+                       talloc_free(ac);
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
                }
-               /* get the domain component part of the provided SID */
-               dom_sid->num_auths--;
-
-               /* find the domain DN */
-
-               ret = gendb_search(module->ldb,
-                                  mem_ctx, NULL, &dom_msgs, dom_attrs,
-                                  "(&(objectSid=%s)(objectclass=domain))",
-                                  ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
-               if (ret >= 1) {
-                       /* We don't really like the idea of foreign sids that are not foreign, but it happens */
-                       const char *name = samdb_result_string(dom_msgs[0], "name", NULL);
-                       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "NOTE (strange but valid): Adding foreign SID record with SID %s, but this domian (%s) is already in the database", 
-                                 dom_sid_string(mem_ctx, sid), name); 
-               } else if (ret == -1) {
-                       ldb_asprintf_errstring(module->ldb,
-                                               "samldb_fill_foreignSecurityPrincipal_object: error searching for a domain with this sid: %s\n", 
-                                               dom_sid_string(mem_ctx, dom_sid));
-                       talloc_free(dom_msgs);
+               if ( ! samldb_msg_add_sid(ac->msg, "objectSid", ac->sid)) {
+                       talloc_free(ac);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
        }
 
-       /* This isn't an operation on a domain we know about, so just
-        * check for the SID, looking for duplicates via the common
-        * code */
-       ret = samldb_notice_sid(module, msg2, sid);
-       if (ret == 0) {
-               talloc_steal(msg, msg2);
-               *ret_msg = msg2;
-       }
-       
-       return ret;
+       /* first look for the template */
+       ac->type = "foreignSecurityPrincipal";
+       ret = samldb_add_step(ac, samldb_search_template);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* then apply it */
+       ret = samldb_add_step(ac, samldb_apply_template);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* check we do not already have this SID */
+       ret = samldb_add_step(ac, samldb_check_sid);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* check if we need to notice this SID */
+       ret = samldb_add_step(ac, samldb_foreign_notice_sid);
+       if (ret != LDB_SUCCESS) return ret;
+
+       /* finally proceed with adding the entry */
+       ret = samldb_add_step(ac, samldb_add_entry);
+       if (ret != LDB_SUCCESS) return ret;
+
+       return samldb_first_step(ac);
 }
 
-/* add_record */
+static int samldb_check_rdn(struct ldb_module *module, struct ldb_dn *dn)
+{
+       const char *rdn_name;
 
-/*
- * FIXME
- *
- * Actually this module is not async at all as it does a number of sync searches
- * in the process. It still to be decided how to deal with it properly so it is
- * left SYNC for now until we think of a good solution.
- */
+       rdn_name = ldb_dn_get_rdn_name(dn);
+
+       if (strcasecmp(rdn_name, "cn") != 0) {
+               ldb_asprintf_errstring(module->ldb,
+                                       "Bad RDN (%s=) for samldb object, "
+                                       "should be CN=!\n", rdn_name);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
 
+       return LDB_SUCCESS;
+}
+
+/* add_record */
 static int samldb_add(struct ldb_module *module, struct ldb_request *req)
 {
-       const struct ldb_message *msg = req->op.add.message;
-       struct ldb_message *msg2 = NULL;
-       struct ldb_request *down_req;
+       struct samldb_ctx *ac;
        int ret;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
 
-       if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.add.message->dn)) {
                return ldb_next_request(module, req);
        }
 
-       /* is user or computer? */
-       if ((samdb_find_attribute(module->ldb, msg, "objectclass", "user") != NULL) || 
-           (samdb_find_attribute(module->ldb, msg, "objectclass", "computer") != NULL)) {
-               /*  add all relevant missing objects */
-               ret = samldb_fill_user_or_computer_object(module, msg, &msg2);
-               if (ret) {
-                       return ret;
-               }
+       ac = samldb_ctx_init(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* is group? add all relevant missing objects */
-       if ( ! msg2 ) {
-               if (samdb_find_attribute(module->ldb, msg, "objectclass", "group") != NULL) {
-                       ret = samldb_fill_group_object(module, msg, &msg2);
-                       if (ret) {
-                               return ret;
-                       }
-               }
+       /* build the new msg */
+       ac->msg = ldb_msg_copy(ac, ac->req->op.add.message);
+       if (!ac->msg) {
+               talloc_free(ac);
+               ldb_debug(ac->module->ldb, LDB_DEBUG_FATAL,
+                         "samldb_add: ldb_msg_copy failed!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* perhaps a foreignSecurityPrincipal? */
-       if ( ! msg2 ) {
-               if (samdb_find_attribute(module->ldb, msg, "objectclass", "foreignSecurityPrincipal") != NULL) {
-                       ret = samldb_fill_foreignSecurityPrincipal_object(module, msg, &msg2);
-                       if (ret) {
-                               return ret;
-                       }
+       if (samdb_find_attribute(module->ldb, ac->msg,
+                                "objectclass", "computer") != NULL) {
+
+               /* make sure the computer object also has the 'user'
+                * objectclass so it will be handled by the next call */
+               ret = samdb_find_or_add_value(module->ldb, ac->msg,
+                                               "objectclass", "user");
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ac);
+                       return ret;
                }
        }
 
-       if (msg2 == NULL) {
-               return ldb_next_request(module, req);
-       }
+       if (samdb_find_attribute(module->ldb, ac->msg,
+                                "objectclass", "user") != NULL) {
 
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
-               return LDB_ERR_OPERATIONS_ERROR;
+               ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ac);
+                       return ret;
+               }
+
+               return samldb_fill_object(ac, "user");
        }
 
-       *down_req = *req;
-       
-       down_req->op.add.message = talloc_steal(down_req, msg2);
+       if (samdb_find_attribute(module->ldb, ac->msg,
+                                "objectclass", "group") != NULL) {
+
+               ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ac);
+                       return ret;
+               }
 
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
+               return samldb_fill_object(ac, "group");
+       }
 
-       /* go on with the call chain */
-       ret = ldb_next_request(module, down_req);
+       /* perhaps a foreignSecurityPrincipal? */
+       if (samdb_find_attribute(module->ldb, ac->msg,
+                                "objectclass",
+                                "foreignSecurityPrincipal") != NULL) {
 
-       /* 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;
+               ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(ac);
+                       return ret;
+               }
+
+               return samldb_fill_foreignSecurityPrincipal_object(ac);
        }
 
-       return ret;
+       talloc_free(ac);
+
+       /* nothing matched, go on */
+       return ldb_next_request(module, req);
 }
 
 /* modify */
@@ -777,6 +1354,8 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
+       /* TODO: do not modify original request, create a new one */
+
        el = ldb_msg_find_element(req->op.mod.message, "groupType");
        if (el && el->flags & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE) && el->num_values == 1) {
                req->op.mod.message = msg = ldb_msg_copy_shallow(req, req->op.mod.message);
index 4b6e9e1d4741086293ae146a56baa84c6567820c..56d24a2962859ada27aa34c4a3f233b3c9a25b8a 100644 (file)
@@ -62,8 +62,10 @@ struct schema_fsmo_private_data {
 };
 
 struct schema_fsmo_search_data {
-       struct schema_fsmo_private_data *module_context;
-       struct ldb_request *orig_req;
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+       const struct dsdb_schema *schema;
 };
 
 static int schema_fsmo_init(struct ldb_module *module)
@@ -325,41 +327,54 @@ static int generate_dITContentRules(struct ldb_context *ldb, struct ldb_message
 /* Add objectClasses, attributeTypes and dITContentRules from the
    schema object (they are not stored in the database)
  */
-static int schema_fsmo_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+static int schema_fsmo_search_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
-       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
-       struct schema_fsmo_search_data *search_data = talloc_get_type(context, struct schema_fsmo_search_data);
-       struct ldb_request *orig_req = search_data->orig_req;
-       TALLOC_CTX *mem_ctx;
+       struct schema_fsmo_search_data *ac;
+       struct schema_fsmo_private_data *mc;
        int i, ret;
 
-       /* Only entries are interesting, and we handle the case of the parent seperatly */
-       if (ares->type != LDB_REPLY_ENTRY) {
-               return orig_req->callback(ldb, orig_req->context, ares);
-       }
+       ac = talloc_get_type(req->context, struct schema_fsmo_search_data);
+       mc = talloc_get_type(ac->module->private_data, struct schema_fsmo_private_data);
 
-       if (ldb_dn_compare(ares->message->dn, search_data->module_context->aggregate_dn) != 0) {
-               talloc_free(mem_ctx);
-               return orig_req->callback(ldb, orig_req->context, ares);
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
-
-       mem_ctx = talloc_new(ares);
-       if (!mem_ctx) {
-               ldb_oom(ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
+       /* Only entries are interesting, and we handle the case of the parent seperatly */
 
-       for (i=0; i < ARRAY_SIZE(generated_attrs); i++) {
-               if (ldb_attr_in_list(orig_req->op.search.attrs, generated_attrs[i].attr)) {
-                       ret = generated_attrs[i].fn(ldb, ares->message, schema);
-                       if (ret != LDB_SUCCESS) {
-                               return ret;
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               if (ldb_dn_compare(ares->message->dn, mc->aggregate_dn) != 0) {
+                       return ldb_module_send_entry(ac->req, ares->message);
+               }
+
+               for (i=0; i < ARRAY_SIZE(generated_attrs); i++) {
+                       if (ldb_attr_in_list(ac->req->op.search.attrs, generated_attrs[i].attr)) {
+                               ret = generated_attrs[i].fn(ac->module->ldb, ares->message, ac->schema);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
                        }
                }
+
+               return ldb_module_send_entry(ac->req, ares->message);
+
+       case LDB_REPLY_REFERRAL:
+
+               return ldb_module_send_referral(ac->req, ares->referral);
+
+       case LDB_REPLY_DONE:
+
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
        }
 
-       talloc_free(mem_ctx);
-       return orig_req->callback(ldb, orig_req->context, ares);
+       return LDB_SUCCESS;
 }
 
 /* search */
@@ -390,27 +405,24 @@ static int schema_fsmo_search(struct ldb_module *module, struct ldb_request *req
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       down_req = talloc(req, struct ldb_request);     
-       if (!down_req) {
-               ldb_oom(module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       
-       *down_req = *req;
-       search_context->orig_req = req;
-       search_context->module_context = talloc_get_type(module->private_data, struct schema_fsmo_private_data);
-       down_req->context = search_context;
-
-       down_req->callback = schema_fsmo_search_callback;
 
-       ret = ldb_next_request(module, down_req);
+       search_context->module = module;
+       search_context->req = req;
+       search_context->schema = schema;
 
-       /* 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;
+       ret = ldb_build_search_req_ex(&down_req, module->ldb, search_context,
+                                       req->op.search.base,
+                                       req->op.search.scope,
+                                       req->op.search.tree,
+                                       req->op.search.attrs,
+                                       req->controls,
+                                       search_context, schema_fsmo_search_callback,
+                                       req);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-       return ret;
+
+       return ldb_next_request(module, down_req);
 }
 
 
index 361cf226dc331b1a8a46059213c512552983fb81..0e3b46debee4354131f109c3e75d19f2e53296aa 100644 (file)
@@ -27,7 +27,7 @@
  *
  *  Component: ldb deleted objects control module
  *
- *  Description: this module hides deleted objects, and returns them if the control is there
+ *  Description: this module hides deleted objects, and returns them if the right control is there
  *
  *  Author: Stefan Metzmacher
  */
 struct show_deleted_search_request {
 
        struct ldb_module *module;
-       void *up_context;
-       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
-
-       bool remove_from_msg;
+       struct ldb_request *req;
 };
 
-static int show_deleted_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+static int show_deleted_search_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
 {
        struct show_deleted_search_request *ar;
 
-       ar = talloc_get_type(context, struct show_deleted_search_request);
+       ar = talloc_get_type(req->context, struct show_deleted_search_request);
 
-       if (ares->type == LDB_REPLY_ENTRY) {
-               bool isDeleted;
+       if (!ares) {
+               return ldb_module_done(ar->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, ares->error);
+       }
 
-               isDeleted = ldb_msg_find_attr_as_bool(ares->message, "isDeleted", false);
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
 
-               if (isDeleted) {
-                       goto skip_deleted;
-               }
+               return ldb_module_send_entry(ar->req, ares->message);
 
-               if (ar->remove_from_msg) {
-                       ldb_msg_remove_attr(ares->message, "isDeleted");
-               }
-       }
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(ar->req, ares->referral);
 
-       return ar->up_callback(ldb, ar->up_context, ares);
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ar->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
 
-skip_deleted:
-       talloc_free(ares);
+       }
        return LDB_SUCCESS;
 }
 
@@ -81,116 +83,68 @@ static int show_deleted_search(struct ldb_module *module, struct ldb_request *re
        struct ldb_control **saved_controls;
        struct show_deleted_search_request *ar;
        struct ldb_request *down_req;
-       char **new_attrs;
-       uint32_t num_attrs = 0;
-       uint32_t i;
+       char *old_filter;
+       char *new_filter;
        int ret;
 
-       /* check if there's a show deleted control */
-       control = ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID);
-
-       /* copy the request for modification */
-       down_req = talloc(req, struct ldb_request);
-       if (down_req == NULL) {
-               ldb_oom(module->ldb);
+       ar = talloc_zero(req, struct show_deleted_search_request);
+       if (ar == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
+       ar->module = module;
+       ar->req = req;
 
-       /* copy the request */
-       *down_req = *req;
+       /* check if there's a show deleted control */
+       control = ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID);
 
-       /* if a control is there remove if from the modified request */
-       if (control && !save_controls(control, down_req, &saved_controls)) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       if ( ! control) {
+               old_filter = ldb_filter_from_tree(ar, req->op.search.tree);
+               new_filter = talloc_asprintf(ar, "(&(!(isDeleted=TRUE))%s)",
+                                                old_filter);
 
-       /* if we had a control, then just go on to the next request as we have nothing to hide */
-       if (control) {
-               goto next_request;
-       }
+               ret = ldb_build_search_req(&down_req, module->ldb, ar,
+                                          req->op.search.base,
+                                          req->op.search.scope,
+                                          new_filter,
+                                          req->op.search.attrs,
+                                          req->controls,
+                                          ar, show_deleted_search_callback,
+                                          req);
 
-       ar = talloc(down_req, struct show_deleted_search_request);
-       if (ar == NULL) {
-               ldb_oom(module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ar->module              = module;
-       ar->up_context          = req->context;
-       ar->up_callback         = req->callback;
-       ar->remove_from_msg     = true;
-
-       /* check if attrs only is specified, in that case check wether we need to modify them */
-       if (down_req->op.search.attrs) {
-               for (i=0; (down_req->op.search.attrs && down_req->op.search.attrs[i]); i++) {
-                       num_attrs++;
-                       if (strcasecmp(down_req->op.search.attrs[i], "*") == 0) {
-                               ar->remove_from_msg = false;
-                       } else if (strcasecmp(down_req->op.search.attrs[i], "isDeleted") == 0) {
-                               ar->remove_from_msg = false;
-                       }
-               }
        } else {
-               ar->remove_from_msg = false;
+               ret = ldb_build_search_req_ex(&down_req, module->ldb, ar,
+                                             req->op.search.base,
+                                             req->op.search.scope,
+                                             req->op.search.tree,
+                                             req->op.search.attrs,
+                                             req->controls,
+                                             ar, show_deleted_search_callback,
+                                             req);
        }
-
-       if (ar->remove_from_msg) {
-               new_attrs = talloc_array(down_req, char *, num_attrs + 2);
-               if (!new_attrs) {
-                       ldb_oom(module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               for (i=0; i < num_attrs; i++) {
-                       new_attrs[i] = discard_const_p(char, down_req->op.search.attrs[i]);             
-               }
-               new_attrs[i] = talloc_strdup(new_attrs, "isDeleted");
-               if (!new_attrs[i]) {
-                       ldb_oom(module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               new_attrs[i+1] = NULL;
-               down_req->op.search.attrs = (const char * const *)new_attrs;
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
 
-       down_req->context = ar;
-       down_req->callback = show_deleted_search_callback;
-       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
-next_request:
-       /* 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;
+       /* if a control is there remove if from the modified request */
+       if (control && !save_controls(control, down_req, &saved_controls)) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       return ret;
+       /* perform the search */
+       return ldb_next_request(module, down_req);
 }
 
 static int show_deleted_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_SHOW_DELETED_OID;
-       req->controls = NULL;
-
-       ret = ldb_request(module->ldb, req);
+       ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_DELETED_OID);
        if (ret != LDB_SUCCESS) {
-               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "show_deleted: Unable to register control with rootdse!\n");
-               talloc_free(req);
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                       "extended_dn: Unable to register control with rootdse!\n");
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       talloc_free(req);
        return ldb_next_init(module);
 }
 
index 8f9299514551a61af6eb1f4c75bbf8795729813a..d15e85ad41a173b2a7dc966e8195c519e8987e3d 100644 (file)
 #include "librpc/ndr/libndr.h"
 #include "dsdb/samdb/samdb.h"
 
+struct entryuuid_private {
+       struct ldb_context *ldb;
+       struct ldb_dn **base_dns;
+};
+
 static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
 {
        struct GUID guid;
@@ -598,10 +603,10 @@ static int nsuniqueid_init(struct ldb_module *module)
        return ldb_next_init(module);
 }
 
-static int get_seq(struct ldb_context *ldb, void *context, 
-                  struct ldb_reply *ares) 
+static int get_seq(struct ldb_request *req,
+                  struct ldb_reply *ares)
 {
-       unsigned long long *seq = (unsigned long long *)context;
+       unsigned long long *seq = (unsigned long long *)req->context;
        if (ares->type == LDB_REPLY_ENTRY) {
                struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
                if (el) {
@@ -609,6 +614,10 @@ static int get_seq(struct ldb_context *ldb, void *context,
                }
        }
 
+       if (ares->type == LDB_REPLY_DONE) {
+               return ldb_request_done(req, LDB_SUCCESS);
+       }
+
        return LDB_SUCCESS;
 }
 
@@ -635,7 +644,7 @@ static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_reque
        partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
        if (!partition_ctrl) {
                ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
-                             "instancetype_add: no current partition control found");
+                             "entryuuid_sequence_number: no current partition control found");
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
@@ -643,39 +652,21 @@ static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_reque
                                    struct dsdb_control_current_partition);
        SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
 
-       search_req = talloc(req, struct ldb_request);
-       if (search_req == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               return LDB_ERR_OPERATIONS_ERROR;
+       ret = ldb_build_search_req(&search_req, module->ldb, req,
+                                  partition->dn, LDB_SCOPE_BASE,
+                                  NULL, contextCSN_attr, NULL,
+                                  &seq, get_seq,
+                                  NULL);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
-       
-       /* Finally, we have it.  This saves searching over more
-        * partitions than we expose to the client, such as a cn=samba
-        * configuration partition */
 
-       search_req->operation = LDB_SEARCH;
-       search_req->op.search.base = partition->dn;
-       search_req->op.search.scope = LDB_SCOPE_BASE;
-       
-       search_req->op.search.tree = ldb_parse_tree(search_req, "objectClass=*");
-       if (search_req->op.search.tree == NULL) {
-               ldb_set_errstring(module->ldb, "Unable to parse search expression");
-               talloc_free(search_req);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       
-       search_req->op.search.attrs = contextCSN_attr;
-       search_req->controls = NULL;
-       search_req->context = &seq;
-       search_req->callback = get_seq;
-       ldb_set_timeout(module->ldb, search_req, 0); /* use default timeout */
-       
        ret = ldb_next_request(module, search_req);
-       
+
        if (ret == LDB_SUCCESS) {
                ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
        }
-       
+
        talloc_free(search_req);
        if (ret != LDB_SUCCESS) {
                return ret;
index 9c332d2969077eecc786c73880973bae206666d4..10e2dc25ce04363157580de04fcf2d954e6c5eb5 100644 (file)
@@ -3,6 +3,7 @@
 
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2007
    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+   Copyright (C) Simo Sorce <idra@samba.org> 2008
 
    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
 #include "ldb_includes.h"
 
 struct subtree_delete_context {
-       enum sd_step {SD_SEARCH, SD_DO_DEL} step;
-
        struct ldb_module *module;
-       struct ldb_handle *handle;
-       struct ldb_request *orig_req;
-
-       struct ldb_request *search_req;
-       struct ldb_request *down_req;
+       struct ldb_request *req;
 
        int num_children;
 };
 
-static struct subtree_delete_context *subtree_delete_init_handle(struct ldb_request *req, 
-                                                                struct ldb_module *module)
+static struct subtree_delete_context *subdel_ctx_init(struct ldb_module *module,
+                                                     struct ldb_request *req)
 {
        struct subtree_delete_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 subtree_delete_context);
+       ac = talloc_zero(req, struct subtree_delete_context);
        if (ac == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
+               ldb_oom(module->ldb);
                return NULL;
        }
 
-       h->private_data = ac;
-
        ac->module = module;
-       ac->handle = h;
-       ac->orig_req = req;
-
-       req->handle = h;
+       ac->req = req;
 
        return ac;
 }
 
-static int subtree_delete_check_for_children(struct subtree_delete_context *ac)
+static int subtree_delete_search_callback(struct ldb_request *req,
+                                         struct ldb_reply *ares)
 {
-       if (ac->num_children > 0) {
-               ldb_asprintf_errstring(ac->module->ldb, "Cannot delete %s, not a leaf node (has %d children)\n",
-                                      ldb_dn_get_linearized(ac->orig_req->op.del.dn), ac->num_children);
-               return LDB_ERR_NOT_ALLOWED_ON_NON_LEAF;
-       } else {
-               struct ldb_request *req = talloc(ac, struct ldb_request);
-               if (!req) {
-                       ldb_oom(ac->module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               *req = *ac->orig_req;
-               
-               /* Ensure any (io) errors during the search for
-                * children don't propgate back in the error string */
-               ldb_set_errstring(ac->module->ldb, NULL);
+       struct subtree_delete_context *ac;
+       int ret;
 
-               ac->down_req = req;
-               ac->step = SD_DO_DEL;
-               return ldb_next_request(ac->module, req);
-       }
-}
+       ac = talloc_get_type(req->context, struct subtree_delete_context);
 
-static int subtree_delete_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
-{
-       struct subtree_delete_context *ac = talloc_get_type(context, struct subtree_delete_context);
-       TALLOC_CTX *mem_ctx = talloc_new(ac);
-    
-       if (!mem_ctx) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       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);
        }
-       /* OK, we have one of *many* search results here:
 
-          We should also get the entry we tried to rename.  This
-          callback handles this and everything below it.
-        */
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
 
-       /* Only entries are interesting, and we handle the case of the parent seperatly */
-       if (ares->type == LDB_REPLY_ENTRY
-           && ldb_dn_compare(ares->message->dn, ac->orig_req->op.del.dn) != 0) {
-               /* And it is an actual entry: now object bitterly that we are not a leaf node */
+               talloc_free(ares);
                ac->num_children++;
+               break;
+
+       case LDB_REPLY_REFERRAL:
+
+               /* ignore */
+               talloc_free(ares);
+               break;
+
+       case LDB_REPLY_DONE:
+
+               if (ac->num_children > 0) {
+                       talloc_free(ares);
+                       ldb_asprintf_errstring(ac->module->ldb,
+                               "Cannot delete %s, not a leaf node "
+                               "(has %d children)\n",
+                               ldb_dn_get_linearized(ac->req->op.del.dn),
+                               ac->num_children);
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                              LDB_ERR_NOT_ALLOWED_ON_NON_LEAF);
+               }
+
+               /* ok no children, let the original request through */
+               ret = ldb_next_request(ac->module, ac->req);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+
+               /* free our own context we are not going to be called back */
+               talloc_free(ac);
        }
-       talloc_free(ares);
        return LDB_SUCCESS;
 }
 
-/* rename */
 static int subtree_delete(struct ldb_module *module, struct ldb_request *req)
 {
-       const char *attrs[] = { NULL };
-       struct ldb_request *new_req;
+       static const char * const attrs[2] = { "distinguishedName", NULL };
+       struct ldb_request *search_req;
        struct subtree_delete_context *ac;
        int ret;
        if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
@@ -142,119 +127,28 @@ static int subtree_delete(struct ldb_module *module, struct ldb_request *req)
           - return an error if there are any
        */
 
-       ac = subtree_delete_init_handle(req, module);
+       ac = subdel_ctx_init(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_build_search_req(&new_req, module->ldb, req,
-                                  req->op.del.dn, 
-                                  LDB_SCOPE_SUBTREE,
-                                  "(objectClass=*)",
-                                  attrs,
+       /* we do not really need to find all descendents,
+        * if there is even one single direct child, that's
+        * enough to bail out */
+       ret = ldb_build_search_req(&search_req, module->ldb, ac,
+                                  req->op.del.dn, LDB_SCOPE_ONELEVEL,
+                                  "(objectClass=*)", attrs,
                                   req->controls,
-                                  ac, 
-                                  subtree_delete_search_callback);
-
+                                  ac, subtree_delete_search_callback,
+                                  req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-
-       if (ret != LDB_SUCCESS) {
-               return ret;
-       }
-
-       ac->search_req = new_req;
-       if (req == NULL) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       return ldb_next_request(module, new_req);
-}
-
-
-static int subtree_delete_wait_none(struct ldb_handle *handle) {
-       struct subtree_delete_context *ac;
-       int ret = LDB_ERR_OPERATIONS_ERROR;
-       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 subtree_delete_context);
-
-       switch (ac->step) {
-       case SD_SEARCH:
-               ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
-
-               if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->search_req->handle->status != LDB_SUCCESS
-                       && ac->search_req->handle->status != LDB_ERR_NO_SUCH_OBJECT) {
-                       handle->status = ac->search_req->handle->status;
-                       goto done;
-               }
-
-               return subtree_delete_check_for_children(ac);
-
-       case SD_DO_DEL:
-               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;
-               }
-
-               break;
-       }
-done:
-       handle->state = LDB_ASYNC_DONE;
-       return ret;
-}
-
-static int subtree_delete_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = subtree_delete_wait_none(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-       }
-
-       return handle->status;
-}
-
-static int subtree_delete_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return subtree_delete_wait_all(handle);
-       } else {
-               return subtree_delete_wait_none(handle);
-       }
+       return ldb_next_request(module, search_req);
 }
 
 const struct ldb_module_ops ldb_subtree_delete_module_ops = {
        .name              = "subtree_delete",
        .del               = subtree_delete,
-       .wait              = subtree_delete_wait,
 };
index fd1388d4164bdb4369894fc8501fd7fc6431d473..d3ceb8ad9728753977d84d6764be7747a0ca4b5c 100644 (file)
 
 #include "ldb_includes.h"
 
+struct subren_msg_store {
+       struct subren_msg_store *next;
+       struct ldb_dn *olddn;
+       struct ldb_dn *newdn;
+};
+
 struct subtree_rename_context {
        struct ldb_module *module;
-       struct ldb_handle *handle;
-       struct ldb_request *orig_req;
-
-       struct ldb_request **down_req;
-       int num_requests;
-       int finished_requests;
+       struct ldb_request *req;
 
-       int num_children;
+       struct subren_msg_store *list;
+       struct subren_msg_store *current;
 };
 
-static struct subtree_rename_context *subtree_rename_init_handle(struct ldb_request *req, 
-                                                                struct ldb_module *module)
+static struct subtree_rename_context *subren_ctx_init(struct ldb_module *module,
+                                        struct ldb_request *req)
 {
        struct subtree_rename_context *ac;
-       struct ldb_handle *h;
 
-       h = talloc_zero(req, struct ldb_handle);
-       if (h == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
+       ac = talloc_zero(req, struct subtree_rename_context);
+       if (ac == NULL) {
+               ldb_oom(module->ldb);
                return NULL;
        }
 
-       h->module = module;
+       ac->module = module;
+       ac->req = req;
 
-       ac = talloc_zero(h, struct subtree_rename_context);
-       if (ac == NULL) {
-               ldb_set_errstring(module->ldb, "Out of Memory");
-               talloc_free(h);
-               return NULL;
+       return ac;
+}
+
+static int subtree_rename_next_request(struct subtree_rename_context *ac);
+
+static int subtree_rename_callback(struct ldb_request *req,
+                                  struct ldb_reply *ares)
+{
+       struct subtree_rename_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct subtree_rename_context);
+
+       if (!ares) {
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
        }
 
-       h->private_data = ac;
+       if (ares->error != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, ares->error);
+       }
 
-       ac->module = module;
-       ac->handle = h;
-       ac->orig_req = req;
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Invalid reply type!\n");
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
 
-       req->handle = h;
+       if (ac->current == NULL) {
+               /* this was the last one */
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
+       }
 
-       return ac;
-}
+       ret = subtree_rename_next_request(ac);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
 
+       talloc_free(ares);
+       return LDB_SUCCESS;
+}
 
-static int subtree_rename_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+static int subtree_rename_next_request(struct subtree_rename_context *ac)
 {
        struct ldb_request *req;
-       struct subtree_rename_context *ac = talloc_get_type(context, struct subtree_rename_context);
-       TALLOC_CTX *mem_ctx = talloc_new(ac);
-    
-       if (!mem_ctx) {
-               ldb_oom(ac->module->ldb);
+       int ret;
+
+       if (ac->current == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       /* OK, we have one of *many* search results here:
-
-          We should also get the entry we tried to rename.  This
-          callback handles this and everything below it.
-        */
-
-       /* Only entries are interesting, and we handle the case of the parent seperatly */
-       if (ares->type == LDB_REPLY_ENTRY
-           && ldb_dn_compare(ares->message->dn, ac->orig_req->op.rename.olddn) != 0) {
-               /* And it is an actual entry: now create a rename from it */
-               int ret;
-
-               struct ldb_dn *newdn = ldb_dn_copy(mem_ctx, ares->message->dn);
-               if (!newdn) {
-                       ldb_oom(ac->module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
+
+       ret = ldb_build_rename_req(&req, ac->module->ldb, ac->current,
+                                  ac->current->olddn,
+                                  ac->current->newdn,
+                                  ac->req->controls,
+                                  ac, subtree_rename_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ac->current = ac->current->next;
+
+       return ldb_next_request(ac->module, req);
+}
+
+static int subtree_rename_search_callback(struct ldb_request *req,
+                                         struct ldb_reply *ares)
+{
+       struct subren_msg_store *store;
+       struct subtree_rename_context *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct subtree_rename_context);
+
+       if (!ares || !ac->current) {
+               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);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               if (ldb_dn_compare(ares->message->dn, ac->list->olddn) == 0) {
+                       /* this was already stored by the
+                        * subtree_rename_search() */
+                       talloc_free(ares);
+                       return LDB_SUCCESS;
                }
-                       
-               ldb_dn_remove_base_components(newdn, ldb_dn_get_comp_num(ac->orig_req->op.rename.olddn));
 
-               if (!ldb_dn_add_base(newdn, ac->orig_req->op.rename.newdn)) {
-                       ldb_oom(ac->module->ldb);
-                       return LDB_ERR_OPERATIONS_ERROR;
+               store = talloc_zero(ac, struct subren_msg_store);
+               if (store == NULL) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
+               ac->current->next = store;
+               ac->current = store;
 
-               ret = ldb_build_rename_req(&req, ldb, mem_ctx,
-                                          ares->message->dn,
-                                          newdn,
-                                          NULL,
-                                          NULL,
-                                          NULL);
-               
-               if (ret != LDB_SUCCESS) {
-                       return ret;
+               /* the first list element contains the base for the rename */
+               store->olddn = talloc_steal(store, ares->message->dn);
+               store->newdn = ldb_dn_copy(store, store->olddn);
+
+               if ( ! ldb_dn_remove_base_components(store->newdn,
+                               ldb_dn_get_comp_num(ac->list->olddn))) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
 
-               ret = ldb_set_timeout_from_prev_req(ldb, ac->orig_req, req);
-               
-               if (ret != LDB_SUCCESS) {
-                       return ret;
+               if ( ! ldb_dn_add_base(store->newdn, ac->list->newdn)) {
+                       return ldb_module_done(ac->req, NULL, NULL,
+                                               LDB_ERR_OPERATIONS_ERROR);
                }
 
-               talloc_steal(req, newdn);
+               break;
 
-               talloc_steal(req, ares->message->dn);
-               
-               talloc_free(ares);
-               
-       } else if (ares->type == LDB_REPLY_DONE) {
-               req = talloc(mem_ctx, struct ldb_request);
-               *req = *ac->orig_req;
-               talloc_free(ares);
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               break;
 
-       } else {
-               talloc_free(ares);
-               return LDB_SUCCESS;
-       }
-       
-       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                     struct ldb_request *, ac->num_requests + 1);
-       if (!ac->down_req) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
+       case LDB_REPLY_DONE:
+
+               /* rewind ac->current */
+               ac->current = ac->list;
+
+               /* All dns set up, start with the first one */
+               ret = subtree_rename_next_request(ac);
+
+               if (ret != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, NULL, NULL, ret);
+               }
+               break;
        }
-       ac->down_req[ac->num_requests] = req;
-       ac->num_requests++;
-       
-       return ldb_next_request(ac->module, req);
-       
+
+       talloc_free(ares);
+       return LDB_SUCCESS;
 }
 
 /* rename */
 static int subtree_rename(struct ldb_module *module, struct ldb_request *req)
 {
-       const char *attrs[] = { NULL };
-       struct ldb_request *new_req;
+       static const char *attrs[2] = { "distinguishedName", NULL };
+       struct ldb_request *search_req;
        struct subtree_rename_context *ac;
        int ret;
        if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
@@ -176,110 +221,37 @@ static int subtree_rename(struct ldb_module *module, struct ldb_request *req)
           - Regain our sainity 
        */
 
-       ac = subtree_rename_init_handle(req, module);
+       ac = subren_ctx_init(module, req);
        if (!ac) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_build_search_req(&new_req, module->ldb, req,
+       /* add this entry as the first to do */
+       ac->current = talloc_zero(ac, struct subren_msg_store);
+       if (ac->current == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->current->olddn = req->op.rename.olddn;
+       ac->current->newdn = req->op.rename.newdn;
+       ac->list = ac->current;
+
+       ret = ldb_build_search_req(&search_req, module->ldb, ac,
                                   req->op.rename.olddn, 
                                   LDB_SCOPE_SUBTREE,
                                   "(objectClass=*)",
                                   attrs,
-                                  req->controls,
+                                  NULL,
                                   ac, 
-                                  subtree_rename_search_callback);
-
-       if (ret != LDB_SUCCESS) {
-               return ret;
-       }
-
-       ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
-       
+                                  subtree_rename_search_callback,
+                                  req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
 
-       ac->down_req = talloc_realloc(ac, ac->down_req, 
-                                       struct ldb_request *, ac->num_requests + 1);
-       if (!ac->down_req) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       ac->down_req[ac->num_requests] = new_req;
-       if (req == NULL) {
-               ldb_oom(ac->module->ldb);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-       ac->num_requests++;
-       return ldb_next_request(module, new_req);
-}
-
-
-static int subtree_rename_wait_none(struct ldb_handle *handle) {
-       struct subtree_rename_context *ac;
-       int i, ret = LDB_ERR_OPERATIONS_ERROR;
-       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 subtree_rename_context);
-
-       for (i=0; i < ac->num_requests; i++) {
-               ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
-               
-               if (ret != LDB_SUCCESS) {
-                       handle->status = ret;
-                       goto done;
-               }
-               if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
-                       handle->status = ac->down_req[i]->handle->status;
-                       goto done;
-               }
-               
-               if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
-                       return LDB_SUCCESS;
-               }
-       }
-
-done:
-       handle->state = LDB_ASYNC_DONE;
-       return ret;
-
-}
-
-static int subtree_rename_wait_all(struct ldb_handle *handle) {
-
-       int ret;
-
-       while (handle->state != LDB_ASYNC_DONE) {
-               ret = subtree_rename_wait_none(handle);
-               if (ret != LDB_SUCCESS) {
-                       return ret;
-               }
-       }
-
-       return handle->status;
-}
-
-static int subtree_rename_wait(struct ldb_handle *handle, enum ldb_wait_type type)
-{
-       if (type == LDB_WAIT_ALL) {
-               return subtree_rename_wait_all(handle);
-       } else {
-               return subtree_rename_wait_none(handle);
-       }
+       return ldb_next_request(module, search_req);
 }
 
 const struct ldb_module_ops ldb_subtree_rename_module_ops = {
        .name              = "subtree_rename",
        .rename            = subtree_rename,
-       .wait              = subtree_rename_wait,
 };
index 68973124ebe21f96d077ac449783df179200d60f..2c6cb102d978b0a56872d7d4ff600ae419bdc8ac 100644 (file)
@@ -43,6 +43,39 @@ struct update_kt_private {
        struct dn_list *changed_dns;
 };
 
+struct update_kt_ctx {
+       struct ldb_module *module;
+       struct ldb_request *req;
+
+       struct ldb_dn *dn;
+       bool delete;
+
+       struct ldb_reply *op_reply;
+       bool found;
+};
+
+struct update_kt_ctx *update_kt_ctx_init(struct ldb_module *module,
+                                        struct ldb_request *req)
+{
+       struct update_kt_ctx *ac;
+
+       ac = talloc_zero(req, struct update_kt_ctx);
+       if (ac == NULL) {
+               ldb_oom(module->ldb);
+               return NULL;
+       }
+
+       ac->module = module;
+       ac->req = req;
+
+       return ac;
+}
+
+/* FIXME: too many semi-async searches here for my taste, direct and indirect as
+ * cli_credentials_set_secrets() performs a sync ldb search.
+ * Just hope we are lucky and nothing breaks (using the tdb backend masks a lot
+ * of async issues). -SSS
+ */
 static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool delete) {
        struct update_kt_private *data = talloc_get_type(module->private_data, struct update_kt_private);
        struct dn_list *item;
@@ -80,7 +113,7 @@ static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool delet
                ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-       
+
        item->creds = cli_credentials_init(item);
        if (!item->creds) {
                DEBUG(1, ("cli_credentials_init failed!"));
@@ -90,7 +123,7 @@ static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool delet
        }
 
        cli_credentials_set_conf(item->creds, ldb_get_opaque(module->ldb, "loadparm"));
-       status = cli_credentials_set_secrets(item->creds, ldb_get_opaque(module->ldb, "EventContext"), ldb_get_opaque(module->ldb, "loadparm"), module->ldb, NULL, filter);
+       status = cli_credentials_set_secrets(item->creds, ldb_get_event_context(module->ldb), ldb_get_opaque(module->ldb, "loadparm"), module->ldb, NULL, filter);
        talloc_free(filter);
        if (NT_STATUS_IS_OK(status)) {
                if (delete) {
@@ -105,60 +138,237 @@ static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool delet
        return LDB_SUCCESS;
 }
 
+static int ukt_search_modified(struct update_kt_ctx *ac);
+
+static int update_kt_op_callback(struct ldb_request *req,
+                                struct ldb_reply *ares)
+{
+       struct update_kt_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct update_kt_ctx);
+
+       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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               ldb_set_errstring(ac->module->ldb, "Invalid request type!\n");
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       if (ac->delete) {
+               return ldb_module_done(ac->req, ares->controls,
+                                       ares->response, LDB_SUCCESS);
+       }
+
+       ac->op_reply = talloc_steal(ac, ares);
+
+       ret = ukt_search_modified(ac);
+       if (ret != LDB_SUCCESS) {
+               return ldb_module_done(ac->req, NULL, NULL, ret);
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int ukt_del_op(struct update_kt_ctx *ac)
+{
+       struct ldb_request *down_req;
+       int ret;
+
+       ret = ldb_build_del_req(&down_req, ac->module->ldb, ac,
+                               ac->dn,
+                               ac->req->controls,
+                               ac, update_kt_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       return ldb_next_request(ac->module, down_req);
+}
+
+static int ukt_search_modified_callback(struct ldb_request *req,
+                                       struct ldb_reply *ares)
+{
+       struct update_kt_ctx *ac;
+       int ret;
+
+       ac = talloc_get_type(req->context, struct update_kt_ctx);
+
+       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);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+
+               ac->found = true;
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               /* ignore */
+               break;
+
+       case LDB_REPLY_DONE:
+
+               if (ac->found) {
+                       /* do the dirty sync job here :/ */
+                       ret = add_modified(ac->module, ac->dn, ac->delete);
+               }
+
+               if (ac->delete) {
+                       ret = ukt_del_op(ac);
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req,
+                                                       NULL, NULL, ret);
+                       }
+                       break;
+               }
+
+               return ldb_module_done(ac->req, ac->op_reply->controls,
+                                       ac->op_reply->response, LDB_SUCCESS);
+       }
+
+       talloc_free(ares);
+       return LDB_SUCCESS;
+}
+
+static int ukt_search_modified(struct update_kt_ctx *ac)
+{
+       static const char * const attrs[] = { "distinguishedName", NULL };
+       struct ldb_request *search_req;
+       int ret;
+
+       ret = ldb_build_search_req(&search_req, ac->module->ldb, ac,
+                                  ac->dn, LDB_SCOPE_BASE,
+                                  "(&(objectClass=kerberosSecret)"
+                                    "(privateKeytab=*))", attrs,
+                                  NULL,
+                                  ac, ukt_search_modified_callback,
+                                  ac->req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       return ldb_next_request(ac->module, search_req);
+}
+
+
 /* add */
 static int update_kt_add(struct ldb_module *module, struct ldb_request *req)
 {
+       struct update_kt_ctx *ac;
+       struct ldb_request *down_req;
        int ret;
-       ret = ldb_next_request(module, req);
+
+       ac = update_kt_ctx_init(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->dn = req->op.add.message->dn;
+
+       ret = ldb_build_add_req(&down_req, module->ldb, ac,
+                               req->op.add.message,
+                               req->controls,
+                               ac, update_kt_op_callback,
+                               req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-       return add_modified(module, req->op.add.message->dn, false);
+
+       return ldb_next_request(module, down_req);
 }
 
 /* modify */
 static int update_kt_modify(struct ldb_module *module, struct ldb_request *req)
 {
+       struct update_kt_ctx *ac;
+       struct ldb_request *down_req;
        int ret;
-       ret = ldb_next_request(module, req);
+
+       ac = update_kt_ctx_init(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->dn = req->op.mod.message->dn;
+
+       ret = ldb_build_mod_req(&down_req, module->ldb, ac,
+                               req->op.mod.message,
+                               req->controls,
+                               ac, update_kt_op_callback,
+                               req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-       return add_modified(module, req->op.mod.message->dn, false);
+
+       return ldb_next_request(module, down_req);
 }
 
 /* delete */
 static int update_kt_delete(struct ldb_module *module, struct ldb_request *req)
 {
-       int ret;
-       /* Before we delete it, record the details */
-       ret = add_modified(module, req->op.del.dn, true);
-       if (ret != LDB_SUCCESS) {
-               return ret;
+       struct update_kt_ctx *ac;
+
+       ac = update_kt_ctx_init(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-       return ldb_next_request(module, req);
+
+       ac->dn = req->op.del.dn;
+       ac->delete = true;
+
+       return ukt_search_modified(ac);
 }
 
 /* rename */
 static int update_kt_rename(struct ldb_module *module, struct ldb_request *req)
 {
+       struct update_kt_ctx *ac;
+       struct ldb_request *down_req;
        int ret;
-       ret = ldb_next_request(module, req);
+
+       ac = update_kt_ctx_init(module, req);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->dn = req->op.rename.newdn;
+
+       ret = ldb_build_rename_req(&down_req, module->ldb, ac,
+                               req->op.rename.olddn,
+                               req->op.rename.newdn,
+                               req->controls,
+                               ac, update_kt_op_callback,
+                               req);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
-       return add_modified(module, req->op.rename.newdn, false);
+
+       return ldb_next_request(module, down_req);
 }
 
 /* end a transaction */
 static int update_kt_end_trans(struct ldb_module *module)
 {
        struct update_kt_private *data = talloc_get_type(module->private_data, struct update_kt_private);
-       
        struct dn_list *p;
+
        for (p=data->changed_dns; p; p = p->next) {
                int kret;
-               kret = cli_credentials_update_keytab(p->creds, ldb_get_opaque(module->ldb, "EventContext"), ldb_get_opaque(module->ldb, "loadparm"));
+               kret = cli_credentials_update_keytab(p->creds, ldb_get_event_context(module->ldb), ldb_get_opaque(module->ldb, "loadparm"));
                if (kret != 0) {
                        talloc_free(data->changed_dns);
                        data->changed_dns = NULL;
@@ -169,6 +379,7 @@ static int update_kt_end_trans(struct ldb_module *module)
 
        talloc_free(data->changed_dns);
        data->changed_dns = NULL;
+
        return ldb_next_end_trans(module);
 }
 
@@ -176,7 +387,7 @@ static int update_kt_end_trans(struct ldb_module *module)
 static int update_kt_del_trans(struct ldb_module *module)
 {
        struct update_kt_private *data = talloc_get_type(module->private_data, struct update_kt_private);
-       
+
        talloc_free(data->changed_dns);
        data->changed_dns = NULL;