r9391: Convert all the code to use struct ldb_dn to ohandle ldap like distinguished...
[ambi/samba-autobuild/.git] / source4 / lib / ldb / modules / schema.c
index 114c7f44aa7fb1f28123e1a4c1faeeb9fe192aad..baf038de0cd93f75c6bedf020021798fc72333c9 100644 (file)
@@ -1,7 +1,7 @@
 /* 
    ldb database library
 
-   Copyright (C) Simo Sorce  2004
+   Copyright (C) Simo Sorce  2004-2005
 
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
 #include "ldb/include/ldb.h"
 #include "ldb/include/ldb_private.h"
 
-struct attribute_syntax {
-       const char *name;
-       const char *syntax_id;
-};
-
-static struct attribute_syntax attrsyn[] = {
-               { "Object(DS-DN)", "2.5.5.1"},
-               { "String(Object-Identifier)", "2.5.5.2"},
-               { "", "2.5.5.3"},
-               { "String(Teletex)", "2.5.5.4"},
-               { "String(IA5)", "2.5.5.5"}, /* Also String(Printable) */
-               { "String(Numeric)", "2.5.5.6"},
-               { "Object(DN-Binary)", "2.5.5.7"}, /* Also Object(OR-Name) */
-               { "Boolean", "2.5.5.8"},
-               { "Integer", "2.5.5.9"}, /* Also Enumeration (3 types ?) ... */
-               { "String(Octet)", "2.5.5.10"}, /* Also Object(Replica-Link) */
-               { "String(UTC-Time)", "2.5.5.11"}, /* Also String(Generalized-Time) */
-               { "String(Unicode)", "2.5.5.12"},
-               { "Object(Presentation-Address)", "2.5.5.13"},
-               { "Object(DN-String)", "2.5.5.14"}, /* Also Object(Access-Point) */
-               { "String(NT-Sec-Desc))", "2.5.5.15"},
-               { "LargeInteger", "2.5.5.16"}, /* Also Interval ... */
-               { "String(Sid)", "2.5.5.17"}
-       };
-
-#define SCHEMA_TALLOC_CHECK(root, mem, ret) do { if (!mem) { talloc_free(root); return ret;} } while(0);
-
 #define SCHEMA_FLAG_RESET      0
-#define SCHEMA_FLAG_MOD_MASK   0x03
-#define SCHEMA_FLAG_MOD_ADD    0x01
-#define SCHEMA_FLAG_MOD_REPLACE        0x02
-#define SCHEMA_FLAG_MOD_DELETE 0x03
-#define SCHEMA_FLAG_AUXCLASS   0x10
-#define SCHEMA_FLAG_CHECKED    0x20
-
+#define SCHEMA_FLAG_MOD_MASK   0x003
+#define SCHEMA_FLAG_MOD_ADD    0x001
+#define SCHEMA_FLAG_MOD_REPLACE        0x002
+#define SCHEMA_FLAG_MOD_DELETE 0x003
+#define SCHEMA_FLAG_AUXILIARY  0x010
+#define SCHEMA_FLAG_ABSTRACT   0x020
+#define SCHEMA_FLAG_STRUCTURAL 0x040
+#define SCHEMA_FLAG_CHECKED    0x100
+
+
+/* TODO: check attributes syntaxes
+        check there's only one structrual class (or a chain of structural classes)
+*/
 
 struct private_data {
-       struct ldb_context *schema_db;
        const char *error_string;
 };
 
