summaryrefslogtreecommitdiff
path: root/source4/lib/ldb/ldb_tdb
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-03-31 06:45:39 +0000
committerAndrew Tridgell <tridge@samba.org>2004-03-31 06:45:39 +0000
commit58d50a614f1b4a3fc6b60ad5f777d987263fe54f (patch)
tree6fc8efe5b4cc66f6e86ad6c11969d000860a253b /source4/lib/ldb/ldb_tdb
parent4aa785b1b23d0add18d5fec6fb6c5b37a6feecac (diff)
downloadsamba-58d50a614f1b4a3fc6b60ad5f777d987263fe54f.tar.gz
samba-58d50a614f1b4a3fc6b60ad5f777d987263fe54f.tar.bz2
samba-58d50a614f1b4a3fc6b60ad5f777d987263fe54f.zip
make a more recent snapshot of ldb available to interested
people. Note that I decided to make it LGPL. ldb is not finished yet, but enough of it is there for people to get an idea of what it does, and quite a few simple tests work (This used to be commit dc6f41f9e777d37f883303ddef0d96840d80f78e)
Diffstat (limited to 'source4/lib/ldb/ldb_tdb')
-rw-r--r--source4/lib/ldb/ldb_tdb/.cvsignore7
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_index.c641
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_ldif.c366
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_match.c176
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_pack.c174
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_parse.c448
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_parse.h40
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_search.c482
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c303
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.h11
-rw-r--r--source4/lib/ldb/ldb_tdb/ldbadd.c55
-rw-r--r--source4/lib/ldb/ldb_tdb/ldbdel.c50
-rw-r--r--source4/lib/ldb/ldb_tdb/ldbsearch.c73
13 files changed, 2826 insertions, 0 deletions
diff --git a/source4/lib/ldb/ldb_tdb/.cvsignore b/source4/lib/ldb/ldb_tdb/.cvsignore
new file mode 100644
index 0000000000..8f968d06f4
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/.cvsignore
@@ -0,0 +1,7 @@
+ldbadd
+ldbsearch
+ldbdel
+test.ldb
+TAGS
+.*~
+*.o
diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c
new file mode 100644
index 0000000000..dda80a6b2a
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_index.c
@@ -0,0 +1,641 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb tdb backend - indexing
+ *
+ * Description: indexing routines for ldb tdb backend
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+
+struct dn_list {
+ unsigned int count;
+ char **dn;
+};
+
+/*
+ free a struct dn_list
+*/
+static void dn_list_free(struct dn_list *list)
+{
+ int i;
+ for (i=0;i<list->count;i++) {
+ free(list->dn[i]);
+ }
+ if (list->dn) free(list->dn);
+}
+
+/*
+ return the dn key to be used for an index
+ caller frees
+*/
+static char *ldb_dn_key(const char *attr, const struct ldb_val *value)
+{
+ char *ret = NULL;
+
+ if (ldb_should_b64_encode(value)) {
+ char *vstr = ldb_base64_encode(value->data, value->length);
+ if (!vstr) return NULL;
+ asprintf(&ret, "@INDEX:%s::%s", attr, vstr);
+ free(vstr);
+ return ret;
+ }
+
+ asprintf(&ret, "@INDEX:%s:%s", attr, (char *)value->data);
+ return ret;
+}
+
+/*
+ see if a attribute value is in the list of indexed attributes
+*/
+static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr)
+{
+ int i;
+ for (i=0;i<msg->num_elements;i++) {
+ if (strcmp(msg->elements[i].name, "@IDXATTR") == 0 &&
+ strcmp((char *)msg->elements[i].value.data, attr) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ return a list of dn's that might match a simple indexed search or
+ */
+static int ltdb_index_dn_simple(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ char *dn = NULL;
+ int ret, i;
+ struct ldb_message msg;
+
+ list->count = 0;
+ list->dn = NULL;
+
+ /*
+ if the value is a wildcard then we can't do a match via indexing
+ */
+ if (ltdb_has_wildcard(&tree->u.simple.value)) {
+ return -1;
+ }
+
+ /* if the attribute isn't in the list of indexed attributes then
+ this node needs a full search */
+ if (ldb_msg_find_idx(index_list, tree->u.simple.attr) == -1) {
+ return -1;
+ }
+
+ /* the attribute is indexed. Pull the list of DNs that match the
+ search criterion */
+ dn = ldb_dn_key(tree->u.simple.attr, &tree->u.simple.value);
+ if (!dn) return -1;
+
+ ret = ltdb_search_dn1(ldb, dn, &msg);
+ free(dn);
+ if (ret == 0 || ret == -1) {
+ return ret;
+ }
+
+ list->dn = malloc_array_p(char *, msg.num_elements);
+ if (!list->dn) {
+ ltdb_search_dn1_free(ldb, &msg);
+ }
+
+ for (i=0;i<msg.num_elements;i++) {
+ if (strcmp(msg.elements[i].name, "@IDX") != 0) {
+ continue;
+ }
+ list->dn[list->count] =
+ strdup((char *)msg.elements[i].value.data);
+ if (!list->dn[list->count]) {
+ dn_list_free(list);
+ ltdb_search_dn1_free(ldb, &msg);
+ return -1;
+ }
+ list->count++;
+ }
+
+ ltdb_search_dn1_free(ldb, &msg);
+
+ qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) strcmp);
+
+ return 1;
+}
+
+/*
+ list intersection
+ list = list & list2
+ relies on the lists being sorted
+*/
+static int list_intersect(struct dn_list *list, const struct dn_list *list2)
+{
+ struct dn_list list3;
+ int i;
+
+ if (list->count == 0 || list2->count == 0) {
+ /* 0 & X == 0 */
+ dn_list_free(list);
+ return 0;
+ }
+
+ list3.dn = malloc_array_p(char *, list->count);
+ if (!list3.dn) {
+ dn_list_free(list);
+ return -1;
+ }
+ list3.count = 0;
+
+ for (i=0;i<list->count;i++) {
+ if (list_find(list->dn[i], list2->dn, list2->count,
+ sizeof(char *), (comparison_fn_t)strcmp) != -1) {
+ list3.dn[list3.count] = list->dn[i];
+ list3.count++;
+ } else {
+ free(list->dn[i]);
+ }
+ }
+
+ free(list->dn);
+ list->dn = list3.dn;
+ list->count = list3.count;
+
+ return 0;
+}
+
+
+/*
+ list union
+ list = list | list2
+ relies on the lists being sorted
+*/
+static int list_union(struct dn_list *list, const struct dn_list *list2)
+{
+ int i;
+ char **d;
+ unsigned int count = list->count;
+
+ if (list->count == 0 && list2->count == 0) {
+ /* 0 | 0 == 0 */
+ dn_list_free(list);
+ return 0;
+ }
+
+ d = realloc_p(list->dn, char *, list->count + list2->count);
+ if (!d) {
+ dn_list_free(list);
+ return -1;
+ }
+ list->dn = d;
+
+ for (i=0;i<list2->count;i++) {
+ if (list_find(list2->dn[i], list->dn, count,
+ sizeof(char *), (comparison_fn_t)strcmp) == -1) {
+ list->dn[list->count] = strdup(list2->dn[i]);
+ if (!list->dn[list->count]) {
+ dn_list_free(list);
+ return -1;
+ }
+ list->count++;
+ }
+ }
+
+ if (list->count != count) {
+ qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)strcmp);
+ }
+
+ return 0;
+}
+
+static int ltdb_index_dn(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list);
+
+
+/*
+ OR two index results
+ */
+static int ltdb_index_dn_or(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ int ret, i;
+
+ ret = -1;
+ list->dn = NULL;
+ list->count = 0;
+
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ struct dn_list list2;
+ int v;
+ v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
+
+ if (v == 0) {
+ /* 0 || X == X */
+ if (ret == -1) {
+ ret = 0;
+ }
+ continue;
+ }
+
+ if (v == -1) {
+ /* 1 || X == 1 */
+ dn_list_free(list);
+ return -1;
+ }
+
+ if (ret == -1) {
+ ret = 1;
+ *list = list2;
+ } else {
+ if (list_union(list, &list2) == -1) {
+ dn_list_free(&list2);
+ return -1;
+ }
+ dn_list_free(&list2);
+ }
+ }
+
+ if (list->count == 0) {
+ dn_list_free(list);
+ return 0;
+ }
+
+ return ret;
+}
+
+
+/*
+ NOT an index results
+ */
+static int ltdb_index_dn_not(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ /* the only way to do an indexed not would be if we could
+ negate the not via another not or if we knew the total
+ number of database elements so we could know that the
+ existing expression covered the whole database.
+
+ instead, we just give up, and rely on a full index scan
+ (unless an outer & manages to reduce the list)
+ */
+ return -1;
+}
+
+/*
+ AND two index results
+ */
+static int ltdb_index_dn_and(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ int ret, i;
+
+ ret = -1;
+ list->dn = NULL;
+ list->count = 0;
+
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ struct dn_list list2;
+ int v;
+ v = ltdb_index_dn(ldb, tree->u.list.elements[i], index_list, &list2);
+
+ if (v == 0) {
+ /* 0 && X == 0 */
+ dn_list_free(list);
+ return 0;
+ }
+
+ if (v == -1) {
+ continue;
+ }
+
+ if (ret == -1) {
+ ret = 1;
+ *list = list2;
+ } else {
+ if (list_intersect(list, &list2) == -1) {
+ dn_list_free(&list2);
+ return -1;
+ }
+ dn_list_free(&list2);
+ }
+
+ if (list->count == 0) {
+ if (list->dn) free(list->dn);
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ return a list of dn's that might match a indexed search or
+ -1 if an error. return 0 for no matches, or 1 for matches
+ */
+static int ltdb_index_dn(struct ldb_context *ldb,
+ struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ int ret;
+
+ switch (tree->operation) {
+ case LDB_OP_SIMPLE:
+ ret = ltdb_index_dn_simple(ldb, tree, index_list, list);
+ break;
+
+ case LDB_OP_AND:
+ ret = ltdb_index_dn_and(ldb, tree, index_list, list);
+ break;
+
+ case LDB_OP_OR:
+ ret = ltdb_index_dn_or(ldb, tree, index_list, list);
+ break;
+
+ case LDB_OP_NOT:
+ ret = ltdb_index_dn_not(ldb, tree, index_list, list);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ filter a candidate dn_list from an indexed search into a set of results
+ extracting just the given attributes
+*/
+static int ldb_index_filter(struct ldb_context *ldb, struct ldb_parse_tree *tree,
+ const char *base,
+ enum ldb_scope scope,
+ const struct dn_list *dn_list,
+ const char *attrs[], struct ldb_message ***res)
+{
+ int i;
+ unsigned int count = 0;
+
+ for (i=0;i<dn_list->count;i++) {
+ struct ldb_message msg;
+ int ret;
+ ret = ltdb_search_dn1(ldb, dn_list->dn[i], &msg);
+ if (ret == 0) {
+ /* the record has disappeared? yes, this can happen */
+ continue;
+ }
+
+ if (ret == -1) {
+ /* an internal error */
+ return -1;
+ }
+
+ if (ldb_message_match(ldb, &msg, tree, base, scope) == 1) {
+ ret = ltdb_add_attr_results(ldb, &msg, attrs, &count, res);
+ }
+ ltdb_search_dn1_free(ldb, &msg);
+ if (ret != 0) {
+ return -1;
+ }
+ }
+
+ return count;
+}
+
+/*
+ search the database with a LDAP-like expression using indexes
+ returns -1 if an indexed search is not possible, in which
+ case the caller should call ltdb_search_full()
+*/
+int ltdb_search_indexed(struct ldb_context *ldb,
+ const char *base,
+ enum ldb_scope scope,
+ struct ldb_parse_tree *tree,
+ const char *attrs[], struct ldb_message ***res)
+{
+ struct ldb_message index_list;
+ struct dn_list dn_list;
+ int ret;
+
+ /* find the list of indexed fields */
+ ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
+ if (ret != 1) {
+ /* no index list? must do full search */
+ return -1;
+ }
+
+ ret = ltdb_index_dn(ldb, tree, &index_list, &dn_list);
+ ltdb_search_dn1_free(ldb, &index_list);
+
+ if (ret == 1) {
+ /* we've got a candidate list - now filter by the full tree
+ and extract the needed attributes */
+ ret = ldb_index_filter(ldb, tree, base, scope, &dn_list,
+ attrs, res);
+ dn_list_free(&dn_list);
+ }
+
+ return ret;
+}
+
+/*
+ add an index entry for one message element
+*/
+static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
+ struct ldb_message_element *el)
+{
+ struct ldb_message msg;
+ char *dn_key;
+ int ret;
+ struct ldb_message_element *el2;
+
+ dn_key = ldb_dn_key(el->name, &el->value);
+ if (!dn_key) {
+ return -1;
+ }
+
+ ret = ltdb_search_dn1(ldb, dn_key, &msg);
+ if (ret == -1) {
+ free(dn_key);
+ return -1;
+ }
+
+ if (ret == 0) {
+ msg.dn = dn_key;
+ msg.num_elements = 0;
+ msg.elements = NULL;
+ msg.private = NULL;
+ }
+
+ /* add another entry */
+ el2 = realloc_p(msg.elements, struct ldb_message_element, msg.num_elements+1);
+ if (!el2) {
+ if (ret == 1) {
+ ltdb_search_dn1_free(ldb, &msg);
+ }
+ free(dn_key);
+ return -1;
+ }
+
+ msg.elements = el2;
+ msg.elements[msg.num_elements].name = "@IDX";
+ msg.elements[msg.num_elements].value.length = strlen(dn);
+ msg.elements[msg.num_elements].value.data = dn;
+ msg.num_elements++;
+
+ ret = ltdb_store(ldb, &msg, TDB_REPLACE);
+
+ if (msg.num_elements == 1) {
+ free(msg.elements);
+ } else {
+ ltdb_search_dn1_free(ldb, &msg);
+ }
+
+ return ret;
+}
+
+/*
+ add the index entries for a new record
+ return -1 on failure
+*/
+int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
+{
+ int ret, i;
+ struct ldb_message index_list;
+
+ /* find the list of indexed fields */
+ ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
+ if (ret != 1) {
+ /* no indexed fields or an error */
+ return ret;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
+ if (ret == -1) {
+ continue;
+ }
+ ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i]);
+ if (ret == -1) {
+ ltdb_search_dn1_free(ldb, &index_list);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ delete an index entry for one message element
+*/
+static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
+ struct ldb_message_element *el)
+{
+ struct ldb_message msg;
+ char *dn_key;
+ int ret, i;
+
+ dn_key = ldb_dn_key(el->name, &el->value);
+ if (!dn_key) {
+ return -1;
+ }
+
+ ret = ltdb_search_dn1(ldb, dn_key, &msg);
+ if (ret == -1) {
+ free(dn_key);
+ return -1;
+ }
+
+ if (ret == 0) {
+ /* it wasn't indexed. Did we have an earlier error? If we did then
+ its gone now */
+ ltdb_search_dn1_free(ldb, &msg);
+ return 0;
+ }
+
+ i = ldb_msg_find_idx(&msg, dn);
+ if (i == -1) {
+ /* it ain't there. hmmm */
+ ltdb_search_dn1_free(ldb, &msg);
+ return 0;
+ }
+
+ if (i != msg.num_elements - 1) {
+ memmove(&msg.elements[i], &msg.elements[i+1], sizeof(msg.elements[i]));
+ }
+ msg.num_elements--;
+
+ if (msg.num_elements == 0) {
+ ret = ltdb_delete_noindex(ldb, dn_key);
+ } else {
+ ret = ltdb_store(ldb, &msg, TDB_REPLACE);
+ }
+
+ ltdb_search_dn1_free(ldb, &msg);
+
+ return ret;
+}
+
+/*
+ delete the index entries for a record
+ return -1 on failure
+*/
+int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
+{
+ int ret, i;
+ struct ldb_message index_list;
+
+ /* find the list of indexed fields */
+ ret = ltdb_search_dn1(ldb, "@INDEXLIST", &index_list);
+ if (ret != 1) {
+ /* no indexed fields or an error */
+ return ret;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
+ if (ret == -1) {
+ continue;
+ }
+ ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i]);
+ if (ret == -1) {
+ ltdb_search_dn1_free(ldb, &index_list);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_ldif.c b/source4/lib/ldb/ldb_tdb/ldb_ldif.c
new file mode 100644
index 0000000000..170685cc03
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_ldif.c
@@ -0,0 +1,366 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ ldif utilities for ldb
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/*
+ this base64 decoder was taken from jitterbug (written by tridge).
+ we might need to replace it with a new version
+*/
+static int base64_decode(char *s)
+{
+ const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset, byte_offset, idx, i, n;
+ unsigned char *d = (unsigned char *)s;
+ char *p;
+
+ n=i=0;
+
+ while (*s && (p=strchr(b64,*s))) {
+ idx = (int)(p - b64);
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ d[byte_offset] &= ~((1<<(8-bit_offset))-1);
+ if (bit_offset < 3) {
+ d[byte_offset] |= (idx << (2-bit_offset));
+ n = byte_offset+1;
+ } else {
+ d[byte_offset] |= (idx >> (bit_offset-2));
+ d[byte_offset+1] = 0;
+ d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
+ n = byte_offset+2;
+ }
+ s++; i++;
+ }
+
+ if (*s && !p) {
+ /* the only termination allowed */
+ if (*s != '=') {
+ return -1;
+ }
+ }
+
+ /* null terminate */
+ d[n] = 0;
+ return n;
+}
+
+
+/*
+ encode as base64
+ caller frees
+*/
+char *ldb_base64_encode(const char *buf, int len)
+{
+ const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset, byte_offset, idx, i;
+ unsigned char *d = (unsigned char *)buf;
+ int bytes = (len*8 + 5)/6;
+ char *out;
+
+ out = malloc(bytes+2);
+ if (!out) return NULL;
+
+ for (i=0;i<bytes;i++) {
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ if (bit_offset < 3) {
+ idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
+ } else {
+ idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
+ if (byte_offset+1 < len) {
+ idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
+ }
+ }
+ out[i] = b64[idx];
+ }
+
+ out[i++] = '=';
+ out[i] = 0;
+
+ return out;
+}
+
+/*
+ see if a buffer should be base64 encoded
+*/
+int ldb_should_b64_encode(const struct ldb_val *val)
+{
+ int i;
+ unsigned char *p = val->data;
+
+ if (val->length == 0 || p[0] == ' ' || p[0] == ':') {
+ return 1;
+ }
+
+ for (i=0; i<val->length; i++) {
+ if (!isprint(p[i]) || p[i] == '\n') {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ encode as base64 to a file
+*/
+static int base64_encode_f(FILE *f, const char *buf, int len, int start_pos)
+{
+ int i;
+ char *b = ldb_base64_encode(buf, len);
+
+ if (!b) {
+ return -1;
+ }
+
+ for (i=0;b[i];i++) {
+ fputc(b[i], f);
+ if (b[i+1] && (i + start_pos) % 77 == 0) {
+ fputc('\n', f);
+ fputc(' ', f);
+ }
+ }
+ free(b);
+ return 0;
+}
+
+/*
+ write a line folded string onto a file
+*/
+static void fold_string(FILE *f, const char *buf, size_t length, int start_pos)
+{
+ int i;
+
+ for (i=0;i<length;i++) {
+ fputc(buf[i], f);
+ if (i != (length-1) && (i + start_pos) % 77 == 0) {
+ fputc('\n', f);
+ fputc(' ', f);
+ }
+ }
+}
+
+
+/*
+ pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
+ this routine removes any RFC2849 continuations and comments
+
+ caller frees
+*/
+static char *next_chunk(FILE *f)
+{
+ size_t alloc_size=0, chunk_size = 0;
+ char *chunk = NULL;
+ int c;
+ int in_comment = 0;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (chunk_size == alloc_size) {
+ char *c2;
+ alloc_size += 1024;
+ c2 = realloc_p(chunk, char, alloc_size);
+ if (!c2) {
+ free(chunk);
+ errno = ENOMEM;
+ return NULL;
+ }
+ chunk = c2;
+ }
+
+ if (in_comment) {
+ if (c == '\n') {
+ in_comment = 0;
+ }
+ continue;
+ }
+
+ /* handle continuation lines - see RFC2849 */
+ if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
+ chunk_size--;
+ continue;
+ }
+
+ /* chunks are terminated by a double line-feed */
+ if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
+ chunk[chunk_size-1] = 0;
+ return chunk;
+ }
+
+ if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
+ in_comment = 1;
+ continue;
+ }
+
+ chunk[chunk_size++] = c;
+ }
+
+ return chunk;
+}
+
+
+/* simple ldif attribute parser */
+static int next_attr(char **s, char **attr, struct ldb_val *value)
+{
+ char *p;
+ int base64_encoded = 0;
+
+ p = strchr(*s, ':');
+ if (!p) {
+ return -1;
+ }
+
+ *p++ = 0;
+
+ if (*p == ':') {
+ base64_encoded = 1;
+ p++;
+ }
+
+ *attr = *s;
+
+ while (isspace(*p)) {
+ p++;
+ }
+
+ value->data = p;
+
+ p = strchr(p, '\n');
+
+ if (!p) {
+ value->length = strlen((char *)value->data);
+ *s = ((char *)value->data) + value->length;
+ } else {
+ value->length = p - (char *)value->data;
+ *s = p+1;
+ *p = 0;
+ }
+
+ if (base64_encoded) {
+ int len = base64_decode(value->data);
+ if (len == -1) {
+ /* it wasn't valid base64 data */
+ return -1;
+ }
+ value->length = len;
+ }
+
+ return 0;
+}
+
+
+/*
+ free a message from a ldif_read
+*/
+void ldif_read_free(struct ldb_message *msg)
+{
+ if (msg->elements) free(msg->elements);
+ if (msg->private) free(msg->private);
+ free(msg);
+}
+
+/*
+ read from a LDIF file, creating a ldb_message
+*/
+struct ldb_message *ldif_read(FILE *f)
+{
+ struct ldb_message *msg;
+ char *attr=NULL, *chunk=NULL, *s;
+ struct ldb_val value;
+
+ value.data = NULL;
+
+ msg = malloc_p(struct ldb_message);
+ if (!msg) return NULL;
+
+ msg->dn = NULL;
+ msg->elements = NULL;
+ msg->num_elements = 0;
+ msg->private = NULL;
+
+ chunk = next_chunk(f);
+ if (!chunk) {
+ goto failed;
+ }
+
+ msg->private = chunk;
+ s = chunk;
+
+ if (next_attr(&s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ /* first line must be a dn */
+ if (strcmp(attr, "dn") != 0) {
+ fprintf(stderr, "First line must be a dn not '%s'\n", attr);
+ goto failed;
+ }
+
+ msg->dn = value.data;
+
+ while (next_attr(&s, &attr, &value) == 0) {
+ msg->elements = realloc_p(msg->elements,
+ struct ldb_message_element,
+ msg->num_elements+1);
+ if (!msg->elements) {
+ goto failed;
+ }
+ msg->elements[msg->num_elements].flags = 0;
+ msg->elements[msg->num_elements].name = attr;
+ msg->elements[msg->num_elements].value = value;
+ msg->num_elements++;
+ }
+
+ return msg;
+
+failed:
+ if (msg) ldif_read_free(msg);
+ return NULL;
+}
+
+
+/*
+ write to a ldif file
+*/
+void ldif_write(FILE *f, const struct ldb_message *msg)
+{
+ int i;
+ fprintf(f, "dn: %s\n", msg->dn);
+ for (i=0;i<msg->num_elements;i++) {
+ if (ldb_should_b64_encode(&msg->elements[i].value)) {
+ fprintf(f, "%s:: ", msg->elements[i].name);
+ base64_encode_f(f,
+ msg->elements[i].value.data,
+ msg->elements[i].value.length,
+ strlen(msg->elements[i].name)+3);
+ fprintf(f, "\n");
+ } else {
+ fprintf(f, "%s: ", msg->elements[i].name);
+ fold_string(f, msg->elements[i].value.data,
+ msg->elements[i].value.length,
+ strlen(msg->elements[i].name)+2);
+ fprintf(f, "\n");
+ }
+ }
+ fprintf(f,"\n");
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_match.c b/source4/lib/ldb/ldb_tdb/ldb_match.c
new file mode 100644
index 0000000000..89d204f56a
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_match.c
@@ -0,0 +1,176 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb expression matching
+ *
+ * Description: ldb expression matching for tdb backend
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+
+
+/*
+ see if two ldb_val structures contain the same data
+ return 1 for a match, 0 for a mis-match
+*/
+static int ldb_val_equal(struct ldb_val *v1, struct ldb_val *v2)
+{
+ if (v1->length != v2->length) return 0;
+
+ if (v1->length == 0) return 1;
+
+ if (memcmp(v1->data, v2->data, v1->length) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ check if the scope matches in a search result
+*/
+static int scope_match(const char *dn, const char *base, enum ldb_scope scope)
+{
+ size_t dn_len, base_len;
+
+ if (base == NULL) {
+ return 1;
+ }
+
+ base_len = strlen(base);
+ dn_len = strlen(dn);
+
+ if (strcmp(dn, base) == 0) {
+ return 1;
+ }
+
+ if (base_len+1 >= dn_len) {
+ return 0;
+ }
+
+ switch (scope) {
+ case LDB_SCOPE_BASE:
+ break;
+
+ case LDB_SCOPE_ONELEVEL:
+ if (strcmp(dn + (dn_len - base_len), base) == 0 &&
+ dn[dn_len - base_len - 1] == ',' &&
+ strchr(dn, ',') == &dn[dn_len - base_len - 1]) {
+ return 1;
+ }
+ break;
+
+ case LDB_SCOPE_SUBTREE:
+ default:
+ if (strcmp(dn + (dn_len - base_len), base) == 0 &&
+ dn[dn_len - base_len - 1] == ',') {
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ match a leaf node
+*/
+static int match_leaf(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ struct ldb_parse_tree *tree,
+ const char *base,
+ enum ldb_scope scope)
+{
+ int i;
+
+ if (!scope_match(msg->dn, base, scope)) {
+ return 0;
+ }
+
+ if (strcmp(tree->u.simple.attr, "dn") == 0) {
+ if (strcmp(tree->u.simple.value.data, "*") == 0) {
+ return 1;
+ }
+ return strcmp(msg->dn, tree->u.simple.value.data) == 0;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0 &&
+ (strcmp(tree->u.simple.value.data, "*") == 0 ||
+ ldb_val_equal(&msg->elements[i].value, &tree->u.simple.value))) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ return 0 if the given parse tree matches the given message. Assumes
+ the message is in sorted order
+
+ return 1 if it matches, and 0 if it doesn't match
+
+ this is a recursive function, and does short-circuit evaluation
+ */
+int ldb_message_match(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ struct ldb_parse_tree *tree,
+ const char *base,
+ enum ldb_scope scope)
+{
+ int v, i;
+
+ switch (tree->operation) {
+ case LDB_OP_SIMPLE:
+ break;
+
+ case LDB_OP_NOT:
+ return ! ldb_message_match(ldb, msg, tree->u.not.child, base, scope);
+
+ case LDB_OP_AND:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ v = ldb_message_match(ldb, msg, tree->u.list.elements[i],
+ base, scope);
+ if (!v) return 0;
+ }
+ return 1;
+
+ case LDB_OP_OR:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ v = ldb_message_match(ldb, msg, tree->u.list.elements[i],
+ base, scope);
+ if (v) return 1;
+ }
+ return 0;
+ }
+
+ return match_leaf(ldb, msg, tree, base, scope);
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_pack.c b/source4/lib/ldb/ldb_tdb/ldb_pack.c
new file mode 100644
index 0000000000..b0c825e8e2
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_pack.c
@@ -0,0 +1,174 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb pack/unpack
+ *
+ * Description: pack/unpack routines for ldb messages as key/value blobs
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb_tdb/ldb_tdb.h"
+
+/* change this if the data format ever changes */
+#define LTDB_PACKING_FORMAT 0x26011966
+
+/*
+ pack a ldb message into a linear buffer in a TDB_DATA
+
+ caller frees the data buffer after use
+*/
+int ltdb_pack_data(struct ldb_context *ctx,
+ const struct ldb_message *message,
+ struct TDB_DATA *data)
+{
+ int i;
+ size_t size;
+ char *p;
+
+ /* work out how big it needs to be */
+ size = 8;
+
+ for (i=0;i<message->num_elements;i++) {
+ size += 1 + strlen(message->elements[i].name);
+ size += 4 + message->elements[i].value.length + 1;
+ }
+
+ /* allocate it */
+ data->dptr = malloc(size);
+ if (!data->dptr) {
+ errno = ENOMEM;
+ return -1;
+ }
+ data->dsize = size;
+
+ p = data->dptr;
+ SIVAL(p, 0, LTDB_PACKING_FORMAT);
+ SIVAL(p, 4, message->num_elements);
+ p += 8;
+
+ for (i=0;i<message->num_elements;i++) {
+ size_t len = strlen(message->elements[i].name);
+ memcpy(p, message->elements[i].name, len+1);
+ p += len + 1;
+ SIVAL(p, 0, message->elements[i].value.length);
+ memcpy(p+4, message->elements[i].value.data,
+ message->elements[i].value.length);
+ p[4+message->elements[i].value.length] = 0;
+ p += 4 + message->elements[i].value.length + 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ unpack a ldb message from a linear buffer in TDB_DATA
+
+ note that this does not fill in the class and key elements
+
+ caller frees. Memory for the elements[] array is malloced,
+ but the memory for the elements is re-used from the TDB_DATA
+ data. This means the caller only has to free the elements array
+*/
+int ltdb_unpack_data(struct ldb_context *ctx,
+ const struct TDB_DATA *data,
+ struct ldb_message *message)
+{
+ char *p;
+ unsigned int remaining;
+ int i;
+
+ message->elements = NULL;
+
+ p = data->dptr;
+ if (data->dsize < 4) {
+ errno = EIO;
+ goto failed;
+ }
+
+ if (IVAL(p, 0) != LTDB_PACKING_FORMAT) {
+ /* this is where we will cope with upgrading the
+ format if/when the format is ever changed */
+ errno = EIO;
+ goto failed;
+ }
+
+ message->num_elements = IVAL(p, 4);
+ p += 8;
+
+ if (message->num_elements == 0) {
+ message->elements = NULL;
+ return 0;
+ }
+
+ /* basic sanity check */
+ remaining = data->dsize - 8;
+
+ if (message->num_elements > remaining / 6) {
+ errno = EIO;
+ goto failed;
+ }
+
+ message->elements = malloc_array_p(struct ldb_message_element,
+ message->num_elements);
+
+ if (!message->elements) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ for (i=0;i<message->num_elements;i++) {
+ size_t len;
+ if (remaining < 6) {
+ errno = EIO;
+ goto failed;
+ }
+ len = strnlen(p, remaining-6);
+ message->elements[i].name = p;
+ remaining -= len + 1;
+ p += len + 1;
+ len = IVAL(p, 0);
+ if (len > remaining-5) {
+ errno = EIO;
+ goto failed;
+ }
+ message->elements[i].value.length = len;
+ message->elements[i].value.data = p+4;
+ remaining -= len+4+1;
+ p += len+4+1;
+ }
+
+ return 0;
+
+failed:
+ if (message->elements) {
+ free(message->elements);
+ }
+ return -1;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_parse.c b/source4/lib/ldb/ldb_tdb/ldb_parse.c
new file mode 100644
index 0000000000..44cfc5b4ff
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_parse.c
@@ -0,0 +1,448 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ parse a LDAP-like expression
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ TODO:
+ - add RFC2254 binary string handling
+ - possibly add ~=, <= and >= handling
+ - expand the test suite
+ - add better parse error handling
+
+*/
+
+#include "includes.h"
+
+
+/*
+a filter is defined by:
+ <filter> ::= '(' <filtercomp> ')'
+ <filtercomp> ::= <and> | <or> | <not> | <simple>
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <not> ::= '!' <filter>
+ <filterlist> ::= <filter> | <filter> <filterlist>
+ <simple> ::= <attributetype> <filtertype> <attributevalue>
+ <filtertype> ::= '=' | '~=' | '<=' | '>='
+*/
+
+/*
+ return next token element. Caller frees
+*/
+static char *ldb_parse_lex(const char **s)
+{
+ const char *p = *s;
+ char *ret;
+
+ while (isspace(*p)) {
+ p++;
+ }
+ *s = p;
+
+ if (*p == 0) {
+ return NULL;
+ }
+
+ if (strchr("()&|=!", *p)) {
+ (*s) = p+1;
+ ret = strndup(p, 1);
+ if (!ret) {
+ errno = ENOMEM;
+ }
+ return ret;
+ }
+
+ while (*p && (isalnum(*p) || !strchr("()&|=!", *p))) {
+ p++;
+ }
+
+ if (p == *s) {
+ return NULL;
+ }
+
+ ret = strndup(*s, p - *s);
+ if (!ret) {
+ errno = ENOMEM;
+ }
+
+ *s = p;
+
+ return ret;
+}
+
+/*
+ find a matching close brace in a string
+*/
+static const char *match_brace(const char *s)
+{
+ unsigned int count = 0;
+ while (*s && (count != 0 || *s != ')')) {
+ if (*s == '(') {
+ count++;
+ }
+ if (*s == ')') {
+ count--;
+ }
+ s++;
+ }
+ if (! *s) {
+ return NULL;
+ }
+ return s;
+}
+
+
+static struct ldb_parse_tree *ldb_parse_filter(const char **s);
+
+/*
+ <simple> ::= <attributetype> <filtertype> <attributevalue>
+*/
+static struct ldb_parse_tree *ldb_parse_simple(const char *s)
+{
+ char *eq, *val, *l;
+ struct ldb_parse_tree *ret;
+
+ l = ldb_parse_lex(&s);
+ if (!l) {
+ fprintf(stderr, "Unexpected end of expression\n");
+ return NULL;
+ }
+
+ if (strchr("()&|=", *l)) {
+ fprintf(stderr, "Unexpected token '%s'\n", l);
+ free(l);
+ return NULL;
+ }
+
+ eq = ldb_parse_lex(&s);
+ if (!eq || strcmp(eq, "=") != 0) {
+ fprintf(stderr, "Expected '='\n");
+ free(l);
+ if (eq) free(eq);
+ return NULL;
+ }
+ free(eq);
+
+ val = ldb_parse_lex(&s);
+ if (val && strchr("()&|=", *val)) {
+ fprintf(stderr, "Unexpected token '%s'\n", val);
+ free(l);
+ if (val) free(val);
+ return NULL;
+ }
+
+ ret = malloc_p(struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = LDB_OP_SIMPLE;
+ ret->u.simple.attr = l;
+ ret->u.simple.value.data = val;
+ ret->u.simple.value.length = val?strlen(val):0;
+
+ return ret;
+}
+
+
+/*
+ parse a filterlist
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <filterlist> ::= <filter> | <filter> <filterlist>
+*/
+static struct ldb_parse_tree *ldb_parse_filterlist(enum ldb_parse_op op, const char *s)
+{
+ struct ldb_parse_tree *ret, *next;
+
+ ret = malloc_p(struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = op;
+ ret->u.list.num_elements = 1;
+ ret->u.list.elements = malloc_p(struct ldb_parse_tree *);
+ if (!ret->u.list.elements) {
+ errno = ENOMEM;
+ free(ret);
+ return NULL;
+ }
+
+ ret->u.list.elements[0] = ldb_parse_filter(&s);
+ if (!ret->u.list.elements[0]) {
+ free(ret->u.list.elements);
+ free(ret);
+ return NULL;
+ }
+
+ while (isspace(*s)) s++;
+
+ while (*s && (next = ldb_parse_filter(&s))) {
+ struct ldb_parse_tree **e;
+ e = realloc_p(ret->u.list.elements,
+ struct ldb_parse_tree *,
+ ret->u.list.num_elements+1);
+ if (!e) {
+ errno = ENOMEM;
+ ldb_parse_tree_free(next);
+ ldb_parse_tree_free(ret);
+ return NULL;
+ }
+ ret->u.list.elements = e;
+ ret->u.list.elements[ret->u.list.num_elements] = next;
+ ret->u.list.num_elements++;
+ while (isspace(*s)) s++;
+ }
+
+ return ret;
+}
+
+
+/*
+ <not> ::= '!' <filter>
+*/
+static struct ldb_parse_tree *ldb_parse_not(const char *s)
+{
+ struct ldb_parse_tree *ret;
+
+ ret = malloc_p(struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = LDB_OP_NOT;
+ ret->u.not.child = ldb_parse_filter(&s);
+ if (!ret->u.not.child) {
+ free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/*
+ parse a filtercomp
+ <filtercomp> ::= <and> | <or> | <not> | <simple>
+*/
+static struct ldb_parse_tree *ldb_parse_filtercomp(const char *s)
+{
+ while (isspace(*s)) s++;
+
+ switch (*s) {
+ case '&':
+ return ldb_parse_filterlist(LDB_OP_AND, s+1);
+
+ case '|':
+ return ldb_parse_filterlist(LDB_OP_OR, s+1);
+
+ case '!':
+ return ldb_parse_not(s+1);
+
+ case '(':
+ case ')':
+ fprintf(stderr, "Unexpected token '%c'\n", *s);
+ return NULL;
+ }
+
+ return ldb_parse_simple(s);
+}
+
+
+/*
+ <filter> ::= '(' <filtercomp> ')'
+*/
+static struct ldb_parse_tree *ldb_parse_filter(const char **s)
+{
+ char *l, *s2;
+ const char *p, *p2;
+ struct ldb_parse_tree *ret;
+
+ l = ldb_parse_lex(s);
+ if (!l) {
+ fprintf(stderr, "Unexpected end of expression\n");
+ return NULL;
+ }
+
+ if (strcmp(l, "(") != 0) {
+ free(l);
+ fprintf(stderr, "Expected '('\n");
+ return NULL;
+ }
+ free(l);
+
+ p = match_brace(*s);
+ if (!p) {
+ fprintf(stderr, "Parse error - mismatched braces\n");
+ return NULL;
+ }
+ p2 = p + 1;
+
+ s2 = strndup(*s, p - *s);
+ if (!s2) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret = ldb_parse_filtercomp(s2);
+ free(s2);
+
+ *s = p2;
+
+ return ret;
+}
+
+
+/*
+ main parser entry point. Takes a search string and returns a parse tree
+
+ expression ::= <simple> | <filter>
+*/
+struct ldb_parse_tree *ldb_parse_tree(const char *s)
+{
+ while (isspace(*s)) s++;
+
+ if (*s == '(') {
+ return ldb_parse_filter(&s);
+ }
+
+ return ldb_parse_simple(s);
+}
+
+/*
+ free a parse tree returned from ldb_parse_tree()
+*/
+void ldb_parse_tree_free(struct ldb_parse_tree *tree)
+{
+ int i;
+
+ switch (tree->operation) {
+ case LDB_OP_SIMPLE:
+ free(tree->u.simple.attr);
+ if (tree->u.simple.value.data) free(tree->u.simple.value.data);
+ break;
+
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ ldb_parse_tree_free(tree->u.list.elements[i]);
+ }
+ if (tree->u.list.elements) free(tree->u.list.elements);
+ break;
+
+ case LDB_OP_NOT:
+ ldb_parse_tree_free(tree->u.not.child);
+ break;
+ }
+
+ free(tree);
+}
+
+#if TEST_PROGRAM
+/*
+ return a string representation of a parse tree
+ used for debugging
+*/
+static char *tree_string(struct ldb_parse_tree *tree)
+{
+ char *s = NULL;
+ char *s1, *s2;
+ int i;
+
+ switch (tree->operation) {
+ case LDB_OP_SIMPLE:
+ asprintf(&s, "( %s = \"%s\" )", tree->u.simple.attr,
+ (char *)tree->u.simple.value.data);
+ break;
+
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ asprintf(&s, "( %c", tree->operation==LDB_OP_AND?'&':'|');
+ if (!s) return NULL;
+
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ s1 = tree_string(tree->u.list.elements[i]);
+ if (!s1) {
+ free(s);
+ return NULL;
+ }
+ asprintf(&s2, "%s %s", s, s1);
+ free(s);
+ free(s1);
+ s = s2;
+ }
+ if (!s) {
+ return NULL;
+ }
+ asprintf(&s2, "%s )", s);
+ free(s);
+ s = s2;
+ break;
+
+ case LDB_OP_NOT:
+ s1 = tree_string(tree->u.not.child);
+ asprintf(&s, "( ! %s )", s1);
+ free(s1);
+ break;
+ }
+ return s;
+}
+
+
+/*
+ print a tree
+ */
+static void print_tree(struct ldb_parse_tree *tree)
+{
+ char *s = tree_string(tree);
+ printf("%s\n", s);
+ free(s);
+}
+
+
+ int main(void)
+{
+ char line[1000];
+ int ret = 0;
+
+ while (fgets(line, sizeof(line)-1, stdin)) {
+ struct ldb_parse_tree *tree;
+
+ if (line[strlen(line)-1] == '\n') {
+ line[strlen(line)-1] = 0;
+ }
+ tree = ldb_parse_tree(line);
+ if (!tree) {
+ fprintf(stderr, "Failed to parse\n");
+ ret = 1;
+ continue;
+ }
+ print_tree(tree);
+ ldb_parse_tree_free(tree);
+ }
+
+ return ret;
+}
+#endif /* TEST_PROGRAM */
+
diff --git a/source4/lib/ldb/ldb_tdb/ldb_parse.h b/source4/lib/ldb/ldb_tdb/ldb_parse.h
new file mode 100644
index 0000000000..995e0e04ff
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_parse.h
@@ -0,0 +1,40 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ parse a LDAP-like expression - header
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+enum ldb_parse_op {LDB_OP_SIMPLE, LDB_OP_AND, LDB_OP_OR, LDB_OP_NOT};
+
+struct ldb_parse_tree {
+ enum ldb_parse_op operation;
+ union {
+ struct {
+ char *attr;
+ struct ldb_val value;
+ } simple;
+ struct {
+ unsigned int num_elements;
+ struct ldb_parse_tree **elements;
+ } list;
+ struct {
+ struct ldb_parse_tree *child;
+ } not;
+ } u;
+};
diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c
new file mode 100644
index 0000000000..952053c920
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_search.c
@@ -0,0 +1,482 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb search functions
+ *
+ * Description: functions to search ldb+tdb databases
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb_tdb/ldb_tdb.h"
+
+/*
+ free a message that has all parts separately allocated
+*/
+static void msg_free_all_parts(struct ldb_message *msg)
+{
+ int i;
+ if (msg->dn) free(msg->dn);
+ for (i=0;i<msg->num_elements;i++) {
+ if (msg->elements[i].name) free(msg->elements[i].name);
+ if (msg->elements[i].value.data) free(msg->elements[i].value.data);
+ }
+ free(msg->elements);
+ free(msg);
+}
+
+
+/*
+ TODO: this should take advantage of the sorted nature of the message
+ return index of the attribute, or -1 if not found
+*/
+int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr)
+{
+ int i;
+ for (i=0;i<msg->num_elements;i++) {
+ if (strcmp(msg->elements[i].name, attr) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ duplicate a ldb_val structure
+*/
+static struct ldb_val ldb_val_dup(const struct ldb_val *v)
+{
+ struct ldb_val v2;
+ v2.length = v->length;
+ if (v->length == 0) {
+ v2.data = NULL;
+ return v2;
+ }
+
+ /* the +1 is to cope with buggy C library routines like strndup
+ that look one byte beyond */
+ v2.data = malloc(v->length+1);
+ if (!v2.data) {
+ v2.length = 0;
+ return v2;
+ }
+
+ memcpy(v2.data, v->data, v->length);
+ ((char *)v2.data)[v->length] = 0;
+ return v2;
+}
+
+
+
+/*
+ add one element to a message
+*/
+static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el)
+{
+ struct ldb_message_element *e2;
+
+ e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1);
+ if (!e2) {
+ return -1;
+ }
+ ret->elements = e2;
+
+ e2[ret->num_elements].name = strdup(el->name);
+ if (!e2[ret->num_elements].name) {
+ return -1;
+ }
+ e2[ret->num_elements].value = ldb_val_dup(&el->value);
+ if (e2[ret->num_elements].value.length != el->value.length) {
+ return -1;
+ }
+
+ ret->num_elements++;
+
+ return 0;
+}
+
+/*
+ add all elements from one message into another
+ */
+static int msg_add_all_elements(struct ldb_message *ret,
+ const struct ldb_message *msg)
+{
+ int i;
+ for (i=0;i<msg->num_elements;i++) {
+ if (msg_add_element(ret, &msg->elements[i]) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ pull the specified list of attributes from a message
+ */
+static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const char **attrs)
+{
+ struct ldb_message *ret;
+ int i;
+
+ ret = malloc_p(struct ldb_message);
+ if (!ret) {
+ return NULL;
+ }
+
+ ret->dn = strdup(msg->dn);
+ if (!ret->dn) {
+ free(ret);
+ return NULL;
+ }
+
+ ret->num_elements = 0;
+ ret->elements = NULL;
+ ret->private = NULL;
+
+ if (!attrs) {
+ if (msg_add_all_elements(ret, msg) != 0) {
+ msg_free_all_parts(ret);
+ return NULL;
+ }
+ return ret;
+ }
+
+ for (i=0;attrs[i];i++) {
+ int j;
+
+ if (strcmp(attrs[i], "*") == 0) {
+ if (msg_add_all_elements(ret, msg) != 0) {
+ msg_free_all_parts(ret);
+ return NULL;
+ }
+ continue;
+ }
+ j = ldb_msg_find_attr(msg, attrs[i]);
+ if (j == -1) {
+ continue;
+ }
+ do {
+ if (msg_add_element(ret, &msg->elements[j]) != 0) {
+ msg_free_all_parts(ret);
+ return NULL;
+ }
+ } while (++j < msg->num_elements &&
+ strcmp(attrs[i], msg->elements[j].name) == 0);
+ }
+
+ return ret;
+}
+
+
+
+/*
+ see if a ldb_val is a wildcard
+*/
+int ltdb_has_wildcard(const struct ldb_val *val)
+{
+ if (val->length == 1 && ((char *)val->data)[0] == '*') {
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ free the results of a ltdb_search_dn1 search
+*/
+void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
+{
+ free(msg->dn);
+ free(msg->private);
+ if (msg->elements) free(msg->elements);
+}
+
+
+/*
+ search the database for a single simple dn, returning all attributes
+ in a single message
+
+ return 1 on success, 0 on record-not-found and -1 on error
+*/
+int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message *msg)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ int ret;
+ TDB_DATA tdb_key, tdb_data;
+
+ /* form the key */
+ tdb_key = ltdb_key(dn);
+ if (!tdb_key.dptr) {
+ return -1;
+ }
+
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+ free(tdb_key.dptr);
+ if (!tdb_data.dptr) {
+ return 0;
+ }
+
+ msg->dn = strdup(dn);
+ if (!msg->dn) {
+ free(tdb_data.dptr);
+ return -1;
+ }
+ msg->private = tdb_data.dptr;
+ msg->num_elements = 0;
+ msg->elements = NULL;
+
+ ret = ltdb_unpack_data(ldb, &tdb_data, msg);
+ if (ret == -1) {
+ free(tdb_data.dptr);
+ return -1;
+ }
+
+ return 1;
+}
+
+
+/*
+ search the database for a single simple dn
+*/
+int ltdb_search_dn(struct ldb_context *ldb, char *dn,
+ const char *attrs[], struct ldb_message ***res)
+{
+ int ret;
+ struct ldb_message msg, *msg2;
+
+ ret = ltdb_search_dn1(ldb, dn, &msg);
+ if (ret != 1) {
+ return ret;
+ }
+
+ msg2 = ltdb_pull_attrs(ldb, &msg, attrs);
+
+ ltdb_search_dn1_free(ldb, &msg);
+
+ if (!msg2) {
+ return -1;
+ }
+
+ *res = malloc_array_p(struct ldb_message *, 2);
+ if (! *res) {
+ msg_free_all_parts(msg2);
+ return -1;
+ }
+
+ (*res)[0] = msg2;
+ (*res)[1] = NULL;
+
+ return 1;
+}
+
+
+/*
+ add a set of attributes from a record to a set of results
+ return 0 on success, -1 on failure
+*/
+int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrs[],
+ unsigned int *count,
+ struct ldb_message ***res)
+{
+ struct ldb_message *msg2;
+ struct ldb_message **res2;
+
+ /* pull the attributes that the user wants */
+ msg2 = ltdb_pull_attrs(ldb, msg, attrs);
+ if (!msg2) {
+ return -1;
+ }
+
+ /* add to the results list */
+ res2 = realloc_p(*res, struct ldb_message *, (*count)+2);
+ if (!res2) {
+ msg_free_all_parts(msg2);
+ return -1;
+ }
+
+ (*res) = res2;
+
+ (*res)[*count] = msg2;
+ (*res)[(*count)+1] = NULL;
+ (*count)++;
+
+ return 0;
+}
+
+
+/*
+ internal search state during a full db search
+*/
+struct ltdb_search_info {
+ struct ldb_context *ldb;
+ struct ldb_parse_tree *tree;
+ const char *base;
+ enum ldb_scope scope;
+ const char **attrs;
+ struct ldb_message **msgs;
+ int failures;
+ int count;
+};
+
+
+/*
+ search function for a non-indexed search
+ */
+static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct ltdb_search_info *sinfo = state;
+ struct ldb_message msg;
+ int ret;
+
+ if (key.dsize < 4 ||
+ strncmp(key.dptr, "DN=", 3) != 0) {
+ return 0;
+ }
+
+ msg.dn = key.dptr + 3;
+
+ /* unpack the record */
+ ret = ltdb_unpack_data(sinfo->ldb, &data, &msg);
+ if (ret == -1) {
+ sinfo->failures++;
+ return 0;
+ }
+
+ /* see if it matches the given expression */
+ if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree,
+ sinfo->base, sinfo->scope)) {
+ if (msg.elements) free(msg.elements);
+ return 0;
+ }
+
+ ret = ltdb_add_attr_results(sinfo->ldb, &msg, sinfo->attrs, &sinfo->count, &sinfo->msgs);
+
+ if (ret == -1) {
+ sinfo->failures++;
+ }
+
+ if (msg.elements) free(msg.elements);
+
+ return ret;
+}
+
+
+/*
+ free a set of search results
+*/
+int ltdb_search_free(struct ldb_context *ldb, struct ldb_message **msgs)
+{
+ int i;
+
+ if (!msgs) return 0;
+
+ for (i=0;msgs[i];i++) {
+ msg_free_all_parts(msgs[i]);
+ }
+
+ free(msgs);
+
+ return 0;
+}
+
+/*
+ search the database with a LDAP-like expression.
+ this is the "full search" non-indexed varient
+*/
+static int ltdb_search_full(struct ldb_context *ldb,
+ const char *base,
+ enum ldb_scope scope,
+ struct ldb_parse_tree *tree,
+ const char *attrs[], struct ldb_message ***res)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ int ret;
+ struct ltdb_search_info sinfo;
+
+ sinfo.tree = tree;
+ sinfo.ldb = ldb;
+ sinfo.scope = scope;
+ sinfo.base = base;
+ sinfo.attrs = attrs;
+ sinfo.msgs = NULL;
+ sinfo.count = 0;
+ sinfo.failures = 0;
+
+ ret = tdb_traverse(ltdb->tdb, search_func, &sinfo);
+
+ if (ret == -1) {
+ ltdb_search_free(ldb, sinfo.msgs);
+ return -1;
+ }
+
+ *res = sinfo.msgs;
+ return sinfo.count;
+}
+
+
+/*
+ search the database with a LDAP-like expression.
+ choses a search method
+*/
+int ltdb_search(struct ldb_context *ldb, const char *base,
+ enum ldb_scope scope, const char *expression,
+ const char *attrs[], struct ldb_message ***res)
+{
+ struct ldb_parse_tree *tree;
+ int ret;
+
+ *res = NULL;
+
+ /* form a parse tree for the expression */
+ tree = ldb_parse_tree(expression);
+ if (!tree) {
+ return -1;
+ }
+
+ if (tree->operation == LDB_OP_SIMPLE &&
+ strcmp(tree->u.simple.attr, "dn") == 0 &&
+ !ltdb_has_wildcard(&tree->u.simple.value)) {
+ /* yay! its a nice simple one */
+ ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res);
+ } else {
+ ret = ltdb_search_indexed(ldb, base, scope, tree, attrs, res);
+ if (ret == -1) {
+ ret = ltdb_search_full(ldb, base, scope, tree, attrs, res);
+ }
+ }
+
+ ldb_parse_tree_free(tree);
+
+ return ret;
+}
+
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
new file mode 100644
index 0000000000..17931352f7
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -0,0 +1,303 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb tdb backend
+ *
+ * Description: core functions for tdb backend
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb_tdb/ldb_tdb.h"
+
+/*
+ form a TDB_DATA for a record key
+ caller frees
+*/
+struct TDB_DATA ltdb_key(const char *dn)
+{
+ TDB_DATA key;
+ char *key_str = NULL;
+
+ asprintf(&key_str, "DN=%s", dn);
+ if (!key_str) {
+ errno = ENOMEM;
+ key.dptr = NULL;
+ key.dsize = 0;
+ return key;
+ }
+
+ key.dptr = key_str;
+ key.dsize = strlen(key_str)+1;
+
+ return key;
+}
+
+
+/*
+ store a record into the db
+*/
+int ltdb_store(struct ldb_context *ldb, const struct ldb_message *msg, int flgs)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ TDB_DATA tdb_key, tdb_data;
+ int ret;
+
+ tdb_key = ltdb_key(msg->dn);
+ if (!tdb_key.dptr) {
+ return -1;
+ }
+
+ ret = ltdb_pack_data(ldb, msg, &tdb_data);
+ if (ret == -1) {
+ free(tdb_key.dptr);
+ return -1;
+ }
+
+ ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs);
+ if (ret == -1) {
+ goto done;
+ }
+
+ ret = ltdb_index_add(ldb, msg);
+ if (ret == -1) {
+ tdb_delete(ltdb->tdb, tdb_key);
+ }
+
+done:
+ free(tdb_key.dptr);
+ free(tdb_data.dptr);
+
+ return ret;
+}
+
+
+/*
+ add a record to the database
+*/
+static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg)
+{
+ return ltdb_store(ldb, msg, TDB_INSERT);
+}
+
+
+/*
+ delete a record from the database, not updating indexes (used for deleting
+ index records)
+*/
+int ltdb_delete_noindex(struct ldb_context *ldb, const char *dn)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ TDB_DATA tdb_key;
+ int ret;
+
+ tdb_key = ltdb_key(dn);
+ if (!tdb_key.dptr) {
+ return -1;
+ }
+
+ ret = tdb_delete(ltdb->tdb, tdb_key);
+ free(tdb_key.dptr);
+
+ return ret;
+}
+
+/*
+ delete a record from the database
+*/
+static int ltdb_delete(struct ldb_context *ldb, const char *dn)
+{
+ int ret;
+ struct ldb_message msg;
+
+ /* in case any attribute of the message was indexed, we need
+ to fetch the old record */
+ ret = ltdb_search_dn1(ldb, dn, &msg);
+ if (ret != 1) {
+ /* not finding the old record is an error */
+ return -1;
+ }
+
+ ret = ltdb_delete_noindex(ldb, dn);
+ if (ret == -1) {
+ ltdb_search_dn1_free(ldb, &msg);
+ return -1;
+ }
+
+ /* remove any indexed attributes */
+ ret = ltdb_index_del(ldb, &msg);
+
+ ltdb_search_dn1_free(ldb, &msg);
+
+ return ret;
+}
+
+
+/*
+ modify a record
+*/
+static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ TDB_DATA tdb_key, tdb_data;
+ struct ldb_message msg2;
+ int ret;
+
+ tdb_key = ltdb_key(msg->dn);
+ if (!tdb_key.dptr) {
+ return -1;
+ }
+
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+ if (!tdb_data.dptr) {
+ free(tdb_key.dptr);
+ return -1;
+ }
+
+ ret = ltdb_unpack_data(ldb, &tdb_data, &msg2);
+ if (ret == -1) {
+ free(tdb_key.dptr);
+ free(tdb_data.dptr);
+ return -1;
+ }
+
+#if 0
+ for (i=0;i<msg->num_elements;i++) {
+ switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_ADD:
+ ret = find_element(&msg2, msg->elements[i].name);
+ if (ret != -1) {
+ errno = EEXIST;
+ goto failed;
+ }
+
+ }
+ }
+
+failed:
+#endif
+
+ free(tdb_key.dptr);
+ free(tdb_data.dptr);
+ if (msg2.elements) free(msg2.elements);
+
+ return -1;
+}
+
+/*
+ close database
+*/
+static int ltdb_close(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ int ret;
+ ret = tdb_close(ltdb->tdb);
+ free(ltdb);
+ free(ldb);
+ return ret;
+}
+
+
+/*
+ return extended error information
+*/
+static const char *ltdb_errstring(struct ldb_context *ldb)
+{
+ struct ltdb_private *ltdb = ldb->private;
+ return tdb_errorstr(ltdb->tdb);
+}
+
+
+static const struct ldb_backend_ops ltdb_ops = {
+ ltdb_close,
+ ltdb_search,
+ ltdb_search_free,
+ ltdb_add,
+ ltdb_modify,
+ ltdb_delete,
+ ltdb_errstring
+};
+
+
+/*
+ connect to the database
+*/
+struct ldb_context *ltdb_connect(const char *url,
+ unsigned int flags,
+ const char *options[])
+{
+ const char *path;
+ int tdb_flags, open_flags;
+ struct ltdb_private *ltdb;
+ TDB_CONTEXT *tdb;
+ struct ldb_context *ldb;
+
+ /* parse the url */
+ if (strncmp(url, "tdb://", 6) != 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ path = url+6;
+
+ tdb_flags = TDB_DEFAULT;
+
+ if (flags & LDB_FLG_RDONLY) {
+ open_flags = O_RDONLY;
+ } else {
+ open_flags = O_CREAT | O_RDWR;
+ }
+
+ tdb = tdb_open(path, 0, tdb_flags, open_flags, 0666);
+ if (!tdb) {
+ return NULL;
+ }
+
+ ltdb = malloc_p(struct ltdb_private);
+ if (!ltdb) {
+ tdb_close(tdb);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ltdb->tdb = tdb;
+
+
+ ldb = malloc_p(struct ldb_context);
+ if (!ldb) {
+ tdb_close(tdb);
+ free(ltdb);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ldb->private = ltdb;
+ ldb->ops = &ltdb_ops;
+
+ return ldb;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
new file mode 100644
index 0000000000..edf525f895
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
@@ -0,0 +1,11 @@
+/* this private structure is used by the ltdb backend in the
+ ldb_context */
+struct ltdb_private {
+ TDB_CONTEXT *tdb;
+ unsigned int connect_flags;
+};
+
+
+#define IVAL(p, ofs) (((unsigned *)((char *)(p) + (ofs)))[0])
+#define SIVAL(p, ofs, v) do { IVAL(p, ofs) = (v); } while (0)
+
diff --git a/source4/lib/ldb/ldb_tdb/ldbadd.c b/source4/lib/ldb/ldb_tdb/ldbadd.c
new file mode 100644
index 0000000000..3959a17525
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldbadd.c
@@ -0,0 +1,55 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ a utility to add elements to a ldb
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+ int main(void)
+{
+ static struct ldb_context *ldb;
+ struct ldb_message *msg;
+ int ret;
+ int count=0, failures=0;
+
+ ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
+
+ if (!ldb) {
+ perror("ldb_connect");
+ exit(1);
+ }
+
+ while ((msg = ldif_read(stdin))) {
+ ret = ldb->ops->add(ldb, msg);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to add record '%s'\n", msg->dn);
+ failures++;
+ } else {
+ count++;
+ }
+ ldif_read_free(msg);
+ }
+
+ ldb->ops->close(ldb);
+
+ printf("Added %d records with %d failures\n", count, failures);
+
+ return 0;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldbdel.c b/source4/lib/ldb/ldb_tdb/ldbdel.c
new file mode 100644
index 0000000000..8f8a03913f
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldbdel.c
@@ -0,0 +1,50 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ a utility to delete elements in a ldb
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+ int main(int argc, const char *argv[])
+{
+ static struct ldb_context *ldb;
+ int ret, i;
+
+ if (argc < 2) {
+ printf("Usage: ldbdel <dn...>\n");
+ exit(1);
+ }
+
+ ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
+ if (!ldb) {
+ perror("ldb_connect");
+ exit(1);
+ }
+
+ for (i=1;i<argc;i++) {
+ ret = ldb->ops->delete(ldb, argv[i]);
+ if (ret != 0) {
+ printf("delete of '%s' failed\n", argv[i]);
+ }
+ }
+
+ ldb->ops->close(ldb);
+ return 0;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldbsearch.c b/source4/lib/ldb/ldb_tdb/ldbsearch.c
new file mode 100644
index 0000000000..a6d63e78d5
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldbsearch.c
@@ -0,0 +1,73 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ simple ldb search tool
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+ int main(int argc, const char *argv[])
+{
+ static struct ldb_context *ldb;
+ struct ldb_message **msgs;
+ int ret, i;
+ const char *expression;
+ const char **attrs = NULL;
+
+ if (argc < 2) {
+ printf("Usage: ldbsearch <expression> [attrs...]\n");
+ exit(1);
+ }
+
+ if (argc > 2) {
+ attrs = argv+2;
+ }
+
+ expression = argv[1];
+
+ ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
+
+ if (!ldb) {
+ perror("ldb_connect");
+ exit(1);
+ }
+
+ ret = ldb->ops->search(ldb, expression, attrs, &msgs);
+
+ if (ret == -1) {
+ printf("search failed\n");
+ exit(1);
+ }
+
+ printf("# returned %d records\n", ret);
+
+ for (i=0;i<ret;i++) {
+ printf("# record %d\n", i+1);
+ ldif_write(stdout, msgs[i]);
+ }
+
+ ret = ldb->ops->search_free(ldb, msgs);
+ if (ret == -1) {
+ fprintf(stderr, "search_free failed\n");
+ exit(1);
+ }
+
+ ldb->ops->close(ldb);
+ return 0;
+}