-struct attribute_list {
+struct schema_attribute {
        int flags;
        char *name;
 };
 
+struct schema_attribute_list {
+       struct schema_attribute *attr;
+       int num;
+};
+
 struct schema_structures {
-       struct attribute_list *check_list;
-       struct attribute_list *objectclass_list;
-       struct attribute_list *must;
-       struct attribute_list *may;
-       int check_list_num;
-       int objectclass_list_num;
-       int must_num;
-       int may_num;
+       struct schema_attribute_list entry_attrs;
+       struct schema_attribute_list objectclasses;
+       struct schema_attribute_list required_attrs;
+       struct schema_attribute_list optional_attrs;
 };
 
-/* This function embedds the knowledge of aliased names.
-   Currently it handles only dn vs distinguishedNAme as a special case as AD
-   only have this special alias case, in future we should read the schema
-   to find out which names have an alias and check for them */
-static int schema_attr_cmp(const char *attr1, const char *attr2)
+static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
 {
-       int ret;
-
-       ret = ldb_attr_cmp(attr1, attr2);
-       if (ret != 0) {
-               if (tolower(*attr1) == 'd' && tolower(*attr2) == 'd') {
-                       if ((ldb_attr_cmp("dn", attr1) == 0) &&
-                           (ldb_attr_cmp("distinguishedName", attr2) == 0)) {
-                               return 0;
-                       }
-                       if ((ldb_attr_cmp("dn", attr2) == 0) &&
-                           (ldb_attr_cmp("distinguishedName", attr1) == 0)) {
-                               return 0;
-                       }
+       unsigned int i;
+       for (i = 0; i < list->num; i++) {
+               if (ldb_attr_cmp(list->attr[i].name, attr_name) == 0) {
+                       return &(list->attr[i]);
                }
        }
-       return ret;
+       return NULL;
 }
 
-static int get_object_objectclasses(struct ldb_context *ldb, const char *dn, struct schema_structures *schema_struct)
+/* get all the attributes and objectclasses found in msg and put them in schema_structure
+   attributes go in the entry_attrs structure for later checking
+   objectclasses go in the objectclasses structure */
+static int get_msg_attributes(struct schema_structures *ss, const struct ldb_message *msg, int flag_mask)
 {
-       char *filter = talloc_asprintf(schema_struct, "dn=%s", dn);
-       const char *attrs[] = {"objectClass", NULL};
-       struct ldb_message **srch;
-       int i, j, ret;
-
-       schema_struct->objectclass_list = NULL;
-       schema_struct->objectclass_list_num = 0;
-       ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, filter, attrs, &srch);
-       if (ret == 1) {
-               for (i = 0; i < (*srch)->num_elements; i++) {
-                       schema_struct->objectclass_list_num = (*srch)->elements[i].num_values;
-                       schema_struct->objectclass_list = talloc_array(schema_struct,
-                                                                        struct attribute_list,
-                                                                        schema_struct->objectclass_list_num);
-                       if (schema_struct->objectclass_list == 0) {
-                               ldb_search_free(ldb, srch);
+       int i, j, anum, cnum;
+
+       ss->entry_attrs.attr = talloc_realloc(ss, ss->entry_attrs.attr,
+                                             struct schema_attribute,
+                                             ss->entry_attrs.num + msg->num_elements);
+       if (ss->entry_attrs.attr == NULL) {
+               return -1;
+       }
+
+       for (i = 0, anum = ss->entry_attrs.num; i < msg->num_elements; i++) {
+
+               if (ldb_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
+
+                       ss->objectclasses.attr = talloc_realloc(ss, ss->objectclasses.attr,
+                                                               struct schema_attribute,
+                                                               ss->objectclasses.num + msg->elements[i].num_values);
+                       if (ss->objectclasses.attr == NULL) {
                                return -1;
                        }
-                       for (j = 0; j < schema_struct->objectclass_list_num; j++) {
-                               schema_struct->objectclass_list[j].name = talloc_strndup(schema_struct->objectclass_list,
-                                                                                        (*srch)->elements[i].values[j].data,
-                                                                                        (*srch)->elements[i].values[j].length);
-                               if (schema_struct->objectclass_list[j].name == 0) {
-                                       ldb_search_free(ldb, srch);
-                                       return -1;
-                               }
-                               schema_struct->objectclass_list[j].flags = SCHEMA_FLAG_RESET;
+
+                       for (j = 0, cnum = ss->objectclasses.num; j < msg->elements[i].num_values; j++) {
+                               ss->objectclasses.attr[cnum+j].name = msg->elements[i].values[j].data;
+                               ss->objectclasses.attr[cnum+j].flags = msg->elements[i].flags & flag_mask;
                        }
+                       ss->objectclasses.num += msg->elements[i].num_values;
+               }
+
+               /* TODO: Check for proper attribute Syntax ! */
+
+               ss->entry_attrs.attr[anum+i].flags = msg->elements[i].flags & flag_mask;
+               ss->entry_attrs.attr[anum+i].name = talloc_reference(ss->entry_attrs.attr,
+                                                           msg->elements[i].name);
+               if (ss->entry_attrs.attr[anum+i].name == NULL) {
+                       return -1;
                }
-               ldb_search_free(ldb, srch);
-       } else {
-               ldb_search_free(ldb, srch);
-               return -1;
        }
+       ss->entry_attrs.num += msg->num_elements;
 
        return 0;
 }
 
-static int get_check_list(struct ldb_module *module, struct schema_structures *schema_struct, const struct ldb_message *msg)
+static int get_entry_attributes(struct ldb_context *ldb, const struct ldb_dn *dn, struct schema_structures *ss)
 {
-       int i, j, k;
-
-       schema_struct->objectclass_list = NULL;
-       schema_struct->objectclass_list_num = 0;
-       schema_struct->check_list_num = msg->num_elements;
-       schema_struct->check_list = talloc_array(schema_struct,
-                                                  struct attribute_list,
-                                                  schema_struct->check_list_num);
-       if (schema_struct->check_list == 0) {
-               return -1;
+       struct ldb_message **srch;
+       int ret;
+
+       ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &srch);
+       if (ret != 1) {
+               return ret;
        }
-       for (i = 0, j = 0; i < msg->num_elements; i++) {
-               if (schema_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
-                       schema_struct->objectclass_list_num = msg->elements[i].num_values;
-                       schema_struct->objectclass_list = talloc_array(schema_struct,
-                                                                        struct attribute_list,
-                                                                        schema_struct->objectclass_list_num);
-                       if (schema_struct->objectclass_list == 0) {
-                               return -1;
-                       }
-                       for (k = 0; k < schema_struct->objectclass_list_num; k++) {
-                               schema_struct->objectclass_list[k].name = talloc_strndup(schema_struct->objectclass_list,
-                                                                                        msg->elements[i].values[k].data,
-                                                                                        msg->elements[i].values[k].length);
-                               if (schema_struct->objectclass_list[k].name == 0) {
-                                       return -1;
-                               }
-                               schema_struct->objectclass_list[k].flags = msg->elements[i].flags;
-                       }
-               }
+       talloc_steal(ss, srch);
 
-               schema_struct->check_list[j].flags = msg->elements[i].flags;
-               schema_struct->check_list[j].name = talloc_strdup(schema_struct->check_list,
-                                                                 msg->elements[i].name);
-               if (schema_struct->check_list[j].name == 0) {
-                       return -1;
-               }
-               j++;
+       /* set flags to 0 as flags on search have undefined values */
+       ret = get_msg_attributes(ss, *srch, 0);
+       if (ret != 0) {
+               talloc_free(srch);
+               return ret;
        }
 
        return 0;
 }
 
-static int add_attribute_uniq(struct attribute_list **list, int *list_num, int flags, struct ldb_message_element *el, void *mem_ctx)
+/* add all attributes in el avoiding duplicates in schema_attribute_list */
+static int add_attribute_uniq(void *mem_ctx, struct schema_attribute_list *list, int flags, struct ldb_message_element *el)
 {
        int i, j, vals;
 
        vals = el->num_values;
-       *list = talloc_realloc(mem_ctx, *list, struct attribute_list, *list_num + vals);
-       if (list == 0) {
+       list->attr = talloc_realloc(mem_ctx, list->attr, struct schema_attribute, list->num + vals);
+       if (list->attr == NULL) {
                return -1;
        }
        for (i = 0, j = 0; i < vals; i++) {
                int c, found, len;
 
                found = 0;
-               for (c = 0; c < *list_num; c++) {
-                       len = strlen((*list)[c].name);
+               for (c = 0; c < list->num; c++) {
+                       len = strlen(list->attr[c].name);
                        if (len == el->values[i].length) {
-                               if (strncasecmp((*list)[c].name, el->values[i].data, len) == 0) {
+                               if (ldb_attr_cmp(list->attr[c].name, el->values[i].data) == 0) {
                                        found = 1;
                                        break;
                                }
                        }
                }
                if (!found) {
-                       (*list)[j + *list_num].name = talloc_strndup(*list, el->values[i].data, el->values[i].length);
-                       if ((*list)[j + *list_num].name == 0) {
-                               return -1;
-                       }
-                       (*list)[j + *list_num].flags = flags;
+                       list->attr[j + list->num].name = el->values[i].data;
+                       list->attr[j + list->num].flags = flags;
                        j++;
                }
        }
-       *list_num += j;
+       list->num += j;
 
        return 0;
 }
 
-static int get_attr_list_recursive(struct ldb_module *module, struct ldb_context *ldb, struct schema_structures *schema_struct)
+
+/* we need to get all attributes referenced by the entry objectclasses,
+   recursively get parent objectlasses attributes */
+static int get_attr_list_recursive(struct ldb_module *module, struct schema_structures *schema_struct)
 {
        struct private_data *data = (struct private_data *)module->private_data;
        struct ldb_message **srch;
        int i, j;
        int ret;
 
-       schema_struct->must = NULL;
-       schema_struct->may = NULL;
-       schema_struct->must_num = 0;
-       schema_struct->may_num = 0;
-       for (i = 0; i < schema_struct->objectclass_list_num; i++) {
+       for (i = 0; i < schema_struct->objectclasses.num; i++) {
                char *filter;
 
-               if ((schema_struct->objectclass_list[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
+               if ((schema_struct->objectclasses.attr[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
                        continue;
                }
-               filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclass_list[i].name);
-               SCHEMA_TALLOC_CHECK(schema_struct, filter, -1);
-               ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
-               if (ret == 0) {
-                       int ok;
-
-                       ok = 0;
-                       /* suppose auxiliary classeschema_struct are not required */
-                       if (schema_struct->objectclass_list[i].flags & SCHEMA_FLAG_AUXCLASS) {
-                               int d;
-                               ok = 1;
-                               schema_struct->objectclass_list_num -= 1;
-                               for (d = i; d < schema_struct->objectclass_list_num; d++) {
-                                       schema_struct->objectclass_list[d] = schema_struct->objectclass_list[d + 1];
-                               }
-                               i -= 1;
-                       }
-                       if (!ok) {
-                               /* Schema Violation: Object Class Description Not Found */
-                               data->error_string = "ObjectClass not found";
-                               return -1;
-                       }
-                       continue;
-               } else {
-                       if (ret < 0) {
-                               /* Schema DB Error: Error occurred retrieving Object Class Description */
-                               data->error_string = "Internal error. Error retrieving schema objectclass";
-                               return -1;
-                       }
-                       if (ret > 1) {
-                               /* Schema DB Error: Too Many Records */
-                               data->error_string = "Internal error. Too many records searching for schema objectclass";
-                               return -1;
-                       }
+               filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclasses.attr[i].name);
+               if (filter == NULL) {
+                       return -1;
+               }
+
+               ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
+               if (ret != 1) {
+                       return ret;
+               }
+               talloc_steal(schema_struct, srch);
+
+               if (ret <= 0) {
+                       /* Schema DB Error: Error occurred retrieving Object Class Description */
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Error retrieving Objectclass %s.\n", schema_struct->objectclasses.attr[i].name);
+                       data->error_string = "Internal error. Error retrieving schema objectclass";
+                       return -1;
+               }
+               if (ret > 1) {
+                       /* Schema DB Error: Too Many Records */
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Too many records found retrieving Objectclass %s.\n", schema_struct->objectclasses.attr[i].name);
+                       data->error_string = "Internal error. Too many records searching for schema objectclass";
+                       return -1;
                }
 
                /* Add inherited classes eliminating duplicates */
-               /* fill in kust and may attribute lists */
+               /* fill in required_attrs and optional_attrs attribute lists */
                for (j = 0; j < (*srch)->num_elements; j++) {
                        int is_aux, is_class;
 
                        is_aux = 0;
                        is_class = 0;
-                       if (schema_attr_cmp((*srch)->elements[j].name, "systemAuxiliaryclass") == 0) {
-                               is_aux = SCHEMA_FLAG_AUXCLASS;
+                       if (ldb_attr_cmp((*srch)->elements[j].name, "systemAuxiliaryclass") == 0) {
+                               is_aux = SCHEMA_FLAG_AUXILIARY;
+                               is_class = 1;
+                       }
+                       if (ldb_attr_cmp((*srch)->elements[j].name, "auxiliaryClass") == 0) {
+                               is_aux = SCHEMA_FLAG_AUXILIARY;
                                is_class = 1;
                        }
-                       if (schema_attr_cmp((*srch)->elements[j].name, "subClassOf") == 0) {
+                       if (ldb_attr_cmp((*srch)->elements[j].name, "subClassOf") == 0) {
                                is_class = 1;
                        }
 
                        if (is_class) {
-                               if (add_attribute_uniq(&schema_struct->objectclass_list,
-                                                       &schema_struct->objectclass_list_num,
+                               if (add_attribute_uniq(schema_struct,
+                                                       &schema_struct->objectclasses,
                                                        is_aux,
-                                                       &(*srch)->elements[j],
-                                                       schema_struct) != 0) {
+                                                       &(*srch)->elements[j]) != 0) {
                                        return -1;
                                }
                        } else {
 
-                               if (schema_attr_cmp((*srch)->elements[j].name, "mustContain") == 0 ||
-                                       schema_attr_cmp((*srch)->elements[j].name, "SystemMustContain") == 0) {
-                                       if (add_attribute_uniq(&schema_struct->must,
-                                                               &schema_struct->must_num,
+                               if (ldb_attr_cmp((*srch)->elements[j].name, "mustContain") == 0 ||
+                                       ldb_attr_cmp((*srch)->elements[j].name, "SystemMustContain") == 0) {
+                                       if (add_attribute_uniq(schema_struct,
+                                                               &schema_struct->required_attrs,
                                                                SCHEMA_FLAG_RESET,
-                                                               &(*srch)->elements[j],
-                                                               schema_struct) != 0) {
+                                                               &(*srch)->elements[j]) != 0) {
                                                return -1;
                                        }
                                }
 
-                               if (schema_attr_cmp((*srch)->elements[j].name, "mayContain") == 0 ||
-                                   schema_attr_cmp((*srch)->elements[j].name, "SystemMayContain") == 0) {
+                               if (ldb_attr_cmp((*srch)->elements[j].name, "mayContain") == 0 ||
+                                   ldb_attr_cmp((*srch)->elements[j].name, "SystemMayContain") == 0) {
 
-                                       if (add_attribute_uniq(&schema_struct->may,
-                                                               &schema_struct->may_num,
+                                       if (add_attribute_uniq(schema_struct,
+                                                               &schema_struct->optional_attrs,
                                                                SCHEMA_FLAG_RESET,
-                                                               &(*srch)->elements[j],
-                                                               schema_struct) != 0) {
+                                                               &(*srch)->elements[j]) != 0) {
                                                return -1;
                                        }
                                }
                        }
                }
-
-               ldb_search_free(ldb, srch);
        }
 
        return 0;
 }
 
-/* close */
-static int schema_close(struct ldb_module *module)
-{
-       return ldb_next_close(module);
-}
-
 /* search */
-static int schema_search(struct ldb_module *module, const char *base,
+static int schema_search(struct ldb_module *module, const struct ldb_dn *base,
                       enum ldb_scope scope, const char *expression,
                       const char * const *attrs, struct ldb_message ***res)
 {
        return ldb_next_search(module, base, scope, expression, attrs, res); 
 }
 
-/* search_free */
-static int schema_search_free(struct ldb_module *module, struct ldb_message **res)
+static int schema_search_bytree(struct ldb_module *module, const struct ldb_dn *base,
+                               enum ldb_scope scope, struct ldb_parse_tree *tree,
+                               const char * const *attrs, struct ldb_message ***res)
 {
-       return ldb_next_search_free(module, res);
+       return ldb_next_search_bytree(module, base, scope, tree, attrs, res); 
 }
 
 /* add_record */
@@ -372,70 +299,76 @@ static int schema_add_record(struct ldb_module *module, const struct ldb_message
 {
        struct private_data *data = (struct private_data *)module->private_data;
        struct schema_structures *entry_structs;
-       int i, j;
+       unsigned int i;
        int ret;
 
        /* First implementation:
-               Build up a list of must and mays from each objectclass
-               Check all the musts are there and all the other attributes are mays
+               Build up a list of required_attrs and optional_attrs attributes from each objectclass
+               Check all the required_attrs attributes are present and all the other attributes
+               are optional_attrs attributes
                Throw an error in case a check fail
                Free all structures and commit the change
        */
 
-       entry_structs = talloc(module, struct schema_structures);
+       /* do not check on our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_add_record(module, msg);
+       }
+
+       /* TODO: check parent exists */
+
+       entry_structs = talloc_zero(module, struct schema_structures);
        if (!entry_structs) {
                return -1;
        }
 
-       ret = get_check_list(module, entry_structs, msg);
+       ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
        if (ret != 0) {
                talloc_free(entry_structs);
                return ret;
        }
 
-       /* find all other objectclasses recursively */
-       ret = get_attr_list_recursive(module, data->schema_db, entry_structs);
+       ret = get_attr_list_recursive(module, entry_structs);
        if (ret != 0) {
                talloc_free(entry_structs);
                return ret;
        }
 
-       /* now check all musts are present */
-       for (i = 0; i < entry_structs->must_num; i++) {
-               int found;
+       /* now check all required_attrs attributes are present */
+       for (i = 0; i < entry_structs->required_attrs.num; i++) {
+               struct schema_attribute *attr;
 
-               found = 0;
-               for (j = 0; j < entry_structs->check_list_num; j++) {
-                       if (schema_attr_cmp(entry_structs->must[i].name, entry_structs->check_list[j].name) == 0) {
-                               entry_structs->check_list[j].flags = SCHEMA_FLAG_CHECKED;
-                               found = 1;
-                               break;
-                       }
-               }
+               attr = schema_find_attribute(&entry_structs->entry_attrs,
+                                            entry_structs->required_attrs.attr[i].name);
 
-               if ( ! found ) {
-                       data->error_string = "Objectclass violation, a required attribute is mischema_structing";
+               if (attr == NULL) { /* not found */
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                                 "The required_attrs attribute %s is missing.\n",
+                                 entry_structs->required_attrs.attr[i].name);
+
+                       data->error_string = "Objectclass violation, a required attribute is missing";
                        talloc_free(entry_structs);
                        return -1;
                }
+
+               /* mark the attribute as checked */
+               attr->flags = SCHEMA_FLAG_CHECKED;
        }
 
-       /* now check all others atribs are found in mays */
-       for (i = 0; i < entry_structs->check_list_num; i++) {
+       /* now check all others atribs are at least optional_attrs */
+       for (i = 0; i < entry_structs->entry_attrs.num; i++) {
 
-               if (entry_structs->check_list[i].flags != SCHEMA_FLAG_CHECKED) {
-                       int found;
+               if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+                       struct schema_attribute *attr;
 
-                       found = 0;
-                       for (j = 0; j < entry_structs->may_num; j++) {
-                               if (schema_attr_cmp(entry_structs->may[j].name, entry_structs->check_list[i].name) == 0) {
-                                       entry_structs->check_list[i].flags = SCHEMA_FLAG_CHECKED;
-                                       found = 1;
-                                       break;
-                               }
-                       }
+                       attr = schema_find_attribute(&entry_structs->optional_attrs,
+                                                    entry_structs->entry_attrs.attr[i].name);
+
+                       if (attr == NULL) { /* not found */
+                               ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                                         "The attribute %s is not referenced by any objectclass.\n",
+                                         entry_structs->entry_attrs.attr[i].name);
 
-                       if ( ! found ) {
                                data->error_string = "Objectclass violation, an invalid attribute name was found";
                                talloc_free(entry_structs);
                                return -1;
@@ -452,133 +385,97 @@ static int schema_add_record(struct ldb_module *module, const struct ldb_message
 static int schema_modify_record(struct ldb_module *module, const struct ldb_message *msg)
 {
        struct private_data *data = (struct private_data *)module->private_data;
-       struct schema_structures *entry_structs, *modify_structs;
-       int i, j;
+       struct schema_structures *entry_structs;
+       unsigned int i;
        int ret;
 
        /* First implementation:
                Retrieve the ldap entry and get the objectclasses,
                add msg contained objectclasses if any.
-               Build up a list of must and mays from each objectclass
-               Check all musts for the defined objectclass and it's specific
-               inheritance are there.
-               Check all other the attributes are mays or musts.
+               Build up a list of required_attrs and optional_attrs attributes from each objectclass
+               Check all the attributes are optional_attrs or required_attrs.
                Throw an error in case a check fail.
                Free all structures and commit the change.
        */
 
-       /* allocate object structs */
-       entry_structs = talloc(module, struct schema_structures);
-       if (!entry_structs) {
-               return -1;
+       /* do not check on our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_add_record(module, msg);
        }
 
-       /* allocate modification entry structs */
-       modify_structs = talloc(entry_structs, struct schema_structures);
-       if (!modify_structs) {
-               talloc_free(entry_structs);
+       /* allocate object structs */
+       entry_structs = talloc_zero(module, struct schema_structures);
+       if (!entry_structs) {
                return -1;
        }
 
-       /* get list of values to modify */
-       ret = get_check_list(module, modify_structs, msg);
+       /* now search for the stored entry objectclasses and attributes*/
+       ret = get_entry_attributes(module->ldb, msg->dn, entry_structs);
        if (ret != 0) {
                talloc_free(entry_structs);
                return ret;
        }
 
-       /* find all modify objectclasses recursively if any objectclass is being added */
-       ret = get_attr_list_recursive(module, data->schema_db, modify_structs);
-       if (ret != 0) {
-               talloc_free(entry_structs);
-               return ret;
-       }
-
-       /* now search for the original object objectclasses */
-       ret = get_object_objectclasses(module->ldb, msg->dn, entry_structs);
+       /* get list of values to modify */
+       ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
        if (ret != 0) {
                talloc_free(entry_structs);
                return ret;
        }
 
-       /* find all other objectclasses recursively */
-       ret = get_attr_list_recursive(module, data->schema_db, entry_structs);
+       ret = get_attr_list_recursive(module, entry_structs);
        if (ret != 0) {
                talloc_free(entry_structs);
                return ret;
        }
 
-       /* now check all entries are present either as musts or mays of curent objectclasses */
-       /* do not return errors there may be attirbutes defined in new objectclasses */
-       /* just mark them as being proved valid attribs */
-       for (i = 0; i < modify_structs->check_list_num; i++) {
-               int found;
+       /* now check all required_attrs attributes are present */
+       for (i = 0; i < entry_structs->required_attrs.num; i++) {
+               struct schema_attribute *attr;
 
-               found = 0;
-               for (j = 0; j < entry_structs->must_num; j++) {
-                       if (schema_attr_cmp(entry_structs->must[j].name, modify_structs->check_list[i].name) == 0) {
-                               if ((modify_structs->check_list[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
-                                       data->error_string = "Objectclass violation: trying to delete a required attribute";
-                                       talloc_free(entry_structs);
-                                       return -1;
-                               }
-                               modify_structs->check_list[i].flags |= SCHEMA_FLAG_CHECKED;
-                               found = 1;
-                               break;
-                       }
-               }
-               if ( ! found) {
-                       for (j = 0; j < entry_structs->may_num; j++) {
-                               if (schema_attr_cmp(entry_structs->may[j].name, modify_structs->check_list[i].name) == 0) {
-                                       modify_structs->check_list[i].flags |= SCHEMA_FLAG_CHECKED;
-                                       break;
-                               }
-                       }
-               }
-       }
+               attr = schema_find_attribute(&entry_structs->entry_attrs,
+                                            entry_structs->required_attrs.attr[i].name);
 
-       /* now check all new objectclasses musts are present */
-       for (i = 0; i < modify_structs->must_num; i++) {
-               int found;
+               if (attr == NULL) { /* not found */
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                                 "The required_attrs attribute %s is missing.\n",
+                                 entry_structs->required_attrs.attr[i].name);
 
-               found = 0;
-               for (j = 0; j < modify_structs->check_list_num; j++) {
-                       if (schema_attr_cmp(modify_structs->must[i].name, modify_structs->check_list[j].name) == 0) {
-                               if ((modify_structs->check_list[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
-                                       data->error_string = "Objectclass violation: trying to delete a required attribute";
-                                       talloc_free(entry_structs);
-                                       return -1;
-                               }
-                               modify_structs->check_list[j].flags |= SCHEMA_FLAG_CHECKED;
-                               found = 1;
-                               break;
-                       }
+                       data->error_string = "Objectclass violation, a required attribute is missing";
+                       talloc_free(entry_structs);
+                       return -1;
                }
 
-               if ( ! found ) {
-                       data->error_string = "Objectclass violation, a required attribute is missing";
+               /* check we are not trying to delete a required attribute */
+               /* TODO: consider multivalued attrs */
+               if ((attr->flags & SCHEMA_FLAG_MOD_DELETE) != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                                 "Trying to delete the required attribute %s.\n",
+                                 attr->name);
+
+                       data->error_string = "Objectclass violation, a required attribute cannot be removed";
                        talloc_free(entry_structs);
                        return -1;
                }
+
+               /* mark the attribute as checked */
+               attr->flags = SCHEMA_FLAG_CHECKED;
        }
 
-       /* now check all others atribs are found in mays */
-       for (i = 0; i < modify_structs->check_list_num; i++) {
+       /* now check all others atribs are at least optional_attrs */
+       for (i = 0; i < entry_structs->entry_attrs.num; i++) {
 
-               if ((modify_structs->check_list[i].flags & SCHEMA_FLAG_CHECKED) == 0 &&
-                   (modify_structs->check_list[i].flags & SCHEMA_FLAG_MOD_MASK) != SCHEMA_FLAG_MOD_DELETE) {
-                       int found;
+               if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+                       struct schema_attribute *attr;
 
-                       found = 0;
-                       for (j = 0; j < modify_structs->may_num; j++) {
-                               if (schema_attr_cmp(modify_structs->may[j].name, modify_structs->check_list[i].name) == 0) {
-                                       modify_structs->check_list[i].flags |= SCHEMA_FLAG_CHECKED;
-                                       found = 1;
-                                       break;
-                               }
-                       }
+                       attr = schema_find_attribute(&entry_structs->optional_attrs,
+                                                    entry_structs->entry_attrs.attr[i].name);
+
+                       if (attr == NULL) { /* not found */
+                               ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+                                         "The attribute %s is not referenced by any objectclass.\n",
+                                         entry_structs->entry_attrs.attr[i].name);
 
-                       if ( ! found ) {
                                data->error_string = "Objectclass violation, an invalid attribute name was found";
                                talloc_free(entry_structs);
                                return -1;
@@ -592,14 +489,14 @@ static int schema_modify_record(struct ldb_module *module, const struct ldb_mess
 }
 
 /* delete_record */
-static int schema_delete_record(struct ldb_module *module, const char *dn)
+static int schema_delete_record(struct ldb_module *module, const struct ldb_dn *dn)
 {
 /*     struct private_data *data = (struct private_data *)module->private_data; */
        return ldb_next_delete_record(module, dn);
 }
 
 /* rename_record */
-static int schema_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
+static int schema_rename_record(struct ldb_module *module, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
 {
        return ldb_next_rename_record(module, olddn, newdn);
 }
@@ -628,23 +525,26 @@ static const char *schema_errstring(struct ldb_module *module)
        return ldb_next_errstring(module);
 }
 
+static int schema_destructor(void *module_ctx)
+{
+/*     struct ldb_module *ctx = module_ctx; */
+       /* put your clean-up functions here */
+       return 0;
+}
+
 static const struct ldb_module_ops schema_ops = {
-       "schema",
-       schema_close, 
-       schema_search,
-       schema_search_free,
-       schema_add_record,
-       schema_modify_record,
-       schema_delete_record,
-       schema_rename_record,
-       schema_named_lock,
-       schema_named_unlock,
-       schema_errstring,
+       .name          = "schema",
+       .search        = schema_search,
+       .search_bytree = schema_search_bytree,
+       .add_record    = schema_add_record,
+       .modify_record = schema_modify_record,
+       .delete_record = schema_delete_record,
+       .rename_record = schema_rename_record,
+       .named_lock    = schema_named_lock,
+       .named_unlock  = schema_named_unlock,
+       .errstring     = schema_errstring,
 };
 
-#define SCHEMA_PREFIX          "schema:"
-#define SCHEMA_PREFIX_LEN      7
-
 #ifdef HAVE_DLOPEN_DISABLED
 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
 #else
@@ -653,57 +553,17 @@ struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *optio
 {
        struct ldb_module *ctx;
        struct private_data *data;
-       char *db_url = NULL;
-       int i;
 
        ctx = talloc(ldb, struct ldb_module);
        if (!ctx) {
                return NULL;
        }
 
-       if (options) {
-               for (i = 0; options[i] != NULL; i++) {
-                       if (strncmp(options[i], SCHEMA_PREFIX, SCHEMA_PREFIX_LEN) == 0) {
-                               db_url = talloc_strdup(ctx, &options[i][SCHEMA_PREFIX_LEN]);
-                               SCHEMA_TALLOC_CHECK(ctx, db_url, NULL);
-                       }
-               }
-       }
-
-       if (!db_url) { /* search if it is defined in the calling ldb */
-               int ret;
-               const char * attrs[] = { "@SCHEMADB", NULL };
-               struct ldb_message **msgs;
-
-               ret = ldb_search(ldb, "", LDB_SCOPE_BASE, "dn=@MODULES", (const char * const *)attrs, &msgs);
-               if (ret == 0) {
-                       ldb_debug(ldb, LDB_DEBUG_TRACE, "Schema DB not found\n");
-                       ldb_search_free(ldb, msgs);
-                       return NULL;
-               } else {
-                       if (ret < 0) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for schema db, bailing out!\n", ldb_errstring(ldb));
-                               ldb_search_free(ldb, msgs);
-                               return NULL;
-                       }
-                       if (ret > 1) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found, bailing out\n");
-                               ldb_search_free(ldb, msgs);
-                               return NULL;
-                       }
-
-                       db_url = talloc_strndup(ctx, msgs[0]->elements[0].values[0].data, msgs[0]->elements[0].values[0].length);
-                       SCHEMA_TALLOC_CHECK(ctx, db_url, NULL);
-               }
-
-               ldb_search_free(ldb, msgs);
-       }
-
        data = talloc(ctx, struct private_data);
-       SCHEMA_TALLOC_CHECK(ctx, data, NULL);
-
-       data->schema_db = ldb_connect(db_url, 0, NULL); 
-       SCHEMA_TALLOC_CHECK(ctx, data->schema_db, NULL);
+       if (data == NULL) {
+               talloc_free(ctx);
+               return NULL;
+       }
 
        data->error_string = NULL;
        ctx->private_data = data;
@@ -711,5 +571,7 @@ struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *optio
        ctx->prev = ctx->next = NULL;
        ctx->ops = &schema_ops;
 
+       talloc_set_destructor (ctx, schema_destructor);
+
        return ctx;
 }