r18866: Jeremy and Volker have given the go-ahead on the group mapping ldb
authorAndrew Tridgell <tridge@samba.org>
Sun, 24 Sep 2006 02:49:04 +0000 (02:49 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:14:36 +0000 (12:14 -0500)
code. Yay!

This first commit copies lib/ldb/ from Samba4. A huge congratulations
should go to Simo on this - he has put an enormous amount of work into
ldb, and it's great to see it go into the Samba3 tree.
(This used to be commit bbedf2e34315f5c420a3a05dfe22b1d5cf79f042)

126 files changed:
source3/lib/ldb/Doxyfile [new file with mode: 0644]
source3/lib/ldb/Makefile.in [new file with mode: 0644]
source3/lib/ldb/README_gcov.txt [new file with mode: 0644]
source3/lib/ldb/aclocal.m4 [new file with mode: 0644]
source3/lib/ldb/autogen.sh [new file with mode: 0755]
source3/lib/ldb/common/attrib_handlers.c [new file with mode: 0644]
source3/lib/ldb/common/ldb.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_attributes.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_controls.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_debug.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_dn.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_ldif.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_match.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_modules.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_msg.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_parse.c [new file with mode: 0644]
source3/lib/ldb/common/ldb_utf8.c [new file with mode: 0644]
source3/lib/ldb/common/qsort.c [new file with mode: 0644]
source3/lib/ldb/config.guess [new file with mode: 0755]
source3/lib/ldb/config.mk [new file with mode: 0644]
source3/lib/ldb/config.sub [new file with mode: 0755]
source3/lib/ldb/configure.ac [new file with mode: 0644]
source3/lib/ldb/docs/builddocs.sh [new file with mode: 0755]
source3/lib/ldb/docs/design.txt [new file with mode: 0644]
source3/lib/ldb/docs/installdocs.sh [new file with mode: 0755]
source3/lib/ldb/examples.dox [new file with mode: 0644]
source3/lib/ldb/examples/ldbreader.c [new file with mode: 0644]
source3/lib/ldb/examples/ldifreader.c [new file with mode: 0644]
source3/lib/ldb/include/dlinklist.h [new file with mode: 0644]
source3/lib/ldb/include/includes.h [new file with mode: 0644]
source3/lib/ldb/include/ldb.h [new file with mode: 0644]
source3/lib/ldb/include/ldb_errors.h [new file with mode: 0644]
source3/lib/ldb/include/ldb_private.h [new file with mode: 0644]
source3/lib/ldb/install-sh [new file with mode: 0755]
source3/lib/ldb/ldap.m4 [new file with mode: 0644]
source3/lib/ldb/ldb.pc.in [new file with mode: 0644]
source3/lib/ldb/ldb_ildap/ldb_ildap.c [new file with mode: 0644]
source3/lib/ldb/ldb_ldap/ldb_ldap.c [new file with mode: 0644]
source3/lib/ldb/ldb_sqlite3/README [new file with mode: 0644]
source3/lib/ldb/ldb_sqlite3/base160.c [new file with mode: 0644]
source3/lib/ldb/ldb_sqlite3/ldb_sqlite3.c [new file with mode: 0644]
source3/lib/ldb/ldb_sqlite3/schema [new file with mode: 0644]
source3/lib/ldb/ldb_sqlite3/trees.ps [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_cache.c [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_index.c [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_pack.c [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_search.c [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_tdb.c [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_tdb.h [new file with mode: 0644]
source3/lib/ldb/ldb_tdb/ldb_tdb_wrap.c [new file with mode: 0644]
source3/lib/ldb/libldb.m4 [new file with mode: 0644]
source3/lib/ldb/mainpage.dox [new file with mode: 0644]
source3/lib/ldb/man/ad2oLschema.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldb.3.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbadd.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbdel.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbedit.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbmodify.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbrename.1.xml [new file with mode: 0644]
source3/lib/ldb/man/ldbsearch.1.xml [new file with mode: 0644]
source3/lib/ldb/man/oLschema2ldif.1.xml [new file with mode: 0644]
source3/lib/ldb/modules/asq.c [new file with mode: 0644]
source3/lib/ldb/modules/ldb_map.c [new file with mode: 0644]
source3/lib/ldb/modules/ldb_map.h [new file with mode: 0644]
source3/lib/ldb/modules/ldb_map_inbound.c [new file with mode: 0644]
source3/lib/ldb/modules/ldb_map_outbound.c [new file with mode: 0644]
source3/lib/ldb/modules/ldb_map_private.h [new file with mode: 0644]
source3/lib/ldb/modules/objectclass.c [new file with mode: 0644]
source3/lib/ldb/modules/operational.c [new file with mode: 0644]
source3/lib/ldb/modules/paged_results.c [new file with mode: 0644]
source3/lib/ldb/modules/paged_searches.c [new file with mode: 0644]
source3/lib/ldb/modules/rdn_name.c [new file with mode: 0644]
source3/lib/ldb/modules/schema.c [new file with mode: 0644]
source3/lib/ldb/modules/skel.c [new file with mode: 0644]
source3/lib/ldb/modules/sort.c [new file with mode: 0644]
source3/lib/ldb/samba/README [new file with mode: 0644]
source3/lib/ldb/samba/ldif_handlers.c [new file with mode: 0644]
source3/lib/ldb/sqlite3.m4 [new file with mode: 0644]
source3/lib/ldb/swig/Ldb.py [new file with mode: 0644]
source3/lib/ldb/swig/ldb.i [new file with mode: 0644]
source3/lib/ldb/tests/init.ldif [new file with mode: 0644]
source3/lib/ldb/tests/init_slapd.sh [new file with mode: 0755]
source3/lib/ldb/tests/kill_slapd.sh [new file with mode: 0755]
source3/lib/ldb/tests/ldapi_url.sh [new file with mode: 0755]
source3/lib/ldb/tests/photo.ldif [new file with mode: 0644]
source3/lib/ldb/tests/samba4.png [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-add-test.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif [new file with mode: 0644]
source3/lib/ldb/tests/schema-tests/schema.ldif [new file with mode: 0644]
source3/lib/ldb/tests/slapd.conf [new file with mode: 0644]
source3/lib/ldb/tests/start_slapd.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-attribs.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-config.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-default-config.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-extended.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-generic.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-index.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-ldap.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-modify.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-schema.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-sqlite3.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-tdb-features.sh [new file with mode: 0644]
source3/lib/ldb/tests/test-tdb.sh [new file with mode: 0755]
source3/lib/ldb/tests/test-wildcard.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test-wrong_attributes.ldif [new file with mode: 0644]
source3/lib/ldb/tests/test.ldif [new file with mode: 0644]
source3/lib/ldb/tests/testdata.txt [new file with mode: 0644]
source3/lib/ldb/tests/testsearch.txt [new file with mode: 0644]
source3/lib/ldb/tools/ad2oLschema.c [new file with mode: 0644]
source3/lib/ldb/tools/cmdline.c [new file with mode: 0644]
source3/lib/ldb/tools/cmdline.h [new file with mode: 0644]
source3/lib/ldb/tools/convert.c [new file with mode: 0644]
source3/lib/ldb/tools/convert.h [new file with mode: 0644]
source3/lib/ldb/tools/ldbadd.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbdel.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbedit.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbmodify.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbrename.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbsearch.c [new file with mode: 0644]
source3/lib/ldb/tools/ldbtest.c [new file with mode: 0644]
source3/lib/ldb/tools/oLschema2ldif.c [new file with mode: 0644]
source3/lib/ldb/web/index.html [new file with mode: 0644]

diff --git a/source3/lib/ldb/Doxyfile b/source3/lib/ldb/Doxyfile
new file mode 100644 (file)
index 0000000..07b12b5
--- /dev/null
@@ -0,0 +1,26 @@
+PROJECT_NAME           = LDB
+OUTPUT_DIRECTORY       = apidocs
+REPEAT_BRIEF           = YES
+OPTIMIZE_OUTPUT_FOR_C  = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+GENERATE_TODOLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+SHOW_USED_FILES        = NO
+SHOW_DIRECTORIES       = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+INPUT                  = include .
+FILE_PATTERNS          = *.h *.dox
+EXCLUDE                = include/config.h include/dlinklist.h \
+                       include/includes.h
+EXAMPLE_PATH           = examples
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+GENERATE_MAN           = YES
+ALWAYS_DETAILED_SEC       = YES
+JAVADOC_AUTOBRIEF         = YES
diff --git a/source3/lib/ldb/Makefile.in b/source3/lib/ldb/Makefile.in
new file mode 100644 (file)
index 0000000..106e493
--- /dev/null
@@ -0,0 +1,159 @@
+#!gmake
+#
+CC = @CC@
+GCOV = @GCOV@
+XSLTPROC = @XSLTPROC@
+DOXYGEN = @DOXYGEN@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+includedir = @includedir@
+libdir = @libdir@
+bindir = @bindir@
+mandir = @mandir@
+VPATH = @srcdir@:@tdbdir@:@tallocdir@:@libreplacedir@:@poptdir@
+srcdir = @srcdir@
+builddir = @builddir@
+SLAPD = @SLAPD@
+EXTRA_OBJ=@EXTRA_OBJ@
+TESTS=test-tdb.sh @TESTS@
+
+CFLAGS=-I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
+       @POPT_CFLAGS@ -I@tallocdir@ -I@tdbdir@/include -I@libreplacedir@ \
+       -DLIBDIR=\"$(libdir)\" -DSHLIBEXT=\"@SHLIBEXT@\" -DUSE_MMAP=1 @CFLAGS@
+
+LIB_FLAGS=-Llib -lldb @LIBS@ @POPT_LIBS@
+
+LDB_TDB_DIR=ldb_tdb
+LDB_TDB_OBJ=$(LDB_TDB_DIR)/ldb_tdb.o \
+       $(LDB_TDB_DIR)/ldb_pack.o $(LDB_TDB_DIR)/ldb_search.o $(LDB_TDB_DIR)/ldb_index.o \
+       $(LDB_TDB_DIR)/ldb_cache.o $(LDB_TDB_DIR)/ldb_tdb_wrap.o
+
+COMDIR=common
+COMMON_OBJ=$(COMDIR)/ldb.o $(COMDIR)/ldb_ldif.o \
+          $(COMDIR)/ldb_parse.o $(COMDIR)/ldb_msg.o $(COMDIR)/ldb_utf8.o \
+          $(COMDIR)/ldb_debug.o $(COMDIR)/ldb_modules.o \
+          $(COMDIR)/ldb_dn.o $(COMDIR)/ldb_match.o $(COMDIR)/ldb_attributes.o \
+          $(COMDIR)/attrib_handlers.o $(COMDIR)/ldb_controls.o $(COMDIR)/qsort.o
+
+MODDIR=modules
+MODULES_OBJ=$(MODDIR)/operational.o $(MODDIR)/schema.o $(MODDIR)/rdn_name.o \
+          $(MODDIR)/objectclass.o \
+          $(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o
+
+OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) @TDBOBJ@ @TALLOCOBJ@ @POPTOBJ@ @LIBREPLACEOBJ@ $(EXTRA_OBJ) 
+
+LDB_LIB = lib/libldb.a
+
+BINS = bin/ldbadd bin/ldbsearch bin/ldbdel bin/ldbmodify bin/ldbedit bin/ldbrename bin/ldbtest bin/oLschema2ldif
+
+LIBS = $(LDB_LIB)
+
+EXAMPLES = examples/ldbreader examples/ldifreader
+
+DIRS = lib bin common ldb_tdb ldb_ldap ldb_sqlite3 modules tools examples
+
+all: showflags dirs $(OBJS) $(LDB_LIB) $(BINS) $(EXAMPLES) manpages
+
+showflags:
+       @echo 'ldb will be compiled with flags:'
+       @echo '  CFLAGS = $(CFLAGS)'
+       @echo '  LIBS = $(LIBS)'
+
+.c.o:
+       @echo Compiling $*.c
+       @mkdir -p `dirname $@`
+       @$(CC) $(CFLAGS) -c $< -o $@
+
+dirs:
+       @mkdir -p $(DIRS)
+
+lib/libldb.a: $(OBJS)
+       ar -rv $@ $(OBJS)
+       @-ranlib $@
+
+bin/ldbadd: tools/ldbadd.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbdel: tools/ldbdel.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbedit: tools/ldbedit.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbrename: tools/ldbrename.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/ldbtest: tools/ldbtest.o tools/cmdline.o $(LIBS)
+       $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS)
+
+bin/oLschema2ldif: tools/oLschema2ldif.o tools/cmdline.o tools/convert.o $(LIBS)
+       $(CC) -o bin/oLschema2ldif tools/oLschema2ldif.o tools/cmdline.o tools/convert.o $(LIB_FLAGS)
+
+examples/ldbreader: examples/ldbreader.o $(LIBS)
+       $(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS)
+
+examples/ldifreader: examples/ldifreader.o $(LIBS)
+       $(CC) -o examples/ldifreader examples/ldifreader.o $(LIB_FLAGS)
+
+.SUFFIXES: .1 .1.xml .3 .3.xml .xml .html
+
+manpages:
+       @$(srcdir)/docs/builddocs.sh "$(XSLTPROC)" "$(srcdir)"
+
+doxygen:
+       test -z "$(DOXYGEN)" || (cd $(srcdir) && "$(DOXYGEN)")
+
+clean:
+       rm -f *.o */*.o *.gcov */*.gc?? tdbtest.ldb*
+       rm -f $(BINS) $(TDB_OBJ) $(TALLOC_OBJ) $(LDB_LIB)
+       rm -f man/*.1 man/*.3 man/*.html
+       rm -f $(EXAMPLES)
+       rm -rf apidocs/
+       rm -rf tests/schema/
+
+distclean: clean
+       rm -f *~ */*~
+       rm -rf bin lib
+       rm -f config.log config.status config.cache include/config.h
+       rm -f ldb.pc
+       rm -f Makefile
+
+realdistclean: distclean
+       rm -f configure.in include/config.h.in
+
+test: all
+       for t in $(TESTS); do echo STARTING $${t}; $(srcdir)/tests/$${t} || exit 1; done
+
+valgrindtest: all
+       for t in $(TESTS); do echo STARTING $${t}; VALGRIND="valgrind -q --db-attach=yes --num-callers=30" $(srcdir)/tests/$${t} || exit 1; done
+
+installcheck: install test
+
+install: all
+       mkdir -p $(includedir) $(libdir)/pkgconfig $(libdir) $(bindir)
+       cp $(srcdir)/include/ldb.h $(srcdir)/include/ldb_errors.h $(includedir)
+       cp $(LDB_LIB) $(libdir)
+       cp $(BINS) $(bindir)
+       cp ldb.pc $(libdir)/pkgconfig
+       $(srcdir)/docs/installdocs.sh $(mandir)
+
+gcov:
+       $(GCOV) -po ldb_sqlite3 $(srcdir)/ldb_sqlite3/*.c 2| tee ldb_sqlite3.report.gcov
+       $(GCOV) -po ldb_ldap $(srcdir)/ldb_ldap/*.c 2| tee ldb_ldap.report.gcov
+       $(GCOV) -po ldb_tdb $(srcdir)/ldb_tdb/*.c 2| tee ldb_tdb.report.gcov
+       $(GCOV) -po common $(srcdir)/common/*.c 2| tee common.report.gcov
+       $(GCOV) -po modules $(srcdir)/modules/*.c 2| tee modules.report.gcov
+       $(GCOV) -po tools $(srcdir)/tools/*.c 2| tee tools.report.gcov
+
+etags:
+       etags `find $(srcdir) -name "*.[ch]"`
+
+ctags:
+       ctags `find $(srcdir) -name "*.[ch]"`
diff --git a/source3/lib/ldb/README_gcov.txt b/source3/lib/ldb/README_gcov.txt
new file mode 100644 (file)
index 0000000..2abd937
--- /dev/null
@@ -0,0 +1,29 @@
+Here is how to use gcov to test code coverage in ldb.
+
+Step 1: build ldb with gcov enabled
+
+     make clean all WITH_GCOV=1
+
+Step 3: run the test suite
+     make test-tdb
+
+Step 4: produce the gcov report
+     make gcov
+
+Step 5: read the summary reports
+     less *.report.gcov
+
+Step 6: examine the per-file reports
+     less ldb_tdb\#ldb_tdb.c.gcov
+
+You can also combine steps 2 to 4 like this:
+
+     make clean all test-tdb gcov WITH_GCOV=1
+
+Note that you should not expect 100% coverage, as some error paths
+(such as memory allocation failures) are very hard to trigger. There
+are ways of working around this, but they are quite tricky (they
+involve allocation wrappers that "fork and fail on malloc").
+
+The lines to look for in the per-file reports are the ones starting
+with "#####". Those are lines that are never executed. 
diff --git a/source3/lib/ldb/aclocal.m4 b/source3/lib/ldb/aclocal.m4
new file mode 100644 (file)
index 0000000..5605e47
--- /dev/null
@@ -0,0 +1 @@
+m4_include(libreplace.m4)
diff --git a/source3/lib/ldb/autogen.sh b/source3/lib/ldb/autogen.sh
new file mode 100755 (executable)
index 0000000..500cab8
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f configure config.h.in
+
+IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace"
+IPATHS="$IPATHS -I lib/talloc -I talloc -I ../talloc"
+IPATHS="$IPATHS -I lib/tdb -I tdb -I ../tdb"
+IPATHS="$IPATHS -I lib/popt -I popt -I ../popt"
+autoheader $IPATHS || exit 1
+autoconf $IPATHS || exit 1
+
+rm -rf autom4te.cache
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/source3/lib/ldb/common/attrib_handlers.c b/source3/lib/ldb/common/attrib_handlers.c
new file mode 100644 (file)
index 0000000..8e43796
--- /dev/null
@@ -0,0 +1,392 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*
+  attribute handlers for well known attribute types, selected by syntax OID
+  see rfc2252
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "system/locale.h"
+
+/*
+  default handler that just copies a ldb_val.
+*/
+int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
+                    const struct ldb_val *in, struct ldb_val *out)
+{
+       *out = ldb_val_dup(mem_ctx, in);
+       if (in->length > 0 && out->data == NULL) {
+               ldb_oom(ldb);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  a case folding copy handler, removing leading and trailing spaces and
+  multiple internal spaces
+
+  We exploit the fact that utf8 never uses the space octet except for
+  the space itself
+*/
+static int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
+                           const struct ldb_val *in, struct ldb_val *out)
+{
+       char *s, *t;
+       int l;
+       if (!in || !out || !(in->data)) {
+               return -1;
+       }
+
+       out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data));
+       if (out->data == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
+               return -1;
+       }
+
+       s = (char *)(out->data);
+       
+       /* remove trailing spaces if any */
+       l = strlen(s);
+       while (l > 0 && s[l - 1] == ' ') l--;
+       s[l] = '\0';
+       
+       /* remove leading spaces if any */
+       if (*s == ' ') {
+               for (t = s; *s == ' '; s++) ;
+
+               /* remove leading spaces by moving down the string */
+               memmove(t, s, l);
+
+               s = t;
+       }
+
+       /* check middle spaces */
+       while ((t = strchr(s, ' ')) != NULL) {
+               for (s = t; *s == ' '; s++) ;
+
+               if ((s - t) > 1) {
+                       l = strlen(s);
+
+                       /* remove all spaces but one by moving down the string */
+                       memmove(t + 1, s, l);
+               }
+       }
+
+       out->length = strlen((char *)out->data);
+       return 0;
+}
+
+
+
+/*
+  canonicalise a ldap Integer
+  rfc2252 specifies it should be in decimal form
+*/
+static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
+                                   const struct ldb_val *in, struct ldb_val *out)
+{
+       char *end;
+       long long i = strtoll((char *)in->data, &end, 0);
+       if (*end != 0) {
+               return -1;
+       }
+       out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
+       if (out->data == NULL) {
+               return -1;
+       }
+       out->length = strlen((char *)out->data);
+       return 0;
+}
+
+/*
+  compare two Integers
+*/
+static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
+                                 const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
+}
+
+/*
+  compare two binary blobs
+*/
+int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
+                         const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       if (v1->length != v2->length) {
+               return v1->length - v2->length;
+       }
+       return memcmp(v1->data, v2->data, v1->length);
+}
+
+/*
+  compare two case insensitive strings, ignoring multiple whitespaces
+  and leading and trailing whitespaces
+  see rfc2252 section 8.1
+       
+  try to optimize for the ascii case,
+  but if we find out an utf8 codepoint revert to slower but correct function
+*/
+static int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
+                              const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
+       char *b1, *b2, *u1, *u2;
+       int ret;
+       while (*s1 == ' ') s1++;
+       while (*s2 == ' ') s2++;
+       /* TODO: make utf8 safe, possibly with helper function from application */
+       while (*s1 && *s2) {
+               /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
+                * never appear in multibyte sequences */
+               if (((unsigned char)s1[0]) & 0x80) goto utf8str;
+               if (((unsigned char)s2[0]) & 0x80) goto utf8str;
+               if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
+                       break;
+               if (*s1 == ' ') {
+                       while (s1[0] == s1[1]) s1++;
+                       while (s2[0] == s2[1]) s2++;
+               }
+               s1++; s2++;
+       }
+       if (! (*s1 && *s2)) {
+               /* check for trailing spaces only if one of the pointers
+                * has reached the end of the strings otherwise we
+                * can mistakenly match.
+                * ex. "domain users" <-> "domainUpdates"
+                */
+               while (*s1 == ' ') s1++;
+               while (*s2 == ' ') s2++;
+       }
+       return (int)(toupper(*s1)) - (int)(toupper(*s2));
+
+utf8str:
+       /* non need to recheck from the start, just from the first utf8 char found */
+       b1 = u1 = ldb_casefold(ldb, mem_ctx, s1);
+       b2 = u2 = ldb_casefold(ldb, mem_ctx, s2);
+       
+       while (*u1 & *u2) {
+               if (*u1 != *u2)
+                       break;
+               if (*u1 == ' ') {
+                       while (u1[0] == u1[1]) u1++;
+                       while (u2[0] == u2[1]) u2++;
+               }
+               u1++; u2++;
+       }
+       if (! (*u1 && *u2)) {
+               while (*u1 == ' ') u1++;
+               while (*u2 == ' ') u2++;
+       }
+       ret = (int)(*u1 - *u2);
+       talloc_free(b1);
+       talloc_free(b2);
+
+       return ret;
+}
+
+/*
+  canonicalise a attribute in DN format
+*/
+static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
+                              const struct ldb_val *in, struct ldb_val *out)
+{
+       struct ldb_dn *dn;
+       int ret = -1;
+
+       out->length = 0;
+       out->data = NULL;
+
+       dn = ldb_dn_explode_casefold(ldb, (char *)in->data);
+       if (dn == NULL) {
+               return -1;
+       }
+
+       out->data = (uint8_t *)ldb_dn_linearize(mem_ctx, dn);
+       if (out->data == NULL) {
+               goto done;
+       }
+       out->length = strlen((char *)out->data);
+
+       ret = 0;
+
+done:
+       talloc_free(dn);
+
+       return ret;
+}
+
+/*
+  compare two dns
+*/
+static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
+                            const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       struct ldb_dn *dn1 = NULL, *dn2 = NULL;
+       int ret;
+
+       dn1 = ldb_dn_explode_casefold(mem_ctx, (char *)v1->data);
+       if (dn1 == NULL) return -1;
+
+       dn2 = ldb_dn_explode_casefold(mem_ctx, (char *)v2->data);
+       if (dn2 == NULL) {
+               talloc_free(dn1);
+               return -1;
+       } 
+
+       ret = ldb_dn_compare(ldb, dn1, dn2);
+
+       talloc_free(dn1);
+       talloc_free(dn2);
+       return ret;
+}
+
+/*
+  compare two objectclasses, looking at subclasses
+*/
+static int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
+                                     const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       int ret, i;
+       const char **subclasses;
+       ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
+       if (ret == 0) {
+               return 0;
+       }
+       subclasses = ldb_subclass_list(ldb, (char *)v1->data);
+       if (subclasses == NULL) {
+               return ret;
+       }
+       for (i=0;subclasses[i];i++) {
+               struct ldb_val vs;
+               vs.data = discard_const(subclasses[i]);
+               vs.length = strlen(subclasses[i]);
+               if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
+                       return 0;
+               }
+       }
+       return ret;
+}
+
+/*
+  compare two utc time values. 1 second resolution
+*/
+static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
+                                 const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       time_t t1, t2;
+       t1 = ldb_string_to_time((char *)v1->data);
+       t2 = ldb_string_to_time((char *)v2->data);
+       return (int)t2 - (int)t1;
+}
+
+/*
+  canonicalise a utc time
+*/
+static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
+                                   const struct ldb_val *in, struct ldb_val *out)
+{
+       time_t t = ldb_string_to_time((char *)in->data);
+       out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
+       if (out->data == NULL) {
+               return -1;
+       }
+       out->length = strlen((char *)out->data);
+       return 0;
+}
+
+/*
+  table of standard attribute handlers
+*/
+static const struct ldb_attrib_handler ldb_standard_attribs[] = {
+       { 
+               .attr            = LDB_SYNTAX_INTEGER,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_canonicalise_Integer,
+               .comparison_fn   = ldb_comparison_Integer
+       },
+       { 
+               .attr            = LDB_SYNTAX_OCTET_STRING,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_handler_copy,
+               .comparison_fn   = ldb_comparison_binary
+       },
+       { 
+               .attr            = LDB_SYNTAX_DIRECTORY_STRING,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_handler_fold,
+               .comparison_fn   = ldb_comparison_fold
+       },
+       { 
+               .attr            = LDB_SYNTAX_DN,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_canonicalise_dn,
+               .comparison_fn   = ldb_comparison_dn
+       },
+       { 
+               .attr            = LDB_SYNTAX_OBJECTCLASS,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_handler_fold,
+               .comparison_fn   = ldb_comparison_objectclass
+       },
+       { 
+               .attr            = LDB_SYNTAX_UTC_TIME,
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldb_canonicalise_utctime,
+               .comparison_fn   = ldb_comparison_utctime
+       }
+};
+
+
+/*
+  return the attribute handlers for a given syntax name
+*/
+const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
+                                                          const char *syntax)
+{
+       int i;
+       unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
+       /* TODO: should be replaced with a binary search */
+       for (i=0;i<num_handlers;i++) {
+               if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
+                       return &ldb_standard_attribs[i];
+               }
+       }
+       return NULL;
+}
+
diff --git a/source3/lib/ldb/common/ldb.c b/source3/lib/ldb/common/ldb.c
new file mode 100644 (file)
index 0000000..760a311
--- /dev/null
@@ -0,0 +1,1075 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+   Copyright (C) Simo Sorce  2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb core API
+ *
+ *  Description: core API routines interfacing to ldb backends
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/* 
+   initialise a ldb context
+   The mem_ctx is optional
+*/
+struct ldb_context *ldb_init(void *mem_ctx)
+{
+       struct ldb_context *ldb = talloc_zero(mem_ctx, struct ldb_context);
+       int ret;
+
+       ret = ldb_setup_wellknown_attributes(ldb);
+       if (ret != 0) {
+               talloc_free(ldb);
+               return NULL;
+       }
+
+       ldb_set_utf8_default(ldb);
+
+       return ldb;
+}
+
+static struct ldb_backend {
+       const char *name;
+       ldb_connect_fn connect_fn;
+       struct ldb_backend *prev, *next;
+} *ldb_backends = NULL;
+
+
+static ldb_connect_fn ldb_find_backend(const char *url)
+{
+       struct ldb_backend *backend;
+
+       for (backend = ldb_backends; backend; backend = backend->next) {
+               if (strncmp(backend->name, url, strlen(backend->name)) == 0) {
+                       return backend->connect_fn;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ register a new ldb backend
+*/
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn)
+{
+       struct ldb_backend *backend = talloc(talloc_autofree_context(), struct ldb_backend);
+
+       if (ldb_find_backend(url_prefix)) {
+               return LDB_SUCCESS;
+       }
+
+       /* Maybe check for duplicity here later on? */
+
+       backend->name = talloc_strdup(backend, url_prefix);
+       backend->connect_fn = connectfn;
+       DLIST_ADD(ldb_backends, backend);
+
+       return LDB_SUCCESS;
+}
+
+/* 
+   Return the ldb module form of a database. The URL can either be one of the following forms
+   ldb://path
+   ldapi://path
+
+   flags is made up of LDB_FLG_*
+
+   the options are passed uninterpreted to the backend, and are
+   backend specific.
+
+  This allows modules to get at only the backend module, for example where a module 
+  may wish to direct certain requests at a particular backend.
+*/
+int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[],
+                       struct ldb_module **backend_module)
+{
+       int ret;
+       char *backend;
+       ldb_connect_fn fn;
+
+       if (strchr(url, ':') != NULL) {
+               backend = talloc_strndup(ldb, url, strchr(url, ':')-url);
+       } else {
+               /* Default to tdb */
+               backend = talloc_strdup(ldb, "tdb");
+       }
+
+       fn = ldb_find_backend(backend);
+
+       if (fn == NULL) {
+               if (ldb_try_load_dso(ldb, backend) == 0) {
+                       fn = ldb_find_backend(backend);
+               }
+       }
+
+       talloc_free(backend);
+
+       if (fn == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to find backend for '%s'\n", url);
+               return LDB_ERR_OTHER;
+       }
+
+       ret = fn(ldb, url, ldb->flags, options, backend_module);
+
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to '%s'\n", url);
+               return ret;
+       }
+       return ret;
+}
+
+/*
+  try to autodetect a basedn if none specified. This fixes one of my
+  pet hates about ldapsearch, which is that you have to get a long,
+  complex basedn right to make any use of it.
+*/
+static const struct ldb_dn *ldb_set_default_basedn(struct ldb_context *ldb)
+{
+       TALLOC_CTX *tmp_ctx;
+       int ret;
+       static const char *attrs[] = { "defaultNamingContext", NULL };
+       struct ldb_result *res;
+       struct ldb_dn *basedn=NULL;
+
+       basedn = ldb_get_opaque(ldb, "default_baseDN");
+       if (basedn) {
+               return basedn;
+       }
+
+       tmp_ctx = talloc_new(ldb);
+       ret = ldb_search(ldb, ldb_dn_new(tmp_ctx), LDB_SCOPE_BASE, 
+                        "(objectClass=*)", attrs, &res);
+       if (ret == LDB_SUCCESS) {
+               if (res->count == 1) {
+                       basedn = ldb_msg_find_attr_as_dn(ldb, res->msgs[0], "defaultNamingContext");
+                       ldb_set_opaque(ldb, "default_baseDN", basedn);
+               }
+               talloc_free(res);
+       }
+
+       talloc_free(tmp_ctx);
+       return basedn;
+}
+
+const struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb)
+{
+       return ldb_get_opaque(ldb, "default_baseDN");
+}
+
+/* 
+ connect to a database. The URL can either be one of the following forms
+   ldb://path
+   ldapi://path
+
+   flags is made up of LDB_FLG_*
+
+   the options are passed uninterpreted to the backend, and are
+   backend specific
+*/
+int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[])
+{
+       int ret;
+
+       ldb->flags = flags;
+
+       ret = ldb_connect_backend(ldb, url, options, &ldb->modules);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       if (ldb_load_modules(ldb, options) != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to load modules for '%s'\n", url);
+               return LDB_ERR_OTHER;
+       }
+
+       /* TODO: get timeout from options if available there */
+       ldb->default_timeout = 300; /* set default to 5 minutes */
+
+       /* set the default base dn */
+       ldb_set_default_basedn(ldb);
+
+       return LDB_SUCCESS;
+}
+
+void ldb_set_errstring(struct ldb_context *ldb, const char *err_string)
+{
+       if (ldb->err_string) {
+               talloc_free(ldb->err_string);
+       }
+       ldb->err_string = talloc_strdup(ldb, err_string);
+}
+
+void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...)
+{
+       va_list ap;
+
+       if (ldb->err_string) {
+               talloc_free(ldb->err_string);
+       }
+
+       va_start(ap, format);
+       ldb->err_string = talloc_vasprintf(ldb, format, ap);
+       va_end(ap);
+}
+
+void ldb_reset_err_string(struct ldb_context *ldb)
+{
+       if (ldb->err_string) {
+               talloc_free(ldb->err_string);
+               ldb->err_string = NULL;
+       }
+}
+
+#define FIRST_OP(ldb, op) do { \
+       module = ldb->modules;                                  \
+       while (module && module->ops->op == NULL) module = module->next; \
+       if (module == NULL) {                                           \
+               ldb_asprintf_errstring(ldb, "unable to find module or backend to handle operation: " #op); \
+               return LDB_ERR_OPERATIONS_ERROR;                        \
+       } \
+} while (0)
+
+/*
+  start a transaction
+*/
+static int ldb_transaction_start_internal(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, start_transaction);
+
+       ldb_reset_err_string(ldb);
+
+       status = module->ops->start_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_asprintf_errstring(ldb,
+                                               "ldb transaction start: %s (%d)", 
+                                               ldb_strerror(status), 
+                                               status);
+               }
+       }
+       return status;
+}
+
+/*
+  commit a transaction
+*/
+static int ldb_transaction_commit_internal(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, end_transaction);
+
+       ldb_reset_err_string(ldb);
+
+       status = module->ops->end_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_asprintf_errstring(ldb, 
+                                               "ldb transaction commit: %s (%d)", 
+                                               ldb_strerror(status), 
+                                               status);
+               }
+       }
+       return status;
+}
+
+/*
+  cancel a transaction
+*/
+static int ldb_transaction_cancel_internal(struct ldb_context *ldb)
+{
+       struct ldb_module *module;
+       int status;
+       FIRST_OP(ldb, del_transaction);
+
+       status = module->ops->del_transaction(module);
+       if (status != LDB_SUCCESS) {
+               if (ldb->err_string == NULL) {
+                       /* no error string was setup by the backend */
+                       ldb_asprintf_errstring(ldb, 
+                                               "ldb transaction cancel: %s (%d)", 
+                                               ldb_strerror(status), 
+                                               status);
+               }
+       }
+       return status;
+}
+
+int ldb_transaction_start(struct ldb_context *ldb)
+{
+       /* disable autotransactions */
+       ldb->transaction_active++;
+
+       return ldb_transaction_start_internal(ldb);
+}
+
+int ldb_transaction_commit(struct ldb_context *ldb)
+{
+       /* renable autotransactions (when we reach 0) */
+       if (ldb->transaction_active > 0)
+               ldb->transaction_active--;
+
+       return ldb_transaction_commit_internal(ldb);
+}
+
+int ldb_transaction_cancel(struct ldb_context *ldb)
+{
+       /* renable autotransactions (when we reach 0) */
+       if (ldb->transaction_active > 0)
+               ldb->transaction_active--;
+
+       return ldb_transaction_cancel_internal(ldb);
+}
+
+static int ldb_autotransaction_start(struct ldb_context *ldb)
+{
+       /* explicit transaction active, ignore autotransaction request */
+       if (ldb->transaction_active)
+               return LDB_SUCCESS;
+
+       return ldb_transaction_start_internal(ldb);
+}
+
+static int ldb_autotransaction_commit(struct ldb_context *ldb)
+{
+       /* explicit transaction active, ignore autotransaction request */
+       if (ldb->transaction_active)
+               return LDB_SUCCESS;
+
+       return ldb_transaction_commit_internal(ldb);
+}
+
+static int ldb_autotransaction_cancel(struct ldb_context *ldb)
+{
+       /* explicit transaction active, ignore autotransaction request */
+       if (ldb->transaction_active)
+               return LDB_SUCCESS;
+
+       return ldb_transaction_cancel_internal(ldb);
+}
+
+/* autostarts a transacion if none active */
+static int ldb_autotransaction_request(struct ldb_context *ldb, struct ldb_request *req)
+{
+       int ret;
+
+       ret = ldb_autotransaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_request(ldb, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_SUCCESS) {
+               return ldb_autotransaction_commit(ldb);
+       }
+       ldb_autotransaction_cancel(ldb);
+
+       if (ldb->err_string == NULL) {
+               /* no error string was setup by the backend */
+               ldb_asprintf_errstring(ldb, "%s (%d)", ldb_strerror(ret), ret);
+       }
+
+       return ret;
+}
+
+int ldb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (!handle) {
+               return LDB_SUCCESS;
+       }
+
+       return handle->module->ops->wait(handle, type);
+}
+
+/* set the specified timeout or, if timeout is 0 set the default timeout */
+/* timeout == -1 means no timeout */
+int ldb_set_timeout(struct ldb_context *ldb, struct ldb_request *req, int timeout)
+{
+       if (req == NULL) return LDB_ERR_OPERATIONS_ERROR;
+       
+       if (timeout != 0) {
+               req->timeout = timeout;
+       } else {
+               req->timeout = ldb->default_timeout;
+       }
+       req->starttime = time(NULL);
+
+       return LDB_SUCCESS;
+}
+
+/* calculates the new timeout based on the previous starttime and timeout */
+int ldb_set_timeout_from_prev_req(struct ldb_context *ldb, struct ldb_request *oldreq, struct ldb_request *newreq)
+{
+       time_t now;
+
+       if (newreq == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+       now = time(NULL);
+
+       if (oldreq == NULL)
+               return ldb_set_timeout(ldb, newreq, 0);
+
+       if ((now - oldreq->starttime) > oldreq->timeout) {
+               return LDB_ERR_TIME_LIMIT_EXCEEDED;
+       }
+       newreq->starttime = oldreq->starttime;
+       newreq->timeout = oldreq->timeout - (now - oldreq->starttime);
+
+       return LDB_SUCCESS;
+}
+
+/*
+  start an ldb request
+  NOTE: the request must be a talloc context.
+  returns LDB_ERR_* on errors.
+*/
+int ldb_request(struct ldb_context *ldb, struct ldb_request *req)
+{
+       struct ldb_module *module;
+       int ret;
+
+       ldb_reset_err_string(ldb);
+
+       /* call the first module in the chain */
+       switch (req->operation) {
+       case LDB_SEARCH:
+               FIRST_OP(ldb, search);
+               ret = module->ops->search(module, req);
+               break;
+       case LDB_ADD:
+               FIRST_OP(ldb, add);
+               ret = module->ops->add(module, req);
+               break;
+       case LDB_MODIFY:
+               FIRST_OP(ldb, modify);
+               ret = module->ops->modify(module, req);
+               break;
+       case LDB_DELETE:
+               FIRST_OP(ldb, del);
+               ret = module->ops->del(module, req);
+               break;
+       case LDB_RENAME:
+               FIRST_OP(ldb, rename);
+               ret = module->ops->rename(module, req);
+               break;
+       case LDB_SEQUENCE_NUMBER:
+               FIRST_OP(ldb, sequence_number);
+               ret = module->ops->sequence_number(module, req);
+               break;
+       default:
+               FIRST_OP(ldb, request);
+               ret = module->ops->request(module, req);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+  search the database given a LDAP-like search expression
+
+  returns an LDB error code
+
+  Use talloc_free to free the ldb_message returned in 'res', if successful
+
+*/
+static int ldb_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct ldb_result *res;
+       int n;
+       
+       if (!context) {
+               ldb_set_errstring(ldb, "NULL Context in callback");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }       
+
+       res = *((struct ldb_result **)context);
+
+       if (!res || !ares) {
+               goto error;
+       }
+
+       if (ares->type == LDB_REPLY_ENTRY) {
+               res->msgs = talloc_realloc(res, res->msgs, struct ldb_message *, res->count + 2);
+               if (! res->msgs) {
+                       goto error;
+               }
+
+               res->msgs[res->count + 1] = NULL;
+
+               res->msgs[res->count] = talloc_move(res->msgs, &ares->message);
+               res->count++;
+       }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               if (res->refs) {
+                       for (n = 0; res->refs[n]; n++) /*noop*/ ;
+               } else {
+                       n = 0;
+               }
+
+               res->refs = talloc_realloc(res, res->refs, char *, n + 2);
+               if (! res->refs) {
+                       goto error;
+               }
+
+               res->refs[n] = talloc_move(res->refs, &ares->referral);
+               res->refs[n + 1] = NULL;
+       }
+
+       talloc_steal(res, ares->controls);
+       talloc_free(ares);
+       return LDB_SUCCESS;
+
+error:
+       talloc_free(ares);
+       talloc_free(res);
+       *((struct ldb_result **)context) = NULL;
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+int ldb_build_search_req(struct ldb_request **ret_req,
+                       struct ldb_context *ldb,
+                       void *mem_ctx,
+                       const struct ldb_dn *base,
+                       enum ldb_scope scope,
+                       const char *expression,
+                       const char * const *attrs,
+                       struct ldb_control **controls,
+                       void *context,
+                       ldb_request_callback_t callback)
+{
+       struct ldb_request *req;
+
+       *ret_req = NULL;
+
+       req = talloc(mem_ctx, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_SEARCH;
+       if (base == NULL) {
+               req->op.search.base = ldb_dn_new(req);
+       } else {
+               req->op.search.base = base;
+       }
+       req->op.search.scope = scope;
+
+       req->op.search.tree = ldb_parse_tree(req, expression);
+       if (req->op.search.tree == NULL) {
+               ldb_set_errstring(ldb, "Unable to parse search expression");
+               talloc_free(req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->op.search.attrs = attrs;
+       req->controls = controls;
+       req->context = context;
+       req->callback = callback;
+
+       *ret_req = req;
+       return LDB_SUCCESS;
+}
+
+int ldb_build_add_req(struct ldb_request **ret_req,
+                       struct ldb_context *ldb,
+                       void *mem_ctx,
+                       struct ldb_message *message,
+                       struct ldb_control **controls,
+                       void *context,
+                       ldb_request_callback_t callback)
+{
+       struct ldb_request *req;
+
+       *ret_req = NULL;
+
+       req = talloc(mem_ctx, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_ADD;
+       req->op.add.message = message;
+       req->controls = controls;
+       req->context = context;
+       req->callback = callback;
+
+       *ret_req = req;
+
+       return LDB_SUCCESS;
+}
+
+int ldb_build_mod_req(struct ldb_request **ret_req,
+                       struct ldb_context *ldb,
+                       void *mem_ctx,
+                       struct ldb_message *message,
+                       struct ldb_control **controls,
+                       void *context,
+                       ldb_request_callback_t callback)
+{
+       struct ldb_request *req;
+
+       *ret_req = NULL;
+
+       req = talloc(mem_ctx, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_MODIFY;
+       req->op.mod.message = message;
+       req->controls = controls;
+       req->context = context;
+       req->callback = callback;
+
+       *ret_req = req;
+
+       return LDB_SUCCESS;
+}
+
+int ldb_build_del_req(struct ldb_request **ret_req,
+                       struct ldb_context *ldb,
+                       void *mem_ctx,
+                       struct ldb_dn *dn,
+                       struct ldb_control **controls,
+                       void *context,
+                       ldb_request_callback_t callback)
+{
+       struct ldb_request *req;
+
+       *ret_req = NULL;
+
+       req = talloc(mem_ctx, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_DELETE;
+       req->op.del.dn = dn;
+       req->controls = controls;
+       req->context = context;
+       req->callback = callback;
+
+       *ret_req = req;
+
+       return LDB_SUCCESS;
+}
+
+int ldb_build_rename_req(struct ldb_request **ret_req,
+                       struct ldb_context *ldb,
+                       void *mem_ctx,
+                       struct ldb_dn *olddn,
+                       struct ldb_dn *newdn,
+                       struct ldb_control **controls,
+                       void *context,
+                       ldb_request_callback_t callback)
+{
+       struct ldb_request *req;
+
+       *ret_req = NULL;
+
+       req = talloc(mem_ctx, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_RENAME;
+       req->op.rename.olddn = olddn;
+       req->op.rename.newdn = newdn;
+       req->controls = controls;
+       req->context = context;
+       req->callback = callback;
+
+       *ret_req = req;
+
+       return LDB_SUCCESS;
+}
+
+/*
+  note that ldb_search() will automatically replace a NULL 'base' value with the 
+  defaultNamingContext from the rootDSE if available.
+*/
+int ldb_search(struct ldb_context *ldb, 
+              const struct ldb_dn *base,
+              enum ldb_scope scope,
+              const char *expression,
+              const char * const *attrs, 
+              struct ldb_result **res)
+{
+       struct ldb_request *req;
+       int ret;
+
+       *res = talloc_zero(ldb, struct ldb_result);
+       if (! *res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       ret = ldb_build_search_req(&req, ldb, ldb,
+                                       base?base:ldb_get_default_basedn(ldb),
+                                       scope,
+                                       expression,
+                                       attrs,
+                                       NULL,
+                                       res,
+                                       ldb_search_callback);
+
+       if (ret != LDB_SUCCESS) goto done;
+
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       ret = ldb_request(ldb, req);
+       
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       talloc_free(req);
+
+done:
+       if (ret != LDB_SUCCESS) {
+               talloc_free(*res);
+               *res = NULL;
+       }
+
+       return ret;
+}
+
+/*
+  add a record to the database. Will fail if a record with the given class and key
+  already exists
+*/
+int ldb_add(struct ldb_context *ldb, 
+           const struct ldb_message *message)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_msg_sanity_check(ldb, message);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_build_add_req(&req, ldb, ldb,
+                                       message,
+                                       NULL,
+                                       NULL,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       /* do request and autostart a transaction */
+       ret = ldb_autotransaction_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
+}
+
+/*
+  modify the specified attributes of a record
+*/
+int ldb_modify(struct ldb_context *ldb, 
+              const struct ldb_message *message)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_msg_sanity_check(ldb, message);
+       if (ret != LDB_SUCCESS) return ret;
+
+       ret = ldb_build_mod_req(&req, ldb, ldb,
+                                       message,
+                                       NULL,
+                                       NULL,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       /* do request and autostart a transaction */
+       ret = ldb_autotransaction_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
+}
+
+
+/*
+  delete a record from the database
+*/
+int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_build_del_req(&req, ldb, ldb,
+                                       dn,
+                                       NULL,
+                                       NULL,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       /* do request and autostart a transaction */
+       ret = ldb_autotransaction_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
+}
+
+/*
+  rename a record in the database
+*/
+int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_build_rename_req(&req, ldb, ldb,
+                                       olddn,
+                                       newdn,
+                                       NULL,
+                                       NULL,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       /* do request and autostart a transaction */
+       ret = ldb_autotransaction_request(ldb, req);
+
+       talloc_free(req);
+       return ret;
+}
+
+
+/*
+  return the global sequence number
+*/
+int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num)
+{
+       struct ldb_request *req;
+       int ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_SEQUENCE_NUMBER;
+       req->controls = NULL;
+       req->context = NULL;
+       req->callback = NULL;
+       ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+       req->op.seq_num.type = type;
+       /* do request and autostart a transaction */
+       ret = ldb_request(ldb, req);
+       
+       if (ret == LDB_SUCCESS) {
+               *seq_num = req->op.seq_num.seq_num;
+       }
+
+       talloc_free(req);
+       return ret;
+}
+
+
+
+/*
+  return extended error information 
+*/
+const char *ldb_errstring(struct ldb_context *ldb)
+{
+       if (ldb->err_string) {
+               return ldb->err_string;
+       }
+
+       return NULL;
+}
+
+/*
+  return a string explaining what a ldb error constant meancs
+*/
+const char *ldb_strerror(int ldb_err)
+{
+       switch (ldb_err) {
+       case LDB_SUCCESS:
+               return "Success";
+       case LDB_ERR_OPERATIONS_ERROR:
+               return "Operations error";
+       case LDB_ERR_PROTOCOL_ERROR:
+               return "Protocol error";
+       case LDB_ERR_TIME_LIMIT_EXCEEDED:
+               return "Time limit exceeded";
+       case LDB_ERR_SIZE_LIMIT_EXCEEDED:
+               return "Size limit exceeded";
+       case LDB_ERR_COMPARE_FALSE:
+               return "Compare false";
+       case LDB_ERR_COMPARE_TRUE:
+               return "Compare true";
+       case LDB_ERR_AUTH_METHOD_NOT_SUPPORTED:
+               return "Auth method not supported";
+       case LDB_ERR_STRONG_AUTH_REQUIRED:
+               return "Strong auth required";
+/* 9 RESERVED */
+       case LDB_ERR_REFERRAL:
+               return "Referral error";
+       case LDB_ERR_ADMIN_LIMIT_EXCEEDED:
+               return "Admin limit exceeded";
+       case LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION:
+               return "Unsupported critical extension";
+       case LDB_ERR_CONFIDENTIALITY_REQUIRED:
+               return "Confidentiality required";
+       case LDB_ERR_SASL_BIND_IN_PROGRESS:
+               return "SASL bind in progress";
+       case LDB_ERR_NO_SUCH_ATTRIBUTE:
+               return "No such attribute";
+       case LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE:
+               return "Undefined attribute type";
+       case LDB_ERR_INAPPROPRIATE_MATCHING:
+               return "Inappropriate matching";
+       case LDB_ERR_CONSTRAINT_VIOLATION:
+               return "Constraint violation";
+       case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+               return "Attribute or value exists";
+       case LDB_ERR_INVALID_ATTRIBUTE_SYNTAX:
+               return "Invalid attribute syntax";
+/* 22-31 unused */
+       case LDB_ERR_NO_SUCH_OBJECT:
+               return "No such object";
+       case LDB_ERR_ALIAS_PROBLEM:
+               return "Alias problem";
+       case LDB_ERR_INVALID_DN_SYNTAX:
+               return "Invalid DN syntax";
+/* 35 RESERVED */
+       case LDB_ERR_ALIAS_DEREFERENCING_PROBLEM:
+               return "Alias dereferencing problem";
+/* 37-47 unused */
+       case LDB_ERR_INAPPROPRIATE_AUTHENTICATION:
+               return "Inappropriate authentication";
+       case LDB_ERR_INVALID_CREDENTIALS:
+               return "Invalid credentials";
+       case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+               return "insufficient access rights";
+       case LDB_ERR_BUSY:
+               return "Busy";
+       case LDB_ERR_UNAVAILABLE:
+               return "Unavailable";
+       case LDB_ERR_UNWILLING_TO_PERFORM:
+               return "Unwilling to perform";
+       case LDB_ERR_LOOP_DETECT:
+               return "Loop detect";
+/* 55-63 unused */
+       case LDB_ERR_NAMING_VIOLATION:
+               return "Naming violation";
+       case LDB_ERR_OBJECT_CLASS_VIOLATION:
+               return "Object class violation";
+       case LDB_ERR_NOT_ALLOWED_ON_NON_LEAF:
+               return "Not allowed on non-leaf";
+       case LDB_ERR_NOT_ALLOWED_ON_RDN:
+               return "Not allowed on RDN";
+       case LDB_ERR_ENTRY_ALREADY_EXISTS:
+               return "Entry already exists";
+       case LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED:
+               return "Object class mods prohibited";
+/* 70 RESERVED FOR CLDAP */
+       case LDB_ERR_AFFECTS_MULTIPLE_DSAS:
+               return "Affects multiple DSAs";
+/* 72-79 unused */
+       case LDB_ERR_OTHER:
+               return "Other";
+       }
+
+       return "Unknown error";
+}
+
+/*
+  set backend specific opaque parameters
+*/
+int ldb_set_opaque(struct ldb_context *ldb, const char *name, void *value)
+{
+       struct ldb_opaque *o;
+
+       /* allow updating an existing value */
+       for (o=ldb->opaque;o;o=o->next) {
+               if (strcmp(o->name, name) == 0) {
+                       o->value = value;
+                       return LDB_SUCCESS;
+               }
+       }
+
+       o = talloc(ldb, struct ldb_opaque);
+       if (o == NULL) {
+               ldb_oom(ldb);
+               return LDB_ERR_OTHER;
+       }
+       o->next = ldb->opaque;
+       o->name = name;
+       o->value = value;
+       ldb->opaque = o;
+       return LDB_SUCCESS;
+}
+
+/*
+  get a previously set opaque value
+*/
+void *ldb_get_opaque(struct ldb_context *ldb, const char *name)
+{
+       struct ldb_opaque *o;
+       for (o=ldb->opaque;o;o=o->next) {
+               if (strcmp(o->name, name) == 0) {
+                       return o->value;
+               }
+       }
+       return NULL;
+}
diff --git a/source3/lib/ldb/common/ldb_attributes.c b/source3/lib/ldb/common/ldb_attributes.c
new file mode 100644 (file)
index 0000000..c8a7909
--- /dev/null
@@ -0,0 +1,295 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*
+  register handlers for specific attributes and objectclass relationships
+
+  this allows a backend to store its schema information in any format
+  it likes (or to not have any schema information at all) while keeping the 
+  message matching logic generic
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+  add to the list of ldif handlers for this ldb context
+*/
+int ldb_set_attrib_handlers(struct ldb_context *ldb, 
+                           const struct ldb_attrib_handler *handlers, 
+                           unsigned num_handlers)
+{
+       struct ldb_attrib_handler *h;
+       h = talloc_realloc(ldb, ldb->schema.attrib_handlers,
+                          struct ldb_attrib_handler,
+                          ldb->schema.num_attrib_handlers + num_handlers);
+       if (h == NULL) {
+               ldb_oom(ldb);
+               return -1;
+       }
+       ldb->schema.attrib_handlers = h;
+       memcpy(h + ldb->schema.num_attrib_handlers, 
+              handlers, sizeof(*h) * num_handlers);
+       ldb->schema.num_attrib_handlers += num_handlers;
+       return 0;
+}
+                         
+
+/*
+  default function for read/write/canonicalise
+*/
+static int ldb_default_copy(struct ldb_context *ldb, 
+                           void *mem_ctx,
+                           const struct ldb_val *in, 
+                           struct ldb_val *out)
+{
+       *out = ldb_val_dup(mem_ctx, in);
+
+       if (out->data == NULL && in->data != NULL) {
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+  default function for comparison
+*/
+static int ldb_default_cmp(struct ldb_context *ldb, 
+                           void *mem_ctx,
+                          const struct ldb_val *v1, 
+                          const struct ldb_val *v2)
+{
+       if (v1->length != v2->length) {
+               return v1->length - v2->length;
+       }
+       return memcmp(v1->data, v2->data, v1->length);
+}
+
+/*
+  default handler function pointers
+*/
+static const struct ldb_attrib_handler ldb_default_attrib_handler = {
+       .attr = NULL,
+       .ldif_read_fn    = ldb_default_copy,
+       .ldif_write_fn   = ldb_default_copy,
+       .canonicalise_fn = ldb_default_copy,
+       .comparison_fn   = ldb_default_cmp,
+};
+
+/*
+  return the attribute handlers for a given attribute
+*/
+const struct ldb_attrib_handler *ldb_attrib_handler(struct ldb_context *ldb,
+                                                   const char *attrib)
+{
+       int i;
+       const struct ldb_attrib_handler *def = &ldb_default_attrib_handler;
+       /* TODO: should be replaced with a binary search, with a sort on add */
+       for (i=0;i<ldb->schema.num_attrib_handlers;i++) {
+               if (strcmp(ldb->schema.attrib_handlers[i].attr, "*") == 0) {
+                       def = &ldb->schema.attrib_handlers[i];
+               }
+               if (ldb_attr_cmp(attrib, ldb->schema.attrib_handlers[i].attr) == 0) {
+                       return &ldb->schema.attrib_handlers[i];
+               }
+       }
+       return def;
+}
+
+
+/*
+  add to the list of ldif handlers for this ldb context
+*/
+void ldb_remove_attrib_handler(struct ldb_context *ldb, const char *attrib)
+{
+       const struct ldb_attrib_handler *h;
+       int i;
+       h = ldb_attrib_handler(ldb, attrib);
+       if (h == &ldb_default_attrib_handler) {
+               return;
+       }
+       i = h - ldb->schema.attrib_handlers;
+       if (i < ldb->schema.num_attrib_handlers - 1) {
+               memmove(&ldb->schema.attrib_handlers[i], 
+                       h+1, sizeof(*h) * (ldb->schema.num_attrib_handlers-(i+1)));
+       }
+       ldb->schema.num_attrib_handlers--;
+}
+
+/*
+  setup a attribute handler using a standard syntax
+*/
+int ldb_set_attrib_handler_syntax(struct ldb_context *ldb, 
+                                 const char *attr, const char *syntax)
+{
+       const struct ldb_attrib_handler *h = ldb_attrib_handler_syntax(ldb, syntax);
+       struct ldb_attrib_handler h2;
+       if (h == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Unknown syntax '%s'\n", syntax);
+               return -1;
+       }
+       h2 = *h;
+       h2.attr = attr;
+       return ldb_set_attrib_handlers(ldb, &h2, 1);
+}
+
+/*
+  setup the attribute handles for well known attributes
+*/
+int ldb_setup_wellknown_attributes(struct ldb_context *ldb)
+{
+       const struct {
+               const char *attr;
+               const char *syntax;
+       } wellknown[] = {
+               { "dn", LDB_SYNTAX_DN },
+               { "ncName", LDB_SYNTAX_DN },
+               { "distinguishedName", LDB_SYNTAX_DN },
+               { "cn", LDB_SYNTAX_DIRECTORY_STRING },
+               { "dc", LDB_SYNTAX_DIRECTORY_STRING },
+               { "ou", LDB_SYNTAX_DIRECTORY_STRING },
+               { "objectClass", LDB_SYNTAX_OBJECTCLASS }
+       };
+       int i;
+       for (i=0;i<ARRAY_SIZE(wellknown);i++) {
+               if (ldb_set_attrib_handler_syntax(ldb, wellknown[i].attr, 
+                                                 wellknown[i].syntax) != 0) {
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+/*
+  return the list of subclasses for a class
+*/
+const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname)
+{
+       int i;
+       for (i=0;i<ldb->schema.num_classes;i++) {
+               if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
+                       return (const char **)ldb->schema.classes[i].subclasses;
+               }
+       }
+       return NULL;
+}
+
+
+/*
+  add a new subclass
+*/
+static int ldb_subclass_new(struct ldb_context *ldb, const char *classname, const char *subclass)
+{
+       struct ldb_subclass *s, *c;
+       s = talloc_realloc(ldb, ldb->schema.classes, struct ldb_subclass, ldb->schema.num_classes+1);
+       if (s == NULL) goto failed;
+
+       ldb->schema.classes = s;
+       c = &s[ldb->schema.num_classes];
+       c->name = talloc_strdup(s, classname);
+       if (c->name == NULL) goto failed;
+
+       c->subclasses = talloc_array(s, char *, 2);
+       if (c->subclasses == NULL) goto failed;
+
+       c->subclasses[0] = talloc_strdup(c->subclasses, subclass);
+       if (c->subclasses[0] == NULL) goto failed;
+       c->subclasses[1] = NULL;
+
+       ldb->schema.num_classes++;
+
+       return 0;
+failed:
+       ldb_oom(ldb);
+       return -1;
+}
+
+/*
+  add a subclass
+*/
+int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass)
+{
+       int i, n;
+       struct ldb_subclass *c;
+       char **s;
+
+       for (i=0;i<ldb->schema.num_classes;i++) {
+               if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
+                       break;
+               }
+       }
+       if (i == ldb->schema.num_classes) {
+               return ldb_subclass_new(ldb, classname, subclass);
+       }
+       c = &ldb->schema.classes[i];
+       
+       for (n=0;c->subclasses[n];n++) /* noop */;
+
+       s = talloc_realloc(ldb->schema.classes, c->subclasses, char *, n+2);
+       if (s == NULL) {
+               ldb_oom(ldb);
+               return -1;
+       }
+
+       c->subclasses = s;
+       s[n] = talloc_strdup(s, subclass);
+       if (s[n] == NULL) {
+               ldb_oom(ldb);
+               return -1;
+       }
+       s[n+1] = NULL;
+
+       return 0;
+}
+
+/*
+  remove a set of subclasses for a class
+*/
+void ldb_subclass_remove(struct ldb_context *ldb, const char *classname)
+{
+       int i;
+       struct ldb_subclass *c;
+
+       for (i=0;i<ldb->schema.num_classes;i++) {
+               if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
+                       break;
+               }
+       }
+       if (i == ldb->schema.num_classes) {
+               return;
+       }
+
+       c = &ldb->schema.classes[i];
+       talloc_free(c->name);
+       talloc_free(c->subclasses);
+       if (ldb->schema.num_classes-(i+1) > 0) {
+               memmove(c, c+1, sizeof(*c) * (ldb->schema.num_classes-(i+1)));
+       }
+       ldb->schema.num_classes--;
+       if (ldb->schema.num_classes == 0) {
+               talloc_free(ldb->schema.classes);
+               ldb->schema.classes = NULL;
+       }
+}
diff --git a/source3/lib/ldb/common/ldb_controls.c b/source3/lib/ldb/common/ldb_controls.c
new file mode 100644 (file)
index 0000000..d2729c8
--- /dev/null
@@ -0,0 +1,106 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb_controls.c
+ *
+ *  Component: ldb controls utility functions
+ *
+ *  Description: helper functions for control modules
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/* check if a control with the specified "oid" exist and return it */
+/* returns NULL if not found */
+struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid)
+{
+       int i;
+
+       /* check if there's a paged request control */
+       if (controls != NULL) {
+               for (i = 0; controls[i]; i++) {
+                       if (strcmp(oid, controls[i]->oid) == 0) {
+                               break;
+                       }
+               }
+
+               return controls[i];
+       }
+
+       return NULL;
+}
+
+/* saves the current controls list into the "saver" and replace the one in req with a new one excluding
+the "exclude" control */
+/* returns False on error */
+int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver)
+{
+       struct ldb_control **lcs;
+       int i, j;
+
+       *saver = req->controls;
+       for (i = 0; req->controls[i]; i++);
+       if (i == 1) {
+               req->controls = NULL;
+               return 1;
+       }
+
+       lcs = talloc_array(req, struct ldb_control *, i);
+       if (!lcs) {
+               return 0;
+       }
+
+       for (i = 0, j = 0; (*saver)[i]; i++) {
+               if (exclude == (*saver)[i]) continue;
+               lcs[j] = (*saver)[i];
+               j++;
+       }
+       lcs[j] = NULL;
+
+       req->controls = lcs;
+       return 1;
+}
+
+/* check if there's any control marked as critical in the list */
+/* return True if any, False if none */
+int check_critical_controls(struct ldb_control **controls)
+{
+       int i;
+
+       if (controls == NULL) {
+               return 0;
+       }
+
+       for (i = 0; controls[i]; i++) {
+               if (controls[i]->critical) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
diff --git a/source3/lib/ldb/common/ldb_debug.c b/source3/lib/ldb/common/ldb_debug.c
new file mode 100644 (file)
index 0000000..2548a54
--- /dev/null
@@ -0,0 +1,105 @@
+/* 
+   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 debug
+ *
+ *  Description: functions for printing debug messages
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+  this allows the user to choose their own debug function
+*/
+int ldb_set_debug(struct ldb_context *ldb,
+                 void (*debug)(void *context, enum ldb_debug_level level, 
+                               const char *fmt, va_list ap),
+                 void *context)
+{
+       ldb->debug_ops.debug = debug;
+       ldb->debug_ops.context = context;
+       return 0;
+}
+
+/*
+  debug function for ldb_set_debug_stderr
+*/
+static void ldb_debug_stderr(void *context, enum ldb_debug_level level, 
+                            const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+static void ldb_debug_stderr(void *context, enum ldb_debug_level level, 
+                            const char *fmt, va_list ap)
+{
+       if (level <= LDB_DEBUG_WARNING) {
+               vfprintf(stderr, fmt, ap);
+       }
+}
+
+/*
+  convenience function to setup debug messages on stderr
+  messages of level LDB_DEBUG_WARNING and higher are printed
+*/
+int ldb_set_debug_stderr(struct ldb_context *ldb)
+{
+       return ldb_set_debug(ldb, ldb_debug_stderr, ldb);
+}
+
+/*
+  log a message
+*/
+void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...)
+{
+       va_list ap;
+       if (ldb->debug_ops.debug == NULL) {
+               ldb_set_debug_stderr(ldb);
+       }
+       va_start(ap, fmt);
+       ldb->debug_ops.debug(ldb->debug_ops.context, level, fmt, ap);
+       va_end(ap);
+}
+
+
+/*
+  log a message, and set the ldb error string to the same message
+*/
+void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level, 
+                  const char *fmt, ...)
+{
+       va_list ap;
+       char *msg;
+       va_start(ap, fmt);
+       msg = talloc_vasprintf(ldb, fmt, ap);
+       va_end(ap);
+       if (msg != NULL) {
+               ldb_set_errstring(ldb, msg);
+               ldb_debug(ldb, level, "%s", msg);
+       }
+       talloc_free(msg);
+}
+
diff --git a/source3/lib/ldb/common/ldb_dn.c b/source3/lib/ldb/common/ldb_dn.c
new file mode 100644 (file)
index 0000000..a0f4872
--- /dev/null
@@ -0,0 +1,944 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce 2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb dn explode and utility functions
+ *
+ *  Description: - explode a dn into it's own basic elements
+ *                 and put them in a structure
+ *               - manipulate ldb_dn structures
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
+
+#define LDB_SPECIAL "@SPECIAL"
+
+int ldb_dn_is_special(const struct ldb_dn *dn)
+{
+       if (dn == NULL || dn->comp_num != 1) return 0;
+
+       return ! strcmp(dn->components[0].name, LDB_SPECIAL);
+}
+
+int ldb_dn_check_special(const struct ldb_dn *dn, const char *check)
+{
+       if (dn == NULL || dn->comp_num != 1) return 0;
+
+       return ! strcmp((char *)dn->components[0].value.data, check);
+}
+
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
+{
+       const char *p, *s, *src;
+       char *d, *dst;
+       int len;
+
+       if (!value.length)
+               return NULL;
+
+       p = s = src = (const char *)value.data;
+       len = value.length;
+
+       /* allocate destination string, it will be at most 3 times the source */
+       dst = d = talloc_array(mem_ctx, char, len * 3 + 1);
+       LDB_DN_NULL_FAILED(dst);
+
+       while (p - src < len) {
+
+               p += strcspn(p, ",=\n+<>#;\\\"");
+
+               if (p - src == len) /* found no escapable chars */
+                       break;
+
+               memcpy(d, s, p - s); /* copy the part of the string before the stop */
+               d += (p - s); /* move to current position */
+
+               if (*p) { /* it is a normal escapable character */
+                       *d++ = '\\';
+                       *d++ = *p++;
+               } else { /* we have a zero byte in the string */
+                       strncpy(d, "\00", 3); /* escape the zero */
+                       d = d + 3;
+                       p++; /* skip the zero */
+               }
+               s = p; /* move forward */
+       }
+
+       /* copy the last part (with zero) and return */
+       memcpy(d, s, &src[len] - s + 1);
+
+       return dst;
+
+failed:
+       talloc_free(dst);
+       return NULL;
+}
+
+static struct ldb_val ldb_dn_unescape_value(void *mem_ctx, const char *src)
+{
+       struct ldb_val value;
+       unsigned x;
+       char *p, *dst = NULL, *end;
+
+       memset(&value, 0, sizeof(value));
+
+       LDB_DN_NULL_FAILED(src);
+
+       dst = p = talloc_memdup(mem_ctx, src, strlen(src) + 1);
+       LDB_DN_NULL_FAILED(dst);
+
+       end = &dst[strlen(dst)];
+
+       while (*p) {
+               p += strcspn(p, ",=\n+<>#;\\\"");
+
+               if (*p == '\\') {
+                       if (strchr(",=\n+<>#;\\\"", p[1])) {
+                               memmove(p, p + 1, end - (p + 1) + 1);
+                               end--;
+                               p++;
+                               continue;
+                       }
+
+                       if (sscanf(p + 1, "%02x", &x) == 1) {
+                               *p = (unsigned char)x;
+                               memmove(p + 1, p + 3, end - (p + 3) + 1);
+                               end -= 2;
+                               p++;
+                               continue;
+                       }
+               }
+
+               /* a string with not escaped specials is invalid (tested) */
+               if (*p != '\0') {
+                       goto failed;
+               }
+       }
+
+       value.length = end - dst;
+       value.data = (uint8_t *)dst;
+       return value;
+
+failed:
+       talloc_free(dst);
+       return value;
+}
+
+/* check if the string contains quotes
+ * skips leading and trailing spaces
+ * - returns 0 if no quotes found
+ * - returns 1 if quotes are found and put their position
+ *   in *quote_start and *quote_end parameters
+ * - return -1 if there are open quotes
+ */
+
+static int get_quotes_position(const char *source, int *quote_start, int *quote_end)
+{
+       const char *p;
+
+       if (source == NULL || quote_start == NULL || quote_end == NULL) return -1;
+
+       p = source;
+
+       /* check if there are quotes surrounding the value */
+       p += strspn(p, " \n"); /* skip white spaces */
+
+       if (*p == '\"') {
+               *quote_start = p - source;
+
+               p++;
+               while (*p != '\"') {
+                       p = strchr(p, '\"');
+                       LDB_DN_NULL_FAILED(p);
+
+                       if (*(p - 1) == '\\')
+                               p++;
+               }
+
+               *quote_end = p - source;
+               return 1;
+       }
+
+       return 0;
+
+failed:
+       return -1;
+}
+
+static char *seek_to_separator(char *string, const char *separators)
+{
+       char *p, *q;
+       int ret, qs, qe, escaped;
+
+       if (string == NULL || separators == NULL) return NULL;
+
+       p = strchr(string, '=');
+       LDB_DN_NULL_FAILED(p);
+
+       p++;
+
+       /* check if there are quotes surrounding the value */
+
+       ret = get_quotes_position(p, &qs, &qe);
+       if (ret == -1)
+               return NULL;
+
+       if (ret == 1) { /* quotes found */
+
+               p += qe; /* positioning after quotes */
+               p += strspn(p, " \n"); /* skip white spaces after the quote */
+
+               if (strcspn(p, separators) != 0) /* if there are characters between quotes */
+                       return NULL;        /* and separators, the dn is invalid */
+
+               return p; /* return on the separator */
+       }
+
+       /* no quotes found seek to separators */
+       q = p;
+       do {
+               escaped = 0;
+               ret = strcspn(q, separators);
+               
+               if (q[ret - 1] == '\\') {
+                       escaped = 1;
+                       q = q + ret + 1;
+               }
+       } while (escaped);
+
+       if (ret == 0 && p == q) /* no separators ?! bail out */
+               return NULL;
+
+       return q + ret;
+
+failed:
+       return NULL;
+}
+
+static char *ldb_dn_trim_string(char *string, const char *edge)
+{
+       char *s, *p;
+
+       /* seek out edge from start of string */
+       s = string + strspn(string, edge);
+
+       /* backwards skip from end of string */
+       p = &s[strlen(s) - 1];
+       while (p > s && strchr(edge, *p)) {
+               *p = '\0';
+               p--;
+       }
+
+       return s;
+}
+
+/* we choosed to not support multpile valued components */
+static struct ldb_dn_component ldb_dn_explode_component(void *mem_ctx, char *raw_component)
+{
+       struct ldb_dn_component dc;
+       char *p;
+       int ret, qs, qe;
+
+       memset(&dc, 0, sizeof(dc));
+
+       if (raw_component == NULL) {
+               return dc;
+       }
+
+       /* find attribute type/value separator */
+       p = strchr(raw_component, '=');
+       LDB_DN_NULL_FAILED(p);
+
+       *p++ = '\0'; /* terminate name and point to value */
+
+       /* copy and trim name in the component */
+       dc.name = talloc_strdup(mem_ctx, ldb_dn_trim_string(raw_component, " \n"));
+       if (!dc.name)
+               return dc;
+
+       if (! ldb_valid_attr_name(dc.name)) {
+               goto failed;
+       }
+
+       ret = get_quotes_position(p, &qs, &qe);
+
+       switch (ret) {
+       case 0: /* no quotes trim the string */
+               p = ldb_dn_trim_string(p, " \n");
+               dc.value = ldb_dn_unescape_value(mem_ctx, p);
+               break;
+
+       case 1: /* quotes found get the unquoted string */
+               p[qe] = '\0';
+               p = p + qs + 1;
+               dc.value.length = strlen(p);
+               dc.value.data = talloc_memdup(mem_ctx, p, dc.value.length + 1);
+               break;
+
+       default: /* mismatched quotes ot other error, bail out */
+               goto failed;
+       }
+
+       if (dc.value.length == 0) {
+               goto failed;
+       }
+
+       return dc;
+
+failed:
+       talloc_free(dc.name);
+       dc.name = NULL;
+       return dc;
+}
+
+struct ldb_dn *ldb_dn_new(void *mem_ctx)
+{
+       struct ldb_dn *edn;
+
+       edn = talloc(mem_ctx, struct ldb_dn);
+       LDB_DN_NULL_FAILED(edn);
+
+       /* Initially there are no components */
+       edn->comp_num = 0;
+       edn->components = NULL;
+
+       return edn;
+
+failed:
+       return NULL;
+}
+
+struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn)
+{
+       struct ldb_dn *edn; /* the exploded dn */
+       char *pdn, *p;
+
+       if (dn == NULL) return NULL;
+
+       /* Allocate a structure to hold the exploded DN */
+       edn = ldb_dn_new(mem_ctx);
+       pdn = NULL;
+
+       /* Empty DNs */
+       if (dn[0] == '\0') {
+               return edn;
+       }
+
+       /* Special DNs case */
+       if (dn[0] == '@') {
+               edn->comp_num = 1;
+               edn->components = talloc(edn, struct ldb_dn_component);
+               if (edn->components == NULL) goto failed;
+               edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
+               if (edn->components[0].name == NULL) goto failed;
+               edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
+               if (edn->components[0].value.data== NULL) goto failed;
+               edn->components[0].value.length = strlen(dn);
+               return edn;
+       }
+
+       pdn = p = talloc_strdup(edn, dn);
+       LDB_DN_NULL_FAILED(pdn);
+
+       /* get the components */
+       do {
+               char *t;
+
+               /* terminate the current component and return pointer to the next one */
+               t = seek_to_separator(p, ",;");
+               LDB_DN_NULL_FAILED(t);
+
+               if (*t) { /* here there is a separator */
+                       *t = '\0'; /*terminate */
+                       t++; /* a separtor means another component follows */
+               }
+
+               /* allocate space to hold the dn component */
+               edn->components = talloc_realloc(edn, edn->components,
+                                                struct ldb_dn_component,
+                                                edn->comp_num + 1);
+               if (edn->components == NULL)
+                       goto failed;
+
+               /* store the exploded component in the main structure */
+               edn->components[edn->comp_num] = ldb_dn_explode_component(edn, p);
+               LDB_DN_NULL_FAILED(edn->components[edn->comp_num].name);
+
+               edn->comp_num++;
+
+               /* jump to the next component if any */
+               p = t;
+
+       } while(*p);
+
+       talloc_free(pdn);
+       return edn;
+
+failed:
+       talloc_free(pdn);
+       talloc_free(edn);
+       return NULL;
+}
+
+struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *dn)
+{
+       struct ldb_dn *edn; /* the exploded dn */
+
+       if (dn == NULL) return NULL;
+
+       if (strncasecmp(dn, "<GUID=", 6) == 0) {
+               /* this is special DN returned when the
+                * exploded_dn control is used
+                */
+
+               /* Allocate a structure to hold the exploded DN */
+               edn = ldb_dn_new(mem_ctx);
+
+               edn->comp_num = 1;
+               edn->components = talloc(edn, struct ldb_dn_component);
+               if (edn->components == NULL) goto failed;
+               edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
+               if (edn->components[0].name == NULL) goto failed;
+               edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
+               if (edn->components[0].value.data== NULL) goto failed;
+               edn->components[0].value.length = strlen(dn);
+               return edn;
+
+       }
+       
+       return ldb_dn_explode(mem_ctx, dn);
+
+failed:
+       talloc_free(edn);
+       return NULL;
+}
+
+char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn)
+{
+       char *dn, *value;
+       int i;
+
+       if (edn == NULL) return NULL;
+
+       /* Special DNs */
+       if (ldb_dn_is_special(edn)) {
+               dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data);
+               return dn;
+       }
+
+       dn = talloc_strdup(mem_ctx, "");
+       LDB_DN_NULL_FAILED(dn);
+
+       for (i = 0; i < edn->comp_num; i++) {
+               value = ldb_dn_escape_value(dn, edn->components[i].value);
+               LDB_DN_NULL_FAILED(value);
+
+               if (i == 0) {
+                       dn = talloc_asprintf_append(dn, "%s=%s", edn->components[i].name, value);
+               } else {
+                       dn = talloc_asprintf_append(dn, ",%s=%s", edn->components[i].name, value);
+               }
+               LDB_DN_NULL_FAILED(dn);
+
+               talloc_free(value);
+       }
+
+       return dn;
+
+failed:
+       talloc_free(dn);
+       return NULL;
+}
+
+/* Determine if dn is below base, in the ldap tree.  Used for
+ * evaluating a subtree search.
+ * 0 if they match, otherwise non-zero
+ */
+
+int ldb_dn_compare_base(struct ldb_context *ldb,
+                       const struct ldb_dn *base,
+                       const struct ldb_dn *dn)
+{
+       int ret;
+       int n0, n1;
+
+       if (base == NULL || base->comp_num == 0) return 0;
+       if (dn == NULL || dn->comp_num == 0) return -1;
+
+       /* if the base has more componts than the dn, then they differ */
+       if (base->comp_num > dn->comp_num) {
+               return (dn->comp_num - base->comp_num);
+       }
+
+       n0 = base->comp_num - 1;
+       n1 = dn->comp_num - 1;
+       while (n0 >= 0 && n1 >= 0) {
+               const struct ldb_attrib_handler *h;
+
+               /* compare names (attribute names are guaranteed to be ASCII only) */
+               ret = ldb_attr_cmp(base->components[n0].name,
+                                  dn->components[n1].name);
+               if (ret) {
+                       return ret;
+               }
+
+               /* names match, compare values */
+               h = ldb_attrib_handler(ldb, base->components[n0].name);
+               ret = h->comparison_fn(ldb, ldb, &(base->components[n0].value),
+                                                 &(dn->components[n1].value));
+               if (ret) {
+                       return ret;
+               }
+               n1--;
+               n0--;
+       }
+
+       return 0;
+}
+
+/* compare DNs using casefolding compare functions.  
+
+   If they match, then return 0
+ */
+
+int ldb_dn_compare(struct ldb_context *ldb,
+                  const struct ldb_dn *edn0,
+                  const struct ldb_dn *edn1)
+{
+       if (edn0 == NULL || edn1 == NULL) return edn1 - edn0;
+
+       if (edn0->comp_num != edn1->comp_num)
+               return (edn1->comp_num - edn0->comp_num);
+
+       return ldb_dn_compare_base(ldb, edn0, edn1);
+}
+
+int ldb_dn_cmp(struct ldb_context *ldb, const char *dn0, const char *dn1)
+{
+       struct ldb_dn *edn0;
+       struct ldb_dn *edn1;
+       int ret;
+
+       if (dn0 == NULL || dn1 == NULL) return dn1 - dn0;
+
+       edn0 = ldb_dn_explode_casefold(ldb, dn0);
+       if (edn0 == NULL) return 1;
+
+       edn1 = ldb_dn_explode_casefold(ldb, dn1);
+       if (edn1 == NULL) {
+               talloc_free(edn0);
+               return -1;
+       }
+
+       ret = ldb_dn_compare(ldb, edn0, edn1);
+
+       talloc_free(edn0);
+       talloc_free(edn1);
+
+       return ret;
+}
+
+/*
+  casefold a dn. We need to casefold the attribute names, and canonicalize 
+  attribute values of case insensitive attributes.
+*/
+struct ldb_dn *ldb_dn_casefold(struct ldb_context *ldb, const struct ldb_dn *edn)
+{
+       struct ldb_dn *cedn;
+       int i;
+
+       if (edn == NULL) return NULL;
+
+       cedn = ldb_dn_new(ldb);
+       if (!cedn) {
+               return NULL;
+       }
+
+       cedn->comp_num = edn->comp_num;
+       cedn->components = talloc_array(cedn, struct ldb_dn_component, edn->comp_num);
+       if (!cedn->components) {
+               talloc_free(cedn);
+               return NULL;
+       }
+
+       for (i = 0; i < edn->comp_num; i++) {
+               struct ldb_dn_component dc;
+               const struct ldb_attrib_handler *h;
+
+               dc.name = ldb_attr_casefold(cedn, edn->components[i].name);
+               if (!dc.name) {
+                       talloc_free(cedn);
+                       return NULL;
+               }
+
+               h = ldb_attrib_handler(ldb, dc.name);
+               if (h->canonicalise_fn(ldb, cedn, &(edn->components[i].value), &(dc.value)) != 0) {
+                       talloc_free(cedn);
+                       return NULL;
+               }
+
+               cedn->components[i] = dc;
+       }
+
+       return cedn;
+}
+
+struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, const char *dn)
+{
+       struct ldb_dn *edn, *cdn;
+
+       if (dn == NULL) return NULL;
+
+       edn = ldb_dn_explode(ldb, dn);
+       if (edn == NULL) return NULL;
+
+       cdn = ldb_dn_casefold(ldb, edn);
+       
+       talloc_free(edn);
+       return cdn;
+}
+
+char *ldb_dn_linearize_casefold(struct ldb_context *ldb, const struct ldb_dn *edn)
+{
+       struct ldb_dn *cdn;
+       char *dn;
+
+       if (edn == NULL) return NULL;
+
+       /* Special DNs */
+       if (ldb_dn_is_special(edn)) {
+               dn = talloc_strdup(ldb, (char *)edn->components[0].value.data);
+               return dn;
+       }
+
+       cdn = ldb_dn_casefold(ldb, edn);
+       if (cdn == NULL) return NULL;
+
+       dn = ldb_dn_linearize(ldb, cdn);
+       if (dn == NULL) {
+               talloc_free(cdn);
+               return NULL;
+       }
+
+       talloc_free(cdn);
+       return dn;
+}
+
+static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+{
+       struct ldb_dn_component dst;
+
+       memset(&dst, 0, sizeof(dst));
+
+       if (src == NULL) {
+               return dst;
+       }
+
+       dst.value = ldb_val_dup(mem_ctx, &(src->value));
+       if (dst.value.data == NULL) {
+               return dst;
+       }
+
+       dst.name = talloc_strdup(mem_ctx, src->name);
+       if (dst.name == NULL) {
+               talloc_free(dst.value.data);
+       }
+
+       return dst;
+}
+
+/* copy specified number of elements of a dn into a new one
+   element are copied from top level up to the unique rdn
+   num_el may be greater than dn->comp_num (see ldb_dn_make_child)
+*/
+struct ldb_dn *ldb_dn_copy_partial(void *mem_ctx, const struct ldb_dn *dn, int num_el)
+{
+       struct ldb_dn *newdn;
+       int i, n, e;
+
+       if (dn == NULL) return NULL;
+       if (num_el <= 0) return NULL;
+
+       newdn = ldb_dn_new(mem_ctx);
+       LDB_DN_NULL_FAILED(newdn);
+
+       newdn->comp_num = num_el;
+       n = newdn->comp_num - 1;
+       newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+
+       if (dn->comp_num == 0) return newdn;
+       e = dn->comp_num - 1;
+
+       for (i = 0; i < newdn->comp_num; i++) {
+               newdn->components[n - i] = ldb_dn_copy_component(newdn->components,
+                                                               &(dn->components[e - i]));
+               if ((e - i) == 0) {
+                       return newdn;
+               }
+       }
+
+       return newdn;
+
+failed:
+       talloc_free(newdn);
+       return NULL;
+}
+
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn)
+{
+       if (dn == NULL) return NULL;
+       return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num);
+}
+
+struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, const struct ldb_dn *dn)
+{
+       if (dn == NULL) return NULL;
+       return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num - 1);
+}
+
+struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr,
+                                               const char *val)
+{
+       struct ldb_dn_component *dc;
+
+       if (attr == NULL || val == NULL) return NULL;
+
+       dc = talloc(mem_ctx, struct ldb_dn_component);
+       if (dc == NULL) return NULL;
+
+       dc->name = talloc_strdup(dc, attr);
+       if (dc->name ==  NULL) {
+               talloc_free(dc);
+               return NULL;
+       }
+
+       dc->value.data = (uint8_t *)talloc_strdup(dc, val);
+       if (dc->value.data ==  NULL) {
+               talloc_free(dc);
+               return NULL;
+       }
+
+       dc->value.length = strlen(val);
+
+       return dc;
+}
+
+struct ldb_dn *ldb_dn_build_child(void *mem_ctx, const char *attr,
+                                                const char * value,
+                                                const struct ldb_dn *base)
+{
+       struct ldb_dn *newdn;
+       if (! ldb_valid_attr_name(attr)) return NULL;
+       if (value == NULL || value == '\0') return NULL; 
+
+       if (base != NULL) {
+               newdn = ldb_dn_copy_partial(mem_ctx, base, base->comp_num + 1);
+               LDB_DN_NULL_FAILED(newdn);
+       } else {
+               newdn = ldb_dn_new(mem_ctx);
+               LDB_DN_NULL_FAILED(newdn);
+
+               newdn->comp_num = 1;
+               newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+       }
+
+       newdn->components[0].name = talloc_strdup(newdn->components, attr);
+       LDB_DN_NULL_FAILED(newdn->components[0].name);
+
+       newdn->components[0].value.data = (uint8_t *)talloc_strdup(newdn->components, value);
+       LDB_DN_NULL_FAILED(newdn->components[0].value.data);
+       newdn->components[0].value.length = strlen((char *)newdn->components[0].value.data);
+
+       return newdn;
+
+failed:
+       talloc_free(newdn);
+       return NULL;
+
+}
+
+struct ldb_dn *ldb_dn_make_child(void *mem_ctx, const struct ldb_dn_component *component,
+                                               const struct ldb_dn *base)
+{
+       if (component == NULL) return NULL;
+
+       return ldb_dn_build_child(mem_ctx, component->name, 
+                                 (char *)component->value.data, base);
+}
+
+struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2)
+{
+       int i;
+       struct ldb_dn *newdn;
+
+       if (dn2 == NULL && dn1 == NULL) {
+               return NULL;
+       }
+
+       if (dn2 == NULL) {
+               newdn = ldb_dn_new(mem_ctx);
+               LDB_DN_NULL_FAILED(newdn);
+
+               newdn->comp_num = dn1->comp_num;
+               newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+       } else {
+               int comp_num = dn2->comp_num;
+               if (dn1 != NULL) comp_num += dn1->comp_num;
+               newdn = ldb_dn_copy_partial(mem_ctx, dn2, comp_num);
+       }
+
+       if (dn1 == NULL) {
+               return newdn;
+       }
+
+       for (i = 0; i < dn1->comp_num; i++) {
+               newdn->components[i] = ldb_dn_copy_component(newdn->components,
+                                                          &(dn1->components[i]));
+       }
+
+       return newdn;
+
+failed:
+       talloc_free(newdn);
+       return NULL;
+}
+
+struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...)
+{
+       struct ldb_dn *dn;
+       char *child_str;
+       va_list ap;
+       
+       if (child_fmt == NULL) return NULL;
+
+       va_start(ap, child_fmt);
+       child_str = talloc_vasprintf(mem_ctx, child_fmt, ap);
+       va_end(ap);
+
+       if (child_str == NULL) return NULL;
+
+       dn = ldb_dn_compose(mem_ctx, ldb_dn_explode(mem_ctx, child_str), base);
+
+       talloc_free(child_str);
+
+       return dn;
+}
+
+struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn)
+{
+       struct ldb_dn_component *rdn;
+
+       if (dn == NULL) return NULL;
+
+       if (dn->comp_num < 1) {
+               return NULL;
+       }
+
+       rdn = talloc(mem_ctx, struct ldb_dn_component);
+       if (rdn == NULL) return NULL;
+
+       *rdn = ldb_dn_copy_component(mem_ctx, &dn->components[0]);
+       if (rdn->name == NULL) {
+               talloc_free(rdn);
+               return NULL;
+       }
+
+       return rdn;
+}
+
+/* Create a 'canonical name' string from a DN:
+
+   ie dc=samba,dc=org -> samba.org/
+      uid=administrator,ou=users,dc=samba,dc=org = samba.org/users/administrator
+
+   There are two formats, the EX format has the last / replaced with a newline (\n).
+
+*/
+static char *ldb_dn_canonical(void *mem_ctx, const struct ldb_dn *dn, int ex_format) {
+       int i;
+       char *cracked = NULL;
+
+       /* Walk backwards down the DN, grabbing 'dc' components at first */
+       for (i = dn->comp_num - 1 ; i >= 0; i--) {
+               if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) {
+                       break;
+               }
+               if (cracked) {
+                       cracked = talloc_asprintf(mem_ctx, "%s.%s",
+                                                 ldb_dn_escape_value(mem_ctx, dn->components[i].value),
+                                                 cracked);
+               } else {
+                       cracked = ldb_dn_escape_value(mem_ctx, dn->components[i].value);
+               }
+               if (!cracked) {
+                       return NULL;
+               }
+       }
+
+       /* Only domain components?  Finish here */
+       if (i < 0) {
+               if (ex_format) {
+                       cracked = talloc_asprintf(mem_ctx, "%s\n", cracked);
+               } else {
+                       cracked = talloc_asprintf(mem_ctx, "%s/", cracked);
+               }
+               return cracked;
+       }
+
+       /* Now walk backwards appending remaining components */
+       for (; i > 0; i--) {
+               cracked = talloc_asprintf(mem_ctx, "%s/%s", cracked, 
+                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
+               if (!cracked) {
+                       return NULL;
+               }
+       }
+
+       /* Last one, possibly a newline for the 'ex' format */
+       if (ex_format) {
+               cracked = talloc_asprintf(mem_ctx, "%s\n%s", cracked, 
+                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
+       } else {
+               cracked = talloc_asprintf(mem_ctx, "%s/%s", cracked, 
+                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
+       }
+       return cracked;
+}
+
+/* Wrapper functions for the above, for the two different string formats */
+char *ldb_dn_canonical_string(void *mem_ctx, const struct ldb_dn *dn) {
+       return ldb_dn_canonical(mem_ctx, dn, 0);
+
+}
+
+char *ldb_dn_canonical_ex_string(void *mem_ctx, const struct ldb_dn *dn) {
+       return ldb_dn_canonical(mem_ctx, dn, 1);
+}
diff --git a/source3/lib/ldb/common/ldb_ldif.c b/source3/lib/ldb/common/ldb_ldif.c
new file mode 100644 (file)
index 0000000..c5084aa
--- /dev/null
@@ -0,0 +1,748 @@
+/* 
+   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: ldif routines
+ *
+ *  Description: ldif pack/unpack routines
+ *
+ *  Author: Andrew Tridgell
+ */
+
+/*
+  see RFC2849 for the LDIF format definition
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "system/locale.h"
+
+/*
+  
+*/
+static int ldb_read_data_file(void *mem_ctx, struct ldb_val *value)
+{
+       struct stat statbuf;
+       char *buf;
+       int count, size, bytes;
+       int ret;
+       int f;
+       const char *fname = (const char *)value->data;
+
+       if (strncmp(fname, "file://", 7) != 0) {
+               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+       }
+       fname += 7;
+
+       f = open(fname, O_RDONLY);
+       if (f == -1) {
+               return -1;
+       }
+
+       if (fstat(f, &statbuf) != 0) {
+               ret = -1;
+               goto done;
+       }
+
+       if (statbuf.st_size == 0) {
+               ret = -1;
+               goto done;
+       }
+
+       value->data = talloc_size(mem_ctx, statbuf.st_size + 1);
+       if (value->data == NULL) {
+               ret = -1;
+               goto done;
+       }
+       value->data[statbuf.st_size] = 0;
+
+       count = 0;
+       size = statbuf.st_size;
+       buf = (char *)value->data;
+       while (count < statbuf.st_size) {
+               bytes = read(f, buf, size);
+               if (bytes == -1) {
+                       talloc_free(value->data);
+                       ret = -1;
+                       goto done;
+               }
+               count += bytes;
+               buf += bytes;
+               size -= bytes;
+       }
+
+       value->length = statbuf.st_size;
+       ret = statbuf.st_size;
+
+done:
+       close(f);
+       return ret;
+}
+
+/*
+  this base64 decoder was taken from jitterbug (written by tridge).
+  we might need to replace it with a new version
+*/
+int ldb_base64_decode(char *s)
+{
+       const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+       int bit_offset=0, byte_offset, idx, i, n;
+       uint8_t *d = (uint8_t *)s;
+       char *p=NULL;
+
+       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 (bit_offset >= 3) {
+               n--;
+       }
+
+       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(void *mem_ctx, const char *buf, int len)
+{
+       const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+       int bit_offset, byte_offset, idx, i;
+       const uint8_t *d = (const uint8_t *)buf;
+       int bytes = (len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
+       char *out;
+
+       out = talloc_array(mem_ctx, char, bytes+pad_bytes+1);
+       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];
+       }
+
+       for (;i<bytes+pad_bytes;i++)
+               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)
+{
+       unsigned int i;
+       uint8_t *p = val->data;
+
+       if (val->length == 0) {
+               return 0;
+       }
+
+       if (p[0] == ' ' || p[0] == ':') {
+               return 1;
+       }
+
+       for (i=0; i<val->length; i++) {
+               if (!isprint(p[i]) || p[i] == '\n') {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* this macro is used to handle the return checking on fprintf_fn() */
+#define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
+
+/*
+  write a line folded string onto a file
+*/
+static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private_data,
+                       const char *buf, size_t length, int start_pos)
+{
+       unsigned int i;
+       int total=0, ret;
+
+       for (i=0;i<length;i++) {
+               ret = fprintf_fn(private_data, "%c", buf[i]);
+               CHECK_RET;
+               if (i != (length-1) && (i + start_pos) % 77 == 0) {
+                       ret = fprintf_fn(private_data, "\n ");
+                       CHECK_RET;
+               }
+       }
+
+       return total;
+}
+
+/*
+  encode as base64 to a file
+*/
+static int base64_encode_f(struct ldb_context *ldb,
+                          int (*fprintf_fn)(void *, const char *, ...), 
+                          void *private_data,
+                          const char *buf, int len, int start_pos)
+{
+       char *b = ldb_base64_encode(ldb, buf, len);
+       int ret;
+
+       if (!b) {
+               return -1;
+       }
+
+       ret = fold_string(fprintf_fn, private_data, b, strlen(b), start_pos);
+
+       talloc_free(b);
+       return ret;
+}
+
+
+static const struct {
+       const char *name;
+       enum ldb_changetype changetype;
+} ldb_changetypes[] = {
+       {"add",    LDB_CHANGETYPE_ADD},
+       {"delete", LDB_CHANGETYPE_DELETE},
+       {"modify", LDB_CHANGETYPE_MODIFY},
+       {NULL, 0}
+};
+
+/*
+  write to ldif, using a caller supplied write method
+*/
+int ldb_ldif_write(struct ldb_context *ldb,
+                  int (*fprintf_fn)(void *, const char *, ...), 
+                  void *private_data,
+                  const struct ldb_ldif *ldif)
+{
+       unsigned int i, j;
+       int total=0, ret;
+       const struct ldb_message *msg;
+
+       msg = ldif->msg;
+
+       ret = fprintf_fn(private_data, "dn: %s\n", ldb_dn_linearize(msg->dn, msg->dn));
+       CHECK_RET;
+
+       if (ldif->changetype != LDB_CHANGETYPE_NONE) {
+               for (i=0;ldb_changetypes[i].name;i++) {
+                       if (ldb_changetypes[i].changetype == ldif->changetype) {
+                               break;
+                       }
+               }
+               if (!ldb_changetypes[i].name) {
+                       ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n",
+                                 ldif->changetype);
+                       return -1;
+               }
+               ret = fprintf_fn(private_data, "changetype: %s\n", ldb_changetypes[i].name);
+               CHECK_RET;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               const struct ldb_attrib_handler *h;
+
+               h = ldb_attrib_handler(ldb, msg->elements[i].name);
+
+               if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
+                       switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+                       case LDB_FLAG_MOD_ADD:
+                               fprintf_fn(private_data, "add: %s\n", 
+                                          msg->elements[i].name);
+                               break;
+                       case LDB_FLAG_MOD_DELETE:
+                               fprintf_fn(private_data, "delete: %s\n", 
+                                          msg->elements[i].name);
+                               break;
+                       case LDB_FLAG_MOD_REPLACE:
+                               fprintf_fn(private_data, "replace: %s\n", 
+                                          msg->elements[i].name);
+                               break;
+                       }
+               }
+
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       struct ldb_val v;
+                       ret = h->ldif_write_fn(ldb, ldb, &msg->elements[i].values[j], &v);
+                       CHECK_RET;
+                       if (ldb_should_b64_encode(&v)) {
+                               ret = fprintf_fn(private_data, "%s:: ", 
+                                                msg->elements[i].name);
+                               CHECK_RET;
+                               ret = base64_encode_f(ldb, fprintf_fn, private_data, 
+                                                     (char *)v.data, v.length,
+                                                     strlen(msg->elements[i].name)+3);
+                               CHECK_RET;
+                               ret = fprintf_fn(private_data, "\n");
+                               CHECK_RET;
+                       } else {
+                               ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
+                               CHECK_RET;
+                               ret = fold_string(fprintf_fn, private_data,
+                                                 (char *)v.data, v.length,
+                                                 strlen(msg->elements[i].name)+2);
+                               CHECK_RET;
+                               ret = fprintf_fn(private_data, "\n");
+                               CHECK_RET;
+                       }
+                       if (v.data != msg->elements[i].values[j].data) {
+                               talloc_free(v.data);
+                       }
+               }
+               if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
+                       fprintf_fn(private_data, "-\n");
+               }
+       }
+       ret = fprintf_fn(private_data,"\n");
+       CHECK_RET;
+
+       return total;
+}
+
+#undef CHECK_RET
+
+
+/*
+  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(struct ldb_context *ldb, 
+                       int (*fgetc_fn)(void *), void *private_data)
+{
+       size_t alloc_size=0, chunk_size = 0;
+       char *chunk = NULL;
+       int c;
+       int in_comment = 0;
+
+       while ((c = fgetc_fn(private_data)) != EOF) {
+               if (chunk_size+1 >= alloc_size) {
+                       char *c2;
+                       alloc_size += 1024;
+                       c2 = talloc_realloc(ldb, chunk, char, alloc_size);
+                       if (!c2) {
+                               talloc_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;
+               }
+
+               /* ignore leading blank lines */
+               if (chunk_size == 0 && c == '\n') {
+                       continue;
+               }
+
+               chunk[chunk_size++] = c;
+       }
+
+       if (chunk) {
+               chunk[chunk_size] = 0;
+       }
+
+       return chunk;
+}
+
+
+/* simple ldif attribute parser */
+static int next_attr(void *mem_ctx, char **s, const char **attr, struct ldb_val *value)
+{
+       char *p;
+       int base64_encoded = 0;
+       int binary_file = 0;
+
+       if (strncmp(*s, "-\n", 2) == 0) {
+               value->length = 0;
+               *attr = "-";
+               *s += 2;
+               return 0;
+       }
+
+       p = strchr(*s, ':');
+       if (!p) {
+               return -1;
+       }
+
+       *p++ = 0;
+
+       if (*p == ':') {
+               base64_encoded = 1;
+               p++;
+       }
+
+       if (*p == '<') {
+               binary_file = 1;
+               p++;
+       }
+
+       *attr = *s;
+
+       while (*p == ' ' || *p == '\t') {
+               p++;
+       }
+
+       value->data = (uint8_t *)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 = ldb_base64_decode((char *)value->data);
+               if (len == -1) {
+                       /* it wasn't valid base64 data */
+                       return -1;
+               }
+               value->length = len;
+       }
+
+       if (binary_file) {
+               int len = ldb_read_data_file(mem_ctx, value);
+               if (len == -1) {
+                       /* an error occured hile trying to retrieve the file */
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+  free a message from a ldif_read
+*/
+void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *ldif)
+{
+       talloc_free(ldif);
+}
+
+/*
+ read from a LDIF source, creating a ldb_message
+*/
+struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
+                              int (*fgetc_fn)(void *), void *private_data)
+{
+       struct ldb_ldif *ldif;
+       struct ldb_message *msg;
+       const char *attr=NULL;
+       char *chunk=NULL, *s;
+       struct ldb_val value;
+       unsigned flags = 0;
+
+       value.data = NULL;
+
+       ldif = talloc(ldb, struct ldb_ldif);
+       if (!ldif) return NULL;
+
+       ldif->msg = talloc(ldif, struct ldb_message);
+       if (ldif->msg == NULL) {
+               talloc_free(ldif);
+               return NULL;
+       }
+
+       ldif->changetype = LDB_CHANGETYPE_NONE;
+       msg = ldif->msg;
+
+       msg->dn = NULL;
+       msg->elements = NULL;
+       msg->num_elements = 0;
+       msg->private_data = NULL;
+
+       chunk = next_chunk(ldb, fgetc_fn, private_data);
+       if (!chunk) {
+               goto failed;
+       }
+
+       msg->private_data = chunk;
+       s = chunk;
+
+       if (next_attr(ldif, &s, &attr, &value) != 0) {
+               goto failed;
+       }
+       
+       /* first line must be a dn */
+       if (ldb_attr_cmp(attr, "dn") != 0) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n", 
+                         attr);
+               goto failed;
+       }
+
+       msg->dn = ldb_dn_explode(msg, (char *)value.data);
+
+       if (msg->dn == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'\n", 
+                                 value.data);
+               goto failed;
+       }
+
+       while (next_attr(ldif, &s, &attr, &value) == 0) {
+               const struct ldb_attrib_handler *h;             
+               struct ldb_message_element *el;
+               int ret, empty = 0;
+
+               if (ldb_attr_cmp(attr, "changetype") == 0) {
+                       int i;
+                       for (i=0;ldb_changetypes[i].name;i++) {
+                               if (ldb_attr_cmp((char *)value.data, ldb_changetypes[i].name) == 0) {
+                                       ldif->changetype = ldb_changetypes[i].changetype;
+                                       break;
+                               }
+                       }
+                       if (!ldb_changetypes[i].name) {
+                               ldb_debug(ldb, LDB_DEBUG_ERROR, 
+                                         "Error: Bad ldif changetype '%s'\n",(char *)value.data);
+                       }
+                       flags = 0;
+                       continue;
+               }
+
+               if (ldb_attr_cmp(attr, "add") == 0) {
+                       flags = LDB_FLAG_MOD_ADD;
+                       empty = 1;
+               }
+               if (ldb_attr_cmp(attr, "delete") == 0) {
+                       flags = LDB_FLAG_MOD_DELETE;
+                       empty = 1;
+               }
+               if (ldb_attr_cmp(attr, "replace") == 0) {
+                       flags = LDB_FLAG_MOD_REPLACE;
+                       empty = 1;
+               }
+               if (ldb_attr_cmp(attr, "-") == 0) {
+                       flags = 0;
+                       continue;
+               }
+
+               if (empty) {
+                       if (ldb_msg_add_empty(msg, (char *)value.data, flags) != 0) {
+                               goto failed;
+                       }
+                       continue;
+               }
+               
+               el = &msg->elements[msg->num_elements-1];
+
+               h = ldb_attrib_handler(ldb, attr);
+
+               if (msg->num_elements > 0 && ldb_attr_cmp(attr, el->name) == 0 &&
+                   flags == el->flags) {
+                       /* its a continuation */
+                       el->values = 
+                               talloc_realloc(msg->elements, el->values, 
+                                                struct ldb_val, el->num_values+1);
+                       if (!el->values) {
+                               goto failed;
+                       }
+                       ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[el->num_values]);
+                       if (ret != 0) {
+                               goto failed;
+                       }
+                       if (value.length == 0) {
+                               ldb_debug(ldb, LDB_DEBUG_ERROR,
+                                         "Error: Attribute value cannot be empty for attribute '%s'\n", el->name);
+                               goto failed;
+                       }
+                       if (value.data != el->values[el->num_values].data) {
+                               talloc_steal(el->values, el->values[el->num_values].data);
+                       }
+                       el->num_values++;
+               } else {
+                       /* its a new attribute */
+                       msg->elements = talloc_realloc(ldif, msg->elements, 
+                                                        struct ldb_message_element, 
+                                                        msg->num_elements+1);
+                       if (!msg->elements) {
+                               goto failed;
+                       }
+                       el = &msg->elements[msg->num_elements];
+                       el->flags = flags;
+                       el->name = talloc_strdup(msg->elements, attr);
+                       el->values = talloc(msg->elements, struct ldb_val);
+                       if (!el->values || !el->name) {
+                               goto failed;
+                       }
+                       el->num_values = 1;
+                       ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[0]);
+                       if (ret != 0) {
+                               goto failed;
+                       }
+                       if (value.data != el->values[0].data) {
+                               talloc_steal(el->values, el->values[0].data);
+                       }
+                       msg->num_elements++;
+               }
+       }
+
+       return ldif;
+
+failed:
+       talloc_free(ldif);
+       return NULL;
+}
+
+
+
+/*
+  a wrapper around ldif_read() for reading from FILE*
+*/
+struct ldif_read_file_state {
+       FILE *f;
+};
+
+static int fgetc_file(void *private_data)
+{
+       struct ldif_read_file_state *state = private_data;
+       return fgetc(state->f);
+}
+
+struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f)
+{
+       struct ldif_read_file_state state;
+       state.f = f;
+       return ldb_ldif_read(ldb, fgetc_file, &state);
+}
+
+
+/*
+  a wrapper around ldif_read() for reading from const char*
+*/
+struct ldif_read_string_state {
+       const char *s;
+};
+
+static int fgetc_string(void *private_data)
+{
+       struct ldif_read_string_state *state = private_data;
+       if (state->s[0] != 0) {
+               return *state->s++;
+       }
+       return EOF;
+}
+
+struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s)
+{
+       struct ldif_read_string_state state;
+       struct ldb_ldif *ldif;
+       state.s = *s;
+       ldif = ldb_ldif_read(ldb, fgetc_string, &state);
+       *s = state.s;
+       return ldif;
+}
+
+
+/*
+  wrapper around ldif_write() for a file
+*/
+struct ldif_write_file_state {
+       FILE *f;
+};
+
+static int fprintf_file(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
+
+static int fprintf_file(void *private_data, const char *fmt, ...)
+{
+       struct ldif_write_file_state *state = private_data;
+       int ret;
+       va_list ap;
+
+       va_start(ap, fmt);
+       ret = vfprintf(state->f, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *ldif)
+{
+       struct ldif_write_file_state state;
+       state.f = f;
+       return ldb_ldif_write(ldb, fprintf_file, &state, ldif);
+}
diff --git a/source3/lib/ldb/common/ldb_match.c b/source3/lib/ldb/common/ldb_match.c
new file mode 100644 (file)
index 0000000..64e52d2
--- /dev/null
@@ -0,0 +1,431 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004-2005
+   Copyright (C) Simo Sorce            2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb expression matching
+ *
+ *  Description: ldb expression matching 
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+  check if the scope matches in a search result
+*/
+static int ldb_match_scope(struct ldb_context *ldb,
+                          const struct ldb_dn *base,
+                          const struct ldb_dn *dn,
+                          enum ldb_scope scope)
+{
+       int ret = 0;
+
+       if (base == NULL || dn == NULL) {
+               return 1;
+       }
+
+       switch (scope) {
+       case LDB_SCOPE_BASE:
+               if (ldb_dn_compare(ldb, base, dn) == 0) {
+                       ret = 1;
+               }
+               break;
+
+       case LDB_SCOPE_ONELEVEL:
+               if (dn->comp_num == (base->comp_num + 1)) {
+                       if (ldb_dn_compare_base(ldb, base, dn) == 0) {
+                               ret = 1;
+                       }
+               }
+               break;
+               
+       case LDB_SCOPE_SUBTREE:
+       default:
+               if (ldb_dn_compare_base(ldb, base, dn) == 0) {
+                       ret = 1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+
+/*
+  match if node is present
+*/
+static int ldb_match_present(struct ldb_context *ldb, 
+                            const struct ldb_message *msg,
+                            const struct ldb_parse_tree *tree,
+                            enum ldb_scope scope)
+{
+       if (ldb_attr_dn(tree->u.present.attr) == 0) {
+               return 1;
+       }
+
+       if (ldb_msg_find_element(msg, tree->u.present.attr)) {
+               return 1;
+       }
+
+       return 0;
+}
+
+static int ldb_match_comparison(struct ldb_context *ldb, 
+                               const struct ldb_message *msg,
+                               const struct ldb_parse_tree *tree,
+                               enum ldb_scope scope,
+                               enum ldb_parse_op comp_op)
+{
+       unsigned int i;
+       struct ldb_message_element *el;
+       const struct ldb_attrib_handler *h;
+       int ret;
+
+       /* FIXME: APPROX comparison not handled yet */
+       if (comp_op == LDB_OP_APPROX) return 0;
+
+       el = ldb_msg_find_element(msg, tree->u.comparison.attr);
+       if (el == NULL) {
+               return 0;
+       }
+
+       h = ldb_attrib_handler(ldb, el->name);
+
+       for (i = 0; i < el->num_values; i++) {
+               ret = h->comparison_fn(ldb, ldb, &el->values[i], &tree->u.comparison.value);
+
+               if (ret == 0) {
+                       return 1;
+               }
+               if (ret > 0 && comp_op == LDB_OP_GREATER) {
+                       return 1;
+               }
+               if (ret < 0 && comp_op == LDB_OP_LESS) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+  match a simple leaf node
+*/
+static int ldb_match_equality(struct ldb_context *ldb, 
+                             const struct ldb_message *msg,
+                             const struct ldb_parse_tree *tree,
+                             enum ldb_scope scope)
+{
+       unsigned int i;
+       struct ldb_message_element *el;
+       const struct ldb_attrib_handler *h;
+       struct ldb_dn *valuedn;
+       int ret;
+
+       if (ldb_attr_dn(tree->u.equality.attr) == 0) {
+               valuedn = ldb_dn_explode_casefold(ldb, 
+                                                 (char *)tree->u.equality.value.data);
+               if (valuedn == NULL) {
+                       return 0;
+               }
+
+               ret = ldb_dn_compare(ldb, msg->dn, valuedn);
+
+               talloc_free(valuedn);
+
+               if (ret == 0) return 1;
+               return 0;
+       }
+
+       /* TODO: handle the "*" case derived from an extended search
+          operation without the attibute type defined */
+       el = ldb_msg_find_element(msg, tree->u.equality.attr);
+       if (el == NULL) {
+               return 0;
+       }
+
+       h = ldb_attrib_handler(ldb, el->name);
+
+       for (i=0;i<el->num_values;i++) {
+               if (h->comparison_fn(ldb, ldb, &tree->u.equality.value, 
+                                    &el->values[i]) == 0) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int ldb_wildcard_compare(struct ldb_context *ldb,
+                               const struct ldb_parse_tree *tree,
+                               const struct ldb_val value)
+{
+       const struct ldb_attrib_handler *h;
+       struct ldb_val val;
+       struct ldb_val cnk;
+       struct ldb_val *chunk;
+       char *p, *g;
+       uint8_t *save_p = NULL;
+       int c = 0;
+
+       h = ldb_attrib_handler(ldb, tree->u.substring.attr);
+
+       if(h->canonicalise_fn(ldb, ldb, &value, &val) != 0)
+               return -1;
+
+       save_p = val.data;
+       cnk.data = NULL;
+
+       if ( ! tree->u.substring.start_with_wildcard ) {
+
+               chunk = tree->u.substring.chunks[c];
+               if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
+
+               /* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */
+               if (cnk.length > val.length) {
+                       goto failed;
+               }
+               if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto failed;
+               val.length -= cnk.length;
+               val.data += cnk.length;
+               c++;
+               talloc_free(cnk.data);
+               cnk.data = NULL;
+       }
+
+       while (tree->u.substring.chunks[c]) {
+
+               chunk = tree->u.substring.chunks[c];
+               if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
+
+               /* FIXME: case of embedded nulls */
+               p = strstr((char *)val.data, (char *)cnk.data);
+               if (p == NULL) goto failed;
+               if ( (! tree->u.substring.chunks[c + 1]) && (! tree->u.substring.end_with_wildcard) ) {
+                       do { /* greedy */
+                               g = strstr((char *)p + cnk.length, (char *)cnk.data);
+                               if (g) p = g;
+                       } while(g);
+               }
+               val.length = val.length - (p - (char *)(val.data)) - cnk.length;
+               val.data = (uint8_t *)(p + cnk.length);
+               c++;
+               talloc_free(cnk.data);
+               cnk.data = NULL;
+       }
+
+       if ( (! tree->u.substring.end_with_wildcard) && (*(val.data) != 0) ) goto failed; /* last chunk have not reached end of string */
+       talloc_free(save_p);
+       return 1;
+
+failed:
+       talloc_free(save_p);
+       talloc_free(cnk.data);
+       return 0;
+}
+
+/*
+  match a simple leaf node
+*/
+static int ldb_match_substring(struct ldb_context *ldb, 
+                              const struct ldb_message *msg,
+                              const struct ldb_parse_tree *tree,
+                              enum ldb_scope scope)
+{
+       unsigned int i;
+       struct ldb_message_element *el;
+
+       el = ldb_msg_find_element(msg, tree->u.substring.attr);
+       if (el == NULL) {
+               return 0;
+       }
+
+       for (i = 0; i < el->num_values; i++) {
+               if (ldb_wildcard_compare(ldb, tree, el->values[i]) == 1) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+  bitwise-and comparator
+*/
+static int ldb_comparator_and(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       uint64_t i1, i2;
+       i1 = strtoull((char *)v1->data, NULL, 0);
+       i2 = strtoull((char *)v2->data, NULL, 0);
+       return ((i1 & i2) == i2);
+}
+
+/*
+  bitwise-or comparator
+*/
+static int ldb_comparator_or(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       uint64_t i1, i2;
+       i1 = strtoull((char *)v1->data, NULL, 0);
+       i2 = strtoull((char *)v2->data, NULL, 0);
+       return ((i1 & i2) != 0);
+}
+
+
+/*
+  extended match, handles things like bitops
+*/
+static int ldb_match_extended(struct ldb_context *ldb, 
+                             const struct ldb_message *msg,
+                             const struct ldb_parse_tree *tree,
+                             enum ldb_scope scope)
+{
+       int i;
+       const struct {
+               const char *oid;
+               int (*comparator)(const struct ldb_val *, const struct ldb_val *);
+       } rules[] = {
+               { LDB_OID_COMPARATOR_AND, ldb_comparator_and},
+               { LDB_OID_COMPARATOR_OR, ldb_comparator_or}
+       };
+       int (*comp)(const struct ldb_val *, const struct ldb_val *) = NULL;
+       struct ldb_message_element *el;
+
+       if (tree->u.extended.dnAttributes) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
+               return -1;
+       }
+       if (tree->u.extended.rule_id == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
+               return -1;
+       }
+       if (tree->u.extended.attr == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
+               return -1;
+       }
+
+       for (i=0;i<ARRAY_SIZE(rules);i++) {
+               if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
+                       comp = rules[i].comparator;
+                       break;
+               }
+       }
+       if (comp == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
+                         tree->u.extended.rule_id);
+               return -1;
+       }
+
+       /* find the message element */
+       el = ldb_msg_find_element(msg, tree->u.extended.attr);
+       if (el == NULL) {
+               return 0;
+       }
+
+       for (i=0;i<el->num_values;i++) {
+               int ret = comp(&el->values[i], &tree->u.extended.value);
+               if (ret == -1 || ret == 1) return ret;
+       }
+
+       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
+ */
+static int ldb_match_message(struct ldb_context *ldb, 
+                            const struct ldb_message *msg,
+                            const struct ldb_parse_tree *tree,
+                            enum ldb_scope scope)
+{
+       unsigned int i;
+       int v;
+
+       switch (tree->operation) {
+       case LDB_OP_AND:
+               for (i=0;i<tree->u.list.num_elements;i++) {
+                       v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
+                       if (!v) return 0;
+               }
+               return 1;
+
+       case LDB_OP_OR:
+               for (i=0;i<tree->u.list.num_elements;i++) {
+                       v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
+                       if (v) return 1;
+               }
+               return 0;
+
+       case LDB_OP_NOT:
+               return ! ldb_match_message(ldb, msg, tree->u.isnot.child, scope);
+
+       case LDB_OP_EQUALITY:
+               return ldb_match_equality(ldb, msg, tree, scope);
+
+       case LDB_OP_SUBSTRING:
+               return ldb_match_substring(ldb, msg, tree, scope);
+
+       case LDB_OP_GREATER:
+               return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_GREATER);
+
+       case LDB_OP_LESS:
+               return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_LESS);
+
+       case LDB_OP_PRESENT:
+               return ldb_match_present(ldb, msg, tree, scope);
+
+       case LDB_OP_APPROX:
+               return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_APPROX);
+
+       case LDB_OP_EXTENDED:
+               return ldb_match_extended(ldb, msg, tree, scope);
+
+       }
+
+       return 0;
+}
+
+int ldb_match_msg(struct ldb_context *ldb,
+                 const struct ldb_message *msg,
+                 const struct ldb_parse_tree *tree,
+                 const struct ldb_dn *base,
+                 enum ldb_scope scope)
+{
+       if ( ! ldb_match_scope(ldb, base, msg->dn, scope) ) {
+               return 0;
+       }
+
+       return ldb_match_message(ldb, msg, tree, scope);
+}
diff --git a/source3/lib/ldb/common/ldb_modules.c b/source3/lib/ldb/common/ldb_modules.c
new file mode 100644 (file)
index 0000000..06a8a4b
--- /dev/null
@@ -0,0 +1,442 @@
+
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2004
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb modules core
+ *
+ *  Description: core modules routines
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#if (_SAMBA_BUILD_ >= 4)
+#include "build.h"
+#include "dynconfig.h"
+#endif
+
+#define LDB_MODULE_PREFIX      "modules:"
+#define LDB_MODULE_PREFIX_LEN  8
+
+static char *talloc_strdup_no_spaces(struct ldb_context *ldb, const char *string)
+{
+       int i, len;
+       char *trimmed;
+
+       trimmed = talloc_strdup(ldb, string);
+       if (!trimmed) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in talloc_strdup_trim_spaces()\n");
+               return NULL;
+       }
+
+       len = strlen(trimmed);
+       for (i = 0; trimmed[i] != '\0'; i++) {
+               switch (trimmed[i]) {
+               case ' ':
+               case '\t':
+               case '\n':
+                       memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
+                       break;
+               }
+       }
+
+       return trimmed;
+}
+
+
+/* modules are called in inverse order on the stack.
+   Lets place them as an admin would think the right order is.
+   Modules order is important */
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
+{
+       char **modules = NULL;
+       const char **m;
+       char *modstr, *p;
+       int i;
+
+       /* spaces not admitted */
+       modstr = talloc_strdup_no_spaces(mem_ctx, string);
+       if ( ! modstr) {
+               return NULL;
+       }
+
+       modules = talloc_realloc(mem_ctx, modules, char *, 2);
+       if ( ! modules ) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
+               talloc_free(modstr);
+               return NULL;
+       }
+       talloc_steal(modules, modstr);
+
+       i = 0;
+       /* The str*r*chr walks backwards:  This is how we get the inverse order mentioned above */
+       while ((p = strrchr(modstr, ',')) != NULL) {
+               *p = '\0';
+               p++;
+               modules[i] = p;
+
+               i++;
+               modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
+               if ( ! modules ) {
+                       ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
+                       return NULL;
+               }
+
+       }
+       modules[i] = modstr;
+
+       modules[i + 1] = NULL;
+
+       m = (const char **)modules;
+
+       return m;
+}
+
+static struct ops_list_entry {
+       const struct ldb_module_ops *ops;
+       struct ops_list_entry *next;    
+} *registered_modules = NULL;
+
+static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
+{
+       struct ops_list_entry *e;
+       for (e = registered_modules; e; e = e->next) {
+               if (strcmp(e->ops->name, name) == 0) 
+                       return e->ops;
+       }
+
+       return NULL;
+}
+
+#ifndef STATIC_ldb_MODULES
+
+#ifdef HAVE_LDAP
+#define LDAP_INIT ldb_ldap_init,
+#else
+#define LDAP_INIT
+#endif
+
+#ifdef HAVE_SQLITE3
+#define SQLITE3_INIT ldb_sqlite3_init,
+#else
+#define SQLITE3_INIT
+#endif
+
+#define STATIC_ldb_MODULES \
+       {       \
+               LDAP_INIT \
+               SQLITE3_INIT \
+               ldb_tdb_init,   \
+               ldb_schema_init,        \
+               ldb_operational_init,   \
+               ldb_rdn_name_init,      \
+               ldb_objectclass_init,   \
+               ldb_paged_results_init, \
+               ldb_sort_init,          \
+               NULL                    \
+       }
+#endif
+
+int ldb_global_init(void)
+{
+       static int (*static_init_fns[])(void) = STATIC_ldb_MODULES;
+
+       static int initialized = 0;
+       int ret = 0, i;
+
+       if (initialized) 
+               return 0;
+
+       initialized = 1;
+       
+       for (i = 0; static_init_fns[i]; i++) {
+               if (static_init_fns[i]() == -1)
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+int ldb_register_module(const struct ldb_module_ops *ops)
+{
+       struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
+
+       if (ldb_find_module_ops(ops->name) != NULL)
+               return -1;
+
+       if (entry == NULL)
+               return -1;
+
+       entry->ops = ops;
+       entry->next = registered_modules;
+       registered_modules = entry;
+
+       return 0;
+}
+
+int ldb_try_load_dso(struct ldb_context *ldb, const char *name)
+{
+       char *path;
+       void *handle;
+       int (*init_fn) (void);
+
+#ifdef HAVE_DLOPEN
+#ifdef _SAMBA_BUILD_
+       path = talloc_asprintf(ldb, "%s/ldb/%s.%s", dyn_MODULESDIR, name, dyn_SHLIBEXT);
+#else
+       path = talloc_asprintf(ldb, "%s/%s.%s", MODULESDIR, name, SHLIBEXT);
+#endif
+
+       ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path);
+
+       handle = dlopen(path, 0);
+       if (handle == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror());
+               return -1;
+       }
+
+       init_fn = (int (*)(void))dlsym(handle, "init_module");
+
+       if (init_fn == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `init_module' found in %s: %s\n", path, dlerror());
+               return -1;
+       }
+
+       talloc_free(path);
+
+       return init_fn();
+#else
+       ldb_debug(ldb, LDB_DEBUG_TRACE, "no dlopen() - not trying to load %s module\n", name);
+       return -1;
+#endif
+}
+
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
+{
+       struct ldb_module *module;
+       int i;
+       
+       module = backend;
+
+       for (i = 0; module_list[i] != NULL; i++) {
+               struct ldb_module *current;
+               const struct ldb_module_ops *ops;
+               
+               ops = ldb_find_module_ops(module_list[i]);
+               if (ops == NULL) {
+                       if (ldb_try_load_dso(ldb, module_list[i]) == 0) {
+                               ops = ldb_find_module_ops(module_list[i]);
+                       }
+               }
+               
+               if (ops == NULL) {
+                       ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n", 
+                                 module_list[i]);
+                       continue;
+               }
+               
+               current = talloc_zero(ldb, struct ldb_module);
+               if (current == NULL) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               
+               current->ldb = ldb;
+               current->ops = ops;
+               
+               DLIST_ADD(module, current);
+       }
+       *out = module;
+       return LDB_SUCCESS;
+}
+
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) 
+{
+       while (module && module->ops->init_context == NULL) 
+               module = module->next;
+
+       if (module && module->ops->init_context &&
+               module->ops->init_context(module) != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "module initialization failed\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return LDB_SUCCESS;
+}
+
+int ldb_load_modules(struct ldb_context *ldb, const char *options[])
+{
+       const char **modules = NULL;
+       int i;
+       int ret;
+       TALLOC_CTX *mem_ctx = talloc_new(ldb);
+       if (!mem_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* find out which modules we are requested to activate */
+
+       /* check if we have a custom module list passd as ldb option */
+       if (options) {
+               for (i = 0; options[i] != NULL; i++) {
+                       if (strncmp(options[i], LDB_MODULE_PREFIX, LDB_MODULE_PREFIX_LEN) == 0) {
+                               modules = ldb_modules_list_from_string(ldb, mem_ctx, &options[i][LDB_MODULE_PREFIX_LEN]);
+                       }
+               }
+       }
+
+       /* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
+       if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) { 
+               const char * const attrs[] = { "@LIST" , NULL};
+               struct ldb_result *res = NULL;
+               struct ldb_dn *mods_dn;
+
+               mods_dn = ldb_dn_explode(mem_ctx, "@MODULES");
+               if (mods_dn == NULL) {
+                       talloc_free(mem_ctx);
+                       return -1;
+               }
+
+               ret = ldb_search(ldb, mods_dn, LDB_SCOPE_BASE, "", attrs, &res);
+               if (res) talloc_steal(mods_dn, res);
+               if (ret == LDB_SUCCESS && (res->count == 0 || res->msgs[0]->num_elements == 0)) {
+                       ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db\n");
+               } else {
+                       if (ret != LDB_SUCCESS) {
+                               ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
+                               talloc_free(mem_ctx);
+                               return -1;
+                       }
+                       if (res->count > 1) {
+                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count);
+                               talloc_free(mem_ctx);
+                               return -1;
+                       }
+
+                       modules = ldb_modules_list_from_string(ldb, mem_ctx,
+                                                              (const char *)res->msgs[0]->elements[0].values[0].data);
+
+               }
+
+               talloc_free(mods_dn);
+       }
+
+       if (modules != NULL) {
+               ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
+               talloc_free(modules);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       } else {
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database\n");
+       }
+
+       return ldb_init_module_chain(ldb, ldb->modules);
+}
+
+/*
+  by using this we allow ldb modules to only implement the functions they care about,
+  which makes writing a module simpler, and makes it more likely to keep working
+  when ldb is extended
+*/
+#define FIND_OP(module, op) do { \
+       struct ldb_context *ldb = module->ldb; \
+       module = module->next; \
+       while (module && module->ops->op == NULL) module = module->next; \
+       if (module == NULL) { \
+               ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
+               return LDB_ERR_OPERATIONS_ERROR;        \
+       }                                               \
+} while (0)
+
+
+/*
+   helper functions to call the next module in chain
+*/
+
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
+{
+       switch (request->operation) {
+       case LDB_SEARCH:
+               FIND_OP(module, search);
+               return module->ops->search(module, request);
+       case LDB_ADD:
+               FIND_OP(module, add);
+               return module->ops->add(module, request);
+       case LDB_MODIFY:
+               FIND_OP(module, modify);
+               return module->ops->modify(module, request);
+       case LDB_DELETE:
+               FIND_OP(module, del);
+               return module->ops->del(module, request);
+       case LDB_RENAME:
+               FIND_OP(module, rename);
+               return module->ops->rename(module, request);
+       case LDB_SEQUENCE_NUMBER:
+               FIND_OP(module, sequence_number);
+               return module->ops->sequence_number(module, request);
+       default:
+               FIND_OP(module, request);
+               return module->ops->request(module, request);
+       }
+}
+
+int ldb_next_init(struct ldb_module *module)
+{
+       /* init is different in that it is not an error if modules
+        * do not require initialization */
+
+       module = module->next;
+
+       while (module && module->ops->init_context == NULL) 
+               module = module->next;
+
+       if (module == NULL) 
+               return LDB_SUCCESS;
+
+       return module->ops->init_context(module);
+}
+
+int ldb_next_start_trans(struct ldb_module *module)
+{
+       FIND_OP(module, start_transaction);
+       return module->ops->start_transaction(module);
+}
+
+int ldb_next_end_trans(struct ldb_module *module)
+{
+       FIND_OP(module, end_transaction);
+       return module->ops->end_transaction(module);
+}
+
+int ldb_next_del_trans(struct ldb_module *module)
+{
+       FIND_OP(module, del_transaction);
+       return module->ops->del_transaction(module);
+}
diff --git a/source3/lib/ldb/common/ldb_msg.c b/source3/lib/ldb/common/ldb_msg.c
new file mode 100644 (file)
index 0000000..52c6b82
--- /dev/null
@@ -0,0 +1,802 @@
+/* 
+   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 message component utility functions
+ *
+ *  Description: functions for manipulating ldb_message structures
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+  create a new ldb_message in a given memory context (NULL for top level)
+*/
+struct ldb_message *ldb_msg_new(void *mem_ctx)
+{
+       return talloc_zero(mem_ctx, struct ldb_message);
+}
+
+/*
+  find an element in a message by attribute name
+*/
+struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
+                                                const char *attr_name)
+{
+       unsigned int i;
+       for (i=0;i<msg->num_elements;i++) {
+               if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+                       return &msg->elements[i];
+               }
+       }
+       return NULL;
+}
+
+/*
+  see if two ldb_val structures contain exactly the same data
+  return 1 for a match, 0 for a mis-match
+*/
+int ldb_val_equal_exact(const struct ldb_val *v1, const 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;
+}
+
+/*
+  find a value in an element
+  assumes case sensitive comparison
+*/
+struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
+                                struct ldb_val *val)
+{
+       unsigned int i;
+       for (i=0;i<el->num_values;i++) {
+               if (ldb_val_equal_exact(val, &el->values[i])) {
+                       return &el->values[i];
+               }
+       }
+       return NULL;
+}
+
+/*
+  duplicate a ldb_val structure
+*/
+struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
+{
+       struct ldb_val v2;
+       v2.length = v->length;
+       if (v->data == NULL) {
+               v2.data = NULL;
+               return v2;
+       }
+
+       /* the +1 is to cope with buggy C library routines like strndup
+          that look one byte beyond */
+       v2.data = talloc_array(mem_ctx, uint8_t, 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 an empty element to a message
+*/
+int ldb_msg_add_empty(struct ldb_message *msg, const char *attr_name, int flags)
+{
+       struct ldb_message_element *els;
+
+       if (! ldb_valid_attr_name(attr_name)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       els = talloc_realloc(msg, msg->elements, 
+                            struct ldb_message_element, msg->num_elements+1);
+       if (!els) {
+               errno = ENOMEM;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       els[msg->num_elements].values = NULL;
+       els[msg->num_elements].num_values = 0;
+       els[msg->num_elements].flags = flags;
+       els[msg->num_elements].name = talloc_strdup(els, attr_name);
+       if (!els[msg->num_elements].name) {
+               errno = ENOMEM;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->elements = els;
+       msg->num_elements++;
+
+       return LDB_SUCCESS;
+}
+
+/*
+  add an empty element to a message
+*/
+int ldb_msg_add(struct ldb_message *msg, 
+               const struct ldb_message_element *el, 
+               int flags)
+{
+       if (ldb_msg_add_empty(msg, el->name, flags) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->elements[msg->num_elements-1] = *el;
+       msg->elements[msg->num_elements-1].flags = flags;
+
+       return LDB_SUCCESS;
+}
+
+/*
+  add a value to a message
+*/
+int ldb_msg_add_value(struct ldb_message *msg, 
+                     const char *attr_name,
+                     const struct ldb_val *val)
+{
+       struct ldb_message_element *el;
+       struct ldb_val *vals;
+
+       el = ldb_msg_find_element(msg, attr_name);
+       if (!el) {
+               ldb_msg_add_empty(msg, attr_name, 0);
+               el = ldb_msg_find_element(msg, attr_name);
+       }
+       if (!el) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
+       if (!vals) {
+               errno = ENOMEM;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       el->values = vals;
+       el->values[el->num_values] = *val;
+       el->num_values++;
+
+       return LDB_SUCCESS;
+}
+
+
+/*
+  add a value to a message, stealing it into the 'right' place
+*/
+int ldb_msg_add_steal_value(struct ldb_message *msg, 
+                           const char *attr_name,
+                           struct ldb_val *val)
+{
+       int ret;
+       ret = ldb_msg_add_value(msg, attr_name, val);
+       if (ret == LDB_SUCCESS) {
+               struct ldb_message_element *el;
+               el = ldb_msg_find_element(msg, attr_name);
+               talloc_steal(el->values, val->data);
+       }
+       return ret;
+}
+
+
+/*
+  add a string element to a message
+*/
+int ldb_msg_add_string(struct ldb_message *msg, 
+                      const char *attr_name, const char *str)
+{
+       struct ldb_val val;
+
+       val.data = discard_const_p(uint8_t, str);
+       val.length = strlen(str);
+
+       return ldb_msg_add_value(msg, attr_name, &val);
+}
+
+/*
+  add a string element to a message, stealing it into the 'right' place
+*/
+int ldb_msg_add_steal_string(struct ldb_message *msg, 
+                            const char *attr_name, char *str)
+{
+       struct ldb_val val;
+
+       val.data = (uint8_t *)str;
+       val.length = strlen(str);
+
+       return ldb_msg_add_steal_value(msg, attr_name, &val);
+}
+
+/*
+  add a printf formatted element to a message
+*/
+int ldb_msg_add_fmt(struct ldb_message *msg, 
+                   const char *attr_name, const char *fmt, ...)
+{
+       struct ldb_val val;
+       va_list ap;
+       char *str;
+
+       va_start(ap, fmt);
+       str = talloc_vasprintf(msg, fmt, ap);
+       va_end(ap);
+
+       if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+       val.data   = (uint8_t *)str;
+       val.length = strlen(str);
+
+       return ldb_msg_add_steal_value(msg, attr_name, &val);
+}
+
+/*
+  compare two ldb_message_element structures
+  assumes case senistive comparison
+*/
+int ldb_msg_element_compare(struct ldb_message_element *el1, 
+                           struct ldb_message_element *el2)
+{
+       unsigned int i;
+
+       if (el1->num_values != el2->num_values) {
+               return el1->num_values - el2->num_values;
+       }
+
+       for (i=0;i<el1->num_values;i++) {
+               if (!ldb_msg_find_val(el2, &el1->values[i])) {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+  compare two ldb_message_element structures
+  comparing by element name
+*/
+int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
+                                struct ldb_message_element *el2)
+{
+       return ldb_attr_cmp(el1->name, el2->name);
+}
+
+/*
+  convenience functions to return common types from a message
+  these return the first value if the attribute is multi-valued
+*/
+const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
+{
+       struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
+       if (!el || el->num_values == 0) {
+               return NULL;
+       }
+       return &el->values[0];
+}
+
+int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
+                            const char *attr_name,
+                            int default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtol((const char *)v->data, NULL, 0);
+}
+
+unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
+                                      const char *attr_name,
+                                      unsigned int default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtoul((const char *)v->data, NULL, 0);
+}
+
+int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
+                                  const char *attr_name,
+                                  int64_t default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtoll((const char *)v->data, NULL, 0);
+}
+
+uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
+                                    const char *attr_name,
+                                    uint64_t default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtoull((const char *)v->data, NULL, 0);
+}
+
+double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
+                                  const char *attr_name,
+                                  double default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtod((const char *)v->data, NULL);
+}
+
+int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
+                             const char *attr_name,
+                             int default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       if (strcasecmp((const char *)v->data, "FALSE") == 0) {
+               return 0;
+       }
+       if (strcasecmp((const char *)v->data, "TRUE") == 0) {
+               return 1;
+       }
+       return default_value;
+}
+
+const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
+                                       const char *attr_name,
+                                       const char *default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return (const char *)v->data;
+}
+
+struct ldb_dn *ldb_msg_find_attr_as_dn(void *mem_ctx,
+                                      const struct ldb_message *msg,
+                                      const char *attr_name)
+{
+       const struct ldb_val *v;
+
+       v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return NULL;
+       }
+       return ldb_dn_explode(mem_ctx, (const char *)v->data);
+}
+
+/*
+  sort the elements of a message by name
+*/
+void ldb_msg_sort_elements(struct ldb_message *msg)
+{
+       qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
+             (comparison_fn_t)ldb_msg_element_compare_name);
+}
+
+/*
+  shallow copy a message - copying only the elements array so that the caller
+  can safely add new elements without changing the message
+*/
+struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx, 
+                                        const struct ldb_message *msg)
+{
+       struct ldb_message *msg2;
+       int i;
+
+       msg2 = talloc(mem_ctx, struct ldb_message);
+       if (msg2 == NULL) return NULL;
+
+       *msg2 = *msg;
+       msg2->private_data = NULL;
+
+       msg2->elements = talloc_array(msg2, struct ldb_message_element, 
+                                     msg2->num_elements);
+       if (msg2->elements == NULL) goto failed;
+
+       for (i=0;i<msg2->num_elements;i++) {
+               msg2->elements[i] = msg->elements[i];
+       }
+
+       return msg2;
+
+failed:
+       talloc_free(msg2);
+       return NULL;
+}
+
+
+/*
+  copy a message, allocating new memory for all parts
+*/
+struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx, 
+                                const struct ldb_message *msg)
+{
+       struct ldb_message *msg2;
+       int i, j;
+
+       msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
+       if (msg2 == NULL) return NULL;
+
+       msg2->dn = ldb_dn_copy(msg2, msg2->dn);
+       if (msg2->dn == NULL) goto failed;
+
+       for (i=0;i<msg2->num_elements;i++) {
+               struct ldb_message_element *el = &msg2->elements[i];
+               struct ldb_val *values = el->values;
+               el->name = talloc_strdup(msg2->elements, el->name);
+               if (el->name == NULL) goto failed;
+               el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
+               for (j=0;j<el->num_values;j++) {
+                       el->values[j] = ldb_val_dup(el->values, &values[j]);
+                       if (el->values[j].data == NULL && values[j].length != 0) {
+                               goto failed;
+                       }
+               }
+       }
+
+       return msg2;
+
+failed:
+       talloc_free(msg2);
+       return NULL;
+}
+
+
+/*
+  canonicalise a message, merging elements of the same name
+*/
+struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
+                                        const struct ldb_message *msg)
+{
+       int i;
+       struct ldb_message *msg2;
+
+       msg2 = ldb_msg_copy(ldb, msg);
+       if (msg2 == NULL) return NULL;
+
+       ldb_msg_sort_elements(msg2);
+
+       for (i=1;i<msg2->num_elements;i++) {
+               struct ldb_message_element *el1 = &msg2->elements[i-1];
+               struct ldb_message_element *el2 = &msg2->elements[i];
+               if (ldb_msg_element_compare_name(el1, el2) == 0) {
+                       el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
+                                                      el1->num_values + el2->num_values);
+                       if (el1->values == NULL) {
+                               return NULL;
+                       }
+                       memcpy(el1->values + el1->num_values,
+                              el2->values,
+                              sizeof(struct ldb_val) * el2->num_values);
+                       el1->num_values += el2->num_values;
+                       talloc_free(discard_const_p(char, el2->name));
+                       if (i+1<msg2->num_elements) {
+                               memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
+                                       (msg2->num_elements - (i+1)));
+                       }
+                       msg2->num_elements--;
+                       i--;
+               }
+       }
+
+       return msg2;
+}
+
+
+/*
+  return a ldb_message representing the differences between msg1 and msg2. If you
+  then use this in a ldb_modify() call it can be used to save edits to a message
+*/
+struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
+                                struct ldb_message *msg1,
+                                struct ldb_message *msg2)
+{
+       struct ldb_message *mod;
+       struct ldb_message_element *el;
+       unsigned int i;
+
+       mod = ldb_msg_new(ldb);
+
+       mod->dn = msg1->dn;
+       mod->num_elements = 0;
+       mod->elements = NULL;
+
+       msg2 = ldb_msg_canonicalize(ldb, msg2);
+       if (msg2 == NULL) {
+               return NULL;
+       }
+       
+       /* look in msg2 to find elements that need to be added
+          or modified */
+       for (i=0;i<msg2->num_elements;i++) {
+               el = ldb_msg_find_element(msg1, msg2->elements[i].name);
+
+               if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
+                       continue;
+               }
+
+               if (ldb_msg_add(mod, 
+                               &msg2->elements[i],
+                               el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
+                       return NULL;
+               }
+       }
+
+       /* look in msg1 to find elements that need to be deleted */
+       for (i=0;i<msg1->num_elements;i++) {
+               el = ldb_msg_find_element(msg2, msg1->elements[i].name);
+               if (!el) {
+                       if (ldb_msg_add_empty(mod, 
+                                             msg1->elements[i].name,
+                                             LDB_FLAG_MOD_DELETE) != 0) {
+                               return NULL;
+                       }
+               }
+       }
+
+       return mod;
+}
+
+int ldb_msg_sanity_check(struct ldb_context *ldb, 
+                        const struct ldb_message *msg)
+{
+       int i, j;
+
+       /* basic check on DN */
+       if (msg->dn == NULL) {
+               /* TODO: return also an error string */
+               ldb_set_errstring(ldb, "ldb message lacks a DN!");
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+       if (msg->dn->comp_num == 0) {
+               /* root dse has empty dn */
+               ldb_set_errstring(ldb, "DN on new ldb message is '' (not permitted)!");
+               return LDB_ERR_ENTRY_ALREADY_EXISTS;
+       }
+
+       /* basic syntax checks */
+       for (i = 0; i < msg->num_elements; i++) {
+               for (j = 0; j < msg->elements[i].num_values; j++) {
+                       if (msg->elements[i].values[j].length == 0) {
+                               TALLOC_CTX *mem_ctx = talloc_new(ldb);
+                               /* an attribute cannot be empty */
+                               /* TODO: return also an error string */
+                               ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
+                                                           msg->elements[i].name, 
+                                                           ldb_dn_linearize(mem_ctx, msg->dn));
+                               talloc_free(mem_ctx);
+                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+                       }
+               }
+       }
+
+       return LDB_SUCCESS;
+}
+
+
+
+
+/*
+  copy an attribute list. This only copies the array, not the elements
+  (ie. the elements are left as the same pointers)
+*/
+const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
+{
+       const char **ret;
+       int i;
+       for (i=0;attrs[i];i++) /* noop */ ;
+       ret = talloc_array(mem_ctx, const char *, i+1);
+       if (ret == NULL) {
+               return NULL;
+       }
+       for (i=0;attrs[i];i++) {
+               ret[i] = attrs[i];
+       }
+       ret[i] = attrs[i];
+       return ret;
+}
+
+
+/*
+  copy an attribute list. This only copies the array, not the elements
+  (ie. the elements are left as the same pointers)
+*/
+const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
+{
+       const char **ret;
+       int i;
+       for (i=0;attrs[i];i++) /* noop */ ;
+       ret = talloc_array(mem_ctx, const char *, i+2);
+       if (ret == NULL) {
+               return NULL;
+       }
+       for (i=0;attrs[i];i++) {
+               ret[i] = attrs[i];
+       }
+       ret[i] = new_attr;
+       ret[i+1] = NULL;
+       return ret;
+}
+
+
+/*
+  return 1 if an attribute is in a list of attributes, or 0 otherwise
+*/
+int ldb_attr_in_list(const char * const *attrs, const char *attr)
+{
+       int i;
+       for (i=0;attrs[i];i++) {
+               if (ldb_attr_cmp(attrs[i], attr) == 0) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+
+/*
+  rename the specified attribute in a search result
+*/
+int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
+{
+       struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+       if (el == NULL) {
+               return LDB_SUCCESS;
+       }
+       el->name = talloc_strdup(msg->elements, replace);
+       if (el->name == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       return LDB_SUCCESS;
+}
+
+
+/*
+  copy the specified attribute in a search result to a new attribute
+*/
+int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
+{
+       struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+       if (el == NULL) {
+               return LDB_SUCCESS;
+       }
+       if (ldb_msg_add(msg, el, 0) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       return ldb_msg_rename_attr(msg, attr, replace);
+}
+
+
+/*
+  remove the specified attribute in a search result
+*/
+void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
+{
+       struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+       if (el) {
+               int n = (el - msg->elements);
+               if (n != msg->num_elements-1) {
+                       memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
+               }
+               msg->num_elements--;
+       }
+}
+
+/*
+  return a LDAP formatted time string
+*/
+char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
+{
+       struct tm *tm = gmtime(&t);
+
+       if (!tm) {
+               return NULL;
+       }
+
+       /* formatted like: 20040408072012.0Z */
+       return talloc_asprintf(mem_ctx, 
+                              "%04u%02u%02u%02u%02u%02u.0Z",
+                              tm->tm_year+1900, tm->tm_mon+1,
+                              tm->tm_mday, tm->tm_hour, tm->tm_min,
+                              tm->tm_sec);
+}
+
+
+/*
+  convert a LDAP time string to a time_t. Return 0 if unable to convert
+*/
+time_t ldb_string_to_time(const char *s)
+{
+       struct tm tm;
+       
+       if (s == NULL) return 0;
+       
+       memset(&tm, 0, sizeof(tm));
+       if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
+                  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
+                  &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+               return 0;
+       }
+       tm.tm_year -= 1900;
+       tm.tm_mon -= 1;
+       
+       return timegm(&tm);
+}
+
+
+/*
+  dump a set of results to a file. Useful from within gdb
+*/
+void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
+{
+       int i;
+
+       for (i = 0; i < result->count; i++) {
+               struct ldb_ldif ldif;
+               fprintf(f, "# record %d\n", i+1);
+               ldif.changetype = LDB_CHANGETYPE_NONE;
+               ldif.msg = result->msgs[i];
+               ldb_ldif_write_file(ldb, f, &ldif);
+       }
+}
+
+int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
+{
+       struct ldb_message_element *el;
+       struct ldb_val val;
+       
+       el = ldb_msg_find_element(msg, name);
+       if (el == NULL)
+               return 0;
+
+       val.data = discard_const(value);
+       val.length = strlen(value);
+
+       if (ldb_msg_find_val(el, &val))
+               return 1;
+
+       return 0;
+}
diff --git a/source3/lib/ldb/common/ldb_parse.c b/source3/lib/ldb/common/ldb_parse.c
new file mode 100644 (file)
index 0000000..d9044a8
--- /dev/null
@@ -0,0 +1,817 @@
+/* 
+   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 parsing
+ *
+ *  Description: parse LDAP-like search expressions
+ *
+ *  Author: Andrew Tridgell
+ */
+
+/*
+  TODO:
+      - add RFC2254 binary string handling
+      - possibly add ~=, <= and >= handling
+      - expand the test suite
+      - add better parse error handling
+
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "system/locale.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> ::= '=' | '~=' | '<=' | '>='
+*/
+
+/*
+   decode a RFC2254 binary string representation of a buffer.
+   Used in LDAP filters.
+*/
+struct ldb_val ldb_binary_decode(void *mem_ctx, const char *str)
+{
+       int i, j;
+       struct ldb_val ret;
+       int slen = str?strlen(str):0;
+
+       ret.data = talloc_size(mem_ctx, slen+1);
+       ret.length = 0;
+       if (ret.data == NULL) return ret;
+
+       for (i=j=0;i<slen;i++) {
+               if (str[i] == '\\') {
+                       unsigned c;
+                       if (sscanf(&str[i+1], "%02X", &c) != 1) {
+                               talloc_free(ret.data);
+                               memset(&ret, 0, sizeof(ret));
+                               return ret;
+                       }
+                       ((uint8_t *)ret.data)[j++] = c;
+                       i += 2;
+               } else {
+                       ((uint8_t *)ret.data)[j++] = str[i];
+               }
+       }
+       ret.length = j;
+       ((uint8_t *)ret.data)[j] = 0;
+
+       return ret;
+}
+
+
+/*
+   encode a blob as a RFC2254 binary string, escaping any
+   non-printable or '\' characters
+*/
+char *ldb_binary_encode(void *mem_ctx, struct ldb_val val)
+{
+       int i;
+       char *ret;
+       int len = val.length;
+       unsigned char *buf = val.data;
+
+       for (i=0;i<val.length;i++) {
+               if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
+                       len += 2;
+               }
+       }
+       ret = talloc_array(mem_ctx, char, len+1);
+       if (ret == NULL) return NULL;
+
+       len = 0;
+       for (i=0;i<val.length;i++) {
+               if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
+                       snprintf(ret+len, 4, "\\%02X", buf[i]);
+                       len += 3;
+               } else {
+                       ret[len++] = buf[i];
+               }
+       }
+
+       ret[len] = 0;
+
+       return ret;     
+}
+
+/*
+   encode a string as a RFC2254 binary string, escaping any
+   non-printable or '\' characters.  This routine is suitable for use
+   in escaping user data in ldap filters.
+*/
+char *ldb_binary_encode_string(void *mem_ctx, const char *string)
+{
+       struct ldb_val val;
+       val.data = discard_const(string);
+       val.length = strlen(string);
+       return ldb_binary_encode(mem_ctx, val);
+}
+
+/* find the first matching wildcard */
+static char *ldb_parse_find_wildcard(char *value)
+{
+       while (*value) {
+               value = strpbrk(value, "\\*");
+               if (value == NULL) return NULL;
+
+               if (value[0] == '\\') {
+                       if (value[1] == '\0') return NULL;
+                       value += 2;
+                       continue;
+               }
+
+               if (value[0] == '*') return value;
+       }
+
+       return NULL;
+}
+
+/* return a NULL terminated list of binary strings representing the value
+   chunks separated by wildcards that makes the value portion of the filter
+*/
+static struct ldb_val **ldb_wildcard_decode(void *mem_ctx, const char *string)
+{
+       struct ldb_val **ret = NULL;
+       int val = 0;
+       char *wc, *str;
+
+       wc = talloc_strdup(mem_ctx, string);
+       if (wc == NULL) return NULL;
+
+       while (wc && *wc) {
+               str = wc;
+               wc = ldb_parse_find_wildcard(str);
+               if (wc && *wc) {
+                       if (wc == str) {
+                               wc++;
+                               continue;
+                       }
+                       *wc = 0;
+                       wc++;
+               }
+
+               ret = talloc_realloc(mem_ctx, ret, struct ldb_val *, val + 2);
+               if (ret == NULL) return NULL;
+
+               ret[val] = talloc(mem_ctx, struct ldb_val);
+               if (ret[val] == NULL) return NULL;
+
+               *(ret[val]) = ldb_binary_decode(mem_ctx, str);
+               if ((ret[val])->data == NULL) return NULL;
+
+               val++;
+       }
+
+       if (ret != NULL) {
+               ret[val] = NULL;
+       }
+
+       return ret;
+}
+
+static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s);
+
+
+/*
+  parse an extended match
+
+  possible forms:
+        (attr:oid:=value)
+        (attr:dn:oid:=value)
+        (attr:dn:=value)
+        (:dn:oid:=value)
+
+  the ':dn' part sets the dnAttributes boolean if present
+  the oid sets the rule_id string
+  
+*/
+static struct ldb_parse_tree *ldb_parse_extended(struct ldb_parse_tree *ret, 
+                                                char *attr, char *value)
+{
+       char *p1, *p2;
+
+       ret->operation = LDB_OP_EXTENDED;
+       ret->u.extended.value = ldb_binary_decode(ret, value);
+       if (ret->u.extended.value.data == NULL) goto failed;
+
+       p1 = strchr(attr, ':');
+       if (p1 == NULL) goto failed;
+       p2 = strchr(p1+1, ':');
+
+       *p1 = 0;
+       if (p2) *p2 = 0;
+
+       ret->u.extended.attr = attr;
+       if (strcmp(p1+1, "dn") == 0) {
+               ret->u.extended.dnAttributes = 1;
+               if (p2) {
+                       ret->u.extended.rule_id = talloc_strdup(ret, p2+1);
+                       if (ret->u.extended.rule_id == NULL) goto failed;
+               } else {
+                       ret->u.extended.rule_id = NULL;
+               }
+       } else {
+               ret->u.extended.dnAttributes = 0;
+               ret->u.extended.rule_id = talloc_strdup(ret, p1+1);
+               if (ret->u.extended.rule_id == NULL) goto failed;
+       }
+
+       return ret;
+
+failed:
+       talloc_free(ret);
+       return NULL;
+}
+
+static enum ldb_parse_op ldb_parse_filtertype(void *mem_ctx, char **type, char **value, const char **s)
+{
+       enum ldb_parse_op filter = 0;
+       char *name, *val, *k;
+       const char *p = *s;
+       const char *t, *t1;
+
+       /* retrieve attributetype name */
+       t = p;
+
+       while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-')) { /* attribute names can only be alphanums */
+               p++;
+       }
+
+       if (*p == ':') { /* but extended searches have : and . chars too */
+               p = strstr(p, ":=");
+               if (p == NULL) { /* malformed attribute name */
+                       return 0;
+               }
+       }
+
+       t1 = p;
+
+       while (isspace((unsigned char)*p)) p++;
+
+       if (!strchr("=<>~:", *p)) {
+               return 0;
+       }
+
+       /* save name */
+       name = talloc_memdup(mem_ctx, t, t1 - t + 1);
+       if (name == NULL) return 0;
+       name[t1 - t] = '\0';
+
+       /* retrieve filtertype */
+
+       if (*p == '=') {
+               filter = LDB_OP_EQUALITY;
+       } else if (*(p + 1) == '=') {
+               switch (*p) {
+               case '<':
+                       filter = LDB_OP_LESS;
+                       p++;
+                       break;
+               case '>':
+                       filter = LDB_OP_GREATER;
+                       p++;
+                       break;
+               case '~':
+                       filter = LDB_OP_APPROX;
+                       p++;
+                       break;
+               case ':':
+                       filter = LDB_OP_EXTENDED;
+                       p++;
+                       break;
+               }
+       }
+       if (!filter) {
+               talloc_free(name);
+               return filter;
+       }
+       p++;
+
+       while (isspace((unsigned char)*p)) p++;
+
+       /* retieve value */
+       t = p;
+
+       while (*p && ((*p != ')') || ((*p == ')') && (*(p - 1) == '\\')))) p++;
+
+       val = talloc_memdup(mem_ctx, t, p - t + 1);
+       if (val == NULL) {
+               talloc_free(name);
+               return 0;
+       }
+       val[p - t] = '\0';
+
+       k = &(val[p - t]);
+
+       /* remove trailing spaces from value */
+       while ((k > val) && (isspace((unsigned char)*(k - 1)))) k--;
+       *k = '\0';
+
+       *type = name;
+       *value = val;
+       *s = p;
+       return filter;
+}
+
+/*
+  <simple> ::= <attributetype> <filtertype> <attributevalue>
+*/
+static struct ldb_parse_tree *ldb_parse_simple(void *mem_ctx, const char **s)
+{
+       char *attr, *value;
+       struct ldb_parse_tree *ret;
+       enum ldb_parse_op filtertype;
+
+       ret = talloc(mem_ctx, struct ldb_parse_tree);
+       if (!ret) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       filtertype = ldb_parse_filtertype(ret, &attr, &value, s);
+       if (!filtertype) {
+               talloc_free(ret);
+               return NULL;
+       }
+
+       switch (filtertype) {
+
+               case LDB_OP_PRESENT:
+                       ret->operation = LDB_OP_PRESENT;
+                       ret->u.present.attr = attr;
+                       break;
+
+               case LDB_OP_EQUALITY:
+
+                       if (strcmp(value, "*") == 0) {
+                               ret->operation = LDB_OP_PRESENT;
+                               ret->u.present.attr = attr;
+                               break;
+                       }
+
+                       if (ldb_parse_find_wildcard(value) != NULL) {
+                               ret->operation = LDB_OP_SUBSTRING;
+                               ret->u.substring.attr = attr;
+                               ret->u.substring.start_with_wildcard = 0;
+                               ret->u.substring.end_with_wildcard = 0;
+                               ret->u.substring.chunks = ldb_wildcard_decode(ret, value);
+                               if (ret->u.substring.chunks == NULL){
+                                       talloc_free(ret);
+                                       return NULL;
+                               }
+                               if (value[0] == '*')
+                                       ret->u.substring.start_with_wildcard = 1;
+                               if (value[strlen(value) - 1] == '*')
+                                       ret->u.substring.end_with_wildcard = 1;
+                               talloc_free(value);
+
+                               break;
+                       }
+
+                       ret->operation = LDB_OP_EQUALITY;
+                       ret->u.equality.attr = attr;
+                       ret->u.equality.value = ldb_binary_decode(ret, value);
+                       if (ret->u.equality.value.data == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       talloc_free(value);
+                       break;
+
+               case LDB_OP_GREATER:
+                       ret->operation = LDB_OP_GREATER;
+                       ret->u.comparison.attr = attr;
+                       ret->u.comparison.value = ldb_binary_decode(ret, value);
+                       if (ret->u.comparison.value.data == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       talloc_free(value);
+                       break;
+
+               case LDB_OP_LESS:
+                       ret->operation = LDB_OP_LESS;
+                       ret->u.comparison.attr = attr;
+                       ret->u.comparison.value = ldb_binary_decode(ret, value);
+                       if (ret->u.comparison.value.data == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       talloc_free(value);
+                       break;
+
+               case LDB_OP_APPROX:
+                       ret->operation = LDB_OP_APPROX;
+                       ret->u.comparison.attr = attr;
+                       ret->u.comparison.value = ldb_binary_decode(ret, value);
+                       if (ret->u.comparison.value.data == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       talloc_free(value);
+                       break;
+
+               case LDB_OP_EXTENDED:
+
+                       ret = ldb_parse_extended(ret, attr, value);
+                       break;
+
+               default:
+                       talloc_free(ret);
+                       return NULL;
+       }
+
+       return ret;
+}
+
+
+/*
+  parse a filterlist
+  <and> ::= '&' <filterlist>
+  <or> ::= '|' <filterlist>
+  <filterlist> ::= <filter> | <filter> <filterlist>
+*/
+static struct ldb_parse_tree *ldb_parse_filterlist(void *mem_ctx, const char **s)
+{
+       struct ldb_parse_tree *ret, *next;
+       enum ldb_parse_op op;
+       const char *p = *s;
+
+       switch (*p) {
+               case '&':
+                       op = LDB_OP_AND;
+                       break;
+               case '|':
+                       op = LDB_OP_OR;
+                       break;
+               default:
+                       return NULL;
+       }
+       p++;
+
+       while (isspace((unsigned char)*p)) p++;
+
+       ret = talloc(mem_ctx, struct ldb_parse_tree);
+       if (!ret) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ret->operation = op;
+       ret->u.list.num_elements = 1;
+       ret->u.list.elements = talloc(ret, struct ldb_parse_tree *);
+       if (!ret->u.list.elements) {
+               errno = ENOMEM;
+               talloc_free(ret);
+               return NULL;
+       }
+
+       ret->u.list.elements[0] = ldb_parse_filter(ret->u.list.elements, &p);
+       if (!ret->u.list.elements[0]) {
+               talloc_free(ret);
+               return NULL;
+       }
+
+       while (isspace((unsigned char)*p)) p++;
+
+       while (*p && (next = ldb_parse_filter(ret->u.list.elements, &p))) {
+               struct ldb_parse_tree **e;
+               e = talloc_realloc(ret, ret->u.list.elements, 
+                                    struct ldb_parse_tree *, 
+                                    ret->u.list.num_elements + 1);
+               if (!e) {
+                       errno = ENOMEM;
+                       talloc_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((unsigned char)*p)) p++;
+       }
+
+       *s = p;
+
+       return ret;
+}
+
+
+/*
+  <not> ::= '!' <filter>
+*/
+static struct ldb_parse_tree *ldb_parse_not(void *mem_ctx, const char **s)
+{
+       struct ldb_parse_tree *ret;
+       const char *p = *s;
+
+       if (*p != '!') {
+               return NULL;
+       }
+       p++;
+
+       ret = talloc(mem_ctx, struct ldb_parse_tree);
+       if (!ret) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ret->operation = LDB_OP_NOT;
+       ret->u.isnot.child = ldb_parse_filter(ret, &p);
+       if (!ret->u.isnot.child) {
+               talloc_free(ret);
+               return NULL;
+       }
+
+       *s = p;
+
+       return ret;
+}
+
+/*
+  parse a filtercomp
+  <filtercomp> ::= <and> | <or> | <not> | <simple>
+*/
+static struct ldb_parse_tree *ldb_parse_filtercomp(void *mem_ctx, const char **s)
+{
+       struct ldb_parse_tree *ret;
+       const char *p = *s;
+
+       while (isspace((unsigned char)*p)) p++;
+
+       switch (*p) {
+       case '&':
+               ret = ldb_parse_filterlist(mem_ctx, &p);
+               break;
+
+       case '|':
+               ret = ldb_parse_filterlist(mem_ctx, &p);
+               break;
+
+       case '!':
+               ret = ldb_parse_not(mem_ctx, &p);
+               break;
+
+       case '(':
+       case ')':
+               return NULL;
+
+       default:
+               ret = ldb_parse_simple(mem_ctx, &p);
+
+       }
+
+       *s = p;
+       return ret;
+}
+
+
+/*
+  <filter> ::= '(' <filtercomp> ')'
+*/
+static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s)
+{
+       struct ldb_parse_tree *ret;
+       const char *p = *s;
+
+       if (*p != '(') {
+               return NULL;
+       }
+       p++;
+
+       ret = ldb_parse_filtercomp(mem_ctx, &p);
+
+       if (*p != ')') {
+               return NULL;
+       }
+       p++;
+
+       while (isspace((unsigned char)*p)) {
+               p++;
+       }
+
+       *s = p;
+
+       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(void *mem_ctx, const char *s)
+{
+       if (s == NULL || *s == 0) {
+               s = "(|(objectClass=*)(distinguishedName=*))";
+       }
+
+       while (isspace((unsigned char)*s)) s++;
+
+       if (*s == '(') {
+               return ldb_parse_filter(mem_ctx, &s);
+       }
+
+       return ldb_parse_simple(mem_ctx, &s);
+}
+
+
+/*
+  construct a ldap parse filter given a parse tree
+*/
+char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree)
+{
+       char *s, *s2, *ret;
+       int i;
+
+       if (tree == NULL) {
+               return NULL;
+       }
+
+       switch (tree->operation) {
+       case LDB_OP_AND:
+       case LDB_OP_OR:
+               ret = talloc_asprintf(mem_ctx, "(%c", tree->operation==LDB_OP_AND?'&':'|');
+               if (ret == NULL) return NULL;
+               for (i=0;i<tree->u.list.num_elements;i++) {
+                       s = ldb_filter_from_tree(mem_ctx, tree->u.list.elements[i]);
+                       if (s == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       s2 = talloc_asprintf_append(ret, "%s", s);
+                       talloc_free(s);
+                       if (s2 == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       ret = s2;
+               }
+               s = talloc_asprintf_append(ret, ")");
+               if (s == NULL) {
+                       talloc_free(ret);
+                       return NULL;
+               }
+               return s;
+       case LDB_OP_NOT:
+               s = ldb_filter_from_tree(mem_ctx, tree->u.isnot.child);
+               if (s == NULL) return NULL;
+
+               ret = talloc_asprintf(mem_ctx, "(!%s)", s);
+               talloc_free(s);
+               return ret;
+       case LDB_OP_EQUALITY:
+               s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+               if (s == NULL) return NULL;
+               ret = talloc_asprintf(mem_ctx, "(%s=%s)", 
+                                     tree->u.equality.attr, s);
+               talloc_free(s);
+               return ret;
+       case LDB_OP_SUBSTRING:
+               ret = talloc_asprintf(mem_ctx, "(%s=%s", tree->u.substring.attr,
+                                     tree->u.substring.start_with_wildcard?"*":"");
+               if (ret == NULL) return NULL;
+               for (i = 0; tree->u.substring.chunks[i]; i++) {
+                       s2 = ldb_binary_encode(mem_ctx, *(tree->u.substring.chunks[i]));
+                       if (s2 == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       if (tree->u.substring.chunks[i+1] ||
+                           tree->u.substring.end_with_wildcard) {
+                               s = talloc_asprintf_append(ret, "%s*", s2);
+                       } else {
+                               s = talloc_asprintf_append(ret, "%s", s2);
+                       }
+                       if (s == NULL) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       ret = s;
+               }
+               s = talloc_asprintf_append(ret, ")");
+               if (s == NULL) {
+                       talloc_free(ret);
+                       return NULL;
+               }
+               ret = s;
+               return ret;
+       case LDB_OP_GREATER:
+               s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+               if (s == NULL) return NULL;
+               ret = talloc_asprintf(mem_ctx, "(%s>=%s)", 
+                                     tree->u.equality.attr, s);
+               talloc_free(s);
+               return ret;
+       case LDB_OP_LESS:
+               s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+               if (s == NULL) return NULL;
+               ret = talloc_asprintf(mem_ctx, "(%s<=%s)", 
+                                     tree->u.equality.attr, s);
+               talloc_free(s);
+               return ret;
+       case LDB_OP_PRESENT:
+               ret = talloc_asprintf(mem_ctx, "(%s=*)", tree->u.present.attr);
+               return ret;
+       case LDB_OP_APPROX:
+               s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+               if (s == NULL) return NULL;
+               ret = talloc_asprintf(mem_ctx, "(%s~=%s)", 
+                                     tree->u.equality.attr, s);
+               talloc_free(s);
+               return ret;
+       case LDB_OP_EXTENDED:
+               s = ldb_binary_encode(mem_ctx, tree->u.extended.value);
+               if (s == NULL) return NULL;
+               ret = talloc_asprintf(mem_ctx, "(%s%s%s%s:=%s)", 
+                                     tree->u.extended.attr?tree->u.extended.attr:"", 
+                                     tree->u.extended.dnAttributes?":dn":"",
+                                     tree->u.extended.rule_id?":":"", 
+                                     tree->u.extended.rule_id?tree->u.extended.rule_id:"", 
+                                     s);
+               talloc_free(s);
+               return ret;
+       }
+       
+       return NULL;
+}
+
+
+/*
+  replace any occurances of an attribute name in the parse tree with a
+  new name
+*/
+void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree, 
+                                const char *attr, 
+                                const char *replace)
+{
+       int i;
+       switch (tree->operation) {
+       case LDB_OP_AND:
+       case LDB_OP_OR:
+               for (i=0;i<tree->u.list.num_elements;i++) {
+                       ldb_parse_tree_attr_replace(tree->u.list.elements[i],
+                                                   attr, replace);
+               }
+               break;
+       case LDB_OP_NOT:
+               ldb_parse_tree_attr_replace(tree->u.isnot.child, attr, replace);
+               break;
+       case LDB_OP_EQUALITY:
+       case LDB_OP_GREATER:
+       case LDB_OP_LESS:
+       case LDB_OP_APPROX:
+               if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
+                       tree->u.equality.attr = replace;
+               }
+               break;
+       case LDB_OP_SUBSTRING:
+               if (ldb_attr_cmp(tree->u.substring.attr, attr) == 0) {
+                       tree->u.substring.attr = replace;
+               }
+               break;
+       case LDB_OP_PRESENT:
+               if (ldb_attr_cmp(tree->u.present.attr, attr) == 0) {
+                       tree->u.present.attr = replace;
+               }
+               break;
+       case LDB_OP_EXTENDED:
+               if (tree->u.extended.attr &&
+                   ldb_attr_cmp(tree->u.extended.attr, attr) == 0) {
+                       tree->u.extended.attr = replace;
+               }
+               break;
+       }
+}
diff --git a/source3/lib/ldb/common/ldb_utf8.c b/source3/lib/ldb/common/ldb_utf8.c
new file mode 100644 (file)
index 0000000..d2d12b8
--- /dev/null
@@ -0,0 +1,149 @@
+/* 
+   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 utf8 handling
+ *
+ *  Description: case folding and case comparison for UTF8 strings
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "system/locale.h"
+
+
+/*
+  this allow the user to pass in a caseless comparison
+  function to handle utf8 caseless comparisons
+ */
+void ldb_set_utf8_fns(struct ldb_context *ldb,
+                       void *context,
+                       char *(*casefold)(void *, void *, const char *))
+{
+       if (context)
+               ldb->utf8_fns.context = context;
+       if (casefold)
+               ldb->utf8_fns.casefold = casefold;
+}
+
+/*
+  a simple case folding function
+  NOTE: does not handle UTF8
+*/
+char *ldb_casefold_default(void *context, void *mem_ctx, const char *s)
+{
+       int i;
+       char *ret = talloc_strdup(mem_ctx, s);
+       if (!s) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       for (i=0;ret[i];i++) {
+               ret[i] = toupper((unsigned char)ret[i]);
+       }
+       return ret;
+}
+
+void ldb_set_utf8_default(struct ldb_context *ldb)
+{
+       ldb_set_utf8_fns(ldb, NULL, ldb_casefold_default);
+}
+
+char *ldb_casefold(struct ldb_context *ldb, void *mem_ctx, const char *s)
+{
+       return ldb->utf8_fns.casefold(ldb->utf8_fns.context, mem_ctx, s);
+}
+
+/*
+  check the attribute name is valid according to rfc2251
+  returns 1 if the name is ok
+ */
+
+int ldb_valid_attr_name(const char *s)
+{
+       int i;
+
+       if (!s || !s[0])
+               return 0;
+
+       /* handle special ldb_tdb wildcard */
+       if (strcmp(s, "*") == 0) return 1;
+
+       for (i = 0; s[i]; i++) {
+               if (! isascii(s[i])) {
+                       return 0;
+               }
+               if (i == 0) { /* first char must be an alpha (or our special '@' identifier) */
+                       if (! (isalpha(s[i]) || (s[i] == '@'))) {
+                               return 0;
+                       }
+               } else {
+                       if (! (isalnum(s[i]) || (s[i] == '-'))) {
+                               return 0;
+                       }
+               }
+       }
+       return 1;
+}
+
+/*
+  compare two attribute names
+  attribute names are restricted by rfc2251 so using
+  strcasecmp and toupper here is ok.
+  return 0 for match
+*/
+int ldb_attr_cmp(const char *attr1, const char *attr2)
+{
+       return strcasecmp(attr1, attr2);
+}
+
+char *ldb_attr_casefold(void *mem_ctx, const char *s)
+{
+       int i;
+       char *ret = talloc_strdup(mem_ctx, s);
+       if (!s) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       for (i = 0; ret[i]; i++) {
+               ret[i] = toupper((unsigned char)ret[i]);
+       }
+       return ret;
+}
+
+/*
+  we accept either 'dn' or 'distinguishedName' for a distinguishedName
+*/
+int ldb_attr_dn(const char *attr)
+{
+       if (ldb_attr_cmp(attr, "dn") == 0 ||
+           ldb_attr_cmp(attr, "distinguishedName") == 0) {
+               return 0;
+       }
+       return -1;
+}
diff --git a/source3/lib/ldb/common/qsort.c b/source3/lib/ldb/common/qsort.c
new file mode 100644 (file)
index 0000000..ef7be2c
--- /dev/null
@@ -0,0 +1,254 @@
+/* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
+
+   The GNU C 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.1 of the License, or (at your option) any later version.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* If you consider tuning this algorithm, you should consult first:
+   Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
+   Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993.  */
+
+/* Modified to be used in samba4 by
+ * Simo Sorce <idra@samba.org>         2005
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/* Byte-wise swap two items of size SIZE. */
+#define SWAP(a, b, size)                                                     \
+  do                                                                         \
+    {                                                                        \
+      register size_t __size = (size);                                       \
+      register char *__a = (a), *__b = (b);                                  \
+      do                                                                     \
+       {                                                                     \
+         char __tmp = *__a;                                                  \
+         *__a++ = *__b;                                                      \
+         *__b++ = __tmp;                                                     \
+       } while (--__size > 0);                                               \
+    } while (0)
+
+/* Discontinue quicksort algorithm when partition gets below this size.
+   This particular magic number was chosen to work best on a Sun 4/260. */
+#define MAX_THRESH 4
+
+/* Stack node declarations used to store unfulfilled partition obligations. */
+typedef struct
+  {
+    char *lo;
+    char *hi;
+  } stack_node;
+
+/* The next 4 #defines implement a very fast in-line stack abstraction. */
+/* The stack needs log (total_elements) entries (we could even subtract
+   log(MAX_THRESH)).  Since total_elements has type size_t, we get as
+   upper bound for log (total_elements):
+   bits per byte (CHAR_BIT) * sizeof(size_t).  */
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+#define STACK_SIZE     (CHAR_BIT * sizeof(size_t))
+#define PUSH(low, high)        ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
+#define        POP(low, high)  ((void) (--top, (low = top->lo), (high = top->hi)))
+#define        STACK_NOT_EMPTY (stack < top)
+
+
+/* Order size using quicksort.  This implementation incorporates
+   four optimizations discussed in Sedgewick:
+
+   1. Non-recursive, using an explicit stack of pointer that store the
+      next array partition to sort.  To save time, this maximum amount
+      of space required to store an array of SIZE_MAX is allocated on the
+      stack.  Assuming a 32-bit (64 bit) integer for size_t, this needs
+      only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
+      Pretty cheap, actually.
+
+   2. Chose the pivot element using a median-of-three decision tree.
+      This reduces the probability of selecting a bad pivot value and
+      eliminates certain extraneous comparisons.
+
+   3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
+      insertion sort to order the MAX_THRESH items within each partition.
+      This is a big win, since insertion sort is faster for small, mostly
+      sorted array segments.
+
+   4. The larger of the two sub-partitions is always pushed onto the
+      stack first, with the algorithm then concentrating on the
+      smaller partition.  This *guarantees* no more than log (total_elems)
+      stack size is needed (actually O(1) in this case)!  */
+
+void ldb_qsort (void *const pbase, size_t total_elems, size_t size,
+               void *opaque, ldb_qsort_cmp_fn_t cmp)
+{
+  register char *base_ptr = (char *) pbase;
+
+  const size_t max_thresh = MAX_THRESH * size;
+
+  if (total_elems == 0)
+    /* Avoid lossage with unsigned arithmetic below.  */
+    return;
+
+  if (total_elems > MAX_THRESH)
+    {
+      char *lo = base_ptr;
+      char *hi = &lo[size * (total_elems - 1)];
+      stack_node stack[STACK_SIZE];
+      stack_node *top = stack;
+
+      PUSH (NULL, NULL);
+
+      while (STACK_NOT_EMPTY)
+        {
+          char *left_ptr;
+          char *right_ptr;
+
+         /* Select median value from among LO, MID, and HI. Rearrange
+            LO and HI so the three values are sorted. This lowers the
+            probability of picking a pathological pivot value and
+            skips a comparison for both the LEFT_PTR and RIGHT_PTR in
+            the while loops. */
+
+         char *mid = lo + size * ((hi - lo) / size >> 1);
+
+         if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
+           SWAP (mid, lo, size);
+         if ((*cmp) ((void *) hi, (void *) mid, opaque) < 0)
+           SWAP (mid, hi, size);
+         else
+           goto jump_over;
+         if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
+           SWAP (mid, lo, size);
+       jump_over:;
+
+         left_ptr  = lo + size;
+         right_ptr = hi - size;
+
+         /* Here's the famous ``collapse the walls'' section of quicksort.
+            Gotta like those tight inner loops!  They are the main reason
+            that this algorithm runs much faster than others. */
+         do
+           {
+             while ((*cmp) ((void *) left_ptr, (void *) mid, opaque) < 0)
+               left_ptr += size;
+
+             while ((*cmp) ((void *) mid, (void *) right_ptr, opaque) < 0)
+               right_ptr -= size;
+
+             if (left_ptr < right_ptr)
+               {
+                 SWAP (left_ptr, right_ptr, size);
+                 if (mid == left_ptr)
+                   mid = right_ptr;
+                 else if (mid == right_ptr)
+                   mid = left_ptr;
+                 left_ptr += size;
+                 right_ptr -= size;
+               }
+             else if (left_ptr == right_ptr)
+               {
+                 left_ptr += size;
+                 right_ptr -= size;
+                 break;
+               }
+           }
+         while (left_ptr <= right_ptr);
+
+          /* Set up pointers for next iteration.  First determine whether
+             left and right partitions are below the threshold size.  If so,
+             ignore one or both.  Otherwise, push the larger partition's
+             bounds on the stack and continue sorting the smaller one. */
+
+          if ((size_t) (right_ptr - lo) <= max_thresh)
+            {
+              if ((size_t) (hi - left_ptr) <= max_thresh)
+               /* Ignore both small partitions. */
+                POP (lo, hi);
+              else
+               /* Ignore small left partition. */
+                lo = left_ptr;
+            }
+          else if ((size_t) (hi - left_ptr) <= max_thresh)
+           /* Ignore small right partition. */
+            hi = right_ptr;
+          else if ((right_ptr - lo) > (hi - left_ptr))
+            {
+             /* Push larger left partition indices. */
+              PUSH (lo, right_ptr);
+              lo = left_ptr;
+            }
+          else
+            {
+             /* Push larger right partition indices. */
+              PUSH (left_ptr, hi);
+              hi = right_ptr;
+            }
+        }
+    }
+
+  /* Once the BASE_PTR array is partially sorted by quicksort the rest
+     is completely sorted using insertion sort, since this is efficient
+     for partitions below MAX_THRESH size. BASE_PTR points to the beginning
+     of the array to sort, and END_PTR points at the very last element in
+     the array (*not* one beyond it!). */
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+  {
+    char *const end_ptr = &base_ptr[size * (total_elems - 1)];
+    char *tmp_ptr = base_ptr;
+    char *thresh = min(end_ptr, base_ptr + max_thresh);
+    register char *run_ptr;
+
+    /* Find smallest element in first threshold and place it at the
+       array's beginning.  This is the smallest array element,
+       and the operation speeds up insertion sort's inner loop. */
+
+    for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
+      if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
+        tmp_ptr = run_ptr;
+
+    if (tmp_ptr != base_ptr)
+      SWAP (tmp_ptr, base_ptr, size);
+
+    /* Insertion sort, running from left-hand-side up to right-hand-side.  */
+
+    run_ptr = base_ptr + size;
+    while ((run_ptr += size) <= end_ptr)
+      {
+       tmp_ptr = run_ptr - size;
+       while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
+         tmp_ptr -= size;
+
+       tmp_ptr += size;
+        if (tmp_ptr != run_ptr)
+          {
+            char *trav;
+
+           trav = run_ptr + size;
+           while (--trav >= run_ptr)
+              {
+                char c = *trav;
+                char *hi, *lo;
+
+                for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
+                  *hi = *lo;
+                *hi = c;
+              }
+          }
+      }
+  }
+}
diff --git a/source3/lib/ldb/config.guess b/source3/lib/ldb/config.guess
new file mode 100755 (executable)
index 0000000..ad5281e
--- /dev/null
@@ -0,0 +1,1466 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-08-03'
+
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep __ELF__ >/dev/null
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       exit ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit ;;
+    macppc:MirBSD:*:*)
+       echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit ;;
+    *:z/VM:*:*)
+       echo s390-ibm-zvmoe
+       exit ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+       exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+       echo arm-unknown-riscos
+       exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7; exit ;;
+       esac ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c &&
+         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`$dummy $dummyarg` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               then
+                       echo "$SYSTEM_NAME"
+               else
+                       echo rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit ;;
+    *:AIX:*:[45])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           eval $set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep __LP64__ >/dev/null
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       echo unknown-hitachi-hiuxwe2
+       exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:FreeBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit ;;
+    i*:windows32*:*)
+       # uname -m includes "-pc" on this system.
+       echo ${UNAME_MACHINE}-mingw32
+       exit ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit ;;
+    x86:Interix*:[34]*)
+       echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+       exit ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       echo x86_64-unknown-cygwin
+       exit ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+       exit ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit ;;
+    arm*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    cris:Linux:*:*)
+       echo cris-axis-linux-gnu
+       exit ;;
+    crisv32:Linux:*:*)
+       echo crisv32-axis-linux-gnu
+       exit ;;
+    frv:Linux:*:*)
+       echo frv-unknown-linux-gnu
+       exit ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    mips:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mipsel
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips64
+       #undef mips64el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mips64el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips64
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    or32:Linux:*:*)
+       echo or32-unknown-linux-gnu
+       exit ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit ;;
+    i*86:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       # Set LC_ALL=C to ensure ld outputs messages in English.
+       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+                        | sed -ne '/supported targets:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported targets: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_targets" in
+         elf32-i386)
+               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+               ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit ;;
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit ;;
+       esac
+       # Determine whether the default compiler is a.out or elf
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <features.h>
+       #ifdef __ELF__
+       # ifdef __GLIBC__
+       #  if __GLIBC__ >= 2
+       LIBC=gnu
+       #  else
+       LIBC=gnulibc1
+       #  endif
+       # else
+       LIBC=gnulibc1
+       # endif
+       #else
+       #ifdef __INTEL_COMPILER
+       LIBC=gnu
+       #else
+       LIBC=gnuaout
+       #endif
+       #endif
+       #ifdef __dietlibc__
+       LIBC=dietlibc
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+       test x"${LIBC}" != x && {
+               echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+               exit
+       }
+       test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit ;;
+    i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && { echo i486-ncr-sysv4; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo ${UNAME_MACHINE}-stratus-vos
+       exit ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           *86) UNAME_PROCESSOR=i686 ;;
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+       echo nse-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+       exit ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms ; exit ;;
+           I*) echo ia64-dec-vms ; exit ;;
+           V*) echo vax-dec-vms ; exit ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit ;;
+    i*86:skyos:*:*)
+       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+       exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    c34*)
+       echo c34-convex-bsd
+       exit ;;
+    c38*)
+       echo c38-convex-bsd
+       exit ;;
+    c4*)
+       echo c4-convex-bsd
+       exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source3/lib/ldb/config.mk b/source3/lib/ldb/config.mk
new file mode 100644 (file)
index 0000000..6a23005
--- /dev/null
@@ -0,0 +1,323 @@
+################################################
+# Start MODULE ldb_asq
+[MODULE::ldb_asq]
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_asq_init
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/asq.o
+# End MODULE ldb_asq
+################################################
+
+################################################
+# Start MODULE ldb_server_sort
+[MODULE::ldb_server_sort]
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_sort_init
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/sort.o
+# End MODULE ldb_sort
+################################################
+
+################################################
+# Start MODULE ldb_paged_results
+[MODULE::ldb_paged_results]
+INIT_FUNCTION = ldb_paged_results_init
+PRIVATE_DEPENDENCIES = LIBTALLOC
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/paged_results.o
+# End MODULE ldb_paged_results
+################################################
+
+################################################
+# Start MODULE ldb_paged_results
+[MODULE::ldb_paged_searches]
+INIT_FUNCTION = ldb_paged_searches_init
+PRIVATE_DEPENDENCIES = LIBTALLOC
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/paged_searches.o
+# End MODULE ldb_paged_results
+################################################
+
+################################################
+# Start MODULE ldb_operational
+[MODULE::ldb_operational]
+SUBSYSTEM = ldb
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_operational_init
+OBJ_FILES = \
+               modules/operational.o
+# End MODULE ldb_operational
+################################################
+
+################################################
+# Start MODULE ldb_objectclass
+[MODULE::ldb_objectclass]
+INIT_FUNCTION = ldb_objectclass_init
+PRIVATE_DEPENDENCIES = LIBTALLOC
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/objectclass.o
+# End MODULE ldb_objectclass
+################################################
+
+################################################
+# Start MODULE ldb_rdn_name
+[MODULE::ldb_rdn_name]
+SUBSYSTEM = ldb
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_rdn_name_init
+OBJ_FILES = \
+               modules/rdn_name.o
+# End MODULE ldb_rdn_name
+################################################
+
+# ################################################
+# # Start MODULE ldb_schema
+# [MODULE::ldb_schema]
+# INIT_FUNCTION = ldb_schema_init
+# SUBSYSTEM = ldb
+# OBJ_FILES = \
+#              modules/schema.o
+# # End MODULE ldb_schema
+# ################################################
+
+################################################
+# Start MODULE ldb_ildap
+[MODULE::ldb_ildap]
+SUBSYSTEM = ldb
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_ildap_init
+ALIASES = ldapi ldaps ldap
+OBJ_FILES = \
+               ldb_ildap/ldb_ildap.o
+PUBLIC_DEPENDENCIES = \
+               LIBCLI_LDAP
+# End MODULE ldb_ildap
+################################################
+
+################################################
+# Start MODULE ldb_map
+[MODULE::ldb_map]
+PRIVATE_DEPENDENCIES = LIBTALLOC
+SUBSYSTEM = ldb
+OBJ_FILES = \
+               modules/ldb_map_inbound.o \
+               modules/ldb_map_outbound.o \
+               modules/ldb_map.o
+# End MODULE ldb_map
+################################################
+
+################################################
+# Start MODULE ldb_skel
+[MODULE::ldb_skel]
+SUBSYSTEM = ldb
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_skel_init
+OBJ_FILES = modules/skel.o
+# End MODULE ldb_skel
+################################################
+
+################################################
+# Start MODULE ldb_sqlite3
+[MODULE::ldb_sqlite3]
+SUBSYSTEM = ldb
+PRIVATE_DEPENDENCIES = LIBTALLOC
+INIT_FUNCTION = ldb_sqlite3_init
+OBJ_FILES = \
+               ldb_sqlite3/ldb_sqlite3.o
+PUBLIC_DEPENDENCIES = \
+               SQLITE3 LIBTALLOC
+# End MODULE ldb_sqlite3
+################################################
+
+################################################
+# Start MODULE ldb_tdb
+[MODULE::ldb_tdb]
+SUBSYSTEM = ldb
+INIT_FUNCTION = ldb_tdb_init
+OBJ_FILES = \
+               ldb_tdb/ldb_tdb.o \
+               ldb_tdb/ldb_search.o \
+               ldb_tdb/ldb_pack.o \
+               ldb_tdb/ldb_index.o \
+               ldb_tdb/ldb_cache.o \
+               ldb_tdb/ldb_tdb_wrap.o
+PUBLIC_DEPENDENCIES = \
+               LIBTDB LIBTALLOC
+# End MODULE ldb_tdb
+################################################
+
+./lib/ldb/common/ldb_modules.o: lib/ldb/common/ldb_modules.c Makefile
+       @echo Compiling $<
+       @$(CC) -Iinclude $(CFLAGS) -Ilib/replace -Ilib/talloc -Ilib/ldb $(PICFLAG) -DLDBMODULESDIR=\"$(MODULESDIR)/ldb\" -DSHLIBEXT=\"$(SHLIBEXT)\" -c $< -o $@
+
+################################################
+# Start SUBSYSTEM ldb
+[LIBRARY::ldb]
+VERSION = 0.0.1
+SO_VERSION = 0
+DESCRIPTION = LDAP-like embedded database library
+INIT_FUNCTION_TYPE = int (*) (void)
+OBJ_FILES = \
+               common/ldb.o \
+               common/ldb_ldif.o \
+               common/ldb_parse.o \
+               common/ldb_msg.o \
+               common/ldb_utf8.o \
+               common/ldb_debug.o \
+               common/ldb_modules.o \
+               common/ldb_match.o \
+               common/ldb_attributes.o \
+               common/attrib_handlers.o \
+               common/ldb_dn.o \
+               common/ldb_controls.o \
+               common/qsort.o
+PUBLIC_DEPENDENCIES = \
+               LIBTALLOC
+MANPAGE = man/ldb.3
+PUBLIC_HEADERS = include/ldb.h include/ldb_errors.h
+#
+# End SUBSYSTEM ldb
+################################################
+
+################################################
+# Start SUBSYSTEM LDBSAMBA
+[SUBSYSTEM::LDBSAMBA]
+PRIVATE_DEPENDENCIES = ldb
+PRIVATE_PROTO_HEADER = samba/ldif_handlers.h
+PUBLIC_DEPENDENCIES = LIBSECURITY SAMDB
+OBJ_FILES = \
+               samba/ldif_handlers.o
+# End SUBSYSTEM LDBSAMBA
+################################################
+
+################################################
+# Start SUBSYSTEM LIBLDB_CMDLINE
+[SUBSYSTEM::LIBLDB_CMDLINE]
+OBJ_FILES= \
+               tools/cmdline.o
+PUBLIC_DEPENDENCIES = ldb LIBSAMBA-UTIL LIBPOPT POPT_SAMBA POPT_CREDENTIALS
+PRIVATE_DEPENDENCIES = gensec 
+# End SUBSYSTEM LIBLDB_CMDLINE
+################################################
+
+################################################
+# Start BINARY ldbadd
+[BINARY::ldbadd]
+INSTALLDIR = BINDIR
+OBJ_FILES = \
+               tools/ldbadd.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE LIBCLI_RESOLVE
+MANPAGE = man/ldbadd.1
+# End BINARY ldbadd
+################################################
+
+################################################
+# Start BINARY ldbdel
+[BINARY::ldbdel]
+INSTALLDIR = BINDIR
+OBJ_FILES= \
+               tools/ldbdel.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+MANPAGE = man/ldbdel.1
+# End BINARY ldbdel
+################################################
+
+################################################
+# Start BINARY ldbmodify
+[BINARY::ldbmodify]
+INSTALLDIR = BINDIR
+OBJ_FILES= \
+               tools/ldbmodify.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+MANPAGE = man/ldbmodify.1
+# End BINARY ldbmodify
+################################################
+
+################################################
+# Start BINARY ldbsearch
+[BINARY::ldbsearch]
+INSTALLDIR = BINDIR
+OBJ_FILES= \
+               tools/ldbsearch.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE 
+MANPAGE = man/ldbsearch.1
+# End BINARY ldbsearch
+################################################
+
+################################################
+# Start BINARY ldbedit
+[BINARY::ldbedit]
+INSTALLDIR = BINDIR
+OBJ_FILES= \
+               tools/ldbedit.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+MANPAGE = man/ldbedit.1
+# End BINARY ldbedit
+################################################
+
+################################################
+# Start BINARY ldbrename
+[BINARY::ldbrename]
+INSTALLDIR = BINDIR
+OBJ_FILES= \
+               tools/ldbrename.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+MANPAGE = man/ldbrename.1
+# End BINARY ldbrename
+################################################
+
+################################################
+# Start BINARY ldbtest
+[BINARY::ldbtest]
+OBJ_FILES= \
+               tools/ldbtest.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+# End BINARY ldbtest
+################################################
+
+################################################
+# Start BINARY oLschema2ldif
+[BINARY::oLschema2ldif]
+INSTALLDIR = BINDIR
+MANPAGE = man/oLschema2ldif.1
+OBJ_FILES= \
+               tools/convert.o \
+               tools/oLschema2ldif.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+# End BINARY oLschema2ldif
+################################################
+
+################################################
+# Start BINARY  ad2oLschema
+[BINARY::ad2oLschema]
+INSTALLDIR = BINDIR
+MANPAGE = man/ad2oLschema.1
+OBJ_FILES= \
+               tools/convert.o \
+               tools/ad2oLschema.o
+PRIVATE_DEPENDENCIES = \
+               LIBLDB_CMDLINE
+# End BINARY ad2oLschema
+################################################
+
+#######################
+# Start LIBRARY swig_ldb
+[LIBRARY::swig_ldb]
+PUBLIC_DEPENDENCIES = ldb DYNCONFIG
+LIBRARY_REALNAME = swig/_ldb.$(SHLIBEXT)
+OBJ_FILES = swig/ldb_wrap.o
+# End LIBRARY swig_ldb
+#######################
diff --git a/source3/lib/ldb/config.sub b/source3/lib/ldb/config.sub
new file mode 100755 (executable)
index 0000000..1c366df
--- /dev/null
@@ -0,0 +1,1579 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-07-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+  kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | bfin \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mips64vr5900 | mips64vr5900el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | ms1 \
+       | msp430 \
+       | ns16k | ns32k \
+       | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
+       | sparcv8 | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic4x | tic80 | tron \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m32c)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* \
+       | bfin-* | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mips64vr5900-* | mips64vr5900el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | ms1-* \
+       | msp430-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
+       | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tron-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
+       | xstormy16-* | xtensa-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       m32c-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16c)
+               basic_machine=cr16c-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       djgpp)
+               basic_machine=i586-pc
+               os=-msdosdjgpp
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       openrisc | openrisc-*)
+               basic_machine=or32-unknown
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xbox)
+               basic_machine=i686-pc
+               os=-mingw32
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+             | -skyos* | -haiku*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+        -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+        -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -zvmoe)
+               os=-zvmoe
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+    c4x-* | tic4x-*)
+        os=-coff
+        ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-haiku)
+               os=-haiku
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source3/lib/ldb/configure.ac b/source3/lib/ldb/configure.ac
new file mode 100644 (file)
index 0000000..70a4100
--- /dev/null
@@ -0,0 +1,74 @@
+AC_PREREQ(2.50)
+AC_DEFUN([AC_CHECK_LIB_EXT], [
+       AC_CHECK_LIB([$1],[$3],[$4],[$5],[$7])
+       ac_cv_lib_ext_$1_$3=$ac_cv_lib_$1_$3
+])
+AC_DEFUN([AC_CHECK_FUNC_EXT], [
+       AC_CHECK_FUNC([$1],[$3],[$4])
+       ac_cv_func_ext_$1=$ac_cv_func_$1
+])
+AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
+AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
+AC_DEFUN([SMB_EXT_LIB], [echo -n ""])
+AC_DEFUN([SMB_ENABLE], [echo -n ""])
+AC_INIT(include/ldb.h)
+AC_CONFIG_SRCDIR([common/ldb.c])
+
+AC_LIBREPLACE_ALL_CHECKS
+
+if test "$ac_cv_prog_gcc" = yes; then
+   CFLAGS="$CFLAGS -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings"
+fi
+
+WITH_GCOV=0
+AC_ARG_ENABLE(gcov,
+       AS_HELP_STRING([--enable-gcov],[enable GCOV code coverage tests]),
+       [ WITH_GCOV=1])
+AC_SUBST(WITH_GCOV)
+if test x"$with_gcov_support" = x"yes"; then
+   CFLAGS="$CFLAGS -ftest-coverage -fprofile-arcs"
+   LIBS="$LIBS -lgcov"
+fi
+
+AC_PATH_PROG(XSLTPROC,xsltproc)
+AC_PATH_PROG(DOXYGEN,doxygen)
+AC_PATH_PROG(GCOV,gcov)
+AC_PATH_PROG(SLAPD,slapd)
+AC_CHECK_HEADERS(stdint.h dlfcn.h)
+AC_CONFIG_HEADER(include/config.h)
+AC_SEARCH_LIBS(dlopen, dl, AC_DEFINE(HAVE_DLOPEN, [1], [have dlopen]))
+
+SHLIBEXT="so" # Should be set based on OS later on
+AC_SUBST(SHLIBEXT)
+
+AC_DEFINE_UNQUOTED(MODULESDIR, LIBDIR "/ldb" , [Modules directory] )
+AC_SUBST(MODULESDIR)
+
+TESTS=""
+EXTRA_OBJ=""
+
+m4_include(libpopt.m4)
+m4_include(libtalloc.m4)
+m4_include(libtdb.m4)
+
+m4_include(ldap.m4)
+if test x"$with_ldap_support" = x"yes"; then
+   LIBS="$LIBS -llber -lldap"
+   CFLAGS="$CFLAGS -DHAVE_LDAP=1"
+   EXTRA_OBJ="$EXTRA_OBJ ldb_ldap/ldb_ldap.o"
+   TESTS="$TESTS test-ldap.sh"
+fi
+
+m4_include(sqlite3.m4)
+if test x"$with_sqlite3_support" = x"yes"; then
+   LIBS="$LIBS -lsqlite3"
+   CFLAGS="$CFLAGS -DHAVE_SQLITE3=1"
+   EXTRA_OBJ="$EXTRA_OBJ ldb_sqlite3/ldb_sqlite3.o"
+   TESTS="$TESTS test-sqlite3.sh"
+fi
+
+AC_SUBST(TESTS)
+AC_SUBST(EXTRA_OBJ)
+
+m4_include(libldb.m4)
+AC_OUTPUT(Makefile ldb.pc)
diff --git a/source3/lib/ldb/docs/builddocs.sh b/source3/lib/ldb/docs/builddocs.sh
new file mode 100755 (executable)
index 0000000..449dcb2
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+# build ldb docs
+# tridge@samba.org August 2006
+
+XSLTPROC="$1"
+SRCDIR="$2"
+
+if [ -z "$XSLTPROC" ] || [ ! -x "$XSLTPROC" ]; then
+    echo "xsltproc not installed"
+    exit 0
+fi
+
+MANXSL="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"
+HTMLXSL="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"
+
+mkdir -p man
+
+for f in $SRCDIR/man/*.xml; do
+    base=`basename $f .xml`
+    out=man/"`basename $base`"
+    if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
+       echo Processing manpage $f
+       $XSLTPROC --nonet -o "$out" "$MANXSL" $f
+       ret=$?
+       if [ "$ret" = "4" ]; then
+           echo "ignoring stylesheet error 4 for $MANXSL"
+           exit 0
+       fi
+       if [ "$ret" != "0" ]; then
+           echo "xsltproc failed with error $ret"
+           exit $ret
+       fi
+    fi
+done
+
+for f in $SRCDIR/man/*.xml; do
+    base=`basename $f .xml`
+    out=man/"`basename $base`".html
+    if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
+       echo Processing html $f
+       $XSLTPROC --nonet -o "$out" "$HTMLXSL" $f
+       ret=$?
+       if [ "$ret" = "4" ]; then
+           echo "ignoring stylesheet error 4 for $HTMLXSL"
+           exit 0
+       fi
+       if [ "$ret" != "0" ]; then
+           echo "xsltproc failed with error $ret"
+           exit $ret
+       fi
+    fi
+done
diff --git a/source3/lib/ldb/docs/design.txt b/source3/lib/ldb/docs/design.txt
new file mode 100644 (file)
index 0000000..0bb278b
--- /dev/null
@@ -0,0 +1,41 @@
+The list of indexed fields
+--------------------------
+
+dn=@INDEXLIST
+       list of field names that are indexed
+
+       contains fields of type @IDXATTR which contain attriute names
+       of indexed fields
+
+
+Data records
+------------
+
+for each user record in the db there is:
+    main record
+        key: DN=dn
+        data: packed attribute/value list
+
+    a index record for each indexed field in the record
+
+
+Index Records
+-------------
+
+The index records contain the list of dn's that contain records
+matching the index key
+
+All index records are of the form:
+      dn=@INDEX:field:value
+
+and contain fields of type @IDX which are the dns of the records
+that have that value for some attribute
+
+
+Search Expressions
+------------------
+
+Very similar to LDAP search expressions, but does not allow ~=, <= or >=
+
+       attrib0 := (field=value)
+       attrib := attrib0 | (attrib&&attrib) | (attrib||attrib) | !attrib
diff --git a/source3/lib/ldb/docs/installdocs.sh b/source3/lib/ldb/docs/installdocs.sh
new file mode 100755 (executable)
index 0000000..6cc7b74
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+# install ldb docs
+# tridge@samba.org August 2006
+
+MANDIR="$1"
+
+MAN1="`/bin/ls man/*.1`"
+MAN3="`/bin/ls man/*.3`"
+
+if [ -z "$MAN1" ] && [ -z "$MAN3" ]; then
+    echo "No manpages have been built"
+    exit 0
+fi
+
+mkdir -p "$MANDIR/man1" "$MANDIR/man3" 
+cp $MAN1 "$MANDIR/man1/" || exit 1
+cp $MAN3 "$MANDIR/man3/" || exit 1
diff --git a/source3/lib/ldb/examples.dox b/source3/lib/ldb/examples.dox
new file mode 100644 (file)
index 0000000..ef4b4f0
--- /dev/null
@@ -0,0 +1,16 @@
+/** \example ldbreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the records in a LDB database to standard output.
+
+*/
+
+
+/** \example ldifreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the entries in an LDIF file to standard output.
+
+*/
diff --git a/source3/lib/ldb/examples/ldbreader.c b/source3/lib/ldb/examples/ldbreader.c
new file mode 100644 (file)
index 0000000..207c6c3
--- /dev/null
@@ -0,0 +1,125 @@
+/* 
+   example code for the ldb database library
+
+   Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+   Boston, MA  02110-1301  USA
+*/
+
+/** \example ldbreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the records in a LDB database to standard output.
+
+*/
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+
+/*
+  ldb_ldif_write takes a function pointer to a custom output
+  function. This version is about as simple as the output function can
+  be. In a more complex example, you'd likely be doing something with
+  the private data function (e.g. holding a file handle).
+*/
+static int vprintf_fn(void *private_data, const char *fmt, ...)
+{
+       int retval;
+       va_list ap;
+
+       va_start(ap, fmt);
+       /* We just write to standard output */
+       retval = vprintf(fmt, ap);
+       va_end(ap);
+       /* Note that the function should return the number of 
+          bytes written, or a negative error code */
+       return retval;
+}
+  
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       const char *expression = "(dn=*)";
+       struct ldb_result *resultMsg;
+       int i;
+
+       /*
+         This is the always the first thing you want to do in an LDB
+         application - initialise up the context structure.
+
+         Note that you can use the context structure as a parent
+         for talloc allocations as well
+       */
+       ldb = ldb_init(NULL);
+
+       /*
+         We now open the database. In this example we just hard code the connection path.
+
+         Also note that the database is being opened read-only. This means that the 
+         call will fail unless the database already exists. 
+       */
+       if (LDB_SUCCESS != ldb_connect(ldb, "tdb://tdbtest.ldb", LDB_FLG_RDONLY, NULL) ){
+               printf("Problem on connection\n");
+               exit(-1);
+       }
+
+       /*
+         At this stage we have an open database, and can start using it. It is opened
+         read-only, so a query is possible. 
+
+         We construct a search that just returns all the (sensible) contents. You can do
+         quite fine grained results with the LDAP search syntax, however it is a bit
+         confusing to start with. See RFC2254.
+       */
+       if (LDB_SUCCESS != ldb_search(ldb, NULL, LDB_SCOPE_DEFAULT,
+                                     expression, NULL, &resultMsg) ) {
+               printf("Problem in search\n");
+               exit(-1);
+       }
+       
+       printf("%i records returned\n", resultMsg->count);
+
+       /*
+         We can now iterate through the results, writing them out
+         (to standard output) with our custom output routine as defined
+         at the top of this file
+       */
+       for (i = 0; i < resultMsg->count; ++i) {
+               struct ldb_ldif ldifMsg;
+
+               printf("Message: %i\n", i+1);
+               
+               ldifMsg.changetype = LDB_CHANGETYPE_NONE;
+               ldifMsg.msg = resultMsg->msgs[i];
+               ldb_ldif_write(ldb, vprintf_fn, NULL, &ldifMsg);
+       }
+
+       /*
+         There are two objects to clean up - the result from the 
+         ldb_search() query, and the original ldb context.
+       */
+       talloc_free(resultMsg);
+
+       talloc_free(ldb);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/examples/ldifreader.c b/source3/lib/ldb/examples/ldifreader.c
new file mode 100644 (file)
index 0000000..3b8591e
--- /dev/null
@@ -0,0 +1,129 @@
+/* 
+   example code for the ldb database library
+
+   Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+   Boston, MA  02110-1301  USA
+*/
+
+/** \example ldifreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the entries in an LDIF file to standard output.
+
+*/
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+
+/*
+  ldb_ldif_write takes a function pointer to a custom output
+  function. This version is about as simple as the output function can
+  be. In a more complex example, you'd likely be doing something with
+  the private data function (e.g. holding a file handle).
+*/
+static int vprintf_fn(void *private_data, const char *fmt, ...)
+{
+       int retval;
+       va_list ap;
+
+       va_start(ap, fmt);
+       /* We just write to standard output */
+       retval = vprintf(fmt, ap);
+       va_end(ap);
+       /* Note that the function should return the number of 
+          bytes written, or a negative error code */
+       return retval;
+}
+  
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       FILE *fileStream;
+       struct ldb_ldif *ldifMsg;
+
+       if (argc != 2) {
+               printf("Usage %s filename.ldif\n", argv[0]);
+               exit(1);
+       }
+
+       /*
+         This is the always the first thing you want to do in an LDB
+         application - initialise up the context structure.
+
+         Note that you can use the context structure as a parent
+         for talloc allocations as well
+       */
+       ldb = ldb_init(NULL);
+
+       fileStream = fopen(argv[1], "r");
+       if (0 == fileStream) {
+               perror(argv[1]);
+               exit(1);
+       }
+
+       /*
+         We now work through the filestream to get each entry.
+       */
+       while ( (ldifMsg = ldb_ldif_read_file(ldb, fileStream)) ) {
+               /*
+                 Each message has a particular change type. For Add,
+                 Modify and Delete, this will also appear in the
+                 output listing (as changetype: add, changetype:
+                 modify or changetype:delete, respectively).
+               */
+               switch (ldifMsg->changetype) {
+               case LDB_CHANGETYPE_NONE:
+                       printf("ChangeType: None\n");
+                       break;
+               case LDB_CHANGETYPE_ADD:
+                       printf("ChangeType: Add\n");
+                       break;
+               case LDB_CHANGETYPE_MODIFY:
+                       printf("ChangeType: Modify\n");
+                       break;
+               case LDB_CHANGETYPE_DELETE:
+                       printf("ChangeType: Delete\n");
+                       break;
+               default:
+                       printf("ChangeType: Unknown\n");
+               }
+
+               /*
+                 We can now write out the results, using our custom
+                 output routine as defined at the top of this file. 
+               */
+               ldb_ldif_write(ldb, vprintf_fn, NULL, ldifMsg);
+
+               /*
+                 Clean up the message
+               */
+               ldb_ldif_read_free(ldb, ldifMsg);
+       }
+
+       /*
+         Clean up the context
+       */
+       talloc_free(ldb);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/include/dlinklist.h b/source3/lib/ldb/include/dlinklist.h
new file mode 100644 (file)
index 0000000..3779a4c
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+   Unix SMB/CIFS implementation.
+   some simple double linked list macros
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+/* To use these macros you must have a structure containing a next and
+   prev pointer */
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (list) = (p); \
+               (p)->next = (p)->prev = NULL; \
+       } else { \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (p)->prev = NULL; \
+               (list) = (p); \
+       }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#ifndef DLIST_REMOVE
+#define DLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               (list) = (p)->next; \
+               if (list) (list)->prev = NULL; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+#endif
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, type) \
+do { \
+               if (!(list)) { \
+                       (list) = (p); \
+                       (p)->next = (p)->prev = NULL; \
+               } else { \
+                       type tmp; \
+                       for (tmp = (list); tmp->next; tmp = tmp->next) ; \
+                       tmp->next = (p); \
+                       (p)->next = NULL; \
+                       (p)->prev = tmp; \
+               } \
+} while (0)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+   this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+        if (!(list) || !(el)) { \
+               DLIST_ADD(list, p); \
+       } else { \
+               p->prev = el; \
+               p->next = el->next; \
+               el->next = p; \
+               if (p->next) p->next->prev = p; \
+       }\
+} while (0)
+
+/* demote an element to the end of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+do { \
+               DLIST_REMOVE(list, p); \
+               DLIST_ADD_END(list, p, tmp); \
+} while (0)
+
+/* concatenate two lists - putting all elements of the 2nd list at the
+   end of the first list */
+#define DLIST_CONCATENATE(list1, list2, type) \
+do { \
+               if (!(list1)) { \
+                       (list1) = (list2); \
+               } else { \
+                       type tmp; \
+                       for (tmp = (list1); tmp->next; tmp = tmp->next) ; \
+                       tmp->next = (list2); \
+                       if (list2) { \
+                               (list2)->prev = tmp;    \
+                       } \
+               } \
+} while (0)
diff --git a/source3/lib/ldb/include/includes.h b/source3/lib/ldb/include/includes.h
new file mode 100644 (file)
index 0000000..ce0d40e
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _LDB_PRIVATE_INCLUDES_H_
+#define _LDB_PRIVATE_INCLUDES_H_
+/*
+  a temporary includes file until I work on the ldb build system
+*/
+
+#if (_SAMBA_BUILD_ >= 4)
+/* tell ldb we have the internal ldap code */
+#define HAVE_ILDAP 1
+#endif
+
+#if (_SAMBA_BUILD_ <= 3)
+/* allow forbidden string functions - should be replaced with _m functions */
+#undef strcasecmp
+#undef strncasecmp
+#define dyn_MODULESDIR dyn_LIBDIR
+#endif
+
+
+
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#define discard_const_p(type, ptr) ((type *)discard_const(ptr))
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/time.h"
+#include "talloc.h"
+#include "ldb.h"
+#include "ldb_errors.h"
+#include "ldb_private.h"
+#include "dlinklist.h"
+
+#endif /*_LDB_PRIVATE_INCLUDES_H_*/
diff --git a/source3/lib/ldb/include/ldb.h b/source3/lib/ldb/include/ldb.h
new file mode 100644 (file)
index 0000000..cf4a1f2
--- /dev/null
@@ -0,0 +1,1407 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+   Copyright (C) Stefan Metzmacher  2004
+   Copyright (C) Simo Sorce  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb header
+ *
+ *  Description: defines for base ldb API
+ *
+ *  Author: Andrew Tridgell
+ *  Author: Stefan Metzmacher
+ */
+
+/**
+   \file ldb.h Samba's ldb database
+
+   This header file provides the main API for ldb.
+*/
+
+#ifndef _LDB_H_
+
+/*! \cond DOXYGEN_IGNORE */
+#define _LDB_H_ 1
+/*! \endcond */
+
+/*
+  major restrictions as compared to normal LDAP:
+
+     - no async calls.
+     - each record must have a unique key field
+     - the key must be representable as a NULL terminated C string and may not 
+       contain a comma or braces
+
+  major restrictions as compared to tdb:
+
+     - no explicit locking calls
+     UPDATE: we have transactions now, better than locking --SSS.
+
+*/
+
+#ifndef ldb_val
+/**
+   Result value
+
+   An individual lump of data in a result comes in this format. The
+   pointer will usually be to a UTF-8 string if the application is
+   sensible, but it can be to anything you like, including binary data
+   blobs of arbitrary size.
+
+   \note the data is null (0x00) terminated, but the length does not
+   include the terminator. 
+*/
+struct ldb_val {
+       uint8_t *data; /*!< result data */
+       size_t length; /*!< length of data */
+};
+#endif
+
+/*! \cond DOXYGEN_IGNORE */
+#ifndef PRINTF_ATTRIBUTE
+#define PRINTF_ATTRIBUTE(a,b)
+#endif
+/*! \endcond */
+
+/**
+   internal ldb exploded dn structures
+*/
+struct ldb_dn_component {
+       char *name;  
+       struct ldb_val value;
+};
+
+struct ldb_dn {
+       int comp_num;
+       struct ldb_dn_component *components;
+};
+
+/**
+ There are a number of flags that are used with ldap_modify() in
+ ldb_message_element.flags fields. The LDA_FLAGS_MOD_ADD,
+ LDA_FLAGS_MOD_DELETE and LDA_FLAGS_MOD_REPLACE flags are used in
+ ldap_modify() calls to specify whether attributes are being added,
+ deleted or modified respectively.
+*/
+#define LDB_FLAG_MOD_MASK  0x3
+
+/**
+   Flag value used in ldap_modify() to indicate that attributes are
+   being added.
+
+   \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_ADD     1
+
+/**
+   Flag value used in ldap_modify() to indicate that attributes are
+   being replaced.
+
+   \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_REPLACE 2
+
+/**
+   Flag value used in ldap_modify() to indicate that attributes are
+   being deleted.
+
+   \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_DELETE  3
+
+/**
+  OID for logic AND comaprison.
+
+  This is the well known object ID for a logical AND comparitor.
+*/
+#define LDB_OID_COMPARATOR_AND  "1.2.840.113556.1.4.803"
+
+/**
+  OID for logic OR comparison.
+
+  This is the well known object ID for a logical OR comparitor.
+*/
+#define LDB_OID_COMPARATOR_OR   "1.2.840.113556.1.4.804"
+
+/**
+  results are given back as arrays of ldb_message_element
+*/
+struct ldb_message_element {
+       unsigned int flags;
+       const char *name;
+       unsigned int num_values;
+       struct ldb_val *values;
+};
+
+
+/**
+  a ldb_message represents all or part of a record. It can contain an arbitrary
+  number of elements. 
+*/
+struct ldb_message {
+       struct ldb_dn *dn;
+       unsigned int num_elements;
+       struct ldb_message_element *elements;
+       void *private_data; /* private to the backend */
+};
+
+enum ldb_changetype {
+       LDB_CHANGETYPE_NONE=0,
+       LDB_CHANGETYPE_ADD,
+       LDB_CHANGETYPE_DELETE,
+       LDB_CHANGETYPE_MODIFY
+};
+
+/**
+  LDIF record
+
+  This structure contains a LDIF record, as returned from ldif_read()
+  and equivalent functions.
+*/
+struct ldb_ldif {
+       enum ldb_changetype changetype; /*!< The type of change */
+       struct ldb_message *msg;  /*!< The changes */
+};
+
+enum ldb_scope {LDB_SCOPE_DEFAULT=-1, 
+               LDB_SCOPE_BASE=0, 
+               LDB_SCOPE_ONELEVEL=1,
+               LDB_SCOPE_SUBTREE=2};
+
+struct ldb_context;
+
+/*
+  the fuction type for the callback used in traversing the database
+*/
+typedef int (*ldb_traverse_fn)(struct ldb_context *, const struct ldb_message *);
+
+
+/* debugging uses one of the following levels */
+enum ldb_debug_level {LDB_DEBUG_FATAL, LDB_DEBUG_ERROR, 
+                     LDB_DEBUG_WARNING, LDB_DEBUG_TRACE};
+
+/**
+  the user can optionally supply a debug function. The function
+  is based on the vfprintf() style of interface, but with the addition
+  of a severity level
+*/
+struct ldb_debug_ops {
+       void (*debug)(void *context, enum ldb_debug_level level, 
+                     const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+       void *context;
+};
+
+/**
+  The user can optionally supply a custom utf8 functions,
+  to handle comparisons and casefolding.
+*/
+struct ldb_utf8_fns {
+       void *context;
+       char *(*casefold)(void *context, void *mem_ctx, const char *s);
+};
+
+/**
+   Flag value for database connection mode.
+
+   If LDB_FLG_RDONLY is used in ldb_connect, then the database will be
+   opened read-only, if possible.
+*/
+#define LDB_FLG_RDONLY 1
+
+/**
+   Flag value for database connection mode.
+
+   If LDB_FLG_NOSYNC is used in ldb_connect, then the database will be
+   opened without synchronous operations, if possible.
+*/
+#define LDB_FLG_NOSYNC 2
+
+/**
+   Flag value to specify autoreconnect mode.
+
+   If LDB_FLG_RECONNECT is used in ldb_connect, then the backend will
+   be opened in a way that makes it try to auto reconnect if the
+   connection is dropped (actually make sense only with ldap).
+*/
+#define LDB_FLG_RECONNECT 4
+
+/*
+   structures for ldb_parse_tree handling code
+*/
+enum ldb_parse_op { LDB_OP_AND=1, LDB_OP_OR=2, LDB_OP_NOT=3,
+                   LDB_OP_EQUALITY=4, LDB_OP_SUBSTRING=5,
+                   LDB_OP_GREATER=6, LDB_OP_LESS=7, LDB_OP_PRESENT=8,
+                   LDB_OP_APPROX=9, LDB_OP_EXTENDED=10 };
+
+struct ldb_parse_tree {
+       enum ldb_parse_op operation;
+       union {
+               struct {
+                       struct ldb_parse_tree *child;
+               } isnot;
+               struct {
+                       const char *attr;
+                       struct ldb_val value;
+               } equality;
+               struct {
+                       const char *attr;
+                       int start_with_wildcard;
+                       int end_with_wildcard;
+                       struct ldb_val **chunks;
+               } substring;
+               struct {
+                       const char *attr;
+               } present;
+               struct {
+                       const char *attr;
+                       struct ldb_val value;
+               } comparison;
+               struct {
+                       const char *attr;
+                       int dnAttributes;
+                       char *rule_id;
+                       struct ldb_val value;
+               } extended;
+               struct {
+                       unsigned int num_elements;
+                       struct ldb_parse_tree **elements;
+               } list;
+       } u;
+};
+
+struct ldb_parse_tree *ldb_parse_tree(void *mem_ctx, const char *s);
+char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree);
+
+/**
+   Encode a binary blob
+
+   This function encodes a binary blob using the encoding rules in RFC
+   2254 (Section 4). This function also escapes any non-printable
+   characters.
+
+   \param ctx the memory context to allocate the return string in.
+   \param val the (potentially) binary data to be encoded
+
+   \return the encoded data as a null terminated string
+
+   \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>.
+*/
+char *ldb_binary_encode(void *ctx, struct ldb_val val);
+
+/**
+   Encode a string
+
+   This function encodes a string using the encoding rules in RFC 2254
+   (Section 4). This function also escapes any non-printable
+   characters.
+
+   \param mem_ctx the memory context to allocate the return string in.
+   \param string the string to be encoded
+
+   \return the encoded data as a null terminated string
+
+   \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>.
+*/
+char *ldb_binary_encode_string(void *mem_ctx, const char *string);
+
+/*
+  functions for controlling attribute handling
+*/
+typedef int (*ldb_attr_handler_t)(struct ldb_context *, void *mem_ctx, const struct ldb_val *, struct ldb_val *);
+typedef int (*ldb_attr_comparison_t)(struct ldb_context *, void *mem_ctx, const struct ldb_val *, const struct ldb_val *);
+
+struct ldb_attrib_handler {
+       const char *attr;
+
+       /* LDB_ATTR_FLAG_* */
+       unsigned flags;
+
+       /* convert from ldif to binary format */
+       ldb_attr_handler_t ldif_read_fn;
+
+       /* convert from binary to ldif format */
+       ldb_attr_handler_t ldif_write_fn;
+       
+       /* canonicalise a value, for use by indexing and dn construction */
+       ldb_attr_handler_t canonicalise_fn;
+
+       /* compare two values */
+       ldb_attr_comparison_t comparison_fn;
+};
+
+/**
+   The attribute is not returned by default
+*/
+#define LDB_ATTR_FLAG_HIDDEN       (1<<0) 
+
+/**
+   The attribute is constructed from other attributes
+*/
+#define LDB_ATTR_FLAG_CONSTRUCTED  (1<<1) 
+
+/**
+  LDAP attribute syntax for a DN
+
+  This is the well-known LDAP attribute syntax for a DN.
+
+  See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 
+*/
+#define LDB_SYNTAX_DN                   "1.3.6.1.4.1.1466.115.121.1.12"
+
+/**
+  LDAP attribute syntax for a Directory String
+
+  This is the well-known LDAP attribute syntax for a Directory String.
+
+  \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 
+*/
+#define LDB_SYNTAX_DIRECTORY_STRING     "1.3.6.1.4.1.1466.115.121.1.15"
+
+/**
+  LDAP attribute syntax for an integer
+
+  This is the well-known LDAP attribute syntax for an integer.
+
+  See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 
+*/
+#define LDB_SYNTAX_INTEGER              "1.3.6.1.4.1.1466.115.121.1.27"
+
+/**
+  LDAP attribute syntax for an octet string
+
+  This is the well-known LDAP attribute syntax for an octet string.
+
+  See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 
+*/
+#define LDB_SYNTAX_OCTET_STRING         "1.3.6.1.4.1.1466.115.121.1.40"
+
+/**
+  LDAP attribute syntax for UTC time.
+
+  This is the well-known LDAP attribute syntax for a UTC time.
+
+  See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 
+*/
+#define LDB_SYNTAX_UTC_TIME             "1.3.6.1.4.1.1466.115.121.1.53"
+
+#define LDB_SYNTAX_OBJECTCLASS          "LDB_SYNTAX_OBJECTCLASS"
+
+/* sorting helpers */
+typedef int (*ldb_qsort_cmp_fn_t) (void *v1, void *v2, void *opaque);
+
+/**
+   OID for the paged results control. This control is included in the
+   searchRequest and searchResultDone messages as part of the controls
+   field of the LDAPMessage, as defined in Section 4.1.12 of
+   LDAP v3. 
+
+   \sa <a href="http://www.ietf.org/rfc/rfc2696.txt">RFC 2696</a>.
+*/
+#define LDB_CONTROL_PAGED_RESULTS_OID  "1.2.840.113556.1.4.319"
+
+/**
+   OID for specifying the returned elements of the ntSecurityDescriptor
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_sd_flags_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SD_FLAGS_OID       "1.2.840.113556.1.4.801"
+
+/**
+   OID for specifying an advanced scope for the search (one partition)
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_domain_scope_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_DOMAIN_SCOPE_OID   "1.2.840.113556.1.4.1339"
+
+/**
+   OID for specifying an advanced scope for a search
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_search_options_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SEARCH_OPTIONS_OID "1.2.840.113556.1.4.1340"
+
+/**
+   OID for notification
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_notification_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_NOTIFICATION_OID   "1.2.840.113556.1.4.528"
+
+/**
+   OID for getting deleted objects
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_show_deleted_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SHOW_DELETED_OID   "1.2.840.113556.1.4.417"
+
+/**
+   OID for extended DN
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_extended_dn_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_EXTENDED_DN_OID    "1.2.840.113556.1.4.529"
+
+/**
+   OID for LDAP server sort result extension.
+
+   This control is included in the searchRequest message as part of
+   the controls field of the LDAPMessage, as defined in Section 4.1.12
+   of LDAP v3. The controlType is set to
+   "1.2.840.113556.1.4.473". The criticality MAY be either TRUE or
+   FALSE (where absent is also equivalent to FALSE) at the client's
+   option.
+
+   \sa <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
+*/
+#define LDB_CONTROL_SERVER_SORT_OID    "1.2.840.113556.1.4.473"
+
+/**
+   OID for LDAP server sort result response extension.
+
+   This control is included in the searchResultDone message as part of
+   the controls field of the LDAPMessage, as defined in Section 4.1.12 of
+   LDAP v3.
+
+   \sa <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
+*/
+#define LDB_CONTROL_SORT_RESP_OID      "1.2.840.113556.1.4.474"
+
+/**
+   OID for LDAP Attribute Scoped Query extension.
+
+   This control is included in SearchRequest or SearchResponse
+   messages as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_ASQ_OID            "1.2.840.113556.1.4.1504"
+
+/**
+   OID for LDAP Directory Sync extension. 
+
+   This control is included in SearchRequest or SearchResponse
+   messages as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_DIRSYNC_OID                "1.2.840.113556.1.4.841"
+
+
+/**
+   OID for LDAP Virtual List View Request extension.
+
+   This control is included in SearchRequest messages
+   as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_VLV_REQ_OID                "2.16.840.1.113730.3.4.9"
+
+/**
+   OID for LDAP Virtual List View Response extension.
+
+   This control is included in SearchResponse messages
+   as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_VLV_RESP_OID       "2.16.840.1.113730.3.4.10"
+
+/**
+   OID to let modifies don't give an error when adding an existing
+   attribute with the same value or deleting an nonexisting one attribute
+
+   \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_permissive_modify_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_PERMISSIVE_MODIFY_OID      "1.2.840.113556.1.4.1413"
+
+/**
+   OID for LDAP Extended Operation START_TLS.
+
+   This Extended operation is used to start a new TLS
+   channel on top of a clear text channel.
+*/
+#define LDB_EXTENDED_START_TLS_OID     "1.3.6.1.4.1.1466.20037"
+
+/**
+   OID for LDAP Extended Operation START_TLS.
+
+   This Extended operation is used to start a new TLS
+   channel on top of a clear text channel.
+*/
+#define LDB_EXTENDED_DYNAMIC_OID       "1.3.6.1.4.1.1466.101.119.1"
+
+/**
+   OID for LDAP Extended Operation START_TLS.
+
+   This Extended operation is used to start a new TLS
+   channel on top of a clear text channel.
+*/
+#define LDB_EXTENDED_FAST_BIND_OID     "1.2.840.113556.1.4.1781"
+
+struct ldb_sd_flags_control {
+       /*
+        * request the owner    0x00000001
+        * request the group    0x00000002
+        * request the DACL     0x00000004
+        * request the SACL     0x00000008
+        */
+       unsigned secinfo_flags;
+};
+
+struct ldb_search_options_control {
+       /*
+        * DOMAIN_SCOPE         0x00000001
+        * this limits the search to one partition,
+        * and no referrals will be returned.
+        * (Note this doesn't limit the entries by there
+        *  objectSid belonging to a domain! Builtin and Foreign Sids
+        *  are still returned)
+        *
+        * PHANTOM_ROOT         0x00000002
+        * this search on the whole tree on a domain controller
+        * over multiple partitions without referrals.
+        * (This is the default behavior on the Global Catalog Port)
+        */
+       unsigned search_options;
+};
+
+struct ldb_paged_control {
+       int size;
+       int cookie_len;
+       char *cookie;
+};
+
+struct ldb_extended_dn_control {
+       int type;
+};
+
+struct ldb_server_sort_control {
+       char *attributeName;
+       char *orderingRule;
+       int reverse;
+};
+
+struct ldb_sort_resp_control {
+       int result;
+       char *attr_desc;
+};
+
+struct ldb_asq_control {
+       int request;
+       char *source_attribute;
+       int src_attr_len;
+       int result;
+};
+
+struct ldb_dirsync_control {
+       int flags;
+       int max_attributes;
+       int cookie_len;
+       char *cookie;
+};
+
+struct ldb_vlv_req_control {
+       int beforeCount;
+       int afterCount;
+       int type;
+       union {
+               struct {
+                       int offset;
+                       int contentCount;
+               } byOffset;
+               struct {
+                       int value_len;
+                       char *value;
+               } gtOrEq;
+       } match;
+       int ctxid_len;
+       char *contextId;
+};
+
+struct ldb_vlv_resp_control {
+       int targetPosition;
+       int contentCount;
+       int vlv_result;
+       int ctxid_len;
+       char *contextId;
+};
+
+struct ldb_control {
+       const char *oid;
+       int critical;
+       void *data;
+};
+
+enum ldb_request_type {
+       LDB_SEARCH,
+       LDB_ADD,
+       LDB_MODIFY,
+       LDB_DELETE,
+       LDB_RENAME,
+       LDB_EXTENDED,
+       LDB_REQ_REGISTER_CONTROL,
+       LDB_REQ_REGISTER_PARTITION,
+       LDB_SEQUENCE_NUMBER
+};
+
+enum ldb_reply_type {
+       LDB_REPLY_ENTRY,
+       LDB_REPLY_REFERRAL,
+       LDB_REPLY_EXTENDED,
+       LDB_REPLY_DONE
+};
+
+enum ldb_wait_type {
+       LDB_WAIT_ALL,
+       LDB_WAIT_NONE
+};
+
+enum ldb_state {
+       LDB_ASYNC_INIT,
+       LDB_ASYNC_PENDING,
+       LDB_ASYNC_DONE
+};
+
+struct ldb_result {
+       unsigned int count;
+       struct ldb_message **msgs;
+       char **refs;
+       struct ldb_control **controls;
+};
+
+struct ldb_extended {
+       const char *oid;
+       const char *value;
+       int value_len;
+};
+
+struct ldb_reply {
+       enum ldb_reply_type type;
+       struct ldb_message *message;
+       struct ldb_extended *response;
+       char *referral;
+       struct ldb_control **controls;
+};
+
+struct ldb_handle {
+       int status;
+       enum ldb_state state;
+       void *private_data;
+       struct ldb_module *module;
+};
+
+struct ldb_search {
+       const struct ldb_dn *base;
+       enum ldb_scope scope;
+       const struct ldb_parse_tree *tree;
+       const char * const *attrs;
+       struct ldb_result *res;
+};
+
+struct ldb_add {
+       const struct ldb_message *message;
+};
+
+struct  ldb_modify {
+       const struct ldb_message *message;
+};
+
+struct ldb_delete {
+       const struct ldb_dn *dn;
+};
+
+struct ldb_rename {
+       const struct ldb_dn *olddn;
+       const struct ldb_dn *newdn;
+};
+
+struct ldb_register_control {
+       const char *oid;
+};
+
+struct ldb_register_partition {
+       const struct ldb_dn *dn;
+};
+
+struct ldb_sequence_number {
+       enum ldb_sequence_type {
+               LDB_SEQ_HIGHEST_SEQ,
+               LDB_SEQ_HIGHEST_TIMESTAMP,
+               LDB_SEQ_NEXT
+       } type;
+       uint64_t seq_num;
+       uint32_t flags;
+};
+
+typedef int (*ldb_request_callback_t)(struct ldb_context *, void *, struct ldb_reply *);
+struct ldb_request {
+
+       enum ldb_request_type operation;
+
+       union {
+               struct ldb_search search;
+               struct ldb_add    add;
+               struct ldb_modify mod;
+               struct ldb_delete del;
+               struct ldb_rename rename;
+               struct ldb_register_control reg_control;
+               struct ldb_register_partition reg_partition;
+               struct ldb_sequence_number seq_num;
+       } op;
+
+       struct ldb_control **controls;
+
+       void *context;
+       ldb_request_callback_t callback;
+
+       int timeout;
+       time_t starttime;
+       struct ldb_handle *handle;
+};
+
+int ldb_request(struct ldb_context *ldb, struct ldb_request *request);
+
+int ldb_wait(struct ldb_handle *handle, enum ldb_wait_type type);
+
+int ldb_set_timeout(struct ldb_context *ldb, struct ldb_request *req, int timeout);
+int ldb_set_timeout_from_prev_req(struct ldb_context *ldb, struct ldb_request *oldreq, struct ldb_request *newreq);
+
+/**
+  Initialise ldbs' global information
+
+  This is required before any other LDB call
+
+  \return 0 if initialisation succeeded, -1 otherwise
+*/
+int ldb_global_init(void);
+
+/**
+  Initialise an ldb context
+
+  This is required before any other LDB call.
+
+  \param mem_ctx pointer to a talloc memory context. Pass NULL if there is
+  no suitable context available.
+
+  \return pointer to ldb_context that should be free'd (using talloc_free())
+  at the end of the program.
+*/
+struct ldb_context *ldb_init(void *mem_ctx);
+
+/**
+   Connect to a database.
+
+   This is typically called soon after ldb_init(), and is required prior to
+   any search or database modification operations.
+
+   The URL can be one of the following forms:
+    - tdb://path
+    - ldapi://path
+    - ldap://host
+    - sqlite://path
+
+   \param ldb the context associated with the database (from ldb_init())
+   \param url the URL of the database to connect to, as noted above
+   \param flags a combination of LDB_FLG_* to modify the connection behaviour
+   \param options backend specific options - passed uninterpreted to the backend
+
+   \return result code (LDB_SUCCESS on success, or a failure code)
+
+   \note It is an error to connect to a database that does not exist in readonly mode
+   (that is, with LDB_FLG_RDONLY). However in read-write mode, the database will be
+   created if it does not exist.
+*/
+int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[]);
+
+/*
+  return an automatic baseDN from the defaultNamingContext of the rootDSE
+  This value have been set in an opaque pointer at connection time
+*/
+const struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb);
+
+/**
+  Search the database
+
+  This function searches the database, and returns 
+  records that match an LDAP-like search expression
+
+  \param ldb the context associated with the database (from ldb_init())
+  \param base the Base Distinguished Name for the query (pass NULL for root DN)
+  \param scope the search scope for the query
+  \param expression the search expression to use for this query
+  \param attrs the search attributes for the query (pass NULL if none required)
+  \param res the return result
+
+  \return result code (LDB_SUCCESS on success, or a failure code)
+
+  \note use talloc_free() to free the ldb_result returned
+*/
+int ldb_search(struct ldb_context *ldb, 
+              const struct ldb_dn *base,
+              enum ldb_scope scope,
+              const char *expression,
+              const char * const *attrs, struct ldb_result **res);
+
+/*
+  like ldb_search() but takes a parse tree
+*/
+int ldb_search_bytree(struct ldb_context *ldb, 
+                     const struct ldb_dn *base,
+                     enum ldb_scope scope,
+                     struct ldb_parse_tree *tree,
+                     const char * const *attrs, struct ldb_result **res);
+
+/**
+  Add a record to the database.
+
+  This function adds a record to the database. This function will fail
+  if a record with the specified class and key already exists in the
+  database. 
+
+  \param ldb the context associated with the database (from
+  ldb_init())
+  \param message the message containing the record to add.
+
+  \return result code (LDB_SUCCESS if the record was added, otherwise
+  a failure code)
+*/
+int ldb_add(struct ldb_context *ldb, 
+           const struct ldb_message *message);
+
+/**
+  Modify the specified attributes of a record
+
+  This function modifies a record that is in the database.
+
+  \param ldb the context associated with the database (from
+  ldb_init())
+  \param message the message containing the changes required.
+
+  \return result code (LDB_SUCCESS if the record was modified as
+  requested, otherwise a failure code)
+*/
+int ldb_modify(struct ldb_context *ldb, 
+              const struct ldb_message *message);
+
+/**
+  Rename a record in the database
+
+  This function renames a record in the database.
+
+  \param ldb the context associated with the database (from
+  ldb_init())
+  \param olddn the DN for the record to be renamed.
+  \param newdn the new DN 
+
+  \return result code (LDB_SUCCESS if the record was renamed as
+  requested, otherwise a failure code)
+*/
+int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct ldb_dn *newdn);
+
+/**
+  Delete a record from the database
+
+  This function deletes a record from the database.
+
+  \param ldb the context associated with the database (from
+  ldb_init())
+  \param dn the DN for the record to be deleted.
+
+  \return result code (LDB_SUCCESS if the record was deleted,
+  otherwise a failure code)
+*/
+int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn);
+
+/**
+  start a transaction
+*/
+int ldb_transaction_start(struct ldb_context *ldb);
+
+/**
+  commit a transaction
+*/
+int ldb_transaction_commit(struct ldb_context *ldb);
+
+/**
+  cancel a transaction
+*/
+int ldb_transaction_cancel(struct ldb_context *ldb);
+
+
+/**
+  return extended error information from the last call
+*/
+const char *ldb_errstring(struct ldb_context *ldb);
+
+/**
+  return a string explaining what a ldb error constant meancs
+*/
+const char *ldb_strerror(int ldb_err);
+
+/**
+  setup the default utf8 functions
+  FIXME: these functions do not yet handle utf8
+*/
+void ldb_set_utf8_default(struct ldb_context *ldb);
+
+/**
+   Casefold a string
+
+   \param ldb the ldb context
+   \param mem_ctx the memory context to allocate the result string
+   memory from. 
+   \param s the string that is to be folded
+   \return a copy of the string, converted to upper case
+
+   \note The default function is not yet UTF8 aware. Provide your own
+         set of functions through ldb_set_utf8_fns()
+*/
+char *ldb_casefold(struct ldb_context *ldb, void *mem_ctx, const char *s);
+
+/**
+   Check the attribute name is valid according to rfc2251
+   \param s tthe string to check
+
+   \return 1 if the name is ok
+*/
+int ldb_valid_attr_name(const char *s);
+
+/*
+  ldif manipulation functions
+*/
+/**
+   Write an LDIF message
+
+   This function writes an LDIF message using a caller supplied  write
+   function.
+
+   \param ldb the ldb context (from ldb_init())
+   \param fprintf_fn a function pointer for the write function. This must take
+   a private data pointer, followed by a format string, and then a variable argument
+   list. 
+   \param private_data pointer that will be provided back to the write
+   function. This is useful for maintaining state or context.
+   \param ldif the message to write out
+
+   \return the total number of bytes written, or an error code as returned
+   from the write function.
+
+   \sa ldb_ldif_write_file for a more convenient way to write to a
+   file stream.
+
+   \sa ldb_ldif_read for the reader equivalent to this function.
+*/
+int ldb_ldif_write(struct ldb_context *ldb,
+                  int (*fprintf_fn)(void *, const char *, ...) PRINTF_ATTRIBUTE(2,3), 
+                  void *private_data,
+                  const struct ldb_ldif *ldif);
+
+/**
+   Clean up an LDIF message
+
+   This function cleans up a LDIF message read using ldb_ldif_read()
+   or related functions (such as ldb_ldif_read_string() and
+   ldb_ldif_read_file().
+
+   \param ldb the ldb context (from ldb_init())
+   \param msg the message to clean up and free
+
+*/
+void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *msg);
+
+/**
+   Read an LDIF message
+
+   This function creates an LDIF message using a caller supplied read
+   function. 
+
+   \param ldb the ldb context (from ldb_init())
+   \param fgetc_fn a function pointer for the read function. This must
+   take a private data pointer, and must return a pointer to an
+   integer corresponding to the next byte read (or EOF if there is no
+   more data to be read).
+   \param private_data pointer that will be provided back to the read
+   function. This is udeful for maintaining state or context.
+
+   \return the LDIF message that has been read in
+
+   \note You must free the LDIF message when no longer required, using
+   ldb_ldif_read_free().
+
+   \sa ldb_ldif_read_file for a more convenient way to read from a
+   file stream.
+
+   \sa ldb_ldif_read_string for a more convenient way to read from a
+   string (char array).
+
+   \sa ldb_ldif_write for the writer equivalent to this function.
+*/
+struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, 
+                              int (*fgetc_fn)(void *), void *private_data);
+
+/**
+   Read an LDIF message from a file
+
+   This function reads the next LDIF message from the contents of a
+   file stream. If you want to get all of the LDIF messages, you will
+   need to repeatedly call this function, until it returns NULL.
+
+   \param ldb the ldb context (from ldb_init())
+   \param f the file stream to read from (typically from fdopen())
+
+   \sa ldb_ldif_read_string for an equivalent function that will read
+   from a string (char array).
+
+   \sa ldb_ldif_write_file for the writer equivalent to this function.
+
+*/
+struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f);
+
+/**
+   Read an LDIF message from a string
+
+   This function reads the next LDIF message from the contents of a char
+   array. If you want to get all of the LDIF messages, you will need
+   to repeatedly call this function, until it returns NULL.
+
+   \param ldb the ldb context (from ldb_init())
+   \param s pointer to the char array to read from
+
+   \sa ldb_ldif_read_file for an equivalent function that will read
+   from a file stream.
+
+   \sa ldb_ldif_write for a more general (arbitrary read function)
+   version of this function.
+*/
+struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s);
+
+/**
+   Write an LDIF message to a file
+
+   \param ldb the ldb context (from ldb_init())
+   \param f the file stream to write to (typically from fdopen())
+   \param msg the message to write out
+
+   \return the total number of bytes written, or a negative error code
+
+   \sa ldb_ldif_read_file for the reader equivalent to this function.
+*/
+int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *msg);
+
+/**
+   Base64 encode a buffer
+
+   \param mem_ctx the memory context that the result is allocated
+   from. 
+   \param buf pointer to the array that is to be encoded
+   \param len the number of elements in the array to be encoded
+
+   \return pointer to an array containing the encoded data
+
+   \note The caller is responsible for freeing the result
+*/
+char *ldb_base64_encode(void *mem_ctx, const char *buf, int len);
+
+/**
+   Base64 decode a buffer
+
+   This function decodes a base64 encoded string in place.
+
+   \param s the string to decode.
+
+   \return the length of the returned (decoded) string.
+
+   \note the string is null terminated, but the null terminator is not
+   included in the length.
+*/
+int ldb_base64_decode(char *s);
+
+int ldb_attrib_add_handlers(struct ldb_context *ldb, 
+                           const struct ldb_attrib_handler *handlers, 
+                           unsigned num_handlers);
+
+/* The following definitions come from lib/ldb/common/ldb_dn.c  */
+
+int ldb_dn_is_special(const struct ldb_dn *dn);
+int ldb_dn_check_special(const struct ldb_dn *dn, const char *check);
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value);
+struct ldb_dn *ldb_dn_new(void *mem_ctx);
+struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn);
+struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *dn);
+char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn);
+char *ldb_dn_linearize_casefold(struct ldb_context *ldb, const struct ldb_dn *edn);
+int ldb_dn_compare_base(struct ldb_context *ldb, const struct ldb_dn *base, const struct ldb_dn *dn);
+int ldb_dn_compare(struct ldb_context *ldb, const struct ldb_dn *edn0, const struct ldb_dn *edn1);
+struct ldb_dn *ldb_dn_casefold(struct ldb_context *ldb, const struct ldb_dn *edn);
+struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, const char *dn);
+struct ldb_dn *ldb_dn_copy_partial(void *mem_ctx, const struct ldb_dn *dn, int num_el);
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr,
+                                                              const char *val);
+struct ldb_dn *ldb_dn_build_child(void *mem_ctx, const char *attr,
+                                                const char * value,
+                                                const struct ldb_dn *base);
+struct ldb_dn *ldb_dn_make_child(void *mem_ctx,
+                                const struct ldb_dn_component *component,
+                                const struct ldb_dn *base);
+struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2);
+struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...) PRINTF_ATTRIBUTE(3,4);
+struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn);
+
+/* useful functions for ldb_message structure manipulation */
+int ldb_dn_cmp(struct ldb_context *ldb, const char *dn1, const char *dn2);
+
+/**
+   Compare two attributes
+
+   This function compares to attribute names. Note that this is a
+   case-insensitive comparison.
+
+   \param attr1 the first attribute name to compare
+   \param attr2 the second attribute name to compare
+
+   \return 0 if the attribute names are the same, or only differ in
+   case; non-zero if there are any differences
+*/
+int ldb_attr_cmp(const char *attr1, const char *attr2);
+char *ldb_attr_casefold(void *mem_ctx, const char *s);
+int ldb_attr_dn(const char *attr);
+
+/**
+   Create an empty message
+
+   \param mem_ctx the memory context to create in. You can pass NULL
+   to get the top level context, however the ldb context (from
+   ldb_init()) may be a better choice
+*/
+struct ldb_message *ldb_msg_new(void *mem_ctx);
+
+/**
+   Find an element within an message
+*/
+struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
+                                                const char *attr_name);
+
+/**
+   Compare two ldb_val values
+
+   \param v1 first ldb_val structure to be tested
+   \param v2 second ldb_val structure to be tested
+
+   \return 1 for a match, 0 if there is any difference
+*/
+int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2);
+
+/**
+   find a value within an ldb_message_element
+
+   \param el the element to search
+   \param val the value to search for
+
+   \note This search is case sensitive
+*/
+struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
+                                struct ldb_val *val);
+
+/**
+   add a new empty element to a ldb_message
+*/
+int ldb_msg_add_empty(struct ldb_message *msg, const char *attr_name, int flags);
+
+/**
+   add a element to a ldb_message
+*/
+int ldb_msg_add(struct ldb_message *msg, 
+               const struct ldb_message_element *el, 
+               int flags);
+int ldb_msg_add_value(struct ldb_message *msg, 
+                     const char *attr_name,
+                     const struct ldb_val *val);
+int ldb_msg_add_steal_value(struct ldb_message *msg, 
+                     const char *attr_name,
+                     struct ldb_val *val);
+int ldb_msg_add_steal_string(struct ldb_message *msg, 
+                            const char *attr_name, char *str);
+int ldb_msg_add_string(struct ldb_message *msg, 
+                      const char *attr_name, const char *str);
+int ldb_msg_add_fmt(struct ldb_message *msg, 
+                   const char *attr_name, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+
+/**
+   compare two message elements - return 0 on match
+*/
+int ldb_msg_element_compare(struct ldb_message_element *el1, 
+                           struct ldb_message_element *el2);
+
+/**
+   Find elements in a message.
+
+   This function finds elements and converts to a specific type, with
+   a give default value if not found. Assumes that elements are
+   single valued.
+*/
+const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name);
+int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
+                            const char *attr_name,
+                            int default_value);
+unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
+                                      const char *attr_name,
+                                      unsigned int default_value);
+int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
+                                  const char *attr_name,
+                                  int64_t default_value);
+uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
+                                    const char *attr_name,
+                                    uint64_t default_value);
+double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
+                                  const char *attr_name,
+                                  double default_value);
+int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
+                             const char *attr_name,
+                             int default_value);
+const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
+                                       const char *attr_name,
+                                       const char *default_value);
+
+struct ldb_dn *ldb_msg_find_attr_as_dn(void *mem_ctx,
+                                      const struct ldb_message *msg,
+                                       const char *attr_name);
+
+void ldb_msg_sort_elements(struct ldb_message *msg);
+
+struct ldb_message *ldb_msg_copy_shallow(void *mem_ctx, 
+                                        const struct ldb_message *msg);
+struct ldb_message *ldb_msg_copy(void *mem_ctx, 
+                                const struct ldb_message *msg);
+
+struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
+                                        const struct ldb_message *msg);
+
+
+struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
+                                struct ldb_message *msg1,
+                                struct ldb_message *msg2);
+
+int ldb_msg_check_string_attribute(const struct ldb_message *msg,
+                                  const char *name,
+                                  const char *value);
+
+/**
+   Integrity check an ldb_message
+
+   This function performs basic sanity / integrity checks on an
+   ldb_message.
+
+   \param msg the message to check
+
+   \return LDB_SUCCESS if the message is OK, or a non-zero error code
+   (one of LDB_ERR_INVALID_DN_SYNTAX, LDB_ERR_ENTRY_ALREADY_EXISTS or
+   LDB_ERR_INVALID_ATTRIBUTE_SYNTAX) if there is a problem with a
+   message.
+*/
+int ldb_msg_sanity_check(struct ldb_context *ldb,
+                        const struct ldb_message *msg);
+
+/**
+   Duplicate an ldb_val structure
+
+   This function copies an ldb value structure. 
+
+   \param mem_ctx the memory context that the duplicated value will be
+   allocated from
+   \param v the ldb_val to be duplicated.
+
+   \return the duplicated ldb_val structure.
+*/
+struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v);
+
+/**
+  this allows the user to set a debug function for error reporting
+*/
+int ldb_set_debug(struct ldb_context *ldb,
+                 void (*debug)(void *context, enum ldb_debug_level level, 
+                               const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0),
+                 void *context);
+
+/**
+  this allows the user to set custom utf8 function for error reporting
+*/
+void ldb_set_utf8_fns(struct ldb_context *ldb,
+                       void *context,
+                       char *(*casefold)(void *, void *, const char *));
+
+/**
+   this sets up debug to print messages on stderr
+*/
+int ldb_set_debug_stderr(struct ldb_context *ldb);
+
+/* control backend specific opaque values */
+int ldb_set_opaque(struct ldb_context *ldb, const char *name, void *value);
+void *ldb_get_opaque(struct ldb_context *ldb, const char *name);
+
+const struct ldb_attrib_handler *ldb_attrib_handler(struct ldb_context *ldb,
+                                                   const char *attrib);
+
+
+const char **ldb_attr_list_copy(void *mem_ctx, const char * const *attrs);
+const char **ldb_attr_list_copy_add(void *mem_ctx, const char * const *attrs, const char *new_attr);
+int ldb_attr_in_list(const char * const *attrs, const char *attr);
+
+
+void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree, 
+                                const char *attr, 
+                                const char *replace);
+
+int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace);
+int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace);
+void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr);
+
+/**
+   Convert a time structure to a string
+
+   This function converts a time_t structure to an LDAP formatted time
+   string.
+
+   \param mem_ctx the memory context to allocate the return string in
+   \param t the time structure to convert
+
+   \return the formatted string, or NULL if the time structure could
+   not be converted
+*/
+char *ldb_timestring(void *mem_ctx, time_t t);
+
+/**
+   Convert a string to a time structure
+
+   This function converts an LDAP formatted time string to a time_t
+   structure.
+
+   \param s the string to convert
+
+   \return the time structure, or 0 if the string cannot be converted
+*/
+time_t ldb_string_to_time(const char *s);
+
+char *ldb_dn_canonical_string(void *mem_ctx, const struct ldb_dn *dn);
+char *ldb_dn_canonical_ex_string(void *mem_ctx, const struct ldb_dn *dn);
+
+
+void ldb_qsort (void *const pbase, size_t total_elems, size_t size, void *opaque, ldb_qsort_cmp_fn_t cmp);
+#endif
diff --git a/source3/lib/ldb/include/ldb_errors.h b/source3/lib/ldb/include/ldb_errors.h
new file mode 100644 (file)
index 0000000..3b04c7c
--- /dev/null
@@ -0,0 +1,311 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb header
+ *
+ *  Description: defines error codes following RFC 2251 ldap error codes
+ *
+ *  Author: Simo Sorce
+ */
+
+#ifndef _LDB_ERRORS_H_
+
+/*! \cond DOXYGEN_IGNORE */
+#define _LDB_ERRORS_H_ 1
+/*! \endcond */
+
+/** 
+   \file ldb_errors.h
+
+   This header provides a set of result codes for LDB function calls.
+
+   Many LDB function calls return an integer value (int). As shown in
+   the function documentation, those return values may indicate
+   whether the function call worked correctly (in which case it
+   returns LDB_SUCCESS) or some problem occurred (in which case some
+   other value will be returned). As a special case,
+   LDB_ERR_COMPARE_FALSE or LDB_ERR_COMPARE_TRUE may be returned,
+   which does not indicate an error.
+
+   \note Not all error codes make sense for LDB, however they are
+   based on the LDAP error codes, and are kept for reference and to
+   avoid overlap.
+
+   \note Some of this documentation is based on information in
+   the OpenLDAP documentation, as developed and maintained by the
+   <a href="http://www.openldap.org/">The OpenLDAP Project</a>.
+ */
+
+/**
+   The function call succeeded.
+
+   If a function returns LDB_SUCCESS, then that function, and the
+   underlying transactions that may have been required, completed
+   successfully.
+*/
+#define LDB_SUCCESS                            0
+
+/**
+   The function call failed for some non-specific reason.
+*/
+#define LDB_ERR_OPERATIONS_ERROR               1
+
+/**
+   The function call failed because of a protocol violation.
+*/
+#define LDB_ERR_PROTOCOL_ERROR                 2
+
+/**
+   The function call failed because a time limit was exceeded.
+*/
+#define LDB_ERR_TIME_LIMIT_EXCEEDED            3
+
+/**
+   The function call failed because a size limit was exceeded.
+*/
+#define LDB_ERR_SIZE_LIMIT_EXCEEDED            4
+
+/**
+   The function was for value comparison, and the comparison operation
+   returned false.
+
+   \note This is a status value, and doesn't normally indicate an
+   error.
+*/
+#define LDB_ERR_COMPARE_FALSE                  5
+
+/**
+   The function was for value comparison, and the comparison operation
+   returned true.
+
+   \note This is a status value, and doesn't normally indicate an
+   error.
+*/
+#define LDB_ERR_COMPARE_TRUE                   6
+
+/**
+   The function used an authentication method that is not supported by
+   the database.
+*/
+#define LDB_ERR_AUTH_METHOD_NOT_SUPPORTED      7
+
+/**
+   The function call required a underlying operation that required
+   strong authentication.
+
+   This will normally only occur if you are using LDB with a LDAP
+   backend.
+*/
+#define LDB_ERR_STRONG_AUTH_REQUIRED           8
+/* 9 RESERVED */
+
+/**
+   The function resulted in a referral to another server.
+*/
+#define LDB_ERR_REFERRAL                       10
+
+/**
+   The function failed because an administrative / policy limit was
+   exceeded.
+*/
+#define LDB_ERR_ADMIN_LIMIT_EXCEEDED           11
+
+/**
+   The function required an extension or capability that the
+   database cannot provide.
+*/
+#define LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION 12
+
+/**
+   The function involved a transaction or database operation that
+   could not be performed without a secure link.
+*/
+#define LDB_ERR_CONFIDENTIALITY_REQUIRED       13
+
+/**
+   This is an intermediate result code for SASL bind operations that
+   have more than one step.
+
+   \note This is a result code that does not normally indicate an
+   error has occurred.
+*/
+#define LDB_ERR_SASL_BIND_IN_PROGRESS          14
+
+/**
+   The function referred to an attribute type that is not present in
+   the entry.
+*/
+#define LDB_ERR_NO_SUCH_ATTRIBUTE              16
+
+/**
+   The function referred to an attribute type that is invalid
+*/
+#define LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE       17
+
+/**
+   The function required a filter type that is not available for the
+   specified attribute.
+*/
+#define LDB_ERR_INAPPROPRIATE_MATCHING         18
+
+/**
+   The function would have violated an attribute constraint.
+*/
+#define LDB_ERR_CONSTRAINT_VIOLATION           19
+
+/**
+   The function involved an attribute type or attribute value that
+   already exists in the entry.
+*/
+#define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS      20
+/**
+   The function used an invalid (incorrect syntax) attribute value.
+*/
+#define LDB_ERR_INVALID_ATTRIBUTE_SYNTAX       21
+
+/* 22-31 unused */
+
+/**
+   The function referred to an object that does not exist in the
+   database.
+*/
+#define LDB_ERR_NO_SUCH_OBJECT                 32
+
+/**
+   The function referred to an alias which points to a non-existant
+   object in the database.
+*/
+#define LDB_ERR_ALIAS_PROBLEM                  33
+
+/**
+   The function used a DN which was invalid (incorrect syntax).
+*/
+#define LDB_ERR_INVALID_DN_SYNTAX              34
+
+/* 35 RESERVED */
+
+/**
+   The function required dereferencing of an alias, and something went
+   wrong during the dereferencing process.
+*/
+#define LDB_ERR_ALIAS_DEREFERENCING_PROBLEM    36
+
+/* 37-47 unused */
+
+/**
+   The function passed in the wrong authentication method.
+*/
+#define LDB_ERR_INAPPROPRIATE_AUTHENTICATION   48
+
+/**
+   The function passed in or referenced incorrect credentials during
+   authentication. 
+*/
+#define LDB_ERR_INVALID_CREDENTIALS            49
+
+/**
+   The function required access permissions that the user does not
+   possess. 
+*/
+#define LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS     50
+
+/**
+   The function required a transaction or call that the database could
+   not perform because it is busy.
+*/
+#define LDB_ERR_BUSY                           51
+
+/**
+   The function required a transaction or call to a database that is
+   not available.
+*/
+#define LDB_ERR_UNAVAILABLE                    52
+
+/**
+   The function required a transaction or call to a database that the
+   database declined to perform.
+*/
+#define LDB_ERR_UNWILLING_TO_PERFORM           53
+
+/**
+   The function failed because it resulted in a loop being detected.
+*/
+#define LDB_ERR_LOOP_DETECT                    54
+
+/* 55-63 unused */
+
+/**
+   The function failed because it would have violated a naming rule.
+*/
+#define LDB_ERR_NAMING_VIOLATION               64
+
+/**
+   The function failed because it would have violated the schema.
+*/
+#define LDB_ERR_OBJECT_CLASS_VIOLATION         65
+
+/**
+   The function required an operation that is only allowed on leaf
+   objects, but the object is not a leaf.
+*/
+#define LDB_ERR_NOT_ALLOWED_ON_NON_LEAF                66
+
+/**
+   The function required an operation that cannot be performed on a
+   Relative DN, but the object is a Relative DN.
+*/
+#define LDB_ERR_NOT_ALLOWED_ON_RDN             67
+
+/**
+   The function failed because the entry already exists.
+*/
+#define LDB_ERR_ENTRY_ALREADY_EXISTS           68
+
+/**
+   The function failed because modifications to an object class are
+   not allowable.
+*/
+#define LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED   69
+
+/* 70 RESERVED FOR CLDAP */
+
+/**
+   The function failed because it needed to be applied to multiple
+   databases.
+*/
+#define LDB_ERR_AFFECTS_MULTIPLE_DSAS          71
+
+/* 72-79 unused */
+
+/**
+   The function failed for unknown reasons.
+*/
+#define LDB_ERR_OTHER                          80
+
+/* 81-90 RESERVED for APIs */
+
+#endif /* _LDB_ERRORS_H_ */
diff --git a/source3/lib/ldb/include/ldb_private.h b/source3/lib/ldb/include/ldb_private.h
new file mode 100644 (file)
index 0000000..96b71ff
--- /dev/null
@@ -0,0 +1,220 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell    2004
+   Copyright (C) Stefan Metzmacher  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
+     ** 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 private header
+ *
+ *  Description: defines internal ldb structures used by the subsystem and modules
+ *
+ *  Author: Andrew Tridgell
+ *  Author: Stefan Metzmacher
+ */
+
+#ifndef _LDB_PRIVATE_H_
+#define _LDB_PRIVATE_H_ 1
+
+struct ldb_context;
+
+struct ldb_module_ops;
+
+/* basic module structure */
+struct ldb_module {
+       struct ldb_module *prev, *next;
+       struct ldb_context *ldb;
+       void *private_data;
+       const struct ldb_module_ops *ops;
+};
+
+/* 
+   these function pointers define the operations that a ldb module must perform
+   they correspond exactly to the ldb_*() interface 
+*/
+struct ldb_module_ops {
+       const char *name;
+       int (*init_context) (struct ldb_module *);
+       int (*search)(struct ldb_module *, struct ldb_request *); /* search */
+       int (*add)(struct ldb_module *, struct ldb_request *); /* add */
+       int (*modify)(struct ldb_module *, struct ldb_request *); /* modify */
+       int (*del)(struct ldb_module *, struct ldb_request *); /* delete */
+       int (*rename)(struct ldb_module *, struct ldb_request *); /* rename */
+       int (*request)(struct ldb_module *, struct ldb_request *); /* match any other operation */
+       int (*extended)(struct ldb_module *, struct ldb_request *); /* extended operations */
+       int (*start_transaction)(struct ldb_module *);
+       int (*end_transaction)(struct ldb_module *);
+       int (*del_transaction)(struct ldb_module *);
+       int (*wait)(struct ldb_handle *, enum ldb_wait_type);
+       int (*sequence_number)(struct ldb_module *, struct ldb_request *);
+};
+
+typedef int (*ldb_connect_fn) (struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[],
+                              struct ldb_module **module);
+
+/*
+  schema related information needed for matching rules
+*/
+struct ldb_schema {
+       /* attribute handling table */
+       unsigned num_attrib_handlers;
+       struct ldb_attrib_handler *attrib_handlers;
+
+       /* objectclass information */
+       unsigned num_classes;
+       struct ldb_subclass {
+               char *name;
+               char **subclasses;
+       } *classes;
+};
+
+/*
+  every ldb connection is started by establishing a ldb_context
+*/
+struct ldb_context {
+       /* the operations provided by the backend */
+       struct ldb_module *modules;
+
+       /* debugging operations */
+       struct ldb_debug_ops debug_ops;
+
+       /* custom utf8 functions */
+       struct ldb_utf8_fns utf8_fns;
+
+       /* backend specific opaque parameters */
+       struct ldb_opaque {
+               struct ldb_opaque *next;
+               const char *name;
+               void *value;
+       } *opaque;
+
+       struct ldb_schema schema;
+
+       char *err_string;
+
+       int transaction_active;
+
+       int default_timeout;
+
+       unsigned int flags;
+};
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif
+
+/*
+  simplify out of memory handling
+*/
+#define ldb_oom(ldb) ldb_debug_set(ldb, LDB_DEBUG_FATAL, "ldb out of memory at %s:%d\n", __FILE__, __LINE__)
+
+/* The following definitions come from lib/ldb/common/ldb.c  */
+
+int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[],
+                       struct ldb_module **backend_module);
+
+/* The following definitions come from lib/ldb/common/ldb_modules.c  */
+
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string);
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out);
+int ldb_load_modules(struct ldb_context *ldb, const char *options[]);
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module);
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request);
+int ldb_next_start_trans(struct ldb_module *module);
+int ldb_next_end_trans(struct ldb_module *module);
+int ldb_next_del_trans(struct ldb_module *module);
+int ldb_next_init(struct ldb_module *module);
+
+void ldb_set_errstring(struct ldb_context *ldb, const char *err_string);
+void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+void ldb_reset_err_string(struct ldb_context *ldb);
+
+int ldb_register_module(const struct ldb_module_ops *);
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn);
+int ldb_try_load_dso(struct ldb_context *ldb, const char *name);
+
+/* The following definitions come from lib/ldb/common/ldb_debug.c  */
+void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level, 
+                  const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+
+/* The following definitions come from lib/ldb/common/ldb_ldif.c  */
+int ldb_should_b64_encode(const struct ldb_val *val);
+
+int ldb_objectclass_init(void);
+int ldb_operational_init(void);
+int ldb_paged_results_init(void);
+int ldb_rdn_name_init(void);
+int ldb_schema_init(void);
+int ldb_sort_init(void);
+int ldb_ldap_init(void);
+int ldb_ildap_init(void);
+int ldb_tdb_init(void);
+int ldb_sqlite3_init(void);
+
+int ldb_match_msg(struct ldb_context *ldb,
+                 const struct ldb_message *msg,
+                 const struct ldb_parse_tree *tree,
+                 const struct ldb_dn *base,
+                 enum ldb_scope scope);
+
+void ldb_remove_attrib_handler(struct ldb_context *ldb, const char *attrib);
+const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
+                                                          const char *syntax);
+int ldb_set_attrib_handlers(struct ldb_context *ldb, 
+                           const struct ldb_attrib_handler *handlers, 
+                           unsigned num_handlers);
+int ldb_setup_wellknown_attributes(struct ldb_context *ldb);
+int ldb_set_attrib_handler_syntax(struct ldb_context *ldb, 
+                                 const char *attr, const char *syntax);
+
+/* The following definitions come from lib/ldb/common/ldb_attributes.c  */
+const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname);
+void ldb_subclass_remove(struct ldb_context *ldb, const char *classname);
+int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass);
+
+int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
+                    const struct ldb_val *in, struct ldb_val *out);
+int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
+                         const struct ldb_val *v1, const struct ldb_val *v2);
+
+/* The following definitions come from lib/ldb/common/ldb_controls.c  */
+struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid);
+int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver);
+int check_critical_controls(struct ldb_control **controls);
+
+/* The following definitions come from lib/ldb/common/ldb_utf8.c */
+char *ldb_casefold_default(void *context, void *mem_ctx, const char *s);
+
+/**
+  Obtain current/next database sequence number
+*/
+int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num);
+
+#define LDB_SEQ_GLOBAL_SEQUENCE    0x01
+#define LDB_SEQ_TIMESTAMP_SEQUENCE 0x02
+
+
+#endif
diff --git a/source3/lib/ldb/install-sh b/source3/lib/ldb/install-sh
new file mode 100755 (executable)
index 0000000..5871924
--- /dev/null
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/source3/lib/ldb/ldap.m4 b/source3/lib/ldb/ldap.m4
new file mode 100644 (file)
index 0000000..911c136
--- /dev/null
@@ -0,0 +1,89 @@
+########################################################
+# Compile with LDAP support?
+
+LDAP_LIBS=""
+with_ldap_support=auto
+AC_MSG_CHECKING([for LDAP support])
+
+AC_ARG_WITH(ldap,
+AS_HELP_STRING([--with-ldap],[LDAP backend support (default=yes)]),
+[ case "$withval" in
+    yes|no)
+       with_ldap_support=$withval
+       ;;
+  esac ])
+
+AC_MSG_RESULT($with_ldap_support)
+
+if test x"$with_ldap_support" != x"no"; then
+
+  ##################################################################
+  # first test for ldap.h and lber.h
+  # (ldap.h is required for this test)
+  AC_CHECK_HEADERS(ldap.h lber.h)
+  
+  if test x"$ac_cv_header_ldap_h" != x"yes"; then
+       if test x"$with_ldap_support" = x"yes"; then
+        AC_MSG_ERROR(ldap.h is needed for LDAP support)
+       else
+        AC_MSG_WARN(ldap.h is needed for LDAP support)
+       fi
+       
+       with_ldap_support=no
+  fi
+fi
+
+if test x"$with_ldap_support" != x"no"; then
+  ac_save_LIBS=$LIBS
+
+  ##################################################################
+  # we might need the lber lib on some systems. To avoid link errors
+  # this test must be before the libldap test
+  AC_CHECK_LIB_EXT(lber, LDAP_LIBS, ber_scanf)
+
+  ########################################################
+  # now see if we can find the ldap libs in standard paths
+  AC_CHECK_LIB_EXT(ldap, LDAP_LIBS, ldap_init)
+
+  AC_CHECK_FUNC_EXT(ldap_domain2hostlist,$LDAP_LIBS)
+  
+  ########################################################
+  # If we have LDAP, does it's rebind procedure take 2 or 3 arguments?
+  # Check found in pam_ldap 145.
+  AC_CHECK_FUNC_EXT(ldap_set_rebind_proc,$LDAP_LIBS)
+
+  LIBS="$LIBS $LDAP_LIBS"
+  AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, smb_ldap_cv_ldap_set_rebind_proc, [
+    AC_TRY_COMPILE([
+       #include <lber.h>
+       #include <ldap.h>], 
+       [ldap_set_rebind_proc(0, 0, 0);], 
+       [smb_ldap_cv_ldap_set_rebind_proc=3], 
+       [smb_ldap_cv_ldap_set_rebind_proc=2]
+    ) 
+  ])
+  
+  AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $smb_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc])
+
+  AC_CHECK_FUNC_EXT(ldap_initialize,$LDAP_LIBS)        
+  
+  if test x"$ac_cv_lib_ext_ldap_ldap_init" = x"yes" -a x"$ac_cv_func_ext_ldap_domain2hostlist" = x"yes"; then
+    AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])
+    with_ldap_support=yes
+    AC_MSG_CHECKING(whether LDAP support is used)
+    AC_MSG_RESULT(yes)
+    SMB_ENABLE(LDAP,YES)
+  else
+    if test x"$with_ldap_support" = x"yes"; then
+       AC_MSG_ERROR(libldap is needed for LDAP support)
+    else
+       AC_MSG_WARN(libldap is needed for LDAP support)
+    fi
+    
+    LDAP_LIBS=""
+    with_ldap_support=no
+  fi
+  LIBS=$ac_save_LIBS
+fi
+
+SMB_EXT_LIB(LDAP,[${LDAP_LIBS}],[${LDAP_CFLAGS}],[${LDAP_CPPFLAGS}],[${LDAP_LDFLAGS}])
diff --git a/source3/lib/ldb/ldb.pc.in b/source3/lib/ldb/ldb.pc.in
new file mode 100644 (file)
index 0000000..8b0536e
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=@modulesdir@
+
+Name: ldb
+Description: An LDAP-like embedded database
+Version: 4.0
+Libs: @LIBS@ -L${libdir} -lldb
+Cflags: -I${includedir} @CFLAGS@
+Modulesdir: ${modulesdir}
diff --git a/source3/lib/ldb/ldb_ildap/ldb_ildap.c b/source3/lib/ldb/ldb_ildap/ldb_ildap.c
new file mode 100644 (file)
index 0000000..5b69ac0
--- /dev/null
@@ -0,0 +1,825 @@
+/* 
+   ldb database library - ildap backend
+
+   Copyright (C) Andrew Tridgell  2005
+   Copyright (C) Simo Sorce       2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb_ildap
+ *
+ *  Component: ldb ildap backend
+ *
+ *  Description: This is a ldb backend for the internal ldap
+ *  client library in Samba4. By using this backend we are
+ *  independent of a system ldap library
+ *
+ *  Author: Andrew Tridgell
+ *
+ *  Modifications:
+ *
+ *  - description: make the module use asyncronous calls
+ *    date: Feb 2006
+ *    author: Simo Sorce
+ */
+
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "lib/events/events.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "auth/auth.h"
+
+struct ildb_private {
+       struct ldap_connection *ldap;
+       struct ldb_context *ldb;
+};
+
+struct ildb_context {
+       struct ldb_module *module;
+       struct ldap_request *req;
+       void *context;
+       int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
+};
+
+/*
+  convert a ldb_message structure to a list of ldap_mod structures
+  ready for ildap_add() or ildap_modify()
+*/
+static struct ldap_mod **ildb_msg_to_mods(void *mem_ctx, int *num_mods,
+                                         const struct ldb_message *msg, int use_flags)
+{
+       struct ldap_mod **mods;
+       unsigned int i;
+       int n = 0;
+
+       /* allocate maximum number of elements needed */
+       mods = talloc_array(mem_ctx, struct ldap_mod *, msg->num_elements+1);
+       if (!mods) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       mods[0] = NULL;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_message_element *el = &msg->elements[i];
+
+               mods[n] = talloc(mods, struct ldap_mod);
+               if (!mods[n]) {
+                       goto failed;
+               }
+               mods[n + 1] = NULL;
+               mods[n]->type = 0;
+               mods[n]->attrib = *el;
+               if (use_flags) {
+                       switch (el->flags & LDB_FLAG_MOD_MASK) {
+                       case LDB_FLAG_MOD_ADD:
+                               mods[n]->type = LDAP_MODIFY_ADD;
+                               break;
+                       case LDB_FLAG_MOD_DELETE:
+                               mods[n]->type = LDAP_MODIFY_DELETE;
+                               break;
+                       case LDB_FLAG_MOD_REPLACE:
+                               mods[n]->type = LDAP_MODIFY_REPLACE;
+                               break;
+                       }
+               }
+               n++;
+       }
+
+       *num_mods = n;
+       return mods;
+
+failed:
+       talloc_free(mods);
+       return NULL;
+}
+
+
+/*
+  map an ildap NTSTATUS to a ldb error code
+*/
+static int ildb_map_error(struct ildb_private *ildb, NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               return LDB_SUCCESS;
+       }
+       ldb_set_errstring(ildb->ldb, ldap_errstr(ildb->ldap, status));
+       if (NT_STATUS_IS_LDAP(status)) {
+               return NT_STATUS_LDAP_CODE(status);
+       }
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static void ildb_request_timeout(struct event_context *ev, struct timed_event *te,
+                                struct timeval t, void *private_data)
+{
+       struct ldb_handle *handle = talloc_get_type(private_data, struct ldb_handle);
+       struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
+
+       if (ac->req->state == LDAP_REQUEST_PENDING) {
+               DLIST_REMOVE(ac->req->conn->pending, ac->req);
+       }
+
+       handle->status = LDB_ERR_TIME_LIMIT_EXCEEDED;
+
+       return;
+}
+
+static void ildb_callback(struct ldap_request *req)
+{
+       struct ldb_handle *handle = talloc_get_type(req->async.private_data, struct ldb_handle);
+       struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
+       struct ildb_private *ildb = talloc_get_type(ac->module->private_data, struct ildb_private);
+       NTSTATUS status;
+       int i;
+
+       handle->status = LDB_SUCCESS;
+
+       if (!NT_STATUS_IS_OK(req->status)) {
+               handle->status = ildb_map_error(ildb, req->status);
+               return;
+       }
+
+       if (req->num_replies < 1) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return;
+       } 
+               
+       switch (req->type) {
+
+       case LDAP_TAG_ModifyRequest:
+               if (req->replies[0]->type != LDAP_TAG_ModifyResponse) {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       return;
+               }
+               status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
+               handle->status = ildb_map_error(ildb, status);
+               if (ac->callback && handle->status == LDB_SUCCESS) {
+                       /* FIXME: build a corresponding ares to pass on */
+                       handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
+               }
+               handle->state = LDB_ASYNC_DONE;
+               break;
+
+       case LDAP_TAG_AddRequest:
+               if (req->replies[0]->type != LDAP_TAG_AddResponse) {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       return;
+               }
+               status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
+               handle->status = ildb_map_error(ildb, status);
+               if (ac->callback && handle->status == LDB_SUCCESS) {
+                       /* FIXME: build a corresponding ares to pass on */
+                       handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
+               }
+               handle->state = LDB_ASYNC_DONE;
+               break;
+
+       case LDAP_TAG_DelRequest:
+               if (req->replies[0]->type != LDAP_TAG_DelResponse) {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       return;
+               }
+               status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
+               handle->status = ildb_map_error(ildb, status);
+               if (ac->callback && handle->status == LDB_SUCCESS) {
+                       /* FIXME: build a corresponding ares to pass on */
+                       handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
+               }
+               handle->state = LDB_ASYNC_DONE;
+               break;
+
+       case LDAP_TAG_ModifyDNRequest:
+               if (req->replies[0]->type != LDAP_TAG_ModifyDNResponse) {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       return;
+               }
+               status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
+               handle->status = ildb_map_error(ildb, status);
+               if (ac->callback && handle->status == LDB_SUCCESS) {
+                       /* FIXME: build a corresponding ares to pass on */
+                       handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
+               }
+               handle->state = LDB_ASYNC_DONE;
+               break;
+
+       case LDAP_TAG_SearchRequest:
+               /* loop over all messages */
+               for (i = 0; i < req->num_replies; i++) {
+                       struct ldap_SearchResEntry *search;
+                       struct ldb_reply *ares = NULL;
+                       struct ldap_message *msg;
+                       int ret;
+
+                       ares = talloc_zero(ac, struct ldb_reply);
+                       if (!ares) {
+                               handle->status = LDB_ERR_OPERATIONS_ERROR;
+                               return;
+                       }
+
+                       msg = req->replies[i];
+                       switch (msg->type) {
+
+                       case LDAP_TAG_SearchResultDone:
+
+                               status = ldap_check_response(req->conn, &msg->r.GeneralResult);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       handle->status = ildb_map_error(ildb, status);
+                                       return;
+                               }
+                               
+                               ares->controls = talloc_move(ares, &msg->controls);
+                               if (msg->r.SearchResultDone.resultcode) {
+                                       if (msg->r.SearchResultDone.errormessage) {
+                                               ldb_set_errstring(ac->module->ldb, msg->r.SearchResultDone.errormessage);
+                                       }
+                               }
+
+                               handle->status = msg->r.SearchResultDone.resultcode;
+                               handle->state = LDB_ASYNC_DONE;
+                               ares->type = LDB_REPLY_DONE;
+                               break;
+
+                       case LDAP_TAG_SearchResultEntry:
+
+
+                               ares->message = ldb_msg_new(ares);
+                               if (!ares->message) {
+                                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                                       return;
+                               }
+
+                               search = &(msg->r.SearchResultEntry);
+               
+                               ares->message->dn = ldb_dn_explode_or_special(ares->message, search->dn);
+                               if (ares->message->dn == NULL) {
+                                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                                       return;
+                               }
+                               ares->message->num_elements = search->num_attributes;
+                               ares->message->elements = talloc_move(ares->message,
+                                                                     &search->attributes);
+
+                               handle->status = LDB_SUCCESS;
+                               handle->state = LDB_ASYNC_PENDING;
+                               ares->type = LDB_REPLY_ENTRY;
+                               break;
+
+                       case LDAP_TAG_SearchResultReference:
+
+                               ares->referral = talloc_strdup(ares, msg->r.SearchResultReference.referral);
+                               
+                               handle->status = LDB_SUCCESS;
+                               handle->state = LDB_ASYNC_PENDING;
+                               ares->type = LDB_REPLY_REFERRAL;
+                               break;
+
+                       default:
+                               /* TAG not handled, fail ! */
+                               handle->status = LDB_ERR_PROTOCOL_ERROR;
+                               return;
+                       }
+
+                       ret = ac->callback(ac->module->ldb, ac->context, ares);
+                       if (ret) {
+                               handle->status = ret;
+                       }
+               }
+
+               talloc_free(req->replies);
+               req->replies = NULL;
+               req->num_replies = 0;
+
+               break;
+               
+       default:
+               handle->status = LDB_ERR_PROTOCOL_ERROR;
+               return;
+       }
+}
+
+static struct ldb_handle *init_ildb_handle(struct ldb_module *module, 
+                                          void *context,
+                                          int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ildb_context *ildb_ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(ildb->ldap, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ildb_ac = talloc(h, struct ildb_context);
+       if (ildb_ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ildb_ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ildb_ac->module = module;
+       ildb_ac->context = context;
+       ildb_ac->callback = callback;
+
+       return h;
+}
+
+static int ildb_request_send(struct ldb_module *module, struct ldap_message *msg,
+                            void *context,
+                            int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                            int timeout,
+                            struct ldb_handle **handle)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldb_handle *h = init_ildb_handle(module, context, callback);
+       struct ildb_context *ildb_ac;
+       struct ldap_request *req;
+
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;                
+       }
+
+       ildb_ac = talloc_get_type(h->private_data, struct ildb_context);
+
+       req = ldap_request_send(ildb->ldap, msg);
+       if (req == NULL) {
+               ldb_set_errstring(module->ldb, "async send request failed");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (!req->conn) {
+               ldb_set_errstring(module->ldb, "connection to remote LDAP server dropped?");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       talloc_free(req->time_event);
+       req->time_event = NULL;
+       if (timeout) {
+               req->time_event = event_add_timed(req->conn->event.event_ctx, h, 
+                                                 timeval_current_ofs(timeout, 0),
+                                                 ildb_request_timeout, h);
+       }
+
+       req->async.fn = ildb_callback;
+       req->async.private_data = (void *)h;
+       ildb_ac->req = talloc_move(ildb_ac, &req);
+
+       *handle = h;
+       return LDB_SUCCESS;
+}
+
+static int ildb_request_noop(struct ldb_module *module, struct ldb_request *req) 
+{
+       struct ldb_handle *h = init_ildb_handle(module, req->context, req->callback);
+       struct ildb_context *ildb_ac;
+       int ret = LDB_SUCCESS;
+
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;                
+       }
+
+       ildb_ac = talloc_get_type(h->private_data, struct ildb_context);
+       
+       req->handle = h;
+
+       if (ildb_ac->callback) {
+               ret = ildb_ac->callback(module->ldb, ildb_ac->context, NULL);
+       }
+       req->handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+/*
+  search for matching records using an asynchronous function
+ */
+static int ildb_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldap_message *msg;
+       int n;
+
+       req->handle = NULL;
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb, "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       if (req->op.search.tree == NULL) {
+               ldb_set_errstring(module->ldb, "Invalid expression parse tree");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg = new_ldap_message(ildb);
+       if (msg == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->type = LDAP_TAG_SearchRequest;
+
+       if (req->op.search.base == NULL) {
+               msg->r.SearchRequest.basedn = talloc_strdup(msg, "");
+       } else {
+               msg->r.SearchRequest.basedn  = ldb_dn_linearize(msg, req->op.search.base);
+       }
+       if (msg->r.SearchRequest.basedn == NULL) {
+               ldb_set_errstring(module->ldb, "Unable to determine baseDN");
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (req->op.search.scope == LDB_SCOPE_DEFAULT) {
+               msg->r.SearchRequest.scope = LDB_SCOPE_SUBTREE;
+       } else {
+               msg->r.SearchRequest.scope = req->op.search.scope;
+       }
+       
+       msg->r.SearchRequest.deref  = LDAP_DEREFERENCE_NEVER;
+       msg->r.SearchRequest.timelimit = 0;
+       msg->r.SearchRequest.sizelimit = 0;
+       msg->r.SearchRequest.attributesonly = 0;
+       msg->r.SearchRequest.tree = discard_const(req->op.search.tree);
+       
+       for (n = 0; req->op.search.attrs && req->op.search.attrs[n]; n++) /* noop */ ;
+       msg->r.SearchRequest.num_attributes = n;
+       msg->r.SearchRequest.attributes = discard_const(req->op.search.attrs);
+       msg->controls = req->controls;
+
+       return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
+}
+
+/*
+  add a record
+*/
+static int ildb_add(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldap_message *msg;
+       struct ldap_mod **mods;
+       int i,n;
+
+       req->handle = NULL;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(req->op.add.message->dn)) {
+               return ildb_request_noop(module, req);
+       }
+
+       msg = new_ldap_message(ildb->ldap);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->type = LDAP_TAG_AddRequest;
+
+       msg->r.AddRequest.dn = ldb_dn_linearize(msg, req->op.add.message->dn);
+       if (msg->r.AddRequest.dn == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       mods = ildb_msg_to_mods(msg, &n, req->op.add.message, 0);
+       if (mods == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->r.AddRequest.num_attributes = n;
+       msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
+       if (msg->r.AddRequest.attributes == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       for (i = 0; i < n; i++) {
+               msg->r.AddRequest.attributes[i] = mods[i]->attrib;
+       }
+
+       return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
+}
+
+/*
+  modify a record
+*/
+static int ildb_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldap_message *msg;
+       struct ldap_mod **mods;
+       int i,n;
+
+       req->handle = NULL;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(req->op.mod.message->dn)) {
+               return ildb_request_noop(module, req);
+       }
+
+       msg = new_ldap_message(ildb->ldap);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->type = LDAP_TAG_ModifyRequest;
+
+       msg->r.ModifyRequest.dn = ldb_dn_linearize(msg, req->op.mod.message->dn);
+       if (msg->r.ModifyRequest.dn == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       mods = ildb_msg_to_mods(msg, &n, req->op.mod.message, 1);
+       if (mods == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->r.ModifyRequest.num_mods = n;
+       msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
+       if (msg->r.ModifyRequest.mods == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       for (i = 0; i < n; i++) {
+               msg->r.ModifyRequest.mods[i] = *mods[i];
+       }
+
+       return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
+}
+
+/*
+  delete a record
+*/
+static int ildb_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldap_message *msg;
+
+       req->handle = NULL;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(req->op.del.dn)) {
+               return ildb_request_noop(module, req);
+       }
+
+       msg = new_ldap_message(ildb->ldap);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->type = LDAP_TAG_DelRequest;
+       
+       msg->r.DelRequest.dn = ldb_dn_linearize(msg, req->op.del.dn);
+       if (msg->r.DelRequest.dn == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
+}
+
+/*
+  rename a record
+*/
+static int ildb_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
+       struct ldap_message *msg;
+
+       req->handle = NULL;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(req->op.rename.olddn) || ldb_dn_is_special(req->op.rename.newdn)) {
+               return ildb_request_noop(module, req);
+       }
+
+       msg = new_ldap_message(ildb->ldap);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->type = LDAP_TAG_ModifyDNRequest;
+       msg->r.ModifyDNRequest.dn = ldb_dn_linearize(msg, req->op.rename.olddn);
+       if (msg->r.ModifyDNRequest.dn == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       msg->r.ModifyDNRequest.newrdn = 
+               talloc_asprintf(msg, "%s=%s",
+                               req->op.rename.newdn->components[0].name,
+                               ldb_dn_escape_value(msg, req->op.rename.newdn->components[0].value));
+       if (msg->r.ModifyDNRequest.newrdn == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->r.ModifyDNRequest.newsuperior =
+               ldb_dn_linearize(msg,
+                                ldb_dn_get_parent(msg, req->op.rename.newdn));
+       if (msg->r.ModifyDNRequest.newsuperior == NULL) {
+               talloc_free(msg);
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       msg->r.ModifyDNRequest.deleteolddn = True;
+
+       return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
+}
+
+static int ildb_start_trans(struct ldb_module *module)
+{
+       /* TODO implement a local locking mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int ildb_end_trans(struct ldb_module *module)
+{
+       /* TODO implement a local transaction mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int ildb_del_trans(struct ldb_module *module)
+{
+       /* TODO implement a local locking mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int ildb_request(struct ldb_module *module, struct ldb_request *req)
+{
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int ildb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       handle->state = LDB_ASYNC_INIT;
+
+       switch(type) {
+       case LDB_WAIT_NONE:
+               if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
+                       return LDB_ERR_OTHER;
+               }
+               break;
+       case LDB_WAIT_ALL:
+               while (handle->status == LDB_SUCCESS && handle->state != LDB_ASYNC_DONE) {
+                       if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
+                               return LDB_ERR_OTHER;
+                       }
+               }
+               break;
+       default:
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       return handle->status;
+}
+
+static const struct ldb_module_ops ildb_ops = {
+       .name              = "ldap",
+       .search            = ildb_search,
+       .add               = ildb_add,
+       .modify            = ildb_modify,
+       .del               = ildb_delete,
+       .rename            = ildb_rename,
+       .request           = ildb_request,
+       .start_transaction = ildb_start_trans,
+       .end_transaction   = ildb_end_trans,
+       .del_transaction   = ildb_del_trans,
+       .wait              = ildb_wait
+};
+
+/*
+  connect to the database
+*/
+static int ildb_connect(struct ldb_context *ldb, const char *url, 
+                       unsigned int flags, const char *options[],
+                       struct ldb_module **module)
+{
+       struct ildb_private *ildb = NULL;
+       NTSTATUS status;
+       struct cli_credentials *creds;
+
+       ildb = talloc(ldb, struct ildb_private);
+       if (!ildb) {
+               ldb_oom(ldb);
+               goto failed;
+       }
+
+       ildb->ldb     = ldb;
+
+       ildb->ldap = ldap4_new_connection(ildb, ldb_get_opaque(ldb, "EventContext"));
+       if (!ildb->ldap) {
+               ldb_oom(ldb);
+               goto failed;
+       }
+
+       if (flags & LDB_FLG_RECONNECT) {
+               ldap_set_reconn_params(ildb->ldap, 10);
+       }
+
+       status = ldap_connect(ildb->ldap, url);
+       if (!NT_STATUS_IS_OK(status)) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s\n",
+                         url, ldap_errstr(ildb->ldap, status));
+               goto failed;
+       }
+
+
+       *module = talloc(ldb, struct ldb_module);
+       if (!module) {
+               ldb_oom(ldb);
+               talloc_free(ildb);
+               return -1;
+       }
+       (*module)->ldb = ldb;
+       (*module)->prev = (*module)->next = NULL;
+       (*module)->private_data = ildb;
+       (*module)->ops = &ildb_ops;
+
+       /* caller can optionally setup credentials using the opaque token 'credentials' */
+       creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
+       if (creds == NULL) {
+               struct auth_session_info *session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
+               if (session_info) {
+                       creds = session_info->credentials;
+               }
+       }
+
+       if (creds != NULL && cli_credentials_authentication_requested(creds)) {
+               const char *bind_dn = cli_credentials_get_bind_dn(creds);
+               if (bind_dn) {
+                       const char *password = cli_credentials_get_password(creds);
+                       status = ldap_bind_simple(ildb->ldap, bind_dn, password);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
+                                         ldap_errstr(ildb->ldap, status));
+                               goto failed;
+                       }
+               } else {
+                       status = ldap_bind_sasl(ildb->ldap, creds);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
+                                         ldap_errstr(ildb->ldap, status));
+                               goto failed;
+                       }
+               }
+       }
+
+       return 0;
+
+failed:
+       talloc_free(ildb);
+       return -1;
+}
+
+int ldb_ildap_init(void)
+{
+       return ldb_register_backend("ldap", ildb_connect) + 
+                  ldb_register_backend("ldapi", ildb_connect) + 
+                  ldb_register_backend("ldaps", ildb_connect);
+}
diff --git a/source3/lib/ldb/ldb_ldap/ldb_ldap.c b/source3/lib/ldb/ldb_ldap/ldb_ldap.c
new file mode 100644 (file)
index 0000000..9de67e5
--- /dev/null
@@ -0,0 +1,831 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+   Copyright (C) Simo Sorce       2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb_ldap
+ *
+ *  Component: ldb ldap backend
+ *
+ *  Description: core files for LDAP backend
+ *
+ *  Author: Andrew Tridgell
+ *
+ *  Modifications:
+ *
+ *  - description: make the module use asyncronous calls
+ *    date: Feb 2006
+ *    author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+
+struct lldb_private {
+       LDAP *ldap;
+};
+
+struct lldb_context {
+       struct ldb_module *module;
+       int msgid;
+       int timeout;
+       time_t starttime;
+       void *context;
+       int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
+};
+
+static int lldb_ldap_to_ldb(int err) {
+       /* Ldap errors and ldb errors are defined to the same values */
+       return err;
+}
+
+static struct ldb_handle *init_handle(struct lldb_private *lldb, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                                           int timeout, time_t starttime)
+{
+       struct lldb_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(lldb, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc(h, struct lldb_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->context = context;
+       ac->callback = callback;
+       ac->timeout = timeout;
+       ac->starttime = starttime;
+       ac->msgid = 0;
+
+       return h;
+}
+/*
+  convert a ldb_message structure to a list of LDAPMod structures
+  ready for ldap_add() or ldap_modify()
+*/
+static LDAPMod **lldb_msg_to_mods(void *mem_ctx, const struct ldb_message *msg, int use_flags)
+{
+       LDAPMod **mods;
+       unsigned int i, j;
+       int num_mods = 0;
+
+       /* allocate maximum number of elements needed */
+       mods = talloc_array(mem_ctx, LDAPMod *, msg->num_elements+1);
+       if (!mods) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       mods[0] = NULL;
+
+       for (i=0;i<msg->num_elements;i++) {
+               const struct ldb_message_element *el = &msg->elements[i];
+
+               mods[num_mods] = talloc(mods, LDAPMod);
+               if (!mods[num_mods]) {
+                       goto failed;
+               }
+               mods[num_mods+1] = NULL;
+               mods[num_mods]->mod_op = LDAP_MOD_BVALUES;
+               if (use_flags) {
+                       switch (el->flags & LDB_FLAG_MOD_MASK) {
+                       case LDB_FLAG_MOD_ADD:
+                               mods[num_mods]->mod_op |= LDAP_MOD_ADD;
+                               break;
+                       case LDB_FLAG_MOD_DELETE:
+                               mods[num_mods]->mod_op |= LDAP_MOD_DELETE;
+                               break;
+                       case LDB_FLAG_MOD_REPLACE:
+                               mods[num_mods]->mod_op |= LDAP_MOD_REPLACE;
+                               break;
+                       }
+               }
+               mods[num_mods]->mod_type = discard_const_p(char, el->name);
+               mods[num_mods]->mod_vals.modv_bvals = talloc_array(mods[num_mods], 
+                                                                  struct berval *,
+                                                                  1+el->num_values);
+               if (!mods[num_mods]->mod_vals.modv_bvals) {
+                       goto failed;
+               }
+
+               for (j=0;j<el->num_values;j++) {
+                       mods[num_mods]->mod_vals.modv_bvals[j] = talloc(mods[num_mods]->mod_vals.modv_bvals,
+                                                                       struct berval);
+                       if (!mods[num_mods]->mod_vals.modv_bvals[j]) {
+                               goto failed;
+                       }
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_val = el->values[j].data;
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_len = el->values[j].length;
+               }
+               mods[num_mods]->mod_vals.modv_bvals[j] = NULL;
+               num_mods++;
+       }
+
+       return mods;
+
+failed:
+       talloc_free(mods);
+       return NULL;
+}
+
+/*
+  add a single set of ldap message values to a ldb_message
+*/
+static int lldb_add_msg_attr(struct ldb_context *ldb,
+                            struct ldb_message *msg, 
+                            const char *attr, struct berval **bval)
+{
+       int count, i;
+       struct ldb_message_element *el;
+
+       count = ldap_count_values_len(bval);
+
+       if (count <= 0) {
+               return -1;
+       }
+
+       el = talloc_realloc(msg, msg->elements, struct ldb_message_element, 
+                             msg->num_elements + 1);
+       if (!el) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       msg->elements = el;
+
+       el = &msg->elements[msg->num_elements];
+
+       el->name = talloc_strdup(msg->elements, attr);
+       if (!el->name) {
+               errno = ENOMEM;
+               return -1;
+       }
+       el->flags = 0;
+
+       el->num_values = 0;
+       el->values = talloc_array(msg->elements, struct ldb_val, count);
+       if (!el->values) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       for (i=0;i<count;i++) {
+               el->values[i].data = talloc_memdup(el->values, bval[i]->bv_val, bval[i]->bv_len);
+               if (!el->values[i].data) {
+                       return -1;
+               }
+               el->values[i].length = bval[i]->bv_len;
+               el->num_values++;
+       }
+
+       msg->num_elements++;
+
+       return 0;
+}
+
+/*
+  search for matching records
+*/
+static int lldb_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
+       struct lldb_context *lldb_ac;
+       struct timeval tv;
+       int ldap_scope;
+       char *search_base;
+       char *expression;
+       int ret;
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb, "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (req->op.search.tree == NULL) {
+               ldb_set_errstring(module->ldb, "Invalid expression parse tree");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls are not yet supported by ldb_ldap backend!\n");
+       }
+
+       req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
+
+       search_base = ldb_dn_linearize(lldb_ac, req->op.search.base);
+       if (req->op.search.base == NULL) {
+               search_base = talloc_strdup(lldb_ac, "");
+       }
+       if (search_base == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       expression = ldb_filter_from_tree(lldb_ac, req->op.search.tree);
+       if (expression == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       switch (req->op.search.scope) {
+       case LDB_SCOPE_BASE:
+               ldap_scope = LDAP_SCOPE_BASE;
+               break;
+       case LDB_SCOPE_ONELEVEL:
+               ldap_scope = LDAP_SCOPE_ONELEVEL;
+               break;
+       default:
+               ldap_scope = LDAP_SCOPE_SUBTREE;
+               break;
+       }
+
+       tv.tv_sec = req->timeout;
+       tv.tv_usec = 0;
+
+       ret = ldap_search_ext(lldb->ldap, search_base, ldap_scope, 
+                           expression, 
+                           discard_const_p(char *, req->op.search.attrs), 
+                           0,
+                           NULL,
+                           NULL,
+                           &tv,
+                           LDAP_NO_LIMIT,
+                           &lldb_ac->msgid);
+
+       if (ret != LDAP_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldap_err2string(ret));
+       }
+
+       return lldb_ldap_to_ldb(ret);
+}
+
+/*
+  add a record
+*/
+static int lldb_add(struct ldb_module *module, struct ldb_request *req)
+{
+       struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
+       struct lldb_context *lldb_ac;
+       LDAPMod **mods;
+       char *dn;
+       int ret;
+
+       /* ltdb specials should not reach this point */
+       if (ldb_dn_is_special(req->op.add.message->dn)) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
+
+       mods = lldb_msg_to_mods(lldb_ac, req->op.add.message, 0);
+       if (mods == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       dn = ldb_dn_linearize(lldb_ac, req->op.add.message->dn);
+       if (dn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldap_add_ext(lldb->ldap, dn, mods,
+                          NULL,
+                          NULL,
+                          &lldb_ac->msgid);
+
+       if (ret != LDAP_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldap_err2string(ret));
+       }
+
+       return lldb_ldap_to_ldb(ret);
+}
+
+/*
+  modify a record
+*/
+static int lldb_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
+       struct lldb_context *lldb_ac;
+       LDAPMod **mods;
+       char *dn;
+       int ret;
+
+       /* ltdb specials should not reach this point */
+       if (ldb_dn_is_special(req->op.mod.message->dn)) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
+
+       mods = lldb_msg_to_mods(lldb_ac, req->op.mod.message, 1);
+       if (mods == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       dn = ldb_dn_linearize(lldb_ac, req->op.mod.message->dn);
+       if (dn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldap_modify_ext(lldb->ldap, dn, mods,
+                             NULL,
+                             NULL,
+                             &lldb_ac->msgid);
+
+       if (ret != LDAP_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldap_err2string(ret));
+       }
+
+       return lldb_ldap_to_ldb(ret);
+}
+
+/*
+  delete a record
+*/
+static int lldb_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
+       struct lldb_context *lldb_ac;
+       char *dnstr;
+       int ret;
+       
+       /* ltdb specials should not reach this point */
+       if (ldb_dn_is_special(req->op.del.dn)) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
+
+       dnstr = ldb_dn_linearize(lldb_ac, req->op.del.dn);
+
+       ret = ldap_delete_ext(lldb->ldap, dnstr,
+                             NULL,
+                             NULL,
+                             &lldb_ac->msgid);
+
+       if (ret != LDAP_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldap_err2string(ret));
+       }
+
+       return lldb_ldap_to_ldb(ret);
+}
+
+/*
+  rename a record
+*/
+static int lldb_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
+       struct lldb_context *lldb_ac;
+       char *old_dn;
+               char *newrdn;
+       char *parentdn;
+       int ret;
+       
+       /* ltdb specials should not reach this point */
+       if (ldb_dn_is_special(req->op.rename.olddn) || ldb_dn_is_special(req->op.rename.newdn)) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
+
+       old_dn = ldb_dn_linearize(lldb_ac, req->op.rename.olddn);
+       if (old_dn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       newrdn = talloc_asprintf(lldb_ac, "%s=%s",
+                                req->op.rename.newdn->components[0].name,
+                                ldb_dn_escape_value(lldb, req->op.rename.newdn->components[0].value));
+       if (!newrdn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       parentdn = ldb_dn_linearize(lldb_ac, ldb_dn_get_parent(lldb_ac, req->op.rename.newdn));
+       if (!parentdn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldap_rename(lldb->ldap, old_dn, newrdn, parentdn,
+                         1, NULL, NULL,
+                         &lldb_ac->msgid);
+
+       if (ret != LDAP_SUCCESS) {
+               ldb_set_errstring(module->ldb, ldap_err2string(ret));
+       }
+
+       return lldb_ldap_to_ldb(ret);
+}
+
+static int lldb_parse_result(struct ldb_handle *handle, LDAPMessage *result)
+{
+       struct lldb_context *ac = talloc_get_type(handle->private_data, struct lldb_context);
+       struct lldb_private *lldb = talloc_get_type(ac->module->private_data, struct lldb_private);
+       struct ldb_reply *ares = NULL;
+       LDAPMessage *msg;
+       int type;
+       char *matcheddnp = NULL;
+       char *errmsgp = NULL;
+       char **referralsp = NULL;
+       LDAPControl **serverctrlsp = NULL;
+       int ret = LDB_SUCCESS;
+
+       type = ldap_msgtype(result);
+
+       switch (type) {
+
+       case LDAP_RES_SEARCH_ENTRY:
+               msg = ldap_first_entry(lldb->ldap, result);
+               if (msg != NULL) {
+                       BerElement *berptr = NULL;
+                       char *attr, *dn;
+
+                       ares = talloc_zero(ac, struct ldb_reply);
+                       if (!ares) {
+                               ret = LDB_ERR_OPERATIONS_ERROR;
+                               goto error;
+                       }
+
+                       ares->message = ldb_msg_new(ares);
+                       if (!ares->message) {
+                               ret = LDB_ERR_OPERATIONS_ERROR;
+                               goto error;
+                       }
+
+                       dn = ldap_get_dn(lldb->ldap, msg);
+                       if (!dn) {
+                               ret = LDB_ERR_OPERATIONS_ERROR;
+                               goto error;
+                       }
+                       ares->message->dn = ldb_dn_explode_or_special(ares->message, dn);
+                       if (ares->message->dn == NULL) {
+                               ret = LDB_ERR_OPERATIONS_ERROR;
+                               goto error;
+                       }
+                       ldap_memfree(dn);
+
+                       ares->message->num_elements = 0;
+                       ares->message->elements = NULL;
+                       ares->message->private_data = NULL;
+
+                       /* loop over all attributes */
+                       for (attr=ldap_first_attribute(lldb->ldap, msg, &berptr);
+                            attr;
+                            attr=ldap_next_attribute(lldb->ldap, msg, berptr)) {
+                               struct berval **bval;
+                               bval = ldap_get_values_len(lldb->ldap, msg, attr);
+
+                               if (bval) {
+                                       lldb_add_msg_attr(ac->module->ldb, ares->message, attr, bval);
+                                       ldap_value_free_len(bval);
+                               }                                         
+                       }
+                       if (berptr) ber_free(berptr, 0);
+
+
+                       ares->type = LDB_REPLY_ENTRY;
+                       ret = ac->callback(ac->module->ldb, ac->context, ares);
+               } else {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       handle->state = LDB_ASYNC_DONE;
+               }
+               break;
+
+       case LDAP_RES_SEARCH_REFERENCE:
+               if (ldap_parse_result(lldb->ldap, result, &handle->status,
+                                       &matcheddnp, &errmsgp,
+                                       &referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       goto error;
+               }
+               if (referralsp == NULL) {
+                       handle->status = LDB_ERR_PROTOCOL_ERROR;
+                       goto error;
+               }
+
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       goto error;
+               }
+
+               ares->referral = talloc_strdup(ares, *referralsp);
+               ares->type = LDB_REPLY_REFERRAL;
+               ret = ac->callback(ac->module->ldb, ac->context, ares);
+
+               break;
+
+       case LDAP_RES_SEARCH_RESULT:
+               if (ldap_parse_result(lldb->ldap, result, &handle->status,
+                                       &matcheddnp, &errmsgp,
+                                       &referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       goto error;
+               }
+
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       ret = LDB_ERR_OPERATIONS_ERROR;
+                       goto error;
+               }
+
+               if (serverctrlsp != NULL) {
+                       /* FIXME: transform the LDAPControl list into an ldb_control one */
+                       ares->controls = NULL;
+               }
+               
+               ares->type = LDB_REPLY_DONE;
+               handle->state = LDB_ASYNC_DONE;
+               ret = ac->callback(ac->module->ldb, ac->context, ares);
+
+               break;
+
+       case LDAP_RES_MODIFY:
+       case LDAP_RES_ADD:
+       case LDAP_RES_DELETE:
+       case LDAP_RES_MODDN:
+               if (ldap_parse_result(lldb->ldap, result, &handle->status,
+                                       &matcheddnp, &errmsgp,
+                                       &referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       goto error;
+               }
+               if (ac->callback && handle->status == LDB_SUCCESS) {
+                       ares = NULL; /* FIXME: build a corresponding ares to pass on */
+                       ret = ac->callback(ac->module->ldb, ac->context, ares);
+               }
+               handle->state = LDB_ASYNC_DONE;
+               break;
+
+       default:
+               ret = LDB_ERR_PROTOCOL_ERROR;
+               goto error;
+       }
+
+       if (matcheddnp) ldap_memfree(matcheddnp);
+       if (errmsgp) {
+               ldb_set_errstring(ac->module->ldb, errmsgp);
+               ldap_memfree(errmsgp);
+       }
+       if (referralsp) ldap_value_free(referralsp);
+       if (serverctrlsp) ldap_controls_free(serverctrlsp);
+
+       ldap_msgfree(result);
+       return ret;
+
+error:
+       handle->state = LDB_ASYNC_DONE;
+       ldap_msgfree(result);
+       return ret;
+}
+
+static int lldb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       struct lldb_context *ac = talloc_get_type(handle->private_data, struct lldb_context);
+       struct lldb_private *lldb = talloc_get_type(handle->module->private_data, struct lldb_private);
+       struct timeval timeout;
+       LDAPMessage *result;
+       int ret, lret;
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       if (!ac || !ac->msgid) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       switch(type) {
+       case LDB_WAIT_NONE:
+
+               if ((ac->timeout != -1) &&
+                   ((ac->starttime + ac->timeout) > time(NULL))) {
+                       return LDB_ERR_TIME_LIMIT_EXCEEDED;
+               }
+
+               timeout.tv_sec = 0;
+               timeout.tv_usec = 0;
+
+               lret = ldap_result(lldb->ldap, ac->msgid, 0, &timeout, &result);
+               if (lret == -1) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               if (lret == 0) {
+                       ret = LDB_SUCCESS;
+                       goto done;
+               }
+
+               return lldb_parse_result(handle, result);
+
+       case LDB_WAIT_ALL:
+               timeout.tv_usec = 0;
+               ret = LDB_ERR_OPERATIONS_ERROR;
+
+               while (handle->status == LDB_SUCCESS && handle->state != LDB_ASYNC_DONE) {
+
+                       if (ac->timeout == -1) {
+                               lret = ldap_result(lldb->ldap, ac->msgid, 0, NULL, &result);
+                       } else {
+                               timeout.tv_sec = ac->timeout - (time(NULL) - ac->starttime);
+                               if (timeout.tv_sec <= 0)
+                                       return LDB_ERR_TIME_LIMIT_EXCEEDED;
+                               lret = ldap_result(lldb->ldap, ac->msgid, 0, &timeout, &result);
+                       }
+                       if (lret == -1) {
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       if (lret == 0) {
+                               return LDB_ERR_TIME_LIMIT_EXCEEDED;
+                       }
+
+                       ret = lldb_parse_result(handle, result);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
+
+               break;
+               
+       default:
+               handle->state = LDB_ASYNC_DONE;
+               ret = LDB_ERR_OPERATIONS_ERROR;
+       }
+
+done:
+       return ret;
+}
+
+static int lldb_start_trans(struct ldb_module *module)
+{
+       /* TODO implement a local transaction mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int lldb_end_trans(struct ldb_module *module)
+{
+       /* TODO implement a local transaction mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int lldb_del_trans(struct ldb_module *module)
+{
+       /* TODO implement a local transaction mechanism here */
+
+       return LDB_SUCCESS;
+}
+
+static int lldb_request(struct ldb_module *module, struct ldb_request *req)
+{
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static const struct ldb_module_ops lldb_ops = {
+       .name              = "ldap",
+       .search            = lldb_search,
+       .add               = lldb_add,
+       .modify            = lldb_modify,
+       .del               = lldb_delete,
+       .rename            = lldb_rename,
+       .request           = lldb_request,
+       .start_transaction = lldb_start_trans,
+       .end_transaction   = lldb_end_trans,
+       .del_transaction   = lldb_del_trans,
+       .wait              = lldb_wait
+};
+
+
+static int lldb_destructor(struct lldb_private *lldb)
+{
+       ldap_unbind(lldb->ldap);
+       return 0;
+}
+
+/*
+  connect to the database
+*/
+static int lldb_connect(struct ldb_context *ldb,
+                       const char *url, 
+                       unsigned int flags, 
+                       const char *options[],
+                       struct ldb_module **module)
+{
+       struct lldb_private *lldb = NULL;
+       int version = 3;
+       int ret;
+
+       lldb = talloc(ldb, struct lldb_private);
+       if (!lldb) {
+               ldb_oom(ldb);
+               goto failed;
+       }
+
+       lldb->ldap = NULL;
+
+       ret = ldap_initialize(&lldb->ldap, url);
+       if (ret != LDAP_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_initialize failed for URL '%s' - %s\n",
+                         url, ldap_err2string(ret));
+               goto failed;
+       }
+
+       talloc_set_destructor(lldb, lldb_destructor);
+
+       ret = ldap_set_option(lldb->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+       if (ret != LDAP_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_set_option failed - %s\n",
+                         ldap_err2string(ret));
+               goto failed;
+       }
+
+       *module = talloc(ldb, struct ldb_module);
+       if (!module) {
+               ldb_oom(ldb);
+               talloc_free(lldb);
+               return -1;
+       }
+       (*module)->ldb = ldb;
+       (*module)->prev = (*module)->next = NULL;
+       (*module)->private_data = lldb;
+       (*module)->ops = &lldb_ops;
+
+       return 0;
+
+failed:
+       talloc_free(lldb);
+       return -1;
+}
+
+int ldb_ldap_init(void)
+{
+       return ldb_register_backend("ldap", lldb_connect) +
+                  ldb_register_backend("ldapi", lldb_connect) + 
+                  ldb_register_backend("ldaps", lldb_connect);
+}
diff --git a/source3/lib/ldb/ldb_sqlite3/README b/source3/lib/ldb/ldb_sqlite3/README
new file mode 100644 (file)
index 0000000..6cda0a7
--- /dev/null
@@ -0,0 +1,7 @@
+trees.ps contains an explanation of the Genealogical Representation of Trees
+in Databases which is being used in ldb_sqlite3.  Note that we use fgID
+representation with 4 bytes per level, so we can represent 6.5E+08 subclasses
+of any object class.  This should be adequate for our purposes. :-)
+
+The following document is the primary basis for the schema currently being
+used here:  http://www.research.ibm.com/journal/sj/392/shi.html
diff --git a/source3/lib/ldb/ldb_sqlite3/base160.c b/source3/lib/ldb/ldb_sqlite3/base160.c
new file mode 100644 (file)
index 0000000..4286979
--- /dev/null
@@ -0,0 +1,155 @@
+/* 
+   base160 code used by ldb_sqlite3
+
+   Copyright (C) 2004 Derrell Lipman
+
+   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
+*/
+
+
+/*
+ * ldb_sqlite3_base160()
+ *
+ * Convert an integer value to a string containing the base 160 representation
+ * of the integer.  We always convert to a string representation that is 4
+ * bytes in length, and we always null terminate.
+ *
+ * Parameters:
+ *   val --
+ *     The value to be converted
+ *
+ *   result --
+ *     Buffer in which the result is to be placed
+ *
+ * Returns:
+ *   nothing
+ */
+static unsigned char        base160tab[161] =
+{
+    48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , /* 0-9 */
+    58 , 59 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , /* : ; A-H */
+    73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 , 81 , 82 , /* I-R */
+    83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 97 , 98 , /* S-Z , a-b */
+    99 , 100, 101, 102, 103, 104, 105, 106, 107, 108, /* c-l */
+    109, 110, 111, 112, 113, 114, 115, 116, 117, 118, /* m-v */
+    119, 120, 121, 122, 160, 161, 162, 163, 164, 165, /* w-z, latin1 */
+    166, 167, 168, 169, 170, 171, 172, 173, 174, 175, /* latin1 */
+    176, 177, 178, 179, 180, 181, 182, 183, 184, 185, /* latin1 */
+    186, 187, 188, 189, 190, 191, 192, 193, 194, 195, /* latin1 */
+    196, 197, 198, 199, 200, 201, 202, 203, 204, 205, /* latin1 */
+    206, 207, 208, 209, 210, 211, 212, 213, 214, 215, /* latin1 */
+    216, 217, 218, 219, 220, 221, 222, 223, 224, 225, /* latin1 */
+    226, 227, 228, 229, 230, 231, 232, 233, 234, 235, /* latin1 */
+    236, 237, 238, 239, 240, 241, 242, 243, 244, 245, /* latin1 */
+    246, 247, 248, 249, 250, 251, 252, 253, 254, 255, /* latin1 */
+    '\0'
+};
+
+
+/*
+ * lsqlite3_base160()
+ *
+ * Convert an unsigned long integer into a base160 representation of the
+ * number.
+ *
+ * Parameters:
+ *   val --
+ *     value to be converted
+ *
+ *   result --
+ *     character array, 5 bytes long, into which the base160 representation
+ *     will be placed.  The result will be a four-digit representation of the
+ *     number (with leading zeros prepended as necessary), and null
+ *     terminated.
+ *
+ * Returns:
+ *   Nothing
+ */
+void
+lsqlite3_base160(unsigned long val,
+                 unsigned char result[5])
+{
+    int             i;
+
+    for (i = 3; i >= 0; i--) {
+        
+        result[i] = base160tab[val % 160];
+        val /= 160;
+    }
+
+    result[4] = '\0';
+}
+
+
+/*
+ * lsqlite3_base160Next()
+ *
+ * Retrieve the next-greater number in the base160 sequence for the terminal
+ * tree node (the last four digits).  Only one tree level (four digits) are
+ * operated on.
+ *
+ * Parameters:
+ *   base160 -- a character array containing either an empty string (in which
+ *              case no operation is performed), or a string of base160 digits
+ *              with a length of a multiple of four digits.
+ *
+ *              Upon return, the trailing four digits (one tree level) will
+ *              have been incremented by 1.
+ *
+ * Returns:
+ *   base160 -- the modified array
+ */
+char *
+lsqlite3_base160Next(char base160[])
+{
+    int             i;
+    int             len;
+    unsigned char * pTab;
+    char *          pBase160 = base160;
+
+    /*
+     * We need a minimum of four digits, and we will always get a multiple of
+     * four digits.
+     */
+    if (len = strlen(pBase160)) >= 4)
+    {
+        pBase160 += strlen(pBase160) - 1;
+
+        /* We only carry through four digits: one level in the tree */
+        for (i = 0; i < 4; i++) {
+
+            /* What base160 value does this digit have? */
+            pTab = strchr(base160tab, *pBase160);
+
+            /* Is there a carry? */
+            if (pTab < base160tab + sizeof(base160tab) - 1) {
+
+                /* Nope.  Just increment this value and we're done. */
+                *pBase160 = *++pTab;
+                break;
+            } else {
+
+                /*
+                 * There's a carry.  This value gets base160tab[0], we
+                 * decrement the buffer pointer to get the next higher-order
+                 * digit, and continue in the loop.
+                 */
+                *pBase160-- = base160tab[0];
+            }
+        }
+    }
+
+    return base160;
+}
diff --git a/source3/lib/ldb/ldb_sqlite3/ldb_sqlite3.c b/source3/lib/ldb/ldb_sqlite3/ldb_sqlite3.c
new file mode 100644 (file)
index 0000000..9125622
--- /dev/null
@@ -0,0 +1,2145 @@
+/* 
+   ldb database library
+   
+   Copyright (C) Derrell Lipman  2005
+   Copyright (C) Simo Sorce 2005
+   
+   ** NOTE! The following LGPL license applies to the ldb
+   ** library. This does NOT imply that all of Samba is released
+   ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb sqlite3 backend
+ *
+ *  Description: core files for SQLITE3 backend
+ *
+ *  Author: Derrell Lipman (based on Andrew Tridgell's LDAP backend)
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include <sqlite3.h>
+
+struct lsqlite3_private {
+       int trans_count;
+       char **options;
+        sqlite3 *sqlite;
+};
+
+struct lsql_context {
+       struct ldb_module *module;
+
+       /* search stuff */
+       long long current_eid;
+       const char * const * attrs;
+       struct ldb_reply *ares;
+
+       /* async stuff */
+       void *context;
+       int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
+};
+
+static struct ldb_handle *init_handle(struct lsqlite3_private *lsqlite3, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct lsql_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(lsqlite3, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc(h, struct lsql_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->context = context;
+       ac->callback = callback;
+
+       return h;
+}
+
+/*
+ * Macros used throughout
+ */
+
+#ifndef FALSE
+# define FALSE  (0)
+# define TRUE   (! FALSE)
+#endif
+
+#define RESULT_ATTR_TABLE       "temp_result_attrs"
+
+//#define TEMPTAB                 /* for testing, create non-temporary table */
+#define TEMPTAB                 "TEMPORARY"
+
+/*
+ * Static variables
+ */
+sqlite3_stmt *  stmtGetEID = NULL;
+
+static char *lsqlite3_tprintf(TALLOC_CTX *mem_ctx, const char *fmt, ...)
+{
+       char *str, *ret;
+       va_list ap;
+
+       va_start(ap, fmt);
+        str = sqlite3_vmprintf(fmt, ap);
+       va_end(ap);
+
+       if (str == NULL) return NULL;
+
+       ret = talloc_strdup(mem_ctx, str);
+       if (ret == NULL) {
+               sqlite3_free(str);
+               return NULL;
+       }
+
+       sqlite3_free(str);
+       return ret;
+}
+
+static char base160tab[161] = {
+        48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 , /* 0-9 */
+        58 ,59 ,65 ,66 ,67 ,68 ,69 ,70 ,71 ,72 , /* : ; A-H */
+        73 ,74 ,75 ,76 ,77 ,78 ,79 ,80 ,81 ,82 , /* I-R */
+        83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,97 ,98 , /* S-Z , a-b */
+        99 ,100,101,102,103,104,105,106,107,108, /* c-l */
+        109,110,111,112,113,114,115,116,117,118, /* m-v */
+        119,120,121,122,160,161,162,163,164,165, /* w-z, latin1 */
+        166,167,168,169,170,171,172,173,174,175, /* latin1 */
+        176,177,178,179,180,181,182,183,184,185, /* latin1 */
+        186,187,188,189,190,191,192,193,194,195, /* latin1 */
+        196,197,198,199,200,201,202,203,204,205, /* latin1 */
+        206,207,208,209,210,211,212,213,214,215, /* latin1 */
+        216,217,218,219,220,221,222,223,224,225, /* latin1 */
+        226,227,228,229,230,231,232,233,234,235, /* latin1 */
+        236,237,238,239,240,241,242,243,244,245, /* latin1 */
+        246,247,248,249,250,251,252,253,254,255, /* latin1 */
+        '\0'
+};
+
+
+/*
+ * base160()
+ *
+ * Convert an unsigned long integer into a base160 representation of the
+ * number.
+ *
+ * Parameters:
+ *   val --
+ *     value to be converted
+ *
+ *   result --
+ *     character array, 5 bytes long, into which the base160 representation
+ *     will be placed.  The result will be a four-digit representation of the
+ *     number (with leading zeros prepended as necessary), and null
+ *     terminated.
+ *
+ * Returns:
+ *   Nothing
+ */
+static void
+base160_sql(sqlite3_context * hContext,
+            int argc,
+            sqlite3_value ** argv)
+{
+    int             i;
+    long long       val;
+    char            result[5];
+
+    val = sqlite3_value_int64(argv[0]);
+
+    for (i = 3; i >= 0; i--) {
+        
+        result[i] = base160tab[val % 160];
+        val /= 160;
+    }
+
+    result[4] = '\0';
+
+    sqlite3_result_text(hContext, result, -1, SQLITE_TRANSIENT);
+}
+
+
+/*
+ * base160next_sql()
+ *
+ * This function enhances sqlite by adding a "base160_next()" function which is
+ * accessible via queries.
+ *
+ * Retrieve the next-greater number in the base160 sequence for the terminal
+ * tree node (the last four digits).  Only one tree level (four digits) is
+ * operated on.
+ *
+ * Input:
+ *   A character string: either an empty string (in which case no operation is
+ *   performed), or a string of base160 digits with a length of a multiple of
+ *   four digits.
+ *
+ * Output:
+ *   Upon return, the trailing four digits (one tree level) will have been
+ *   incremented by 1.
+ */
+static void
+base160next_sql(sqlite3_context * hContext,
+                int argc,
+                sqlite3_value ** argv)
+{
+        int                         i;
+        int                         len;
+        char *             pTab;
+        char *             pBase160 = strdup((const char *)sqlite3_value_text(argv[0]));
+        char *             pStart = pBase160;
+
+        /*
+         * We need a minimum of four digits, and we will always get a multiple
+         * of four digits.
+         */
+        if (pBase160 != NULL &&
+            (len = strlen(pBase160)) >= 4 &&
+            len % 4 == 0) {
+
+                if (pBase160 == NULL) {
+
+                        sqlite3_result_null(hContext);
+                        return;
+                }
+
+                pBase160 += strlen(pBase160) - 1;
+
+                /* We only carry through four digits: one level in the tree */
+                for (i = 0; i < 4; i++) {
+
+                        /* What base160 value does this digit have? */
+                        pTab = strchr(base160tab, *pBase160);
+
+                        /* Is there a carry? */
+                        if (pTab < base160tab + sizeof(base160tab) - 1) {
+
+                                /*
+                                 * Nope.  Just increment this value and we're
+                                 * done.
+                                 */
+                                *pBase160 = *++pTab;
+                                break;
+                        } else {
+
+                                /*
+                                 * There's a carry.  This value gets
+                                 * base160tab[0], we decrement the buffer
+                                 * pointer to get the next higher-order digit,
+                                 * and continue in the loop.
+                                 */
+                                *pBase160-- = base160tab[0];
+                        }
+                }
+
+                sqlite3_result_text(hContext,
+                                    pStart,
+                                    strlen(pStart),
+                                    free);
+        } else {
+                sqlite3_result_value(hContext, argv[0]);
+                if (pBase160 != NULL) {
+                        free(pBase160);
+                }
+        }
+}
+
+static char *parsetree_to_sql(struct ldb_module *module,
+                             void *mem_ctx,
+                             const struct ldb_parse_tree *t)
+{
+       const struct ldb_attrib_handler *h;
+       struct ldb_val value, subval;
+       char *wild_card_string;
+       char *child, *tmp;
+       char *ret = NULL;
+       char *attr;
+       int i;
+
+
+       switch(t->operation) {
+       case LDB_OP_AND:
+
+               tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
+               if (tmp == NULL) return NULL;
+
+               for (i = 1; i < t->u.list.num_elements; i++) {
+
+                       child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
+                       if (child == NULL) return NULL;
+
+                       tmp = talloc_asprintf_append(tmp, " INTERSECT %s ", child);
+                       if (tmp == NULL) return NULL;
+               }
+
+               ret = talloc_asprintf(mem_ctx, "SELECT * FROM ( %s )\n", tmp);
+
+               return ret;
+                
+       case LDB_OP_OR:
+
+               tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
+               if (tmp == NULL) return NULL;
+
+               for (i = 1; i < t->u.list.num_elements; i++) {
+
+                       child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
+                       if (child == NULL) return NULL;
+
+                       tmp = talloc_asprintf_append(tmp, " UNION %s ", child);
+                       if (tmp == NULL) return NULL;
+               }
+
+               return talloc_asprintf(mem_ctx, "SELECT * FROM ( %s ) ", tmp);
+
+       case LDB_OP_NOT:
+
+               child = parsetree_to_sql(module, mem_ctx, t->u.isnot.child);
+               if (child == NULL) return NULL;
+
+               return talloc_asprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_entry "
+                                       "WHERE eid NOT IN ( %s ) ", child);
+
+       case LDB_OP_EQUALITY:
+               /*
+                * For simple searches, we want to retrieve the list of EIDs that
+                * match the criteria.
+               */
+               attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+               if (attr == NULL) return NULL;
+               h = ldb_attrib_handler(module->ldb, attr);
+
+               /* Get a canonicalised copy of the data */
+               h->canonicalise_fn(module->ldb, mem_ctx, &(t->u.equality.value), &value);
+               if (value.data == NULL) {
+                       return NULL;
+               }
+
+               if (strcasecmp(t->u.equality.attr, "objectclass") == 0) {
+               /*
+                * For object classes, we want to search for all objectclasses
+                * that are subclasses as well.
+               */
+                       return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid  FROM ldb_attribute_values\n"
+                                       "WHERE norm_attr_name = 'OBJECTCLASS' "
+                                       "AND norm_attr_value IN\n"
+                                       "  (SELECT class_name FROM ldb_object_classes\n"
+                                       "   WHERE tree_key GLOB\n"
+                                       "     (SELECT tree_key FROM ldb_object_classes\n"
+                                       "      WHERE class_name = '%q'\n"
+                                       "     ) || '*'\n"
+                                       "  )\n", value.data);
+
+               } else if (strcasecmp(t->u.equality.attr, "dn") == 0) {
+                       /* DN query is a special ldb case */
+                       char *cdn = ldb_dn_linearize_casefold(module->ldb,
+                                                             ldb_dn_explode(module->ldb,
+                                                             (const char *)value.data));
+
+                       return lsqlite3_tprintf(mem_ctx,
+                                               "SELECT eid FROM ldb_entry "
+                                               "WHERE norm_dn = '%q'", cdn);
+
+               } else {
+                       /* A normal query. */
+                       return lsqlite3_tprintf(mem_ctx,
+                                               "SELECT eid FROM ldb_attribute_values "
+                                               "WHERE norm_attr_name = '%q' "
+                                               "AND norm_attr_value = '%q'",
+                                               attr,
+                                               value.data);
+
+               }
+
+       case LDB_OP_SUBSTRING:
+
+               wild_card_string = talloc_strdup(mem_ctx,
+                                       (t->u.substring.start_with_wildcard)?"*":"");
+               if (wild_card_string == NULL) return NULL;
+
+               for (i = 0; t->u.substring.chunks[i]; i++) {
+                       wild_card_string = talloc_asprintf_append(wild_card_string, "%s*",
+                                                       t->u.substring.chunks[i]->data);
+                       if (wild_card_string == NULL) return NULL;
+               }
+
+               if ( ! t->u.substring.end_with_wildcard ) {
+                       /* remove last wildcard */
+                       wild_card_string[strlen(wild_card_string) - 1] = '\0';
+               }
+
+               attr = ldb_attr_casefold(mem_ctx, t->u.substring.attr);
+               if (attr == NULL) return NULL;
+               h = ldb_attrib_handler(module->ldb, attr);
+
+               subval.data = (void *)wild_card_string;
+               subval.length = strlen(wild_card_string) + 1;
+
+               /* Get a canonicalised copy of the data */
+               h->canonicalise_fn(module->ldb, mem_ctx, &(subval), &value);
+               if (value.data == NULL) {
+                       return NULL;
+               }
+
+               return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_attribute_values "
+                                       "WHERE norm_attr_name = '%q' "
+                                       "AND norm_attr_value GLOB '%q'",
+                                       attr,
+                                       value.data);
+
+       case LDB_OP_GREATER:
+               attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+               if (attr == NULL) return NULL;
+               h = ldb_attrib_handler(module->ldb, attr);
+
+               /* Get a canonicalised copy of the data */
+               h->canonicalise_fn(module->ldb, mem_ctx, &(t->u.equality.value), &value);
+               if (value.data == NULL) {
+                       return NULL;
+               }
+
+               return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_attribute_values "
+                                       "WHERE norm_attr_name = '%q' "
+                                       "AND ldap_compare(norm_attr_value, '>=', '%q', '%q') ",
+                                       attr,
+                                       value.data,
+                                       attr);
+
+       case LDB_OP_LESS:
+               attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+               if (attr == NULL) return NULL;
+               h = ldb_attrib_handler(module->ldb, attr);
+
+               /* Get a canonicalised copy of the data */
+               h->canonicalise_fn(module->ldb, mem_ctx, &(t->u.equality.value), &value);
+               if (value.data == NULL) {
+                       return NULL;
+               }
+
+               return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_attribute_values "
+                                       "WHERE norm_attr_name = '%q' "
+                                       "AND ldap_compare(norm_attr_value, '<=', '%q', '%q') ",
+                                       attr,
+                                       value.data,
+                                       attr);
+
+       case LDB_OP_PRESENT:
+               if (strcasecmp(t->u.present.attr, "dn") == 0) {
+                       return talloc_strdup(mem_ctx, "SELECT eid FROM ldb_entry");
+               }
+
+               attr = ldb_attr_casefold(mem_ctx, t->u.present.attr);
+               if (attr == NULL) return NULL;
+
+               return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_attribute_values "
+                                       "WHERE norm_attr_name = '%q' ",
+                                       attr);
+
+       case LDB_OP_APPROX:
+               attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+               if (attr == NULL) return NULL;
+               h = ldb_attrib_handler(module->ldb, attr);
+
+               /* Get a canonicalised copy of the data */
+               h->canonicalise_fn(module->ldb, mem_ctx, &(t->u.equality.value), &value);
+               if (value.data == NULL) {
+                       return NULL;
+               }
+
+               return lsqlite3_tprintf(mem_ctx,
+                                       "SELECT eid FROM ldb_attribute_values "
+                                       "WHERE norm_attr_name = '%q' "
+                                       "AND ldap_compare(norm_attr_value, '~%', 'q', '%q') ",
+                                       attr,
+                                       value.data,
+                                       attr);
+               
+       case LDB_OP_EXTENDED:
+#warning  "work out how to handle bitops"
+               return NULL;
+
+       default:
+               break;
+       };
+
+       /* should never occur */
+       abort();
+       return NULL;
+}
+
+/*
+ * query_int()
+ *
+ * This function is used for the common case of queries that return a single
+ * integer value.
+ *
+ * NOTE: If more than one value is returned by the query, all but the first
+ * one will be ignored.
+ */
+static int
+query_int(const struct lsqlite3_private * lsqlite3,
+          long long * pRet,
+          const char * pSql,
+          ...)
+{
+        int             ret;
+        int             bLoop;
+        char *          p;
+        sqlite3_stmt *  pStmt;
+        va_list         args;
+        
+        /* Begin access to variable argument list */
+        va_start(args, pSql);
+        
+        /* Format the query */
+        if ((p = sqlite3_vmprintf(pSql, args)) == NULL) {
+                return SQLITE_NOMEM;
+        }
+        
+        /*
+         * Prepare and execute the SQL statement.  Loop allows retrying on
+         * certain errors, e.g. SQLITE_SCHEMA occurs if the schema changes,
+         * requiring retrying the operation.
+         */
+        for (bLoop = TRUE; bLoop; ) {
+                
+                /* Compile the SQL statement into sqlite virtual machine */
+                if ((ret = sqlite3_prepare(lsqlite3->sqlite,
+                                           p,
+                                           -1,
+                                           &pStmt,
+                                           NULL)) == SQLITE_SCHEMA) {
+                        if (stmtGetEID != NULL) {
+                                sqlite3_finalize(stmtGetEID);
+                                stmtGetEID = NULL;
+                        }
+                        continue;
+                } else if (ret != SQLITE_OK) {
+                        break;
+                }
+                
+                /* One row expected */
+                if ((ret = sqlite3_step(pStmt)) == SQLITE_SCHEMA) {
+                        if (stmtGetEID != NULL) {
+                                sqlite3_finalize(stmtGetEID);
+                                stmtGetEID = NULL;
+                        }
+                        (void) sqlite3_finalize(pStmt);
+                        continue;
+                } else if (ret != SQLITE_ROW) {
+                        (void) sqlite3_finalize(pStmt);
+                        break;
+                }
+                
+                /* Get the value to be returned */
+                *pRet = sqlite3_column_int64(pStmt, 0);
+                
+                /* Free the virtual machine */
+                if ((ret = sqlite3_finalize(pStmt)) == SQLITE_SCHEMA) {
+                        if (stmtGetEID != NULL) {
+                                sqlite3_finalize(stmtGetEID);
+                                stmtGetEID = NULL;
+                        }
+                        continue;
+                } else if (ret != SQLITE_OK) {
+                        (void) sqlite3_finalize(pStmt);
+                        break;
+                }
+                
+                /*
+                 * Normal condition is only one time through loop.  Loop is
+                 * rerun in error conditions, via "continue", above.
+                 */
+                bLoop = FALSE;
+        }
+        
+        /* All done with variable argument list */
+        va_end(args);
+        
+
+        /* Free the memory we allocated for our query string */
+        sqlite3_free(p);
+        
+        return ret;
+}
+
+/*
+ * This is a bad hack to support ldap style comparisons whithin sqlite.
+ * val is the attribute in the row currently under test
+ * func is the desired test "<=" ">=" "~" ":"
+ * cmp is the value to compare against (eg: "test")
+ * attr is the attribute name the value of which we want to test
+ */
+
+static void lsqlite3_compare(sqlite3_context *ctx, int argc,
+                                       sqlite3_value **argv)
+{
+       struct ldb_context *ldb = (struct ldb_context *)sqlite3_user_data(ctx);
+       const char *val = (const char *)sqlite3_value_text(argv[0]);
+       const char *func = (const char *)sqlite3_value_text(argv[1]);
+       const char *cmp = (const char *)sqlite3_value_text(argv[2]);
+       const char *attr = (const char *)sqlite3_value_text(argv[3]);
+       const struct ldb_attrib_handler *h;
+       struct ldb_val valX;
+       struct ldb_val valY;
+       int ret;
+
+       switch (func[0]) {
+       /* greater */
+       case '>': /* >= */
+               h = ldb_attrib_handler(ldb, attr);
+               valX.data = (void *)cmp;
+               valX.length = strlen(cmp);
+               valY.data = (void *)val;
+               valY.length = strlen(val);
+               ret = h->comparison_fn(ldb, ldb, &valY, &valX);
+               if (ret >= 0)
+                       sqlite3_result_int(ctx, 1);
+               else
+                       sqlite3_result_int(ctx, 0);
+               return;
+
+       /* lesser */
+       case '<': /* <= */
+               h = ldb_attrib_handler(ldb, attr);
+               valX.data = (void *)cmp;
+               valX.length = strlen(cmp);
+               valY.data = (void *)val;
+               valY.length = strlen(val);
+               ret = h->comparison_fn(ldb, ldb, &valY, &valX);
+               if (ret <= 0)
+                       sqlite3_result_int(ctx, 1);
+               else
+                       sqlite3_result_int(ctx, 0);
+               return;
+
+       /* approx */
+       case '~':
+               /* TODO */
+               sqlite3_result_int(ctx, 0);
+               return;
+
+       /* bitops */
+       case ':':
+               /* TODO */
+               sqlite3_result_int(ctx, 0);
+               return;
+
+       default:
+               break;
+       }
+
+       sqlite3_result_error(ctx, "Value must start with a special operation char (<>~:)!", -1);
+       return;
+}
+
+
+/* rename a record */
+static int lsqlite3_safe_rollback(sqlite3 *sqlite)
+{
+       char *errmsg;
+       int ret;
+
+       /* execute */
+       ret = sqlite3_exec(sqlite, "ROLLBACK;", NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       printf("lsqlite3_safe_rollback: Error: %s\n", errmsg);
+                       free(errmsg);
+               }
+               return -1;
+       }
+
+        return 0;
+}
+
+/* return an eid as result */
+static int lsqlite3_eid_callback(void *result, int col_num, char **cols, char **names)
+{
+       long long *eid = (long long *)result;
+
+       if (col_num != 1) return SQLITE_ABORT;
+       if (strcasecmp(names[0], "eid") != 0) return SQLITE_ABORT;
+
+       *eid = atoll(cols[0]);
+       return SQLITE_OK;
+}
+
+/*
+ * add a single set of ldap message values to a ldb_message
+ */
+static int lsqlite3_search_callback(void *result, int col_num, char **cols, char **names)
+{
+       struct ldb_handle *handle = talloc_get_type(result, struct ldb_handle);
+       struct lsql_context *ac = talloc_get_type(handle->private_data, struct lsql_context);
+       struct ldb_message *msg;
+       long long eid;
+       int i;
+
+       /* eid, dn, attr_name, attr_value */
+       if (col_num != 4)
+               return SQLITE_ABORT;
+
+       eid = atoll(cols[0]);
+
+       if (eid != ac->current_eid) { /* here begin a new entry */
+
+               /* call the async callback for the last entry
+                * except the first time */
+               if (ac->current_eid != 0) {
+                       ac->ares->message = ldb_msg_canonicalize(ac->module->ldb, ac->ares->message);
+                       if (ac->ares->message == NULL)
+                               return SQLITE_ABORT;
+                       
+                       handle->status = ac->callback(ac->module->ldb, ac->context, ac->ares);
+                       if (handle->status != LDB_SUCCESS)
+                               return SQLITE_ABORT;
+               }
+
+               /* start over */
+               ac->ares = talloc_zero(ac, struct ldb_reply);
+               if (!ac->ares)
+                       return SQLITE_ABORT;
+
+               ac->ares->message = ldb_msg_new(ac->ares);
+               if (!ac->ares->message)
+                       return SQLITE_ABORT;
+
+               ac->ares->type = LDB_REPLY_ENTRY;
+               ac->current_eid = eid;
+       }
+
+       msg = ac->ares->message;
+
+       if (msg->dn == NULL) {
+               msg->dn = ldb_dn_explode(msg, cols[1]);
+               if (msg->dn == NULL)
+                       return SQLITE_ABORT;
+       }
+
+       if (ac->attrs) {
+               int found = 0;
+               for (i = 0; ac->attrs[i]; i++) {
+                       if (strcasecmp(cols[2], ac->attrs[i]) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found) return SQLITE_OK;
+       }
+
+       if (ldb_msg_add_string(msg, cols[2], cols[3]) != 0) {
+               return SQLITE_ABORT;
+       }
+
+       return SQLITE_OK;
+}
+
+
+/*
+ * lsqlite3_get_eid()
+ * lsqlite3_get_eid_ndn()
+ *
+ * These functions are used for the very common case of retrieving an EID value
+ * given a (normalized) DN.
+ */
+
+static long long lsqlite3_get_eid_ndn(sqlite3 *sqlite, void *mem_ctx, const char *norm_dn)
+{
+       char *errmsg;
+       char *query;
+       long long eid = -1;
+       long long ret;
+
+       /* get object eid */
+       query = lsqlite3_tprintf(mem_ctx, "SELECT eid "
+                                         "FROM ldb_entry "
+                                         "WHERE norm_dn = '%q';", norm_dn);
+       if (query == NULL) return -1;
+
+       ret = sqlite3_exec(sqlite, query, lsqlite3_eid_callback, &eid, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       printf("lsqlite3_get_eid: Fatal Error: %s\n", errmsg);
+                       free(errmsg);
+               }
+               return -1;
+       }
+
+       return eid;
+}
+
+static long long lsqlite3_get_eid(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       TALLOC_CTX *local_ctx;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+       long long eid = -1;
+       char *cdn;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(dn)) {
+               return -1;
+       }
+
+       /* create a local ctx */
+       local_ctx = talloc_named(lsqlite3, 0, "lsqlite3_get_eid local context");
+       if (local_ctx == NULL) {
+               return -1;
+       }
+
+       cdn = ldb_dn_linearize(local_ctx, ldb_dn_casefold(module->ldb, dn));
+       if (!cdn) goto done;
+
+       eid = lsqlite3_get_eid_ndn(lsqlite3->sqlite, local_ctx, cdn);
+
+done:
+       talloc_free(local_ctx);
+       return eid;
+}
+
+/*
+ * Interface functions referenced by lsqlite3_ops
+ */
+
+static int lsql_search_sync_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct ldb_result *res = NULL;
+       
+       if (!context) {
+               ldb_set_errstring(ldb, "NULL Context in callback");
+               goto error;
+       }       
+
+       res = *((struct ldb_result **)context);
+
+       if (!res || !ares) {
+               goto error;
+       }
+
+       if (ares->type == LDB_REPLY_ENTRY) {
+               res->msgs = talloc_realloc(res, res->msgs, struct ldb_message *, res->count + 2);
+               if (! res->msgs) {
+                       goto error;
+               }
+
+               res->msgs[res->count + 1] = NULL;
+
+               res->msgs[res->count] = talloc_move(res->msgs, &ares->message);
+               res->count++;
+       } else {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "unrecognized async reply in ltdb_search_sync_callback!\n");
+               goto error;
+       }
+
+       talloc_free(ares);
+       return LDB_SUCCESS;
+
+error:
+       if (ares) talloc_free(ares);
+       if (res) talloc_free(res);
+       if (context) *((struct ldb_result **)context) = NULL;
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* search for matching records, by tree */
+int lsql_search_async(struct ldb_module *module, const struct ldb_dn *base,
+                     enum ldb_scope scope, struct ldb_parse_tree *tree,
+                     const char * const *attrs,
+                     void *context,
+                     int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                     struct ldb_handle **handle)
+{
+       struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private);
+       struct lsql_context *lsql_ac;
+       char *norm_basedn;
+       char *sqlfilter;
+       char *errmsg;
+       char *query = NULL;
+        int ret;
+
+       *handle = init_handle(lsqlite3, module, context, callback);
+       if (*handle == NULL) {
+               talloc_free(*handle);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       lsql_ac = talloc_get_type((*handle)->private_data, struct lsql_context);
+
+       if (base) {
+               norm_basedn = ldb_dn_linearize(lsql_ac, ldb_dn_casefold(module->ldb, base));
+               if (norm_basedn == NULL) {
+                       ret = LDB_ERR_INVALID_DN_SYNTAX;
+                       goto failed;
+               }
+       } else norm_basedn = talloc_strdup(lsql_ac, "");
+
+       if (*norm_basedn == '\0' &&
+               (scope == LDB_SCOPE_BASE || scope == LDB_SCOPE_ONELEVEL)) {
+                       ret = LDB_ERR_UNWILLING_TO_PERFORM;
+                       goto failed;
+               }
+
+        /* Convert filter into a series of SQL conditions (constraints) */
+       sqlfilter = parsetree_to_sql(module, lsql_ac, tree);
+        
+        switch(scope) {
+        case LDB_SCOPE_DEFAULT:
+        case LDB_SCOPE_SUBTREE:
+               if (*norm_basedn != '\0') {
+                       query = lsqlite3_tprintf(lsql_ac,
+                               "SELECT entry.eid,\n"
+                               "       entry.dn,\n"
+                               "       av.attr_name,\n"
+                               "       av.attr_value\n"
+                               "  FROM ldb_entry AS entry\n"
+
+                               "  LEFT OUTER JOIN ldb_attribute_values AS av\n"
+                               "    ON av.eid = entry.eid\n"
+
+                               "  WHERE entry.eid IN\n"
+                               "    (SELECT DISTINCT ldb_entry.eid\n"
+                               "       FROM ldb_entry\n"
+                               "       WHERE (ldb_entry.norm_dn GLOB('*,%q')\n"
+                               "       OR ldb_entry.norm_dn = '%q')\n"
+                               "       AND ldb_entry.eid IN\n"
+                               "         (%s)\n"
+                               "    )\n"
+
+                               "  ORDER BY entry.eid ASC;",
+                               norm_basedn,
+                               norm_basedn,
+                               sqlfilter);
+               } else {
+                       query = lsqlite3_tprintf(lsql_ac,
+                               "SELECT entry.eid,\n"
+                               "       entry.dn,\n"
+                               "       av.attr_name,\n"
+                               "       av.attr_value\n"
+                               "  FROM ldb_entry AS entry\n"
+
+                               "  LEFT OUTER JOIN ldb_attribute_values AS av\n"
+                               "    ON av.eid = entry.eid\n"
+
+                               "  WHERE entry.eid IN\n"
+                               "    (SELECT DISTINCT ldb_entry.eid\n"
+                               "       FROM ldb_entry\n"
+                               "       WHERE ldb_entry.eid IN\n"
+                               "         (%s)\n"
+                               "    )\n"
+
+                               "  ORDER BY entry.eid ASC;",
+                               sqlfilter);
+               }
+
+               break;
+                
+        case LDB_SCOPE_BASE:
+                query = lsqlite3_tprintf(lsql_ac,
+                        "SELECT entry.eid,\n"
+                        "       entry.dn,\n"
+                        "       av.attr_name,\n"
+                        "       av.attr_value\n"
+                        "  FROM ldb_entry AS entry\n"
+
+                        "  LEFT OUTER JOIN ldb_attribute_values AS av\n"
+                        "    ON av.eid = entry.eid\n"
+
+                        "  WHERE entry.eid IN\n"
+                        "    (SELECT DISTINCT ldb_entry.eid\n"
+                        "       FROM ldb_entry\n"
+                        "       WHERE ldb_entry.norm_dn = '%q'\n"
+                        "         AND ldb_entry.eid IN\n"
+                       "           (%s)\n"
+                        "    )\n"
+
+                        "  ORDER BY entry.eid ASC;",
+                       norm_basedn,
+                        sqlfilter);
+                break;
+                
+        case LDB_SCOPE_ONELEVEL:
+                query = lsqlite3_tprintf(lsql_ac,
+                        "SELECT entry.eid,\n"
+                        "       entry.dn,\n"
+                        "       av.attr_name,\n"
+                        "       av.attr_value\n"
+                        "  FROM ldb_entry AS entry\n"
+
+                        "  LEFT OUTER JOIN ldb_attribute_values AS av\n"
+                        "    ON av.eid = entry.eid\n"
+
+                        "  WHERE entry.eid IN\n"
+                        "    (SELECT DISTINCT ldb_entry.eid\n"
+                        "       FROM ldb_entry\n"
+                       "       WHERE norm_dn GLOB('*,%q')\n"
+                       "         AND NOT norm_dn GLOB('*,*,%q')\n"
+                        "         AND ldb_entry.eid IN\n(%s)\n"
+                        "    )\n"
+
+                        "  ORDER BY entry.eid ASC;",
+                        norm_basedn,
+                        norm_basedn,
+                        sqlfilter);
+                break;
+        }
+
+        if (query == NULL) {
+                goto failed;
+        }
+
+       /* * /
+       printf ("%s\n", query);
+       / * */
+
+       lsql_ac->current_eid = 0;
+       lsql_ac->attrs = attrs;
+       lsql_ac->ares = NULL;
+
+       (*handle)->state = LDB_ASYNC_PENDING;
+
+       ret = sqlite3_exec(lsqlite3->sqlite, query, lsqlite3_search_callback, *handle, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       ldb_set_errstring(module->ldb, errmsg);
+                       free(errmsg);
+               }
+               goto failed;
+       }
+
+       /* complete the last message if any */
+       if (lsql_ac->ares) {
+               lsql_ac->ares->message = ldb_msg_canonicalize(module->ldb, lsql_ac->ares->message);
+               if (lsql_ac->ares->message == NULL)
+                       goto failed;
+                       
+               (*handle)->status = lsql_ac->callback(module->ldb, lsql_ac->context, lsql_ac->ares);
+               if ((*handle)->status != LDB_SUCCESS)
+                       goto failed;
+       }
+
+       (*handle)->state = LDB_ASYNC_DONE;
+
+       return LDB_SUCCESS;
+
+failed:
+        talloc_free(*handle);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int lsql_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_result ** res)
+{
+       struct ldb_handle *handle;
+       int ret;
+
+       *res = talloc_zero(module, struct ldb_result);
+       if (! *res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = lsql_search_async(module, base, scope, tree, attrs,
+                               res, &lsql_search_sync_callback,
+                               &handle);
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(handle, LDB_WAIT_ALL);
+               talloc_free(handle);
+       }
+
+       if (ret != LDB_SUCCESS) {
+               talloc_free(*res);
+       }
+
+       return ret;
+}
+
+/* add a record */
+static int lsql_add_async(struct ldb_module *module, struct ldb_message *msg,
+                         void *context,
+                         int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                         struct ldb_handle **handle)
+{
+       struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private);
+       struct lsql_context *lsql_ac;
+        long long eid;
+       char *dn, *ndn;
+       char *errmsg;
+       char *query;
+       int i;
+       int ret = LDB_ERR_OPERATIONS_ERROR;
+
+       *handle = init_handle(lsqlite3, module, context, callback);
+       if (*handle == NULL) {
+               goto failed;
+       }
+       lsql_ac = talloc_get_type((*handle)->private_data, struct lsql_context);
+       (*handle)->state = LDB_ASYNC_DONE;
+       (*handle)->status = LDB_SUCCESS;
+
+        /* See if this is an ltdb special */
+       if (ldb_dn_is_special(msg->dn)) {
+               struct ldb_dn *c;
+
+               c = ldb_dn_explode(lsql_ac, "@SUBCLASSES");
+               if (ldb_dn_compare(module->ldb, msg->dn, c) == 0) {
+#warning "insert subclasses into object class tree"
+                       ret = LDB_ERR_UNWILLING_TO_PERFORM;
+                       goto failed;
+               }
+
+/*
+               c = ldb_dn_explode(local_ctx, "@INDEXLIST");
+               if (ldb_dn_compare(module->ldb, msg->dn, c) == 0) {
+#warning "should we handle indexes somehow ?"
+                       goto failed;
+               }
+*/
+                /* Others are implicitly ignored */
+                return LDB_SUCCESS;
+       }
+
+       /* create linearized and normalized dns */
+       dn = ldb_dn_linearize(lsql_ac, msg->dn);
+       ndn = ldb_dn_linearize(lsql_ac, ldb_dn_casefold(module->ldb, msg->dn));
+       if (dn == NULL || ndn == NULL) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       query = lsqlite3_tprintf(lsql_ac,
+                                  /* Add new entry */
+                                  "INSERT OR ABORT INTO ldb_entry "
+                                  "('dn', 'norm_dn') "
+                                  "VALUES ('%q', '%q');",
+                               dn, ndn);
+       if (query == NULL) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       ldb_set_errstring(module->ldb, errmsg);
+                       free(errmsg);
+               }
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       eid = lsqlite3_get_eid_ndn(lsqlite3->sqlite, lsql_ac, ndn);
+       if (eid == -1) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_message_element *el = &msg->elements[i];
+               const struct ldb_attrib_handler *h;
+               char *attr;
+               int j;
+
+               /* Get a case-folded copy of the attribute name */
+               attr = ldb_attr_casefold(lsql_ac, el->name);
+               if (attr == NULL) {
+                       ret = LDB_ERR_OTHER;
+                       goto failed;
+               }
+
+               h = ldb_attrib_handler(module->ldb, el->name);
+
+               /* For each value of the specified attribute name... */
+               for (j = 0; j < el->num_values; j++) {
+                       struct ldb_val value;
+                       char *insert;
+
+                       /* Get a canonicalised copy of the data */
+                       h->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value);
+                       if (value.data == NULL) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+
+                       insert = lsqlite3_tprintf(lsql_ac,
+                                       "INSERT OR ROLLBACK INTO ldb_attribute_values "
+                                       "('eid', 'attr_name', 'norm_attr_name',"
+                                       " 'attr_value', 'norm_attr_value') "
+                                       "VALUES ('%lld', '%q', '%q', '%q', '%q');",
+                                       eid, el->name, attr,
+                                       el->values[j].data, value.data);
+                       if (insert == NULL) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+
+                       ret = sqlite3_exec(lsqlite3->sqlite, insert, NULL, NULL, &errmsg);
+                       if (ret != SQLITE_OK) {
+                               if (errmsg) {
+                                       ldb_set_errstring(module->ldb, errmsg);
+                                       free(errmsg);
+                               }
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+               }
+       }
+
+       if (lsql_ac->callback)
+               (*handle)->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL);
+       
+        return LDB_SUCCESS;
+
+failed:
+       talloc_free(*handle);
+       return ret;
+}
+
+static int lsql_add(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ldb_handle *handle;
+       int ret;
+
+       ret = lsql_add_async(module, msg, NULL, NULL, &handle);
+
+       if (ret != LDB_SUCCESS)
+               return ret;
+
+       ret = ldb_wait(handle, LDB_WAIT_ALL);
+
+       talloc_free(handle);
+       return ret;
+}
+
+
+/* modify a record */
+static int lsql_modify_async(struct ldb_module *module, const struct ldb_message *msg,
+                            void *context,
+                            int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                            struct ldb_handle **handle)
+{
+       struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private);
+       struct lsql_context *lsql_ac;
+        long long eid;
+       char *errmsg;
+       int i;
+       int ret = LDB_ERR_OPERATIONS_ERROR;
+
+       *handle = init_handle(lsqlite3, module, context, callback);
+       if (*handle == NULL) {
+               goto failed;
+       }
+       lsql_ac = talloc_get_type((*handle)->private_data, struct lsql_context);
+       (*handle)->state = LDB_ASYNC_DONE;
+       (*handle)->status = LDB_SUCCESS;
+
+        /* See if this is an ltdb special */
+       if (ldb_dn_is_special(msg->dn)) {
+               struct ldb_dn *c;
+
+               c = ldb_dn_explode(lsql_ac, "@SUBCLASSES");
+               if (ldb_dn_compare(module->ldb, msg->dn, c) == 0) {
+#warning "modify subclasses into object class tree"
+                       ret = LDB_ERR_UNWILLING_TO_PERFORM;
+                       goto failed;
+               }
+
+                /* Others are implicitly ignored */
+                return LDB_SUCCESS;
+       }
+
+       eid = lsqlite3_get_eid(module, msg->dn);
+       if (eid == -1) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_message_element *el = &msg->elements[i];
+               const struct ldb_attrib_handler *h;
+               int flags = el->flags & LDB_FLAG_MOD_MASK;
+               char *attr;
+               char *mod;
+               int j;
+
+               /* Get a case-folded copy of the attribute name */
+               attr = ldb_attr_casefold(lsql_ac, el->name);
+               if (attr == NULL) {
+                       ret = LDB_ERR_OTHER;
+                       goto failed;
+               }
+
+               h = ldb_attrib_handler(module->ldb, el->name);
+
+               switch (flags) {
+
+               case LDB_FLAG_MOD_REPLACE:
+                       
+                       /* remove all attributes before adding the replacements */
+                       mod = lsqlite3_tprintf(lsql_ac,
+                                               "DELETE FROM ldb_attribute_values "
+                                               "WHERE eid = '%lld' "
+                                               "AND norm_attr_name = '%q';",
+                                               eid, attr);
+                       if (mod == NULL) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+
+                       ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+                       if (ret != SQLITE_OK) {
+                               if (errmsg) {
+                                       ldb_set_errstring(module->ldb, errmsg);
+                                       free(errmsg);
+                               }
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                        }
+
+                       /* MISSING break is INTENTIONAL */
+
+               case LDB_FLAG_MOD_ADD:
+#warning "We should throw an error if no value is provided!"
+                       /* For each value of the specified attribute name... */
+                       for (j = 0; j < el->num_values; j++) {
+                               struct ldb_val value;
+
+                               /* Get a canonicalised copy of the data */
+                               h->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value);
+                               if (value.data == NULL) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+
+                               mod = lsqlite3_tprintf(lsql_ac,
+                                       "INSERT OR ROLLBACK INTO ldb_attribute_values "
+                                       "('eid', 'attr_name', 'norm_attr_name',"
+                                       " 'attr_value', 'norm_attr_value') "
+                                       "VALUES ('%lld', '%q', '%q', '%q', '%q');",
+                                       eid, el->name, attr,
+                                       el->values[j].data, value.data);
+
+                               if (mod == NULL) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+
+                               ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+                               if (ret != SQLITE_OK) {
+                                       if (errmsg) {
+                                               ldb_set_errstring(module->ldb, errmsg);
+                                               free(errmsg);
+                                       }
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+                       }
+
+                       break;
+
+               case LDB_FLAG_MOD_DELETE:
+#warning "We should throw an error if the attribute we are trying to delete does not exist!"
+                       if (el->num_values == 0) {
+                               mod = lsqlite3_tprintf(lsql_ac,
+                                                       "DELETE FROM ldb_attribute_values "
+                                                       "WHERE eid = '%lld' "
+                                                       "AND norm_attr_name = '%q';",
+                                                       eid, attr);
+                               if (mod == NULL) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+
+                               ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+                               if (ret != SQLITE_OK) {
+                                       if (errmsg) {
+                                               ldb_set_errstring(module->ldb, errmsg);
+                                               free(errmsg);
+                                       }
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+                       }
+
+                       /* For each value of the specified attribute name... */
+                       for (j = 0; j < el->num_values; j++) {
+                               struct ldb_val value;
+
+                               /* Get a canonicalised copy of the data */
+                               h->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value);
+                               if (value.data == NULL) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+
+                               mod = lsqlite3_tprintf(lsql_ac,
+                                       "DELETE FROM ldb_attribute_values "
+                                       "WHERE eid = '%lld' "
+                                       "AND norm_attr_name = '%q' "
+                                       "AND norm_attr_value = '%q';",
+                                       eid, attr, value.data);
+
+                               if (mod == NULL) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+
+                               ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+                               if (ret != SQLITE_OK) {
+                                       if (errmsg) {
+                                               ldb_set_errstring(module->ldb, errmsg);
+                                               free(errmsg);
+                                       }
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+                       }
+
+                       break;
+               }
+       }
+
+       if (lsql_ac->callback)
+               (*handle)->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL);
+       
+        return LDB_SUCCESS;
+
+failed:
+       talloc_free(*handle);
+       return ret;
+}
+
+static int lsql_modify(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ldb_handle *handle;
+       int ret;
+
+       ret = lsql_modify_async(module, msg, NULL, NULL, &handle);
+
+       if (ret != LDB_SUCCESS)
+               return ret;
+
+       ret = ldb_wait(handle, LDB_WAIT_ALL);
+
+       talloc_free(handle);
+       return ret;
+}
+
+/* delete a record */
+static int lsql_delete_async(struct ldb_module *module, const struct ldb_dn *dn,
+                            void *context,
+                            int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                            struct ldb_handle **handle)
+{
+       struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private);
+       struct lsql_context *lsql_ac;
+        long long eid;
+       char *errmsg;
+       char *query;
+       int ret = LDB_ERR_OPERATIONS_ERROR;
+
+
+       *handle = init_handle(lsqlite3, module, context, callback);
+       if (*handle == NULL) {
+               goto failed;
+       }
+       lsql_ac = talloc_get_type((*handle)->private_data, struct lsql_context);
+       (*handle)->state = LDB_ASYNC_DONE;
+       (*handle)->status = LDB_SUCCESS;
+
+       eid = lsqlite3_get_eid(module, dn);
+       if (eid == -1) {
+               goto failed;
+       }
+
+       query = lsqlite3_tprintf(lsql_ac,
+                                  /* Delete entry */
+                                  "DELETE FROM ldb_entry WHERE eid = %lld; "
+                                  /* Delete attributes */
+                                  "DELETE FROM ldb_attribute_values WHERE eid = %lld; ",
+                               eid, eid);
+       if (query == NULL) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       ldb_set_errstring(module->ldb, errmsg);
+                       free(errmsg);
+               }
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto failed;
+       }
+
+       if (lsql_ac->callback)
+               (*handle)->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL);
+       
+        return LDB_SUCCESS;
+
+failed:
+       talloc_free(*handle);
+       return ret;
+}
+
+static int lsql_delete(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       struct ldb_handle *handle;
+       int ret;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(dn)) {
+               return LDB_SUCCESS;
+       }
+
+       ret = lsql_delete_async(module, dn, NULL, NULL, &handle);
+
+       if (ret != LDB_SUCCESS)
+               return ret;
+
+       ret = ldb_wait(handle, LDB_WAIT_ALL);
+
+       talloc_free(handle);
+       return ret;
+}
+
+/* rename a record */
+static int lsql_rename_async(struct ldb_module *module, const struct ldb_dn *olddn, const struct ldb_dn *newdn,
+                            void *context,
+                            int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
+                            struct ldb_handle **handle)
+{
+       struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private);
+       struct lsql_context *lsql_ac;
+       char *new_dn, *new_cdn, *old_cdn;
+       char *errmsg;
+       char *query;
+       int ret = LDB_ERR_OPERATIONS_ERROR;
+
+       *handle = init_handle(lsqlite3, module, context, callback);
+       if (*handle == NULL) {
+               goto failed;
+       }
+       lsql_ac = talloc_get_type((*handle)->private_data, struct lsql_context);
+       (*handle)->state = LDB_ASYNC_DONE;
+       (*handle)->status = LDB_SUCCESS;
+
+       /* create linearized and normalized dns */
+       old_cdn = ldb_dn_linearize(lsql_ac, ldb_dn_casefold(module->ldb, olddn));
+       new_cdn = ldb_dn_linearize(lsql_ac, ldb_dn_casefold(module->ldb, newdn));
+       new_dn = ldb_dn_linearize(lsql_ac, newdn);
+       if (old_cdn == NULL || new_cdn == NULL || new_dn == NULL) {
+               goto failed;
+       }
+
+       /* build the SQL query */
+       query = lsqlite3_tprintf(lsql_ac,
+                                "UPDATE ldb_entry SET dn = '%q', norm_dn = '%q' "
+                                "WHERE norm_dn = '%q';",
+                                new_dn, new_cdn, old_cdn);
+       if (query == NULL) {
+               goto failed;
+       }
+
+       /* execute */
+       ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       ldb_set_errstring(module->ldb, errmsg);
+                       free(errmsg);
+               }
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto failed;
+       }
+
+       if (lsql_ac->callback)
+               (*handle)->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL);
+       
+        return LDB_SUCCESS;
+
+failed:
+       talloc_free(*handle);
+       return ret;
+}
+
+static int lsql_rename(struct ldb_module *module, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
+{
+       struct ldb_handle *handle;
+       int ret;
+
+       /* ignore ltdb specials */
+       if (ldb_dn_is_special(olddn) || ldb_dn_is_special(newdn)) {
+               return LDB_SUCCESS;
+       }
+
+
+       ret = lsql_rename_async(module, olddn, newdn, NULL, NULL, &handle);
+
+       if (ret != LDB_SUCCESS)
+               return ret;
+
+       ret = ldb_wait(handle, LDB_WAIT_ALL);
+
+       talloc_free(handle);
+       return ret;
+}
+
+static int lsql_start_trans(struct ldb_module * module)
+{
+       int ret;
+       char *errmsg;
+       struct lsqlite3_private *   lsqlite3 = module->private_data;
+
+       if (lsqlite3->trans_count == 0) {
+               ret = sqlite3_exec(lsqlite3->sqlite, "BEGIN IMMEDIATE;", NULL, NULL, &errmsg);
+               if (ret != SQLITE_OK) {
+                       if (errmsg) {
+                               printf("lsqlite3_start_trans: error: %s\n", errmsg);
+                               free(errmsg);
+                       }
+                       return -1;
+               }
+       };
+
+       lsqlite3->trans_count++;
+
+       return 0;
+}
+
+static int lsql_end_trans(struct ldb_module *module)
+{
+       int ret;
+       char *errmsg;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       if (lsqlite3->trans_count > 0) {
+               lsqlite3->trans_count--;
+       } else return -1;
+
+       if (lsqlite3->trans_count == 0) {
+               ret = sqlite3_exec(lsqlite3->sqlite, "COMMIT;", NULL, NULL, &errmsg);
+               if (ret != SQLITE_OK) {
+                       if (errmsg) {
+                               printf("lsqlite3_end_trans: error: %s\n", errmsg);
+                               free(errmsg);
+                       }
+                       return -1;
+               }
+       }
+
+        return 0;
+}
+
+static int lsql_del_trans(struct ldb_module *module)
+{
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       if (lsqlite3->trans_count > 0) {
+               lsqlite3->trans_count--;
+       } else return -1;
+
+       if (lsqlite3->trans_count == 0) {
+               return lsqlite3_safe_rollback(lsqlite3->sqlite);
+       }
+
+       return -1;
+}
+
+/*
+ * Static functions
+ */
+
+static int initialize(struct lsqlite3_private *lsqlite3,
+                     struct ldb_context *ldb, const char *url, int flags)
+{
+       TALLOC_CTX *local_ctx;
+        long long queryInt;
+       int rollback = 0;
+       char *errmsg;
+        char *schema;
+        int ret;
+
+       /* create a local ctx */
+       local_ctx = talloc_named(lsqlite3, 0, "lsqlite3_rename local context");
+       if (local_ctx == NULL) {
+               return -1;
+       }
+
+       schema = lsqlite3_tprintf(local_ctx,
+                
+                
+                "CREATE TABLE ldb_info AS "
+                "  SELECT 'LDB' AS database_type,"
+                "         '1.0' AS version;"
+                
+                /*
+                 * The entry table holds the information about an entry. 
+                 * This table is used to obtain the EID of the entry and to 
+                 * support scope=one and scope=base.  The parent and child
+                 * table is included in the entry table since all the other
+                 * attributes are dependent on EID.
+                 */
+                "CREATE TABLE ldb_entry "
+                "("
+                "  eid     INTEGER PRIMARY KEY AUTOINCREMENT,"
+                "  dn      TEXT UNIQUE NOT NULL,"
+               "  norm_dn TEXT UNIQUE NOT NULL"
+                ");"
+                
+
+                "CREATE TABLE ldb_object_classes"
+                "("
+                "  class_name            TEXT PRIMARY KEY,"
+                "  parent_class_name     TEXT,"
+                "  tree_key              TEXT UNIQUE,"
+                "  max_child_num         INTEGER DEFAULT 0"
+                ");"
+                
+                /*
+                 * We keep a full listing of attribute/value pairs here
+                 */
+                "CREATE TABLE ldb_attribute_values"
+                "("
+                "  eid             INTEGER REFERENCES ldb_entry,"
+                "  attr_name       TEXT,"
+                "  norm_attr_name  TEXT,"
+                "  attr_value      TEXT,"
+                "  norm_attr_value TEXT "
+                ");"
+                
+               
+                /*
+                 * Indexes
+                 */
+                "CREATE INDEX ldb_attribute_values_eid_idx "
+                "  ON ldb_attribute_values (eid);"
+                
+                "CREATE INDEX ldb_attribute_values_name_value_idx "
+                "  ON ldb_attribute_values (attr_name, norm_attr_value);"
+                
+                
+
+                /*
+                 * Triggers
+                 */
+                "CREATE TRIGGER ldb_object_classes_insert_tr"
+                "  AFTER INSERT"
+                "  ON ldb_object_classes"
+                "  FOR EACH ROW"
+                "    BEGIN"
+                "      UPDATE ldb_object_classes"
+                "        SET tree_key = COALESCE(tree_key, "
+                "              ("
+                "                SELECT tree_key || "
+                "                       (SELECT base160(max_child_num + 1)"
+                "                                FROM ldb_object_classes"
+                "                                WHERE class_name = "
+                "                                      new.parent_class_name)"
+                "                  FROM ldb_object_classes "
+                "                  WHERE class_name = new.parent_class_name "
+                "              ));"
+                "      UPDATE ldb_object_classes "
+                "        SET max_child_num = max_child_num + 1"
+                "        WHERE class_name = new.parent_class_name;"
+                "    END;"
+
+                /*
+                 * Table initialization
+                 */
+
+                "INSERT INTO ldb_object_classes "
+                "    (class_name, tree_key) "
+                "  VALUES "
+                "    ('TOP', '0001');");
+        
+        /* Skip protocol indicator of url  */
+        if (strncmp(url, "sqlite://", 9) != 0) {
+                return SQLITE_MISUSE;
+        }
+        
+        /* Update pointer to just after the protocol indicator */
+        url += 9;
+        
+        /* Try to open the (possibly empty/non-existent) database */
+        if ((ret = sqlite3_open(url, &lsqlite3->sqlite)) != SQLITE_OK) {
+                return ret;
+        }
+        
+        /* In case this is a new database, enable auto_vacuum */
+       ret = sqlite3_exec(lsqlite3->sqlite, "PRAGMA auto_vacuum = 1;", NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       printf("lsqlite3 initializaion error: %s\n", errmsg);
+                       free(errmsg);
+               }
+               goto failed;
+       }
+        
+       if (flags & LDB_FLG_NOSYNC) {
+               /* DANGEROUS */
+               ret = sqlite3_exec(lsqlite3->sqlite, "PRAGMA synchronous = OFF;", NULL, NULL, &errmsg);
+               if (ret != SQLITE_OK) {
+                       if (errmsg) {
+                               printf("lsqlite3 initializaion error: %s\n", errmsg);
+                               free(errmsg);
+                       }
+                       goto failed;
+               }
+       }
+        
+       /* */
+        
+        /* Establish a busy timeout of 30 seconds */
+        if ((ret = sqlite3_busy_timeout(lsqlite3->sqlite,
+                                        30000)) != SQLITE_OK) {
+                return ret;
+        }
+
+        /* Create a function, callable from sql, to increment a tree_key */
+        if ((ret =
+             sqlite3_create_function(lsqlite3->sqlite,/* handle */
+                                     "base160_next",  /* function name */
+                                     1,               /* number of args */
+                                     SQLITE_ANY,      /* preferred text type */
+                                     NULL,            /* user data */
+                                     base160next_sql, /* called func */
+                                     NULL,            /* step func */
+                                     NULL             /* final func */
+                     )) != SQLITE_OK) {
+                return ret;
+        }
+
+        /* Create a function, callable from sql, to convert int to base160 */
+        if ((ret =
+             sqlite3_create_function(lsqlite3->sqlite,/* handle */
+                                     "base160",       /* function name */
+                                     1,               /* number of args */
+                                     SQLITE_ANY,      /* preferred text type */
+                                     NULL,            /* user data */
+                                     base160_sql,     /* called func */
+                                     NULL,            /* step func */
+                                     NULL             /* final func */
+                     )) != SQLITE_OK) {
+                return ret;
+        }
+
+        /* Create a function, callable from sql, to perform various comparisons */
+        if ((ret =
+             sqlite3_create_function(lsqlite3->sqlite, /* handle */
+                                     "ldap_compare",   /* function name */
+                                     4,                /* number of args */
+                                     SQLITE_ANY,       /* preferred text type */
+                                     ldb  ,            /* user data */
+                                     lsqlite3_compare, /* called func */
+                                     NULL,             /* step func */
+                                     NULL              /* final func */
+                     )) != SQLITE_OK) {
+                return ret;
+        }
+
+        /* Begin a transaction */
+       ret = sqlite3_exec(lsqlite3->sqlite, "BEGIN EXCLUSIVE;", NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       printf("lsqlite3: initialization error: %s\n", errmsg);
+                       free(errmsg);
+               }
+               goto failed;
+       }
+       rollback = 1;
+        /* Determine if this is a new database.  No tables means it is. */
+        if (query_int(lsqlite3,
+                      &queryInt,
+                      "SELECT COUNT(*)\n"
+                      "  FROM sqlite_master\n"
+                      "  WHERE type = 'table';") != 0) {
+               goto failed;
+        }
+        
+        if (queryInt == 0) {
+                /*
+                 * Create the database schema
+                 */
+               ret = sqlite3_exec(lsqlite3->sqlite, schema, NULL, NULL, &errmsg);
+               if (ret != SQLITE_OK) {
+                       if (errmsg) {
+                               printf("lsqlite3 initializaion error: %s\n", errmsg);
+                               free(errmsg);
+                       }
+                       goto failed;
+               }
+        } else {
+                /*
+                 * Ensure that the database we opened is one of ours
+                 */
+                if (query_int(lsqlite3,
+                              &queryInt,
+                              "SELECT "
+                              "  (SELECT COUNT(*) = 2"
+                              "     FROM sqlite_master "
+                              "     WHERE type = 'table' "
+                              "       AND name IN "
+                              "         ("
+                              "           'ldb_entry', "
+                              "           'ldb_object_classes' "
+                              "         ) "
+                              "  ) "
+                              "  AND "
+                              "  (SELECT 1 "
+                              "     FROM ldb_info "
+                              "     WHERE database_type = 'LDB' "
+                              "       AND version = '1.0'"
+                              "  );") != 0 ||
+                    queryInt != 1) {
+                        
+                        /* It's not one that we created.  See ya! */
+                       goto failed;
+                }
+        }
+        
+        /* Commit the transaction */
+       ret = sqlite3_exec(lsqlite3->sqlite, "COMMIT;", NULL, NULL, &errmsg);
+       if (ret != SQLITE_OK) {
+               if (errmsg) {
+                       printf("lsqlite3: iniialization error: %s\n", errmsg);
+                       free(errmsg);
+               }
+               goto failed;
+       }
+        return SQLITE_OK;
+
+failed:
+       if (rollback) lsqlite3_safe_rollback(lsqlite3->sqlite); 
+       sqlite3_close(lsqlite3->sqlite);
+       return -1;
+}
+
+static int destructor(struct lsqlite3_private *lsqlite3)
+{        
+       if (lsqlite3->sqlite) {
+               sqlite3_close(lsqlite3->sqlite);
+       }
+       return 0;
+}
+
+static int lsql_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       return handle->status;
+}
+
+static int lsql_request(struct ldb_module *module, struct ldb_request *req)
+{
+       /* check for oustanding critical controls and return an error if found */
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_sqlite3 backend!\n");
+       }
+
+       if (check_critical_controls(req->controls)) {
+               return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+       }
+       
+       switch (req->operation) {
+
+       case LDB_SEARCH:
+               return lsql_search_bytree(module,
+                                         req->op.search.base,
+                                         req->op.search.scope, 
+                                         req->op.search.tree, 
+                                         req->op.search.attrs, 
+                                         &req->op.search.res);
+
+       case LDB_ADD:
+               return lsql_add(module, req->op.add.message);
+
+       case LDB_MODIFY:
+               return lsql_modify(module, req->op.mod.message);
+
+       case LDB_DELETE:
+               return lsql_delete(module, req->op.del.dn);
+
+       case LDB_RENAME:
+               return lsql_rename(module,
+                                       req->op.rename.olddn,
+                                       req->op.rename.newdn);
+
+       case LDB_SEARCH:
+               return lsql_search_async(module,
+                                       req->op.search.base,
+                                       req->op.search.scope, 
+                                       req->op.search.tree, 
+                                       req->op.search.attrs,
+                                       req->context,
+                                       req->callback,
+                                       &req->handle);
+/*
+       case LDB_ADD:
+               return lsql_add_async(module,
+                                       req->op.add.message,
+                                       req->context,
+                                       req->callback,
+                                       &req->handle);
+
+       case LDB_MODIFY:
+               return lsql_modify_async(module,
+                                       req->op.mod.message,
+                                       req->context,
+                                       req->callback,
+                                       &req->handle);
+*/
+       case LDB_DELETE:
+               return lsql_delete_async(module,
+                                       req->op.del.dn,
+                                       req->context,
+                                       req->callback,
+                                       &req->handle);
+
+       case LDB_RENAME:
+               return lsql_rename_async(module,
+                                       req->op.rename.olddn,
+                                       req->op.rename.newdn,
+                                       req->context,
+                                       req->callback,
+                                       &req->handle);
+
+       default:
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       }
+}
+
+/*
+ * Table of operations for the sqlite3 backend
+ */
+static const struct ldb_module_ops lsqlite3_ops = {
+       .name              = "sqlite",
+       .request           = lsql_request,
+       .start_transaction = lsql_start_trans,
+       .end_transaction   = lsql_end_trans,
+       .del_transaction   = lsql_del_trans,
+       .wait              = lsql_wait,
+};
+
+/*
+ * connect to the database
+ */
+static int lsqlite3_connect(struct ldb_context *ldb,
+                           const char *url, 
+                           unsigned int flags, 
+                           const char *options[],
+                           struct ldb_module **module)
+{
+       int                         i;
+        int                         ret;
+       struct lsqlite3_private *   lsqlite3 = NULL;
+        
+       lsqlite3 = talloc(ldb, struct lsqlite3_private);
+       if (!lsqlite3) {
+               goto failed;
+       }
+        
+       lsqlite3->sqlite = NULL;
+       lsqlite3->options = NULL;
+       lsqlite3->trans_count = 0;
+        
+       ret = initialize(lsqlite3, ldb, url, flags);
+       if (ret != SQLITE_OK) {
+               goto failed;
+       }
+        
+       talloc_set_destructor(lsqlite3, destructor);
+        
+
+
+       *module = talloc(ldb, struct ldb_module);
+       if (!module) {
+               ldb_oom(ldb);
+               goto failed;
+       }
+       (*module)->ldb = ldb;
+       (*module)->prev = (*module)->next = NULL;
+       (*module)->private_data = lsqlite3;
+       (*module)->ops = &lsqlite3_ops;
+
+       if (options) {
+               /*
+                 * take a copy of the options array, so we don't have to rely
+                 * on the caller keeping it around (it might be dynamic)
+                 */
+               for (i=0;options[i];i++) ;
+                
+               lsqlite3->options = talloc_array(lsqlite3, char *, i+1);
+               if (!lsqlite3->options) {
+                       goto failed;
+               }
+                
+               for (i=0;options[i];i++) {
+                        
+                       lsqlite3->options[i+1] = NULL;
+                       lsqlite3->options[i] =
+                                talloc_strdup(lsqlite3->options, options[i]);
+                       if (!lsqlite3->options[i]) {
+                               goto failed;
+                       }
+               }
+       }
+        
+       return 0;
+        
+failed:
+        if (lsqlite3->sqlite != NULL) {
+                (void) sqlite3_close(lsqlite3->sqlite);
+        }
+       talloc_free(lsqlite3);
+       return -1;
+}
+
+int ldb_sqlite3_init(void)
+{
+       return ldb_register_backend("sqlite3", lsqlite3_connect);
+}
diff --git a/source3/lib/ldb/ldb_sqlite3/schema b/source3/lib/ldb/ldb_sqlite3/schema
new file mode 100644 (file)
index 0000000..08dc50d
--- /dev/null
@@ -0,0 +1,363 @@
+                -- ------------------------------------------------------
+
+                PRAGMA auto_vacuum=1;
+
+                -- ------------------------------------------------------
+
+                BEGIN EXCLUSIVE;
+
+                -- ------------------------------------------------------
+
+                CREATE TABLE ldb_info AS 
+                  SELECT 'LDB' AS database_type, 
+                         '1.0' AS version;
+
+                /*
+                 * Get the next USN value with:
+                 *   BEGIN EXCLUSIVE;
+                 *   UPDATE usn SET value = value + 1;
+                 *   SELECT value FROM usn;
+                 *   COMMIT;
+                 */
+                CREATE TABLE usn
+                (
+                  value           INTEGER
+                );
+
+                CREATE TABLE ldb_object
+                (
+                  /* tree_key is auto-generated by the insert trigger */
+                  tree_key        TEXT PRIMARY KEY,
+
+                  parent_tree_key TEXT,
+                  dn              TEXT,
+
+                  attr_name       TEXT REFERENCES ldb_attributes,
+                  attr_value      TEXT,
+
+                  /*
+                   * object_type can take on these values (to date):
+                   *   1: object is a node of a DN
+                   *   2: object is an attribute/value pair of its parent DN
+                   */
+                  object_type     INTEGER,
+
+                  /*
+                   * if object_type is 1, the node can have children.
+                   * this tracks the maximum previously assigned child
+                   * number so we can generate a new unique tree key for
+                   * a new child object.  note that this is always incremented,
+                   * so if children are deleted, this will not represent
+                   * the _number_ of children.
+                   */
+                  max_child_num   INTEGER,
+
+                  /*
+                   * Automatically maintained meta-data (a gift for metze)
+                   */
+                  object_guid     TEXT UNIQUE,
+                  timestamp       INTEGER,  -- originating_time
+                  invoke_id       TEXT,     -- GUID: originating_invocation_id
+                  usn             INTEGER,  -- hyper: originating_usn
+
+                  /* do not allow duplicate name/value pairs */
+                  UNIQUE (parent_tree_key, attr_name, attr_value, object_type)
+                );
+
+                CREATE TABLE ldb_attributes
+                (
+                  attr_name             TEXT PRIMARY KEY,
+                  parent_tree_key       TEXT,
+
+                  objectclass_p         BOOLEAN DEFAULT 0,
+
+                  case_insensitive_p    BOOLEAN DEFAULT 0,
+                  wildcard_p            BOOLEAN DEFAULT 0,
+                  hidden_p              BOOLEAN DEFAULT 0,
+                  integer_p             BOOLEAN DEFAULT 0,
+
+                  /* tree_key is auto-generated by the insert trigger */
+                  tree_key              TEXT, -- null if not a object/sub class
+                                              -- level 1 if an objectclass
+                                              -- level 1-n if a subclass
+                  max_child_num         INTEGER
+                );
+
+                -- ------------------------------------------------------
+
+                CREATE INDEX ldb_object_dn_idx
+                  ON ldb_object (dn);
+
+                CREATE INDEX ldb_attributes_tree_key_ids
+                  ON ldb_attributes (tree_key);
+
+                -- ------------------------------------------------------
+
+                /* Gifts for metze.  Automatically updated meta-data */
+                CREATE TRIGGER ldb_object_insert_tr
+                  AFTER INSERT
+                  ON ldb_object
+                  FOR EACH ROW
+                    BEGIN
+                      UPDATE ldb_object
+                        SET max_child_num = max_child_num + 1
+                        WHERE tree_key = new.parent_tree_key;
+                      UPDATE usn SET value = value + 1;
+                      UPDATE ldb_object
+                        SET tree_key =
+                              (SELECT
+                                 new.tree_key ||
+                                 base160(SELECT max_child_num
+                                           FROM ldb_object
+                                           WHERE tree_key =
+                                                 new.parent_tree_key));
+                            max_child_num = 0,
+                            object_guid = random_guid(),
+                            timestamp = strftime('%s', 'now'),
+                            usn = (SELECT value FROM usn);
+                        WHERE tree_key = new.tree_key;
+                    END;
+
+                CREATE TRIGGER ldb_object_update_tr
+                  AFTER UPDATE
+                  ON ldb_object
+                  FOR EACH ROW
+                    BEGIN
+                      UPDATE usn SET value = value + 1;
+                      UPDATE ldb_object
+                        SET timestamp = strftime('%s', 'now'),
+                            usn = (SELECT value FROM usn);
+                        WHERE tree_key = new.tree_key;
+                    END;
+
+                CREATE TRIGGER ldb_attributes_insert_tr
+                  AFTER INSERT
+                  ON ldb_attributes
+                  FOR EACH ROW
+                    BEGIN
+                      UPDATE ldb_attributes
+                        SET max_child_num = max_child_num + 1
+                        WHERE tree_key = new.parent_tree_key;
+                      UPDATE ldb_attributes
+                        SET tree_key =
+                              (SELECT
+                                 new.tree_key ||
+                                 base160(SELECT max_child_num
+                                           FROM ldb_attributes
+                                           WHERE tree_key =
+                                                 new.parent_tree_key));
+                            max_child_num = 0
+                        WHERE tree_key = new.tree_key;
+                    END;
+
+
+                -- ------------------------------------------------------
+
+                /* Initialize usn */
+                INSERT INTO usn (value) VALUES (0);
+
+                /* Create root object */
+                INSERT INTO ldb_object
+                    (tree_key, parent_tree_key,
+                     dn,
+                     object_type, max_child_num)
+                  VALUES ('', NULL,
+                          '',
+                          1, 0);
+
+                /* We need an implicit "top" level object class */
+                INSERT INTO ldb_attributes (attr_name,
+                                            parent_tree_key)
+                  SELECT 'top', '';
+
+                -- ------------------------------------------------------
+
+                COMMIT;
+
+                -- ------------------------------------------------------
+
+/*
+ * dn: o=University of Michigan,c=US
+ * objectclass: organization
+ * objectclass: domainRelatedObject
+ */
+-- newDN
+BEGIN;
+
+INSERT OR IGNORE INTO ldb_object
+    (parent_tree_key
+     dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('',
+          'c=US',
+          'c', 'US', 1, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key,
+     dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('0001',
+          'o=University of Michigan,c=US',
+          'o', 'University of Michigan', 1, 0);
+
+-- newObjectClass
+INSERT OR IGNORE INTO ldb_attributes
+    (attr_name, parent_tree_key, objectclass_p)
+  VALUES
+    ('objectclass', '', 1);
+
+INSERT INTO ldb_object
+    (parent_tree_key,
+     dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001',
+          NULL,
+          'objectclass', 'organization', 2, 0);
+
+INSERT OR IGNORE INTO ldb_attributes
+    (attr_name, parent_tree_key, objectclass_p)
+  VALUES
+    ('objectclass', '', 1);
+
+INSERT INTO ldb_object
+    (parent_tree_key,
+     dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001',
+          NULL,
+          'objectclass', 'domainRelatedObject', 2, 0);
+
+COMMIT;
+
+
+/*
+ * dn: o=University of Michigan,c=US
+ * l: Ann Arbor, Michigan
+ * st: Michigan
+ * o: University of Michigan
+ * o: UMICH
+ * seeAlso:
+ * telephonenumber: +1 313 764-1817
+ */
+-- addAttrValuePair
+BEGIN;
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'l', 'Ann Arbor, Michigan', 2, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'st', 'Michigan', 2, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'o', 'University of Michigan', 2, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'o', 'UMICH', 2, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'seeAlso', '', 2, 0);
+
+INSERT INTO ldb_object
+    (parent_tree_key, dn,
+     attr_name, attr_value, object_type, max_child_num)
+  VALUES ('00010001', NULL,
+          'telephonenumber', '+1 313 764-1817', 2, 0);
+
+COMMIT;
+
+-- ----------------------------------------------------------------------
+
+/*
+ * dn: @ATTRIBUTES
+ * uid: CASE_INSENSITIVE WILDCARD
+ * cn: CASE_INSENSITIVE
+ * ou: CASE_INSENSITIVE
+ * dn: CASE_INSENSITIVE
+ */
+-- newAttribute
+
+BEGIN;
+
+INSERT OR IGNORE INTO ldb_attributes
+    (attr_name, parent_tree_key, objectclass_p)
+  VALUES
+    ('uid', '', 0);
+
+UPDATE ldb_attributes
+  SET case_insensitive_p = 1,
+      wildcard_p = 1,
+      hidden_p = 0,
+      integer_p = 0
+  WHERE attr_name = 'uid'
+
+UPDATE ldb_attributes
+  SET case_insensitive_p = 1,
+      wildcard_p = 0,
+      hidden_p = 0,
+      integer_p = 0
+  WHERE attr_name = 'cn'
+
+UPDATE ldb_attributes
+  SET case_insensitive_p = 1,
+      wildcard_p = 0,
+      hidden_p = 0,
+      integer_p = 0
+  WHERE attr_name = 'ou'
+
+UPDATE ldb_attributes
+  SET case_insensitive_p = 1,
+      wildcard_p = 0,
+      hidden_p = 0,
+      integer_p = 0
+  WHERE attr_name = 'dn'
+
+-- ----------------------------------------------------------------------
+
+/*
+ * dn: @SUBCLASSES
+ * top: domain
+ * top: person
+ * domain: domainDNS
+ * person: organizationalPerson
+ * person: fooPerson
+ * organizationalPerson: user
+ * organizationalPerson: OpenLDAPperson
+ * user: computer
+ */
+-- insertSubclass
+
+/* NOT YET UPDATED!!! *
+
+
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'domain', /* next_tree_key('top') */ '00010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'person', /* next_tree_key('top') */ '00010002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'domainDNS', /* next_tree_key('domain') */ '000100010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'organizationalPerson', /* next_tree_key('person') */ '000100020001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'fooPerson', /* next_tree_key('person') */ '000100020002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'user', /* next_tree_key('organizationalPerson') */ '0001000200010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'OpenLDAPperson', /* next_tree_key('organizationPerson') */ '0001000200010002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'computer', /* next_tree_key('user') */ '0001000200010001';
diff --git a/source3/lib/ldb/ldb_sqlite3/trees.ps b/source3/lib/ldb/ldb_sqlite3/trees.ps
new file mode 100644 (file)
index 0000000..433a064
--- /dev/null
@@ -0,0 +1,1760 @@
+%!PS-Adobe-2.0
+%%Creator: dvips(k) 5.86 Copyright 1999 Radical Eye Software
+%%Title: trees.dvi
+%%Pages: 7
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 596 842
+%%EndComments
+%DVIPSWebPage: (www.radicaleye.com)
+%DVIPSCommandLine: dvips -f trees.dvi
+%DVIPSParameters: dpi=600, compressed
+%DVIPSSource:  TeX output 2000.05.06:2055
+%%BeginProcSet: texc.pro
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/id 0 N/rw 0 N/rc 0 N/gp 0 N/cp 0 N/G 0 N/CharBuilder{save 3
+1 roll S A/base get 2 index get S/BitMaps get S get/Cd X pop/ctr 0 N Cdx
+0 Cx Cy Ch sub Cx Cw add Cy setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx
+sub Cy .1 sub]/id Ci N/rw Cw 7 add 8 idiv string N/rc 0 N/gp 0 N/cp 0 N{
+rc 0 ne{rc 1 sub/rc X rw}{G}ifelse}imagemask restore}B/G{{id gp get/gp
+gp 1 add N A 18 mod S 18 idiv pl S get exec}loop}B/adv{cp add/cp X}B
+/chg{rw cp id gp 4 index getinterval putinterval A gp add/gp X adv}B/nd{
+/cp 0 N rw exit}B/lsh{rw cp 2 copy get A 0 eq{pop 1}{A 255 eq{pop 254}{
+A A add 255 and S 1 and or}ifelse}ifelse put 1 adv}B/rsh{rw cp 2 copy
+get A 0 eq{pop 128}{A 255 eq{pop 127}{A 2 idiv S 128 and or}ifelse}
+ifelse put 1 adv}B/clr{rw cp 2 index string putinterval adv}B/set{rw cp
+fillstr 0 4 index getinterval putinterval adv}B/fillstr 18 string 0 1 17
+{2 copy 255 put pop}for N/pl[{adv 1 chg}{adv 1 chg nd}{1 add chg}{1 add
+chg nd}{adv lsh}{adv lsh nd}{adv rsh}{adv rsh nd}{1 add adv}{/rc X nd}{
+1 add set}{1 add clr}{adv 2 chg}{adv 2 chg nd}{pop nd}]A{bind pop}
+forall N/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+TeXDict begin 39158280 55380996 1000 600 600 (trees.dvi)
+@start
+%DVIPSBitmapFont: Fa cmr10 10 6
+/Fa 6 55 df<146014E0EB01C0EB0380EB0700130E131E5B5BA25B485AA2485AA212075B
+120F90C7FCA25A121EA2123EA35AA65AB2127CA67EA3121EA2121F7EA27F12077F1203A2
+6C7EA26C7E1378A27F7F130E7FEB0380EB01C0EB00E01460135278BD20>40
+D<12C07E12707E7E7E120F6C7E6C7EA26C7E6C7EA21378A2137C133C133E131EA2131F7F
+A21480A3EB07C0A6EB03E0B2EB07C0A6EB0F80A31400A25B131EA2133E133C137C1378A2
+5BA2485A485AA2485A48C7FC120E5A5A5A5A5A13527CBD20>I<15301578B3A6007FB812
+F8B912FCA26C17F8C80078C8FCB3A6153036367BAF41>43 D<EB03F8EB1FFF90387E0FC0
+9038F803E03901E000F0484813780007147C48487FA248C77EA2481580A3007EEC0FC0A6
+00FE15E0B3007E15C0A4007F141F6C1580A36C15006D5B000F143EA26C6C5B6C6C5B6C6C
+485A6C6C485A90387E0FC0D91FFFC7FCEB03F8233A7DB72A>48 D<EB01C013031307131F
+13FFB5FCA2131F1200B3B3A8497E007FB512F0A31C3879B72A>I<EC3FC0903801FFF001
+0713FC90380FE03E90383F800790387E001F49EB3F804848137F485AA2485A000FEC3F00
+49131E001F91C7FCA2485AA3127F90C9FCEB01FC903807FF8039FF1E07E090383801F049
+6C7E01607F01E0137E497FA249148016C0151FA290C713E0A57EA56C7E16C0A2121FED3F
+807F000F15006C6C5B15FE6C6C5B6C6C485A3900FE07F090383FFFC06D90C7FCEB03FC23
+3A7DB72A>54 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fb cmr7 7 3
+/Fb 3 55 df<EB3F803801FFF03803E0F83807803C48487E001E7F003E1480A2003C1307
+007C14C0A400FC14E0AE007C14C0A36CEB0F80A36CEB1F006C131E6C6C5A3803E0F86CB4
+5A38003F801B277EA521>48 D<13381378EA01F8121F12FE12E01200B3AB487EB512F8A2
+15267BA521>I<EB0FE0EB3FF8EBF81C3801E0063803C01F48485AEA0F005A121E003E13
+1E91C7FC5AA21304EB3FC038FCFFF038FDC078EB003CB4133E48131E141FA2481480A412
+7CA4003C1400123E001E131E143E6C133C6C6C5A3803C1F03801FFC06C6CC7FC19277DA5
+21>54 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fc cmmi10 10 1
+/Fc 1 69 df<0103B7FC4916E018F8903B0007F80007FE4BEB00FFF03F80020FED1FC018
+0F4B15E0F007F0021F1503A24B15F81801143F19FC5DA2147FA292C8FCA25C18035CA213
+0119F84A1507A2130319F04A150FA2010717E0181F4A16C0A2010FEE3F80A24AED7F0018
+7E011F16FE4D5A4A5D4D5A013F4B5A4D5A4A4A5A057FC7FC017F15FEEE03FC91C7EA0FF0
+49EC7FC0B8C8FC16FC16C03E397DB845>68 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fd ectt1000 10 73
+/Fd 73 126 df<D807801307D81FE0EB0F80151F487E486C133F1600007C5CD8FCFC137E
+EAF87C15FE5D14015DA21403D8FCFC5BEA7CF8007F13075D383FF00FD81FE05BA2380780
+1FC75B143F92C7FCA25C147E14FE5CA213015CA213035C13075CA2130F5C131FEC800FED
+3FC0013FEB7FE0140049EBFFF0017E13F9A2D9FE0113F801FC13F0A2120113F8120313F0
+15F90007010013F05B000F14FF49EB7FE0A20007EC3FC06C48EB0F0025417DB92C>37
+D<EA0F80EA1FE0EA3FF0127F13F8A213FCA2123F121F120FEA007CA313FC13F8A2120113
+F01203EA07E0A2EA0FC0EA3F80127FEAFF005A12F812700E1D71B22C>39
+D<143814FC13011303EB07F8EB0FF0EB1FC0EB3F80EB7F0013FE485A485A5B12075B120F
+5B485AA2123F90C7FCA25A127EA312FE5AAC7E127EA3127F7EA27F121FA26C7E7F12077F
+12037F6C7E6C7E137FEB3F80EB1FC0EB0FF0EB07F8EB03FC130113001438164272B92C>
+I<127012FC7E7E6C7E6C7EEA0FE06C7E6C7E6C7E6C7E137F7F1480131F14C0130FEB07E0
+A214F01303A214F81301A314FC1300AC130114F8A3130314F0A2130714E0A2EB0FC0131F
+1480133F14005B13FE485A485A485A485AEA3FC0485A48C7FC5A5A1270164279B92C>I<
+EB0380497EA60020140800F8143E00FE14FE00FF13C1EBC7C7EBE7CF003FB512F8000F14
+E0000314806C140038007FFCA248B5FC481480000F14E0003F14F839FFE7CFFEEBC7C7EB
+07C100FE13C000F8143E0020140800001400A66D5A1F247AAA2C>I<147014F8AF003FB6
+12E0B712F8A4C700F8C7FCB0147025267DAB2C>I<EA0F80EA1FE0EA3FF0EA7FF8A213FC
+A3123F121F120F120013F8A21201EA03F01207EA1FE0EA7FC0EAFF80130012FC12700E17
+718A2C>I<121FEA3F80EA7FC0EAFFE0A5EA7FC0EA3F80EA1F000B0B708A2C>46
+D<1507ED0F80A2151F16005D153E157E157CA215FC5D14015D14035D14075D140F5D141F
+92C7FC5C143EA2147E147C14FC5C13015C13035C13075C130F5C131F91C8FC5B133EA213
+7E137C13FC5B12015B12035B12075B120F5B121F90C9FCA25A123E127E127C12FC5AA212
+7021417BB92C>I<EB03F8EB0FFE90383FFF80497F90B57E3901FE0FF03903F803F84848
+6C7EEBE0004848137EA248487FA248C7EA1F80A2003E140F007E15C0A3007C140700FC15
+E0AC6C140F007E15C0A46CEC1F80A36C6CEB3F00A26C6C137E6D13FE00075CEBF0016C6C
+485A3901FE0FF06CB55A6D5B6D5BD90FFEC7FCEB03F823357CB32C>I<1307497EA2131F
+A2133F137F13FF5A1207127FB5FC13DF139FEA7C1F1200B3AE007FB512E0B612F0A36C14
+E01C3477B32C>I<EB0FF890387FFF8048B512E00007804814FC391FF80FFE393FE001FF
+903880007F48C7EA3F80007E141F00FE15C0150F6C15E01507A3127E123CC8FCA2150F16
+C0151F1680153F16005D15FE4A5A14034A5A4A5A4A5A4A5AECFF804948C7FC495A495A49
+5AEB3FE0EB7F8049C8FC485A4848EB03C04848EB07E0EA1FE0485A48B6FCB7FCA36C15C0
+23347CB32C>I<000FB512FE4880A35D0180C8FCADEB83FE90389FFF8090B512E015F881
+9038FE03FE9038F000FF01C07F49EB3F8090C7121F6C15C0C8120FA2ED07E0A4123C127E
+B4FC150F16C0A248141F007EEC3F80007FEC7F006C6C5B6D485A391FF80FFC6CB55A6C5C
+000114C06C6C90C7FCEB0FF823347CB22C>53 D<EC3FC0903801FFF801077F011F7F497F
+90387FE07F9039FF003F804848137FEA03F8485A5B000FEC3F004848131E4990C7FC123F
+90C9FCA25A127EEB03FE90381FFF80D8FC7F13E000FDB57EB67E9038FE07FC9038F001FE
+9038C0007F49EB3F8090C7121F16C048140F16E01507A3127EA47E150F6D14C0001F141F
+6D1480000F143F6DEB7F003907F801FE3903FE07FC6CB55A6C5C6D5B011F1380D907FCC7
+FC23357CB32C>I<1278B712C016E0A316C000FCC7EA3F80ED7F0015FE00785CC712014A
+5A4A5A5D140F5D4A5A143F92C7FC5C147E14FE5C13015CA2495AA213075CA3495AA4495A
+A5133F91C8FCAA131E23357CB32C>I<EA0F80EA1FC0EA3FE0EA7FF0A5EA3FE0EA1FC0EA
+0F80C7FCAEEA0F80EA1FE0EA3FF0EA7FF8A213FCA3123F121F120F120013F8A21201EA03
+F01207EA1FE0EA7FC0EAFF80130012FC12700E3071A32C>59 D<1502ED0F80151F157F15
+FF913803FE00EC0FFCEC1FF0EC7FE0ECFF80D903FEC7FC495AEB1FF0495AEBFF80000390
+C8FCEA07FCEA1FF8EA3FE0EAFF8090C9FCA27FEA3FE0EA1FF8EA07FC6CB4FCC67FEB3FE0
+6D7EEB07FC6D7E903800FF80EC7FE0EC1FF0EC0FFCEC03FE913800FF80157F151F150FED
+0200212A7BAD2C>I<007FB612F0B712F8A36C15F0CAFCA8007FB612F0B712F8A36C15F0
+25127DA12C>I<122012F87EB4FC7FEA3FE0EA1FF8EA07FC6CB4FCC67FEB3FE06D7EEB07
+FC6D7E903800FF80EC7FE0EC1FF0EC0FFCEC03FE913800FF80157FA215FF913803FE00EC
+0FFCEC1FF0EC7FE0ECFF80D903FEC7FC495AEB1FF0495AEBFF80000390C8FCEA07FCEA1F
+F8EA3FE0EAFF8090C9FC12FC5A1220212A7BAD2C>I<14FE497EA4497FA214EFA2130781
+A214C7A2010F7FA314C390381F83F0A590383F01F8A490387E00FCA549137E90B512FEA3
+4880A29038F8003FA34848EB1F80A4000715C049130FD87FFEEBFFFC6D5AB514FE6C15FC
+497E27347EB32C>65 D<007FB512E015F8B612FE6C8016C03903F0003FED0FE0ED07F015
+03A2ED01F8A6ED03F0A21507ED0FE0ED1FC0EDFF8090B612005D5D15FF16C09039F0001F
+E0ED07F0ED03F81501ED00FCA216FE167EA616FE16FC1501ED03F8150FED3FF0007FB612
+E016C0B712806CECFE0015F027337FB22C>I<02FF13700107EBE0F84913F9013F13FD49
+13FFEBFF813901FE007F4848131FD807F0130F1507485A491303485A150148C7FCA25A00
+7EEC00F01600A212FE5AAB7E127EA3007F15F06CEC01F8A26C7EA26C6C13036D14F06C6C
+130716E0D803FC131F6C6CEB3FC03A00FF81FF806DB512006D5B010F5B6D13F001001380
+25357DB32C>I<007FB5FCB612C015F0816C803907E003FEEC00FFED7F80153FED1FC0ED
+0FE0A2150716F0150316F81501A4ED00FCACED01F8A3150316F0A2150716E0150FED1FC0
+153FED7F80EDFF00EC03FE007FB55AB65A5D15C06C91C7FC26337EB22C>I<007FB612F0
+B712F8A37E3903F00001A7ED00F01600A4EC01E04A7EA490B5FCA5EBF003A46E5A91C8FC
+A5163C167EA8007FB612FEB7FCA36C15FC27337EB22C>I<007FB612F8B712FCA37ED803
+F0C7FCA716781600A515F04A7EA490B5FCA5EBF001A46E5A92C7FCAD387FFFE0B5FC805C
+7E26337EB22C>I<903901FC038090390FFF87C04913EF017F13FF90B6FC4813073803FC
+01497E4848137F4848133F49131F121F5B003F140F90C7FCA2127EED078092C7FCA212FE
+5AA8913803FFF84A13FCA27E007E6D13F89138000FC0A36C141FA27F121F6D133F120F6D
+137F6C7E6C6C13FF6D5A3801FF076C90B5FC6D13EF011F13CF6DEB0780D901FCC7FC2635
+7DB32C>I<D87FFEEBFFFCB54813FEA36C486C13FCD807E0EB0FC0B190B6FCA59038E000
+0FB3D87FFEEBFFFCB54813FEA36C486C13FC27337EB22C>I<007FB512F8B612FCA36C14
+F839000FC000B3B3A5007FB512F8B612FCA36C14F81E3379B22C>I<D87FFCEB7FF8486C
+EBFFFCA36C48EB7FF8D807C0EB1F80153FED7F00157E5D4A5A14034A5A5D4A5A4A5A143F
+4AC7FC147E5CEBC1F813C3EBC7FCA2EBCFFEEBDFBEEBFFBF141F01FE7F496C7E13F86E7E
+EBF00301E07FEBC001816E7EA2157E153E153F811680ED0FC0A2ED07E0D87FFCEB1FFC48
+6CEB3FFEA36C48EB1FFC27337EB22C>75 D<387FFFE0B57EA36C5BD803F0C8FCB3AE16F0
+ED01F8A8007FB6FCB7FCA36C15F025337DB22C>I<D87FE0EB0FFC486CEB1FFEA26D133F
+007F15FC000F15E001BC137BA4019E13F3A3EB9F01A2018F13E3A21483A2018713C314C7
+A201831383A214EFA201811303A214FFEB80FEA3147C14381400ACD87FF0EB1FFC486CEB
+3FFEA36C48EB1FFC27337EB22C>I<D87FF0EB7FFC486CEBFFFEA27F007FEC7FFCD807FE
+EB07C013DEA213DF13CFA2148013C714C0A213C314E0A213C114F0A213C014F8A2147CA3
+143EA2141E141FA2140F1587A2140715C7A2140315E71401A215F71400A215FFD87FFC13
+7F487E153FA26C48EB1F8027337EB22C>I<EB7FFF0003B512E0000F14F848804880EBE0
+03EB800048C7127FA2007E80A300FE158048141FB3A86C143FA2007E1500A3007F5CA26C
+6C13FEEBF00790B5FC6C5C6C5C000314E0C66C90C7FC21357BB32C>I<007FB512C0B612
+F88115FF6C15802603F00013C0153FED0FE0ED07F0A2150316F81501A6150316F01507A2
+ED0FE0ED3FC015FF90B61280160015FC5D15C001F0C8FCB0387FFF80B57EA36C5B25337E
+B22C>I<EB7FFF0003B512E0000F14F848804880EBF007EB800048C7127FA2007E80A300
+FE158048141FB3A7EB01F0EB03F800FE143F267E01FC1300A2EB00FE007F5C147FD83F80
+13FEEBF03F90B5FC6C5C6C5C000314E0C67E90380007F0A26E7EA26E7EA26E7EA2157FA2
+153E21407BB32C>I<387FFFFCB67E15E015F86C803907E007FE1401EC007F6F7E151FA2
+6F7EA64B5AA2153F4BC7FCEC01FE140790B55A5D15E081819038E007FCEC01FE1400157F
+81A8160FEE1F80A5D87FFEEB1FBFB5ECFF00815E6C486D5AC8EA01F029347EB22C>I<90
+381FF80790B5EA0F804814CF000714FF5A381FF01F383FC003497E48C7FC007E147F00FE
+143F5A151FA46CEC0F00007E91C7FC127F7FEA3FE0EA1FFCEBFFC06C13FC0003EBFFC06C
+14F06C6C7F01077F9038007FFEEC07FF02001380153FED1FC0A2ED0FE0A20078140712FC
+A56CEC0FC0A26CEC1F806D133F01E0EB7F009038FE01FF90B55A5D00F914F0D8F83F13C0
+D8700790C7FC23357CB32C>I<007FB612FCB712FEA43AFC007E007EA70078153CC71400
+B3AF90383FFFFCA2497F6D5BA227337EB22C>I<3B7FFF803FFFC0B56C4813E0A36C496C
+13C03B03F00001F800B3AF6D130300015DA26D130700005D6D130F017F495A6D6C485AEC
+E0FF6DB5C7FC6D5B010313F86D5B9038003F802B3480B22C>I<D87FFCEB7FFC486CEBFF
+FEA36C48EB7FFCD80FC0EB07E06D130F000715C0A36D131F00031580A36D133F00011500
+A36D5B0000147EA4017E5BA46D485AA490381F83F0A4010F5B14C7A301075BA214EFA201
+035BA214FFA26D90C7FCA46D5A27347EB22C>I<D87FF0EB07FF486C491380A36C486D13
+00001FC8127CA46C6C5CA76C6C495AA4143E147FA33A03E0FF83E0A214F7A201E113C3A3
+000101E35BA201F113C701F313E7A314C1A200005DA201F713F71480A301FF13FF017F91
+C7FC4A7EA4013E133E29347FB22C>I<3A3FFF03FFE0484913F0148714076C6D13E03A01
+F800FE007F0000495A13FE017E5BEB7F03013F5B1487011F5B14CF010F5B14FF6D5BA26D
+90C7FCA26D5AA26D5AA2497EA2497EA2497F81EB0FCF81EB1FC7EC87F0EB3F83EC03F8EB
+7F01017E7FEBFE00497F0001147E49137F000380491480151FD87FFEEBFFFC6D5AB514FE
+6C15FC497E27337EB22C>I<D87FFCEB7FFC486CEBFFFEA36C48EB7FFCD807F0EB0FC015
+1F000315806D133F12016DEB7F0012006D137E017E13FE017F5BEB3F01EC81F8131FEC83
+F0EB0FC314C7903807E7E0A201035B14EF6DB45AA292C7FC7F5C147EB0903807FFE0497F
+A36D5B27337EB22C>I<387FFFFCB512FEA314FC00FCC7FCB3B3B3B512FC14FEA36C13FC
+17416FB92C>91 D<127012F8A27E127C127E123E123F7EA27F120F7F12077F12037F1201
+7F12007F137C137E133EA2133F7F80130F80130780130380130180130080147C147E143E
+A2143F8081140F81140781140381140181140081157CA2157E153E153F811680150FA2ED
+070021417BB92C>I<387FFFFCB512FEA37EC7127EB3B3B3387FFFFEB5FCA36C13FC1741
+7DB92C>I<EB07C0EB1FF0EB7FFC48B5FC000714C0001F14F0397FFC7FFC39FFF01FFEEB
+C007EB0001007CEB007C003014181F0C7AAE2C>I<007FB6FCB71280A46C150021067B7D
+2C>I<1338137CEA01FC1203EA07F813F0EA0FC0EA1F80A2EA3F00123E127E127CA212FC
+5AA3EAFFC013E013F013F8A2127FA2123F13F0EA1FE0EA07C00E1D72B82C>I<3801FFF0
+000713FE001F6D7E15E048809038C01FF81407EC01FC381F80000006C77EC8127EA3ECFF
+FE131F90B5FC1203120F48EB807E383FF800EA7FC090C7FC12FE5AA47E007F14FEEB8003
+383FE01F6CB612FC6C15FE6C14BF0001EBFE1F3A003FF007FC27247CA32C>I<EA7FF048
+7EA3127F1201AAEC1FE0ECFFF801FB13FE90B6FC16809138F07FC09138801FE091380007
+F049EB03F85BED01FC491300A216FE167EA816FE6D14FCA2ED01F86D13036DEB07F0150F
+9138801FE09138E07FC091B51280160001FB5B01F813F83900F03FC027337FB22C>I<90
+3803FFE0011F13F8017F13FE48B5FC48804848C6FCEA0FF0485A49137E4848131890C9FC
+5A127EA25AA8127EA2127F6C140F6DEB1F806C7E6D133F6C6CEB7F003907FE03FF6CB55A
+6C5C6C6C5B011F13E0010390C7FC21247AA32C>I<EC0FFE4A7EA380EC003FAAEB07F8EB
+3FFE90B512BF4814FF5A3807FC0F380FF00348487E497E48487F90C7FC007E80A212FE5A
+A87E007E5CA2007F5C6C7E5C6C6C5A380FF0073807FC1F6CB612FC6CECBFFE6C143FEB3F
+FC90390FF01FFC27337DB22C>I<EB03FE90381FFFC0017F13F048B57E48803907FE03FE
+390FF800FFD81FE0EB3F805B4848EB1FC090C7120F5A007E15E015075AB7FCA416C000FC
+C9FC7E127EA2127F6CEC03C06DEB07E06C7ED80FF0130F6C6CEB3FC001FF13FF000190B5
+12806C1500013F13FC010F13F00101138023247CA32C>I<ED03F8903907F80FFC90391F
+FE3FFE017FB6FC48B7FC48ECFE7F9038FC0FF82607F003133E3A0FE001FC1CD9C0001300
+001F8049137EA66D13FE000F5CEBE0016C6C485A3903FC0FF048B5FC5D481480D99FFEC7
+FCEB87F80180C8FCA37F6C7E90B512F06C14FE48ECFF804815E04815F03A3FC0001FF848
+C7EA03FC007E1400007C157C00FC157E48153EA46C157E007E15FCD87F801303D83FE0EB
+0FF8D81FFCEB7FF06CB612E0000315806C1500D8003F13F8010713C028387EA42C>103
+D<EA7FF0487EA3127F1201AAEC1FE0EC7FFC9038F9FFFE01FB7F90B6FC9138F03F80ECC0
+1F02807FEC000F5B5BA25BB3267FFFE0B5FCB500F11480A36C01E0140029337FB22C>I<
+1307EB1FC0A2497EA36D5AA20107C7FC90C8FCA7387FFFC080B5FC7EA2EA0007B3A8007F
+B512FCB612FEA36C14FC1F3479B32C>I<EA7FE0487EA3127F1201AA91381FFFF04A13F8
+A36E13F0913800FE004A5A4A5A4A5A4A5A4A5A4A5A4AC7FC14FEEBF1FC13F3EBF7FE90B5
+FCA2EC9F80EC0FC001FE7FEBFC07496C7E496C7E811400157E811680151F3A7FFFC0FFFC
+B500E113FEA36C01C013FC27337EB22C>107 D<387FFFE0B57EA37EEA0003B3B3A5007F
+B61280B712C0A36C158022337BB22C>I<3A7F83F007E09039CFFC1FF83AFFDFFE3FFCD8
+7FFF13FF91B57E3A07FE1FFC3E01FCEBF83F496C487E01F013E001E013C0A301C01380B3
+3B7FFC3FF87FF0027F13FFD8FFFE6D13F8D87FFC4913F0023F137F2D2481A32C>I<397F
+F01FE039FFF87FFC9038F9FFFE01FB7F6CB6FC00019038F03F80ECC01F02807FEC000F5B
+5BA25BB3267FFFE0B5FCB500F11480A36C01E0140029247FA32C>I<EB07FCEB1FFF017F
+13C048B512F048803907FC07FC390FF001FE48486C7E0180133F003F158090C7121F007E
+EC0FC0A348EC07E0A76C140F007E15C0A2007F141F6C15806D133F6C6CEB7F006D5B6C6C
+485A3907FC07FC6CB55A6C5C6C6C13C0011F90C7FCEB07FC23247CA32C>I<397FF01FE0
+39FFF8FFF801FB13FE90B6FC6C158000019038F07FC09138801FE091380007F049EB03F8
+5BED01FC491300A216FE167EA816FE6D14FCA2ED01F86D13036DEB07F0150F9138801FE0
+9138E07FC091B51280160001FB5B01F813F8EC3FC091C8FCAD387FFFE0B57EA36C5B2736
+7FA32C>I<903903FC078090391FFF0FC0017F13CF48B512EF4814FF3807FE07380FF001
+48487E49137F4848133F90C7FC48141F127E150F5AA87E007E141FA26C143F7F6C6C137F
+6D13FF380FF0033807FC0F6CB6FC6C14EF6C6C138F6D130FEB07F890C7FCAD0203B5FC4A
+1480A36E140029367DA32C>I<D87FFEEB3FC0B53801FFF0020713F8021F13FC6C5B3900
+3F7FE1ECFF019138FC00F84A13704A13005CA25C5CA391C8FCAF007FB512E0B67EA36C5C
+26247EA32C>I<90387FF8700003B512F8120F5A5A387FC00F387E00034813015AA36CEB
+00F0007F140013F0383FFFC06C13FE6CEBFF80000314E0C66C13F8010113FCEB0007EC00
+FE0078147F00FC143F151F7EA26C143F6D133E6D13FE9038F007FC90B5FC15F815E000F8
+148039701FFC0020247AA32C>I<131E133FA9007FB6FCB71280A36C1500D8003FC8FCB1
+ED03C0ED07E0A5EC800F011FEB1FC0ECE07F6DB51280160001035B6D13F89038003FE023
+2E7EAD2C>I<3A7FF003FF80486C487FA3007F7F0001EB000FB3A3151FA2153F6D137F39
+00FE03FF90B7FC6D15807F6D13CF902603FE07130029247FA32C>I<3A3FFF03FFF04801
+8713F8A36C010313F03A00FC007E005D90387E01F8013F5BEB1F83EC87E090380FCFC090
+3807EF80EB03FF6D90C7FC5C6D5A147C14FE130180903803EF80903807CFC0EB0FC7EC83
+E090381F01F0013F7FEB7E00017C137C49137E0001803A7FFF01FFFC1483B514FE6C15FC
+140127247EA32C>120 D<3A7FFF01FFFCB5008113FE148314816C010113FC3A03E0000F
+806C7E151F6D140012005D6D133E137C017E137E013E137CA2013F13FC6D5BA2EB0F815D
+A2EB07C1ECC3E0A2EB03E3ECE7C0130114F75DEB00FFA292C7FC80A2143EA2147E147CA2
+14FC5CA2EA0C01003F5BEA7F83EB87E0EA7E0F495A387FFF806C90C8FC6C5A6C5AEA07E0
+27367EA32C>I<15FF02071380141F147F91B512004913C04AC7FCEB03F85CB31307EB1F
+E013FF007F5BB55A49C8FC6D7E6C7FC67F131FEB07F01303B380EB01FEECFFC06D13FF6E
+1380141F14070200130021417BB92C>123 D<127812FCB3B3B3A9127806416DB92C>I<EA
+7FC0EAFFF813FE6D7E6C7FC67F131FEB07F01303B380EB01FEECFFC06D13FF6E1380141F
+147F91B512004913C04AC7FCEB03F85CB31307EB1FE013FF007F5BB55A49C8FC13F8EA7F
+C021417BB92C>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fe ecti1000 10 33
+/Fe 33 122 df<EE3FFC4BB51280923907E007C092391F8001E0DB3F0013F0037E13034B
+1307A24A5A18E04A48EB038094C7FCA314075DA4140F5DA3010FB7FCA25F903A001F8000
+7EA217FE023F5C92C7FCA216015F5C147E16035FA214FE4A13075FA30101140F5F4AECC1
+C0A2161F1783010316805CA2EF870013074A5CEE0F8EEE079EEE03FC010FEC00F04A91C7
+FCA35C131FA2001C90CAFC127E5BEAFE3E133C137CEAF878EA78F0EA3FE0EA0F80344C82
+BA2F>28 D<150C151C153815F0EC01E0EC03C0EC0780EC0F00141E5C147C5C5C495A1303
+495A5C130F49C7FCA2133EA25BA25BA2485AA212035B12075BA2120F5BA2121FA290C8FC
+A25AA2123EA2127EA2127CA412FC5AAD1278A57EA3121C121EA2120E7EA26C7E6C7EA212
+001E5274BD22>40 D<140C140E80EC0380A2EC01C015E0A2140015F0A21578A4157C153C
+AB157CA715FCA215F8A21401A215F0A21403A215E0A21407A215C0140F1580A2141F1500
+A2143EA25CA25CA2495AA2495A5C1307495A91C7FC5B133E133C5B5B485A12035B48C8FC
+120E5A12785A12C01E527FBD22>I<4B7EA3150393C8FCA35D1506A3150E150CA3151C15
+18A315381530A31570B912E0A2C80060C8FC15E05DA314015DA3140392C9FCA35C1406A3
+140E140CA3141C1418A2333275AD40>43 D<EA03C0EA07F0120F121F13F8A313F0EA07B0
+EA003013701360A213E013C01201EA038013005A120E5A5A5A5A5A0D197A8819>I<120E
+EA3F80127F12FFA31300127E123C0909778819>46 D<0103B612FEEFFFC018F0903B0007
+F8000FF84BEB03FCEF00FE020F157FF03F804B141F19C0021F150F19E05D1807143F19F0
+5DA2147FA292C8FCA25C180F5CA2130119E04A151FA2130319C04A153FA201071780187F
+4A1600A2010F16FEA24A4A5A60011F15034D5A4A5D4D5A013F4B5A173F4A4AC7FC17FC01
+7FEC03F84C5A91C7EA1FC04949B45A007F90B548C8FCB712F016803C397CB83F>68
+D<0103B512F8A390390007F8005DA2140FA25DA2141FA25DA2143FA25DA2147FA292C7FC
+A25CA25CA21301A25CA21303A25CA21307A25CA2130FA25CA2131FA25CA2133FA25CA213
+7FA291C8FC497EB6FCA25C25397CB820>73 D<0107B512FCA25E9026000FF8C7FC5D5D14
+1FA25DA2143FA25DA2147FA292C8FCA25CA25CA21301A25CA21303A25CA21307A25CA213
+0F170C4A141CA2011F153C17384A1478A2013F157017F04A14E01601017F140317C091C7
+1207160F49EC1F80163F4914FF000102071300B8FCA25E2E397BB834>76
+D<ED03FE92383FFFC09238FC07F0913903E001F891390F80007C023FC77E027E8002F815
+804948EC0FC0EB07E04948EC07E0131F4A15F049C81203137E01FE16F8485AA2485AA248
+5AA2120F5B001F16075B123FA34848ED0FF0A448C9EA1FE0A3EF3FC0A21880177F18005F
+5F16015F6C4B5A4C5AA24C5A6C4B5A6D4A5A001F93C7FC6D147E000F5D6C6CEB03F06C6C
+495A6C6CEB0F806C6C013FC8FC90383F01FC90381FFFE0010190C9FC353D74BA40>79
+D<ED03FE92383FFFC09238FC07F0913903E001F891390FC0007C023FC77E027E804A1580
+D901F0EC0FC013074948EC07E0495A4A15F049C8FC49150301FE16F8485AA2485AA2485A
+A2120F491507121FA2485AA34848ED0FF0A448C9EA1FE0A3EF3FC0A21880177F4817005F
+5F16015F007F4B5A5F91380F800791393FE00FE06C903970601FC0902680E0305B261F81
+C049C7FC913880187ED80FC35C3A07E30019F00003EC1FE0D801FB14806CB46C48C8FC90
+263F81FC13186DB45A01010138133890C7003C1330177017F05FED3E03ED3F07EEFFC05F
+A294C7FC5E6F5A6F5AED07E0354B74BA40>81 D<92383FC00E913901FFF01C020713FC91
+391FC07E3C91393F001F7C027CEB0FF84A130749481303495A4948EB01F0A2495AA2011F
+15E091C7FCA34915C0A36E90C7FCA2806D7E14FCECFF806D13F015FE6D6D7E6D14E00100
+80023F7F14079138007FFC150F15031501A21500A2167C120EA3001E15FC5EA3003E4A5A
+A24B5AA2007F4A5A4B5A6D49C7FC6D133ED8F9F013FC39F8FC03F839F07FFFE0D8E01F13
+8026C003FCC8FC2F3D7ABA2F>83 D<0007B812E0A25AD9F800EB001F01C049EB07C0485A
+D900011403121E001C5C003C17801403123800785C00701607140700F01700485CA2140F
+C792C7FC5DA2141FA25DA2143FA25DA2147FA292C9FCA25CA25CA21301A25CA21303A25C
+A21307A25CA2130FA25CEB3FF0007FB512F8B6FCA2333971B83B>I<14F8EB07FE90381F
+871C90383E03FE137CEBF801120148486C5A485A120FEBC001001F5CA2EA3F801403007F
+5C1300A21407485C5AA2140F5D48ECC1C0A2141F15831680143F1587007C017F1300ECFF
+076C485B9038038F8E391F0F079E3907FE03FC3901F000F0222677A42A>97
+D<133FEA1FFFA3C67E137EA313FE5BA312015BA312035BA31207EBE0F8EBE7FE9038EF0F
+80390FFC07C013F89038F003E013E0D81FC013F0A21380A2123F1300A214075A127EA214
+0F12FE4814E0A2141F15C05AEC3F80A215005C147E5C387801F8007C5B383C03E0383E07
+C0381E1F80D80FFEC7FCEA01F01C3B77B926>I<147F903803FFC090380FC1E090381F00
+70017E13784913383901F801F83803F003120713E0120FD81FC013F091C7FC485AA2127F
+90C8FCA35A5AA45AA3153015381578007C14F0007EEB01E0003EEB03C0EC0F806CEB3E00
+380F81F83803FFE0C690C7FC1D2677A426>I<ED01F815FFA3150316F0A21507A216E0A2
+150FA216C0A2151FA21680A2153FA202F81300EB07FE90381F877F90383E03FF017C5BEB
+F80112013803F00048485B120FEBC001121F5DEA3F801403127F01005BA214075A485CA2
+140FA248ECC1C0A2141F15C3ED8380143F1587007C017F1300ECFF076C485B9038038F8E
+391F0F079E3907FE03FC3901F000F0253B77B92A>I<147F903803FFC090380FC1E09038
+3F00F0017E13785B485A485A485A120F4913F8001F14F0383F8001EC07E0EC1F80397F81
+FF00EBFFF8148090C8FC5A5AA55AA21530007C14381578007E14F0003EEB01E0EC03C06C
+EB0F806CEB3E00380781F83803FFE0C690C7FC1D2677A426>I<ED07C0ED1FF0ED3E38ED
+7C3CEDF8FC15F9140115F1020313F8EDF0F0160014075DA4140F5DA4141F5D010FB512C0
+5B16809039003F800092C7FCA45C147EA414FE5CA413015CA413035CA413075CA4130F5C
+A3131F5CA391C8FC5B121CEA7E3EA2EAFE3C137C1378EAF8F01278EA3FC0EA0F80264C82
+BA19>I<EC07C0EC3FF09138FC38E0903901F01FF0EB03E0903807C00FEB0F80011F1307
+D93F0013E05B017E130F13FE4914C01201151F1203491480A2153F1207491400A25DA249
+137EA215FEA25D00031301140314076C6C485A0000131FEB787BEB3FF390380FC3F0EB00
+031407A25DA2140F5D121C007E131F5D00FE49C7FC147E5C387801F8387C07E0381FFF80
+D803FEC8FC24367CA426>I<EB03F0EA01FFA3EA00075CA3130F5CA3131F5CA3133F91C8
+FCA35B90387E07F0EC1FFCEC783E9038FFE01F02C01380EC800F1400485A16C05B49EB1F
+8012035BA2153F000715005BA25D000F147E5B15FE5D121FD98001131C15F8163C003F01
+031338010013F0A216704814E0007E15F016E0EDE1C000FE903801E38048903800FF0000
+38143C263B7BB92A>I<EB01C0EB07E014F0130F14E01307EB038090C7FCAB13F0EA03FC
+EA071EEA0E1F121CA212385B1270A25BEAF07E12E013FEC65AA212015B1203A25B12075B
+A2000F13E013C013C1001F13C01381A2EB83801303EB0700A2130E6C5AEA07F8EA01E014
+3879B619>I<EB0FC0EA07FFA3EA001F1480A2133FA21400A25BA2137EA213FEA25BA212
+01A25BA21203A25BA21207A25BA2120FA25BA2121FA25BA2123FA290C7FCA25AA2EA7E0E
+A212FE131EEAFC1CA2133C133812F81378EA7870EA7CE0121FEA0F80123B79B915>108
+D<D801E001FEEB07F03C07F803FF801FFC3C0E3C0F07C0783E3C1E3E3C03E1E01F261C1F
+78D9F3C013803C383FF001F7800F02E01400007801C013FE007018C002805B4A4848EB1F
+80EAF07FD8E07E5CA200000207143F01FE1700495CA2030F5C0001177E495C18FE031F5C
+120349DA8001131C18F8033F153C00070403133849020013F0A24B1570000F17E049017E
+15F019E003FEECE1C0001FEE01E34949903800FF000007C70038143C3E2679A444>I<D8
+01E013FE3A07F803FF803A0E3C0F07C03A1E3E3C03E0261C1F787F39383FF00114E00078
+13C000708114804A485AEAF07FEAE07EA20000140701FE5C5BA2150F00015D5B151F5E12
+034990383F8380160316070007027F130049137EA2160E000F147C49141E161C5E001FEC
+3C7849EB1FE00007C7EA0780292679A42F>I<147F903803FFC090380FC1F090381F00F8
+017E137C5B4848137E4848133E0007143F5B120F485AA2485A157F127F90C7FCA215FF5A
+4814FEA2140115FC5AEC03F8A2EC07F015E0140F007C14C0007EEB1F80003EEB3F00147E
+6C13F8380F83F03803FFC0C648C7FC202677A42A>I<9039078007C090391FE03FF09039
+3CF0787C903938F8E03E9038787FC00170497EECFF00D9F0FE148013E05CEA01E113C15C
+A2D80003143FA25CA20107147FA24A1400A2010F5C5E5C4B5A131F5EEC80035E013F495A
+6E485A5E6E48C7FC017F133EEC70FC90387E3FF0EC0F8001FEC9FCA25BA21201A25BA212
+03A25B1207B512C0A3293580A42A>I<3903C003F0390FF01FFC391E783C0F381C7C703A
+3C3EE03F8038383FC0EB7F800078150000701300151CD8F07E90C7FCEAE0FE5BA2120012
+015BA312035BA312075BA3120F5BA3121F5BA3123F90C9FC120E212679A423>114
+D<14FE903807FF8090380F83C090383E00E04913F00178137001F813F00001130313F0A2
+15E00003EB01C06DC7FC7FEBFFC06C13F814FE6C7F6D13807F010F13C01300143F141F14
+0F123E127E00FE1480A348EB1F0012E06C133E00705B6C5B381E03E06CB45AD801FEC7FC
+1C267AA422>I<EB0380EB07C0130FA4131F1480A3133F1400A35B137E007FB5FCA2B6FC
+3800FC00A312015BA312035BA312075BA3120F5BA3121FEB801CA2143C003F1338EB0078
+147014F014E0EB01C0EA3E03381F0780380F0F00EA07FCEA01F0183579B31C>I<01F013
+0ED803FC133FD8071EEB7F80EA0E1F121C123C0038143F49131F0070140FA25BD8F07E14
+0000E08013FEC6485B150E12015B151E0003141C5BA2153C000714385B5DA35DA24A5A14
+0300035C6D48C7FC0001130E3800F83CEB7FF8EB0FC0212679A426>118
+D<903907E007C090391FF81FF89039787C383C9038F03E703A01E01EE0FE3803C01F0180
+13C0D8070014FC481480000E1570023F1300001E91C7FC121CA2C75AA2147EA214FEA25C
+A21301A24A1370A2010314F016E0001C5B007E1401010714C000FEEC0380010F1307010E
+EB0F0039781CF81E9038387C3C393FF03FF03907C00FC027267CA427>120
+D<13F0D803FCEB01C0D8071EEB03E0D80E1F1307121C123C0038140F4914C01270A24913
+1FD8F07E148012E013FEC648133F160012015B5D0003147E5BA215FE00075C5BA214015D
+A314035D14070003130FEBF01F3901F87FE038007FF7EB1FC7EB000F5DA2141F003F5C48
+133F92C7FC147E147C007E13FC387001F8EB03E06C485A383C1F80D80FFEC8FCEA03F023
+3679A428>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Ff cmsy10 10 1
+/Ff 1 16 df<EB1FF0EBFFFE487F000714C04814E04814F04814F8A24814FCA3B612FEA9
+6C14FCA36C14F8A26C14F06C14E06C14C0000114006C5BEB1FF01F1F7BA42A>15
+D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fg ecbx1000 10 36
+/Fg 36 119 df<913803FFC0027F13F00103B512FC010FEB00FED93FF8133FD97FE0EBFF
+8049485A5A1480484A13C04A6C1380A36F1300167E93C7FCA592383FFFC0B8FCA4000390
+C7FCB3ABB5D8FC3F13FFA4303A7EB935>28 D<B61280A819087F9620>45
+D<EA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3FE0EA0F800D0D798C1B>I<141E143E
+14FE1307137FB5FCA3138FEA000FB3B3A5007FB61280A4213679B530>49
+D<EB0FFE90387FFFC048B512F0000714FC390FE03FFF261F800F1380263F000313C0D87F
+8014E0EBE00100FF6D13F07FA2ED7FF8A46C5A6C5A0006C7FCC8FCEDFFF0A216E05C16C0
+4A138016004A5A4A5AEC1FF05D4A5A4AC7FC14FE495AD903F01378495A495A495A49C712
+F8017C14F05B49130148B6FC5A5A5A5A5A4815E0B7FCA425367BB530>I<EC0FF8ECFFFE
+0103EBFF8090390FF80FC090393FE003E090397F8001F09038FF000F48EC1FF84848133F
+485A120F5B121FA2003FEC1FF0ED0FE0484890C7FCA31408EC7FF039FFF1FFFC01F313FF
+D9F78013809039FF007FC049EB3FE04914F0ED1FF85B16FCA34914FEA4127FA5123F16FC
+A26C7E16F8000F143F6D14F0000715E06C6CEB7FC03A01FF81FF806C90B51200013F13FC
+010F13F00101138027377CB530>54 D<EA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3F
+E0EA0F80C7FCABEA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3FE0EA0F800D2579A41B
+>58 D<B812C017FC17FF18C028007FF000037F04007F717E717E171F84A2717EA74D5AA2
+60173F4D5A4D5A4C13C0040F5B91B600FCC7FCA2EFFF8002F0C713F0EF3FF8717E717E71
+7E19807113C0A319E0A719C0A25F4D138019005FEF7FFE4C485AB912F018C095C7FC17F0
+3B397DB844>66 D<DB3FFCEB01C00203B5EAC003021FECF00791B6EAFC0F01039039FC00
+FF3F4901C0EB1FFFD91FFEC77E49481403D97FF080494880485B48177F4849153F4890C9
+FC181F485A180F123F5B1807127FA24993C7FC12FFAD127F7FF003C0123FA27F001F1707
+A26C6C1780180F6C6D16006C6D5D6C173E6C6D157ED97FF85D6D6C4A5A6DB44A5A010701
+C0EB0FE06D01FCEBFF80010090B548C7FC021F14F8020314E09126003FFEC8FC3A3B7BB9
+45>I<B87E17F817FF18C028007FF8000713F09338007FF8EF1FFE717E050313807113C0
+A27113E0F07FF0A2F03FF8A219FC181FA219FEA419FFAC19FEA419FC183FA219F8187F19
+F0F0FFE0A24D13C04D13804D1300EF1FFEEF7FFC933807FFF0B912C095C7FC17FC178040
+397DB849>I<B612FCA439007FF800B3B3ADB612FCA41E397DB824>73
+D<B7FCA426007FF8C9FCB3ACEF0780A5170F1800A35FA25FA25F5F5E5EEE0FFE167FB8FC
+A431397DB839>76 D<B500F80403B512F06E5EA26E5ED8007FF1E000A2D97BFF161EA201
+796D5DA201786D5DA26E6C5DA36E6C4A5AA26E6C4A5AA26E6C4A5AA26E6C4A5AA26E6C14
+1EA36E6D5BA26E6D5BA26F6C5BA26F6C485AA36F6C485AA26F6C485AA26F6C48C7FCA292
+3803FF1EA36F13BCA26F13F8A2705AA2705AA213FCB500FC6D4848B612F0A2EE0F80EE07
+0054397DB85B>I<B500FC0203B512F0A28080C66C6D90390003F0006F6E5A81017B7F13
+798101787F6E7E6E7E6E7F6E7FA26E7F6E7F6E7F6E7F6F7E153F826F13806F13C06F13E0
+6F13F06F13F88117FCEE7FFEEE3FFF7013817013C17013E18218F17013F97013FDEF7FFF
+8383A28383838383187FA2183F181F01FC160FB500FC150718031801A244397DB84B>I<
+EDFFF8020FEBFF80027F14F0903A01FFC01FFC010790380007FFD91FFC010113C0D93FF0
+6D6C7E49486E7E49486E7E48496E7E48834890C86C7EA248486F1380A248486F13C0A200
+3F18E0A348486F13F0A400FF18F8AC007F18F06D5DA3003F18E0A26D5D001F18C0A26C6C
+4B13806C18006E5C6C6D4A5A6C5F6C6D4A5A6D6C4A5AD93FFC49485A6DB401075B0107D9
+C01F90C7FC010190B512FC6D6C14F0020F1480020001F8C8FC3D3B7BB948>I<B8FC17F0
+17FEEFFF8028007FF8000F13C0040113E07013F0EF7FF8EF3FFCA2EF1FFEA218FFA818FE
+A2EF3FFCA2EF7FF8EFFFF04C13E0040F13C091B7120017FC17E002F8C9FCB3A4B612FCA4
+38397DB841>I<EDFFF8020FEBFF80027F14F0903A01FFE03FFC010790380007FFD91FFC
+010113C049486D7FD97FE0EC3FF049486E7E488348496E7E4890C86C7EA248486F1380A2
+001F18C04981003F18E0A3007F18F04981A300FF18F8AC007F18F0A36D5D003F18E0A36C
+6C4B13C0A2000FDA1FC014806C6C90267FF0071300EDFFF86C903A81F07C0FFE6C903AC3
+C01E1FFC6CDA800F5BD97FE3ECBFF0D93FF36DB45AD91FFF5D010701C091C7FC01019038
+F01FFC6D6CB500F01308020F6E131C0200EBF9FC92260001FE133C9438FF80FC18FF8219
+F8A28319F0A27113E0A27113C0711380711300EF01FC3E4A7BB948>I<D907FF130E013F
+EBE01E90B5EAF83E0003ECFE7E3A07FC01FFFE390FF0001F4848130F4848130349130100
+7F140090C8FC167E5A163EA27F161E7F7F6D91C7FC13FC387FFFE014FEECFFF06C14FE6F
+7E6C816C15F06C816C81C681133F010F801301D9000F1480EC007F030F13C01503818100
+F0157FA3163FA27E17807E167F6C16007E6D14FE01E0495A01F813039039FF801FF800FC
+90B512E0D8F83F5CD8F00749C7FC39E0007FF02A3B7BB935>83 D<B600FC011FB512C0A4
+26007FF8C8381FC000725AB3B3181F013F94C7FC8060011F163E6D6C157E187C6D6C15FC
+6D6D495A6D6DEB07F06D01F0EB1FE0DA7FFEEBFFC0021FB6C8FC02075C020014F0030F13
+80423A7DB849>85 D<B600F00103B512E0A4C601F0C83807F0006E5E017F5F6E150FA201
+3F5F6E151F011F94C7FC6E5D6D163E6F147E6D167CA26F14FC6D5E6F13016D5E6F13036D
+5E811707027F5D6F130F023F5D6F131F021F92C8FC815F6E143EEE807E6E147CEEC0FC6E
+5C16E016E16E5C16F36E5C16FF6F5BA36F5BA26F90C9FCA26F5AA36F5AA26F5AA26F5A43
+3A7EB848>I<B6D8E01FB500FC90383FFFFCA4000101F0C7D83FFCC8EA7E006C71153C17
+1F6E197C017F701578836E7014F8013F6F5E6E1801011F4B6D5CA26E18036D4B6D5CA26D
+6D496D495A173C6F170F6D037C6D91C7FCEF787F6F5F6D4B6C6C131E816D02016E5BEFE0
+1F03F8177C027F01036E13784D7E03FCEE80F8023F49486C5C15FE021F010FEDC1E04D7E
+03FF16C36E49EDE3C0041E7F049E15F76E01BC6D5C04FC15FF6E95C8FC4C80A26E5F4C14
+3F6E5F4C141FA2037F5E4C140FA26F486E5AA2031F5E93C812036F5E5E3A7EB863>I<13
+FFB5FCA412077EAF4AB47E020F13F0023F13FC9138FE03FFDAF00013804AEB7FC00280EB
+3FE091C713F0EE1FF8A217FC160FA217FEAA17FCA3EE1FF8A217F06E133F6EEB7FE06E14
+C0903AFDF001FF80903AF8FC07FE009039F03FFFF8D9E00F13E0D9C00390C7FC2F3A7EB9
+35>98 D<EE7F80ED7FFFA4150381AF903801FF81010F13F1013F13FD9038FFC07F0003EB
+001FD807FC1307000F8048487F5B123FA2485AA312FFAA127FA27F123FA26C6C5B000F5C
+6C6C5B6C6C4913C02701FF80FD13FE39007FFFF9011F13E1010113012F3A7DB935>100
+D<903803FF80011F13F0017F13FC3901FF83FE3A03FE007F804848133F484814C0001FEC
+1FE05B003FEC0FF0A2485A16F8150712FFA290B6FCA301E0C8FCA4127FA36C7E1678121F
+6C6C14F86D14F000071403D801FFEB0FE06C9038C07FC06DB51200010F13FC010113E025
+257DA42C>I<EC1FF0903801FFFC010713FF90391FF87F8090383FE0FFD9FFC113C0A248
+1381A24813016E1380A2ED3E0092C7FCA8B6FCA4000390C8FCB3ABB512FEA4223A7DB91D
+>I<161FD907FEEBFFC090387FFFE348B6EAEFE02607FE07138F260FF801131F48486C13
+8F003F15CF4990387FC7C0EEC000007F81A6003F5DA26D13FF001F5D6C6C4890C7FC3907
+FE07FE48B512F86D13E0261E07FEC8FC90CAFCA2123E123F7F6C7E90B512F8EDFF8016E0
+6C15F86C816C815A001F81393FC0000F48C8138048157F5A163FA36C157F6C16006D5C6C
+6C495AD81FF0EB07FCD807FEEB3FF00001B612C06C6C91C7FC010713F02B377DA530>I<
+EA01F0EA07FC487EA2487EA56C5AA26C5AEA01F0C8FCA913FF127FA412077EB3A9B512F8
+A4153B7DBA1B>105 D<13FFB5FCA412077EAF92380FFFE0A4923803FC0016F0ED0FE0ED
+1F804BC7FC157E5DEC03F8EC07E04A5A141FEC7FE04A7E8181A2ECCFFEEC0FFF496C7F80
+6E7F6E7F82157F6F7E6F7E82150F82B5D8F83F13F8A42D3A7EB932>107
+D<13FFB5FCA412077EB3B3ACB512FCA4163A7DB91B>I<01FED97FE0EB0FFC00FF902601
+FFFC90383FFF80020701FF90B512E0DA1F81903983F03FF0DA3C00903887801F000749DA
+CF007F00034914DE6D48D97FFC6D7E4A5CA24A5CA291C75BB3A3B5D8FC1FB50083B512F0
+A44C257DA451>I<01FEEB7FC000FF903803FFF8020F13FE91381F03FFDA3C0113800007
+13780003497E6D4814C05CA25CA291C7FCB3A3B5D8FC3F13FFA430257DA435>I<903801
+FFC0010F13F8017F13FFD9FF807F3A03FE003FE048486D7E48486D7E48486D7EA2003F81
+491303007F81A300FF1680A9007F1600A3003F5D6D1307001F5DA26C6C495A6C6C495A6C
+6C495A6C6C6CB45A6C6CB5C7FC011F13FC010113C029257DA430>I<9038FE03F000FFEB
+0FFEEC3FFF91387C7F809138F8FFC000075B6C6C5A5CA29138807F80ED3F00150C92C7FC
+91C8FCB3A2B512FEA422257EA427>114 D<90383FF0383903FFFEF8000F13FF381FC00F
+383F0003007E1301007C130012FC15787E7E6D130013FCEBFFE06C13FCECFF806C14C06C
+14F06C14F81203C614FC131F9038007FFE140700F0130114007E157E7E157C6C14FC6C14
+F8EB80019038F007F090B512C000F8140038E01FF81F257DA426>I<130FA55BA45BA25B
+5BA25A1207001FEBFFE0B6FCA3000390C7FCB21578A815F86CEB80F014816CEBC3E09038
+3FFFC06D1380903803FE001D357EB425>I<B539F001FFF8A4000390C7EA1F00161E6E13
+3E6C153C6E137C6C15786E13F8017F5CECF001013F5C14F8011F495AA2ECFC07010F5CEC
+FE0F010791C7FC6E5A6D131E15BE6D13BC15FC6D5BA36E5AA26E5AA26E5AA26E5AA22D25
+7EA432>118 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fh ecrm1000 10 89
+/Fh 89 126 df<486C1360000314E039070001C0000EEB038048EB070000181306003813
+0E0030130C0070131C00601318A200E01338481330A400CEEB338039FF803FE001C013F0
+A3007F131FA2393F800FE0390E0003801C1981B91C>16 D<001C1307007FEB1FC039FF80
+3FE0A201C013F0A3007F131F001CEB073000001300A400011470491360A2000314E090C7
+12C048130100061480000E130348EB070048130E485B006013181C1980B91C>I<B81280
+A2290280962A>21 D<DA0FF813FC91397FFF07FF903B01F807DF83C0903A07E001FF0F90
+3B1F8007FE1FE090393F000FFC137E16F85B9338F007804848010790C7FC1503ACB812F8
+A32801F80003F0C7FCB3AB486C497E267FFFE0B512F0A3333B7FBA30>27
+D<EC0FF8EC7FFE903901F80780903907E001C090391F8000E090383F0007017E497EA25B
+A2485A6F5AED018092C8FCA9ED03F0B7FCA33901F8000F1503B3AA486C497E267FFFE0B5
+12C0A32A3B7FBA2E>I<DA0FF0EB1FF0DA7FFEEBFFFC903B01F80F83F00F903C07E001CF
+C00380903C1F8000FF0001C090273F0007FE130F017E4948497EA2495CA248485C03076E
+5A03030203C7FC95C8FCA9F007E0BAFCA33C01F80003F0001F1807B3AA486C496C497E26
+7FFFE0B500C1B51280A3413B7FBA45>30 D<EB0380A3EB0FF0EB7FFE48B512803903F38F
+C03907C381E0390F8380F0D81F031338123E003C141C007C140C150E0078143E00F814FE
+1481A400FCEB80FC157800FE140012FF127F13C313E3EA3FFF6C7F14F86C13FE6CEBFF80
+000114C06C14E0013F13F01303ECBFF8148FEC83FC1481A2EC80FE157E123C12FF153EA4
+12FE00F8143C00E0147C12600070147815F8003814F0003C1381001EEB83E0000FEB87C0
+3907E39F803901FFFE006C5BEB1FE0EB0380A41F437BBD2A>36 D<141FEC7FC0903801F0
+E0903803C0600107137090380F803090381F00381518A25BA2133E133F15381530A21570
+5D5D140190381F838092CAFC1487148E02DC49B51280EB0FF85C4A9039003FF8000107ED
+0FC06E5D71C7FC6E140E010F150CD91DFC141C01391518D970FE143801E015302601C07F
+1470D803805D00076D6C5BD80F00EBC00148011F5C4890380FE003003E6E48C8FC007E90
+3807F8060203130E00FE6E5A6E6C5A1400ED7F706C4B13036F5A6F7E6C6C6D6C5B701306
+6C6C496C130E6DD979FE5B281FF001F07F133C3C07F80FE03FC0F86CB539800FFFF0C690
+26FE000313C0D91FF0D9007FC7FC393E7DBB41>38 D<121C127FEAFF80A213C0A3127F12
+1C1200A412011380A2120313005A1206120E5A5A5A12600A1979B917>I<146014E0EB01
+C0EB0380EB0700130E131E5B5BA25B485AA2485AA212075B120F90C7FCA25A121EA2123E
+A35AA65AB2127CA67EA3121EA2121F7EA27F12077F1203A26C7EA26C7E1378A27F7F130E
+7FEB0380EB01C0EB00E01460135278BD20>I<12C07E12707E7E7E120F6C7E6C7EA26C7E
+6C7EA21378A2137C133C133E131EA2131F7FA21480A3EB07C0A6EB03E0B2EB07C0A6EB0F
+80A31400A25B131EA2133E133C137C1378A25BA2485A485AA2485A48C7FC120E5A5A5A5A
+5A13527CBD20>I<1530B3A8B912FCA2C80030C8FCB3A836367BAF41>43
+D<121C127FEAFF80A213C0A3127F121C1200A412011380A2120313005A1206120E5A5A5A
+12600A19798817>I<B512FCA516057F941C>I<121C127FEAFF80A5EA7F00121C09097988
+17>I<1506A2150E150CA2151C151815381530A215701560A215E015C0A214011580A214
+0315005C1406A2140E140CA2141C1418A214381430A21470146014E05CA213015CA21303
+91C7FCA25B1306A2130E130C131C1318A213381330A213701360A213E05BA212015B1203
+90C8FCA25A1206A2120E120CA2121C1218A21238123012701260A212E05AA21F537BBD2A
+>I<EB03F8EB1FFF90387E0FC09038F803E03901E000F0484813780007147C48487FA248
+C77EA2481580A3007EEC0FC0A500FE15E0B3007E15C0A4007F141F6C1580A36C1500A26C
+6C133EA26C6C5B6C6C5BEBF0013900F803E090387E0FC0D91FFFC7FCEB03F823397DB62A
+>I<EB01C013031307131F13FFB5FCA2131F1200B3B3A7497E007FB512F0A31C3779B62A>
+I<EB0FF0EB7FFE48B57E3903E03FE0390F000FF0001E6D7E001C6D7E486D7E5A6E7E1260
+12FE6CEC7F807FA56CC7FC121CC8FCEDFF00A25D14015D14035D4A5A4A5A5D4A5A4AC7FC
+147E5C495A14E0495A495A49C8FC011EEB01805B5B49130348481400485A485A90C75A48
+B6FC5A5A485CB6FCA321377CB62A>I<EB07F8EB3FFF90B512C03901F80FF03903C007F8
+48486C7E390E0001FEEA0F80391FE000FF7FA56C5A6C5AC7485AA25D14035D4A5A5DEC0F
+80027FC7FCEB1FFCECFF809038000FE06E7EEC01FC816E7EED7F80A216C0A2153F16E0A2
+121EEA7F80A2487EA316C0157F491480007EC7FC0070ECFF006C495A121E390F8003F839
+07F00FF00001B512C06C6C90C7FCEB0FF823397DB62A>I<1538A2157815F8A214011403
+1407A2140F141F141B14331473146314C313011483EB030313071306130C131C13181330
+1370136013C01201EA038013005A120E120C5A123812305A12E0B712F8A3C73803F800AA
+4A7E0103B512F8A325387EB72A>I<0006140CD80780133C9038F003F890B5FC5D5D1580
+92C7FC14FC38067FE090C9FCAAEB07F8EB1FFE9038780F809038E007E03907C003F0496C
+7E130000066D7E81C8FC8181A21680A4121C127F5A7FA390C713005D12FC00605C12704A
+5A6C5C6C1303001E495A6C6C485A3907E03F800001B5C7FC38007FFCEB1FE021397CB62A
+>I<EC3FC0903801FFF0010713FC90380FE03E90383F800790387E001F49EB3F80484813
+7F485A12075B000FEC3F0049131E001F91C7FC5B123FA3127F90C9FCEB01FC903807FF80
+39FF1E07E090383801F0496C7E01607F01E0137E497F16805BED1FC0A390C713E0A57EA4
+7F123F16C0A2001FEC3F807F000F15006D5B000714FE6C6C5B6C6C485A3900FE07F09038
+7FFFC0011F90C7FCEB03FC23397DB62A>I<12301238123E003FB612E0A316C05A168016
+000070C712060060140E5D5D00E014304814705D5DC712014A5A4AC7FC1406140E5CA25C
+1478147014F05C1301A213035C1307A2130FA3131F5CA2133FA5137FA96DC8FC131E233A
+7BB72A>I<EB03F8EB1FFF017F13C09038FC07F03901E001F83903C0007C4848133C90C7
+123E48141E000E141F001E80A3121FA26D5B6D131E7FD80FF85B6D137C01FF13786C6D5A
+6CEBE3E0ECF780C601FFC7FC6D5A6D6C7E010F13E0013F7F01F97F3901E07FFE48486C7E
+380F800F48486C1380001E010113C0487F007C143F0078EC1FE0150F00F81407481403A2
+1501A36C15C0A200781403007C15806C14076CEC0F006C6C131ED807E0137C3903F803F0
+C6B55A013F1380D907FCC7FC23397DB62A>I<EB03F8EB1FFF017F13C03901FC07E04848
+6C7E3907E001F8000F6D7E4848137E5B003F80A248C71380A25AED1FC0A516E0A56C143F
+A36C7E157F121F6C6C13FF6C6C13DF000313013901F0039F3900FC0F1FD93FFC13C0EB07
+F090C7FCA2153F1680A216005D120F486C137E486C5BA24A5A4A5A49485A381F000F001C
+EB1F80260F807FC7FC3807FFFE000113F838003FC023397DB62A>I<121C127FEAFF80A5
+EA7F00121CC7FCB2121C127FEAFF80A5EA7F00121C092479A317>I<121C127FEAFF80A5
+EA7F00121CC7FCB2121C127FEAFF80A213C0A3127F121C1200A412011380A2120313005A
+1206120E5A5A5A12600A3479A317>I<EF01C0EF0780EF1E0017F8EE03E0040FC7FC163C
+16F0ED03C0030FC8FC153CEC01F0EC07C0021EC9FC1478EB01E0EB0780011ECAFC13F8EA
+03E0000FCBFC123C12F0A2123C120FEA03E0EA00F8131EEB0780EB01E0EB0078141EEC07
+C0EC01F0EC003C150FED03C0ED00F0163C160FEE03E0EE00F8171EEF0780EF01C0322E79
+AB41>I<007FB812F8B912FCCCFCB0B912FC6C17F836147B9E41>I<12E01278121EEA07C0
+EA01F0EA003C130FEB03C0EB00F0143C140FEC03E0EC00F8151EED0780ED01E0ED007816
+1EEE07C0EE01F0EE003C170FEF03C0A2EF0F00173CEE01F0EE07C0041EC7FC1678ED01E0
+ED0780031EC8FC15F8EC03E0020FC9FC143C14F0EB03C0010FCAFC133CEA01F0EA07C000
+1ECBFC127812E0322E79AB41>I<EB3FE03801FFFE3907C03F80390E000FC0003CEB07F0
+00301303007014F8007C130100FE14FC7EA4127E003CEB03F8C7FCEC07F0A2EC0FE0EC1F
+80EC3F00147E147C5C495A5C495A5CA249C7FCA31306AA90C8FCA8130EEB3F80497EA56D
+5A010EC7FC1E3B7CBA27>I<1538A3157CA315FEA34A7EA34A6C7EA202077FEC063FA202
+0E7FEC0C1FA2021C7FEC180FA202387FEC3007A202707FEC6003A202C07F1501A2D90180
+7F81A249C77F167FA20106810107B6FCA24981010CC7121FA2496E7EA3496E7EA3496E7E
+A213E0707E1201486C81D80FFC02071380B56C90B512FEA3373C7DBB3E>65
+D<B712E016FC16FF0001903980007FC06C90C7EA1FE0707E707E707EA2707EA283A75F16
+035F4C5A4C5A4C5A4C5AEEFF8091B500FCC7FCA291C7EA7F80EE1FE0EE07F0707E707E83
+707EA21880177F18C0A7188017FFA24C13005F16034C5AEE1FF8486DEB7FF0B812C094C7
+FC16F832397DB83B>I<913A01FF800180020FEBE003027F13F8903A01FF807E07903A03
+FC000F0FD90FF0EB039F4948EB01DFD93F80EB00FF49C8127F01FE153F12014848151F48
+48150FA248481507A2485A1703123F5B007F1601A35B00FF93C7FCAD127F6DED0180A312
+3F7F001F160318006C7E5F6C7E17066C6C150E6C6C5D00001618017F15386D6C5CD91FE0
+5C6D6CEB03C0D903FCEB0F80902701FF803FC7FC9039007FFFFC020F13F002011380313D
+7BBA3C>I<B712C016F816FE000190398001FF806C90C7EA3FE0EE0FF0EE03F8707E707E
+177FA2EF3F8018C0171F18E0170F18F0A3EF07F8A418FCAC18F8A4EF0FF0A218E0A2171F
+18C0EF3F80A2EF7F0017FE4C5A4C5AEE0FF0EE3FE0486DEBFF80B8C7FC16F816C036397D
+B83F>I<B812FEA3000190388000076C90C8FC173F838383A383A31880170116C0A394C7
+FCA31501A21503150F91B5FCA3EC000F15031501A21500A21860A318E093C712C0A41701
+A3EF0380A21707A2170F173F177F486D903807FF00B9FCA333397EB838>I<B812F8A300
+01903880001F6C90C71201EE00FC177C173C171CA2170CA4170E1706A2ED0180A21700A4
+1503A21507151F91B5FCA3EC001F15071503A21501A692C8FCAD4813C0B612C0A32F397D
+B836>I<DBFF8013C0020FEBF001023F13FC9139FF803F03903A03FC000787D90FF0EB03
+CF4948EB00EF4948147F4948143F49C8121F485A4848150F48481507A248481503A2485A
+1701123F5B007F1600A448481600AB93B6FCA26C7E9338007FE0EF3FC0A2123F7F121FA2
+6C7EA26C7EA26C7E6C7E6C6C157F6D7E6D6C14FF6D6C14EFD90FF8EB03C7D903FEEB0783
+903A00FFC03F0191393FFFFC00020F01F0130002001380383D7CBA41>I<B648B512FEA3
+0001902680000313006C90C76C5AB3A491B6FCA391C71201B3A6486D497EB648B512FEA3
+37397DB83E>I<B612C0A3C6EBC0006D5AB3B3AD497EB612C0A31A397EB81E>I<013FB512
+E0A39039001FFC00EC07F8B3B3A3123FEA7F80EAFFC0A44A5A1380D87F005B0070131F6C
+5C6C495A6C49C7FC380781FC3801FFF038007F80233B7DB82B>I<B649B5FCA300010180
+9038007FF06C90C8EA3F80053EC7FC173C17385F5F4C5A4C5A4CC8FC160E5E5E5E5E4B5A
+ED0780030EC9FC5D153E157E15FF5C4A7F4A6C7E140E4A6C7E4A6C7E14704A6C7E4A6C7E
+14804A6C7E6F7EA26F7F707EA2707E707EA2707EA2707E707EA2707E707F8484486D497F
+B6011FEBFF80A339397DB841>I<B612E0A3000101C0C8FC6C90C9FCB3AD1718A5173817
+30A31770A317F0A216011603160FEE1FE0486D13FFB8FCA32D397DB834>I<B5933807FF
+F86E5DA20001F0FC002600DFC0ED1BF8A2D9CFE01533A3D9C7F01563A3D9C3F815C3A2D9
+C1FCEC0183A3D9C0FEEC0303A2027F1406A36E6C130CA36E6C1318A26E6C1330A36E6C13
+60A26E6C13C0A3913901FC0180A3913900FE0300A2ED7F06A3ED3F8CA2ED1FD8A3ED0FF0
+A3486C6D5A487ED80FFC6D48497EB500C00203B512F8A2ED018045397DB84C>I<B59138
+07FFFE8080C69238007FE06EEC1F80D9DFF0EC0F001706EBCFF8EBC7FCA2EBC3FEEBC1FF
+A201C07F6E7EA26E7E6E7E81140F6E7E8114036E7E168080ED7FC016E0153FED1FF0ED0F
+F8A2ED07FCED03FEA2ED01FF6F1386A2EE7FC6EE3FE6A2EE1FF6EE0FFEA216071603A216
+011600A2177E486C153E487ED80FFC151EB500C0140EA2170637397DB83E>I<EC03FF02
+1F13E09138FE01FC903901F8007ED907E0EB1F8049486D7ED93F80EB07F049C76C7E01FE
+6E7E48486E7E49157E0003167F4848ED3F80A24848ED1FC0A2001F17E049150F003F17F0
+A3007F17F8491507A300FF17FCAC007F17F86D150FA3003F17F0A26C6CED1FE0A36C6CED
+3FC0000717806D157F000317006C6C15FEA26C6C4A5A017F4A5A6D6C495A6D6C495AD907
+E0EB1F80D903F8017FC7FC903900FE01FC91381FFFE0020390C8FC363D7BBA41>I<B712
+C016FC16FF0001D9800013C06C90C7EA1FE0707EEE03F883707EA2707EA21880A71800A2
+4C5AA24C5A5FEE0FF04C5AEEFF8091B548C7FC16F091CAFCB3A5487FB6FCA331397EB838
+>I<EC03FF021F13E09138FE01FC903901F8007ED907E0EB1F8049486D7ED93F80EB07F0
+49C76C7E01FE6E7E48486E7EA24848157F0007178049153F000F17C049151F001F17E0A2
+4848ED0FF0A3007F17F8A2491507A200FF17FCAC007F17F8A26D150FA2003F17F0A26C6C
+ED1FE0A36C6CED3FC00007027C14804AB4FC3C03F80383807F003B01FC0701C0FEEC0E00
+2600FE0CEBE1FC017FEC63F8D93F8CEB77F0D91FCCEB3FE0D907EE14806DB449C7FC0100
+D981FC130CEC1FFF0203131C91C7001E131C161F183CEF807CEFC0F8EE0FFFA318F08218
+E07013C07013809338007E00364B7BBA41>I<B612FEEDFFE016F8000190388007FE6C90
+C76C7EEE3FC0707E707E707EA2707EA283A65FA24C5AA24C5A4C5AEE3F8004FFC8FCED07
+FC91B512E05E9138000FF0ED03F8ED00FE82707E707EA2161F83A583A6F00180A217F816
+0F1803486D01071400B66D6C5A04011306933800FE0ECAEA3FFCEF07F0393B7DB83D>I<
+D90FF813C090383FFE0190B512813903F807E33907E000F74848137F4848133F48C7121F
+003E140F007E1407A2007C140312FC1501A36C1400A37E6D14006C7E7F13F86CB47E6C13
+F8ECFF806C14E06C14F86C14FEC680013F1480010714C0EB007F020713E0EC007FED3FF0
+151F150FED07F8A200C01403A21501A37EA216F07E15036C15E06C14076C15C06C140F6D
+EB1F80D8FBF0EB3F00D8F0FE13FE39E03FFFF8010F13E0D8C00190C7FC253D7CBA2E>I<
+003FB812E0A3D9C003EB001F273E0001FE130348EE01F00078160000701770A300601730
+A400E01738481718A4C71600B3B0913807FF80011FB612E0A335397DB83C>I<B6903807
+FFFEA3000101809038007FE06C90C8EA1F80EF0F001706B3B2170E6D150C80171C133F17
+186D6C14385F6D6C14F06D6C5C6D6C495A6D6CEB07806D6C49C7FC91387F807E91381FFF
+F8020713E09138007F80373B7DB83E>I<B500FC91387FFF80A30003018091380FFC006C
+90C8EA07E0715A6C705A6E1403017F93C7FCA280013F1506A26E140E011F150C80010F5D
+A28001075DA26E147001031560A26D6C5CA2806D4A5AA2ED8003027F91C8FCA291383FC0
+06A215E0021F5BA2EDF01C020F1318A26E6C5AA215FC02035BA2EDFEE002015BA26E6C5A
+A36FC9FCA3153EA2151CA3393B7EB83E>I<B5D8FC07B5D8F001B5FCA30007902780001F
+FEC7EA1FF86C48C7D80FF8EC07E000010307ED03C01B807F6C6F6C1500A26E5F017F6E6C
+1406A280013F4A6C5CA280011F4A6D5BEE067FA26D6C010E6D5BEE0C3FA26D6C011C6D5B
+EE181FA26D6C6F5BEE300FA26D6C6F485AEE6007A26D6C4CC7FC9338C003FCA203805D91
+3B7F818001FE06A203C1150EDA3FC3C7EAFF0CA203E3151CDA1FE6EC7F98A215F6DA0FFC
+EC3FF0A302075E4B141FA202035E4B140FA202015E4B1407A2020093C8FC4B80503B7EB8
+55>I<B500FE91383FFFE0A3000301E0913807FE00C649EC03F0017F6F5A606D6C5D6D6C
+140395C7FC6D6C1406A26D6C5C6D6C141C17186D6C143817306D6D5B6E6C13E05F91383F
+E0015F91381FF003DA0FF890C8FC1606913807FC0E160C913803FE1C913801FF185E6E13
+B016E0157F6F5AB3A24B7E023FB512C0A33B397FB83E>89 D<003FB7FCA39039FC0001FE
+01C0130349495A003EC7FC003C4A5A5E0038141F00784A5A12704B5A5E006014FF4A90C7
+FCA24A5A5DC712074A5AA24A5A5D143F4A5AA24A5A92C8FC5B495AA2495A5C130F4948EB
+0180A2495A5C137F495A16034890C7FC5B1203485AEE0700485A495C001F5D48485C5E48
+48495A49130FB8FCA329397BB833>I<EAFFFCA2EAFC00B3B3B3B3A7EAFFFCA20E5379BD
+17>I<EAFFFCA21200B3B3B3B3A712FFA20E537FBD17>93 D<007FB81280B912C0A26C17
+803204797041>95 D<EB1FE0EBFFFC3803E03F3907000F80390F8007E0486C6C7E13E06E
+7EA26E7E6C5A6C5AC8FCA4147FEB07FFEB3FE0EBFE00EA03F8EA0FF0EA1FC0123F485A90
+C7FC160C12FEA31401A26C13036CEB077C903980063E18383FC01E3A0FE0781FF03A03FF
+F00FE03A007F8007C026277DA52A>97 D<EA03F012FFA3120F1203B0EC1FE0EC7FF89038
+F1E03E9039F3801F809039F7000FC001FEEB07E049EB03F049EB01F85BED00FCA216FEA2
+167E167FAA167E16FEA216FC15016D14F8ED03F07F01EEEB07E001C6EB0FC09039C7801F
+00903881E07E903800FFF8C7EA1FC0283B7EB92E>I<EB03FC90381FFF8090387E03E039
+01F80070484813F83907E001FC380FC003A2EA1F80123F90380001F848EB00F01500A212
+7E12FEAA127E127FA26C14067F001F140E6D130C000F141C6C6C13386C6C13706C6C13E0
+39007C07C090381FFF00EB07F81F277DA525>I<ED0FC0EC03FFA3EC003F150FB0EB03F8
+EB1FFF90387E078F9038F801EF3903F0007F4848133F4848131FA24848130F123F90C7FC
+5AA2127E12FEAA127E127FA27EA26C6C131FA26C6C133F6C6C137F6C6CEBEFF03A01F801
+CFFF39007C078F90381FFE0FD907F813C0283B7DB92E>I<EB07F8EB1FFF90387C0FC039
+01F803E03903F001F0D807E013F8380FC0004848137CA248C7127E153E5A153F127E12FE
+A3B7FCA248C8FCA5127EA2127FA26C14037F001F14076C6C13060007140E6D131CD801F0
+13386C6C137090387E03E090381FFF80903803FC0020277EA525>I<147E903803FF8090
+380FC1E0EB1F8790383F0FF0137EA213FCA23901F803C091C7FCADB512FCA3D801F8C7FC
+B3AB487E387FFFF8A31C3B7FBA19>I<ED03F090390FF00FF890393FFC3C3C9039F81F70
+7C3901F00FE03903E007C03A07C003E010000FECF000A248486C7EA86C6C485AA200075C
+6C6C485A6D485A6D48C7FC38073FFC38060FF0000EC9FCA4120FA213C06CB512C015F86C
+14FE6CECFF804815C03A0F80007FE048C7EA0FF0003E140348140116F8481400A56C1401
+007C15F06CEC03E0003F1407D80F80EB0F80D807E0EB3F003901FC01FC39007FFFF00107
+90C7FC26387EA52A>I<EA03F012FFA3120F1203B0EC0FF0EC3FFCECF03F9039F1C01F80
+9039F3800FC0EBF70013FE496D7EA25BA35BB3A3486C497EB500C1B51280A3293A7EB92E
+>I<EA0380EA0FE0487EA56C5AEA0380C8FCAAEA03F012FFA312071203B3AA487EB512C0
+A312387EB717>I<EB01C0EB07F0EB0FF8A5EB07F0EB01C090C7FCAAEB01F813FFA31307
+1301B3B3A2123C127E00FF13F01303A214E038FE07C0127C383C0F00EA0FFEEA03F81549
+84B719>I<EA03F012FFA3120F1203B1913801FFFCA39138007FC01600157C15705D4A5A
+4A5A4AC7FC141E1438147814FC13F1EBF3FEEBF73F01FE7FEBF81F496C7E8114076E7E6E
+7E811400157E157F811680ED1FC0486CEB3FF0B500C0B5FCA3283A7EB92C>I<EA03F012
+FFA3120F1203B3B3AD487EB512C0A3123A7EB917>I<2703F00FF0EB1FE000FFD93FFCEB
+7FF8913AF03F01E07E903BF1C01F83803F3D0FF3800FC7001F802603F70013CE01FE14DC
+49D907F8EB0FC0A2495CA3495CB3A3486C496CEB1FE0B500C1B50083B5FCA340257EA445
+>I<3903F00FF000FFEB3FFCECF03F9039F1C01F803A0FF3800FC03803F70013FE496D7E
+A25BA35BB3A3486C497EB500C1B51280A329257EA42E>I<EB03FE90380FFF8090383E03
+E09038F800F84848137C48487F48487F4848EB0F80001F15C090C712074815E0A2007EEC
+03F0A400FE15F8A9007E15F0A2007F14076C15E0A26C6CEB0FC0000F15806D131F6C6CEB
+3F006C6C137EC66C13F890387E03F090381FFFC0D903FEC7FC25277EA52A>I<3903F01F
+E000FFEB7FF89038F1E07E9039F3801F803A07F7000FC0D803FEEB07E049EB03F04914F8
+49130116FC150016FEA3167FAA16FEA3ED01FCA26DEB03F816F06D13076DEB0FE001F614
+C09039F7803F009038F1E07E9038F0FFF8EC1FC091C8FCAB487EB512C0A328357EA42E>
+I<D903F813C090381FFE0190387E07819038FC01C33903F000E300071477484813374913
+3F001F141F485A150F48C7FCA312FEAA127FA37E6D131F121F6D133F120F6C6C137F6C6C
+13EF3901F801CF39007E078F90381FFE0FEB07F890C7FCABED1FE00203B5FCA328357DA4
+2C>I<3807E01F00FFEB7FC09038E1E3E09038E387F0380FE707EA03E613EE9038EC03E0
+9038FC0080491300A45BB3A2487EB512F0A31C257EA421>I<EBFF03000313E7380F80FF
+381E003F487F487F00707F12F0A2807EA27EB490C7FCEA7FE013FF6C13E06C13F86C7F00
+037FC67F01071380EB007F141F00C0EB0FC01407A26C1303A37E15806C13077EEC0F00B4
+131E38F3C07C38E1FFF038C03F801A277DA521>I<1318A51338A31378A313F812011203
+1207001FB5FCB6FCA2D801F8C7FCB215C0A93800FC011580EB7C03017E13006D5AEB0FFE
+EB01F81A347FB220>I<D803F0EB07E000FFEB01FFA3000FEB001F00031407B3A4150FA3
+151F12016D133F0000EC77F86D9038E7FF8090383F03C790381FFF87903A03FC07E00029
+267EA42E>I<B538803FFEA33A0FF8000FF06C48EB07E00003EC03C06D148000011500A2
+6C6C1306A26D130E017E130CA26D5BA2EC8038011F1330A26D6C5AA214E001075BA29038
+03F180A3D901FBC7FCA214FF6D5AA2147CA31438A227257EA32C>I<B53A1FFFE03FFEA3
+260FF8009038000FF86C48017EEB03E018C00003023EEB0180A26C6C013FEB0300A36C6C
+EC8006156FA2017E9038EFC00C15C7171CD93F01EBE01815830281EBF038D91F83143015
+0102C3EBF87090260FC6001360A2D907E66D5A02EC137CA2D903FCEB7F804A133FA20101
+92C7FC4A7FA20100141E4A130E0260130C37257EA33C>I<B538807FFFA33A03FE003FF0
+0001EC1F80000092C7FC017E131C6D13186D6C5AECC070010F5B6D6C5AECF180EB03FB6D
+B4C8FC6D5AA2147F804A7E8114CF903801C7E090380383F090380703F8EB0601496C7E01
+1C137E49137F01787F496D7E486C80000FEC3FF0D8FFFE90B51280A329247FA32C>I<B5
+38803FFEA33A0FF8000FF06C48EB07C00003EC03806C7E16007F00001406A2017E5BA213
+7F6D5BA26D6C5AA2ECC070010F1360A26D6C5AA214F101035BA2D901FBC7FCA214FF6D5A
+A2147CA31438A21430A214701460A25CA2EA7C0100FE5B130391C8FC1306EAFC0EEA701C
+6C5AEA1FF0EA0FC027357EA32C>I<003FB512FCA2EB8003D83E0013F8003CEB07F00038
+EB0FE012300070EB1FC0EC3F800060137F150014FE495AA2C6485A495AA2495A495A495A
+A290387F000613FEA2485A485A0007140E5B4848130C4848131CA24848133C48C7127C48
+EB03FC90B5FCA21F247EA325>I<EC01F8140FEC3F80ECFC00495A495A495AA2130F5CB3
+A7131F5C133F49C7FC13FEEA03F8EA7FE048C8FCEA7FE0EA03F8EA00FE137F6D7E131F80
+130FB3A7801307A26D7E6D7E6D7EEC3F80EC0FF814011D537ABD2A>I<126012F0B3B3B3
+B3A91260045377BD17>I<12FCEAFFC0EA07F0EA01FCEA007E7F80131F80130FB3A78013
+07806D7E6D7EEB007EEC1FF0EC07F8EC1FF0EC7E00495A495A495A5C130F5CB3A7131F5C
+133F91C7FC137E485AEA07F0EAFFC000FCC8FC1D537ABD2A>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fi ecbx1440 14.4 34
+/Fi 34 118 df<EE7FFC031FB57E4AB612E0020715F8023F9038C00FFC913AFFFC0001FE
+4901F0EB007F010701C0EB03FF4949497F4990C75A5B5C495A4D7F01FF6F5B5CA27190C7
+FC715AEF00F895C8FCAA0407B512C0BAFCA5C601F8C7120F83B3B3A6B6D8F807B612C0A5
+42547DD349>28 D<B712E0AB230B7F9F2C>45 D<151E153E15FE1403140F147FEB07FF00
+03B5FCB6FCA3EBF87FEAFC00C7FCB3B3B3A6007FB712FCA52E4E76CD42>49
+D<EC1FFE49B512F0010F14FC013FECFF804915E02701FF803F7F2703FC000713FCD807F0
+01017F48486D7FD81F806E138048C87E7013C0D87FE016E001F8806D16F000FF817F7013
+F8A56C5AA26C5A6C5AEA0380C914F05EA218E05E18C05E18804C13005F4C5A4C5A5F4B5B
+4B5B4B5B94C7FCED0FFC4B5A4B5AED7FC04B5A4A90C8FCEC03FC4A5A4A4814F84A5A4A5A
+4AC8FC02FEEC01F0495A495A495A5CD90F80140349C8FC013E1507017FB7FC90B812E05A
+5A5A5A5A5A5AB9FC18C0A4354E7ACD42>I<913807FFC0027F13FC0103B67E010F15E090
+261FF80313F890267FC0007F01FEC7EA3FFE48488148486E138013FE486C6C6D13C08048
+17E080A66C5B18C06C5B6C90C75AD80038168090C8FC4C1300A24C5A5F4C5A4B5B4B13C0
+030F5BDB7FFEC7FC91387FFFF816C016FCEEFF80DA000313E09238007FF8EE3FFE707E70
+138018C07013E018F07013F8A218FC82A218FEA3EA03C0EA0FF0EA3FFC487EA2B5FCA218
+FCA25E18F8A26C4816F0495C4916E0D83FE04A13C06C485CD80FF04A1380D807FE91387F
+FE003B03FFE003FFFC6C90B65A6C6C15E0010F92C7FC010114FCD9001F1380374F7BCD42
+>I<17FC1601A216031607160FA2161F163F167FA216FF5D5DA25D5D5D167F153E157E15
+FC15F8EC01F01403EC07E015C0EC0F80141FEC3F00143E5C14FC495A5C495A1307495A5C
+49C7FC5B137E137C5B1201485A5B485A120F485A90C8FC123E127E5ABA1280A5C901FCC7
+FCAF021FB71280A5394F7CCE42>I<486C150601F0153E01FEEC01FED9FFF0133F91B65A
+5F5F5F5F5F94C7FC16FC5E16E093C8FC15FC01F0138091CAFCAC913807FF80023F13F891
+B512FE01F36E7E9026FFFC0113E09139E0007FF891C76C7E496E7E01F86E7E5B70138049
+16C0C9FC18E08218F0A418F8A31203EA0FE0EA3FF8487EA212FF7FA218F0A25B5E6C4816
+E05B01C016C06CC85A18806C6C4A13007FD80FF04A5A6C6CECFFFCD803FE4913F02701FF
+E00F5B6C6CB612806D92C7FC010F14F8010114C09026003FFCC8FC354F7ACD42>I<EA07
+E0EA1FF8EA3FFCEA7FFEA2B5FCA6EA7FFEA2EA3FFCEA1FF8EA07E0C7FCB3A3EA07E0EA1F
+F8EA3FFCEA7FFEA2B5FCA6EA7FFEA2EA3FFCEA1FF8EA07E0103576B425>58
+D<932603FFF01407047F01FF5C0307B600E05B033F03F85B92B700FE5B02039126C003FF
+5B020F01F8C7EA3FC1023F01C0EC0FE391B5C80003B5FC4901FC814949814901E082011F
+498249498292CA7E4948834948835A4A83485B4885A2484984A2485B87A2485B87A25AA2
+98C8FC91CFFCA2B5FCAE7E067FB7128080A37E95C76C90C7FC807EA36C7FA26C7FA26C7F
+7E806C7F137F6D7E816D6D93B5FC01077F6D01F85D6D7F6D01FF5D023F01E0EC0FEF020F
+01FCEC3FE30203903AFFE001FF81020091B6C6FC033F03FC133F030703F0130FDB007F02
+801303040301F8CAFC595479D267>71 D<B81280A5D8000701F0C7FCB3B3B3B2B81280A5
+29527DD130>73 D<B812E0A5D8000F01E0CAFCB3B3A91AF8A419011AF0A51903A31907A2
+190F1AE0191FA2193F197F19FF60180760187F0503B5FCBB12C0A545527CD14F>76
+D<B600F84BB612FC818181A2D800076E91C7383FE00070EE0F80828214DF02CF7F02C77F
+8202C37F14C102C0806F7F836F7F816F7F6F7F83816F7F6F80707F8482707F707F707F84
+82707F7080717F8583717F717F85717F83717F7114801AC07213E0847213F07213F81AFC
+7213FE847213FF72148F1BCF7313EF857313FF85A285858585A286868686A286868686EB
+1FF0B600FE177F1B3F1B1F1B0FA25E527CD167>78 D<B912FCF0FFE019FE737E1AE0D800
+0F01E0C7003F7F060313FC06007F737E7313807313C07313E0851BF0A21BF885A21BFCA9
+1BF8A3611BF0A21BE04F13C0614F13804F13004F5A060713F8063F5B92B812C097C7FC19
+F8198003E0CBFCB3AEB712FEA54E527CD15A>80 D<93381FFF800303B512FC033FECFFC0
+92B712F00207D9F80113FE021F903AC0003FFF804A48C700077FDAFFF8020113F049496E
+7F49496F7E49496F7E49496F7E4990C96C7F4948707F4948707F01FF854849707F4A8248
+86A24849717E48864A83A2481B80A248497113C0A4481BE0A291CB7EA3B51AF0AF6C1BE0
+A36E5FA26C1BC0A36C1B806E5FA26C1B006E5F6C62A26C6DD903FC4A5A6CDB0FFF5D6E49
+EBC0016C4B01E05C6D6C90277E07F0035B6E9039F801F807902A3FFF01F000780F5B6D04
+7C5C6DD981E06D4890C7FC6D01E191381F7FFE010101F1EDFFF86DD9F9F06D5BDA3FFF16
+C06E6D013F5B02079027FE01FFFEC8FC020190B612F8DA003F4B141003071838DB001FEB
+83F893C7EA03FC1C7885726C14F8F2C003F2F01F97B512F084A31CE085A27314C01C8085
+1C00735B735B735B735B9638003FC0556A79D263>I<B912E018FF19F019FE737ED80007
+01F0C714E0060F7F060313FC06007F737E737F8587737FA28785A287A863A26163636163
+4F90C8FC4F5A4F5A06035B060F13E095B5128092B748C9FC19F019C019F09226F0000713
+FC050013FF063F7F727F727F727F727FA2727FA28486A886A71D707513F8A2851C017301
+C013F0A273EBE003B86C6D9038F007E0739038FC1FC0070190B51280736C1400080F5BCE
+13F85D537CD162>I<DA0FFE141C91B500F0133C010702FC137C011F02FF13FC017F15C1
+9026FFF00113E148903980001FFB4890C7EA07FFD807FC14014848804848153F171F4848
+150FA2007F1607491503A2170112FFA217007FA26D167CA27F7F6D93C7FC6C7E14C014F8
+ECFF806C14F8EDFFC06C15FC6CEDFF8017F06C16FC6C826C707E6C836D82011F82010782
+13016D6C81020781EC007F030380ED003F040314801600173F837113C0838312F883A383
+7EA319807EA26C5E19007F6D4B5A7F6D4B5A01FC4B5A6D151FD9FFC04A5AD97FF8ECFFE0
+28FE1FFF80075B010790B6C7FCD8FC0115FC486C6C14F048010F14C0489026007FFCC8FC
+3A5479D249>I<003FBB12FCA59126C0007FEB000301FCC7ED003FD87FF0F00FFE491807
+49180349180190C81600A2007E1A7EA3007C1A3EA500FC1A3F481A1FA6C91700B3B3AC49
+B912C0A550517BD05B>I<EC3FFE0107B512E0011F14FC017F14FF2701FFC00F13C02703
+FE00037F486C01007F6E6D7E486D80707EA2707EA3707F6C5B6C90C7FC6C5AC9FCA60307
+B5FC0203B6FC147F0103B7FC011FEBF00F017F1300EBFFFC000313F04813C0485B4890C7
+FC5A5B485AF081F012FF5BA35EA26D5C127F6D5C003F03F713C36DD901E314E06CD9C007
+14FF00079026F01F8114C06C90B5C61480C602FC6D1300011F01F0EB3FFC01010180EB07
+F03C387CB642>97 D<913803FFE0023F13FE91B67E010315E0010F9038003FF8D93FFCEB
+07FC4948497E4948131F4849497E485B485BA24890C7FC5A5B003F6F5A705A705A007F92
+C8FC5BA312FFAD127F7FA3123F7F6CEE0F80A26C6D141F18006C6D5C6C6D143E6C6D147E
+6C6D5C6D6C495A6DB4EB07F0010F9038C01FE06D90B5128001014AC7FCD9003F13F80203
+138031387CB63A>99 D<943803FF80040FB5FCA5EE003F170FB3A4913803FF80023F13F8
+49B512FE0107ECFF8F011F9038C03FEF90273FFE0007B5FCD97FF8130149487F48498048
+4980484980488291C8FC5A5B123FA2127F5BA312FFAD127FA37F123FA3121F7F6C5E6C6D
+5C5F6C6D91B5FC6C6D5B6C6D4914E0D97FFCD90FEFEBFF80D91FFFEB7F8F010790B5120F
+010114FC6D6C13E00207010049C7FC41547CD249>I<913807FF80027F13F849B512FE01
+076E7E011F010313E0903A3FFC007FF0D97FF06D7E49486D7E4849130F48496D7E488248
+90C77E1880485A82003F17C0A3485A18E082A212FFA290B8FCA401FCCAFCA6127FA37F12
+3FA2EF03E06C7E17076C17C06C6D140F18806C6D141F6C6DEC3F006C6D147ED97FFC495A
+D91FFFEB07F86D9038E03FF0010390B512C001005D023F01FCC7FC020113E033387CB63C
+>I<ED1FF8913803FFFE020FEBFF80023F14C09139FFF83FE001039038E0FFF049138049
+010113F85BEB3FFEA2EB7FFCA26F13F0495AEE7FE0EE1F8093C7FCAEB712C0A5C601F8C8
+FCB3B3A7B612FEA52D547CD328>I<DA1FFE14FE49B539E007FF80010FDAFC1F13C0013F
+DAFF7F13E090267FF807EBFF072701FFE001EBF07F48497E484990387FF83F91C7003F14
+C048EEFC1F489338FE070049021F90C7FCA2003F82A9001F5EA26D143F6C5E6C5E6E137F
+6C6D495A6C6D485B6CD9F80713804890B6C8FCD803EF14FC01C114E02707C01FFEC9FC49
+CBFCA2487EA37FA27F13FC90B612FE6CEDFFF017FCEFFF806C8318F06C836C837F48B87E
+1207D80FFCC700037F4848EC003F4848150F48486F138083485A83A56D5D007F18006D5D
+003F5F6C6C4B5A01FE153FD807FFED7FF06C01C049485AC601FC011F1380013FB648C7FC
+010F15F8010115C0D9000F01F8C8FC3B4F7CB542>I<EB3FF8B5FCA51203C6FCB3A4EE1F
+FC93B57E030314E0030F14F892391FC07FFC92397E003FFE03F86D7EECF9F04B6D7FECFB
+C0ECFF8092C76C7FA25CA25CA45CB3ACB6D8F807B612C0A542537CD249>I<133FEBFFC0
+487F487FA2487FA66C5BA26C5B6C5B013FC7FC90C8FCAEEB1FF8B5FCA512017EB3B3A6B6
+12F0A51C547CD324>I<EB3FF8B5FCA51203C6FCB3B3B3B1B612F8A51D537CD224>108
+D<D93FF0D91FF84AB47EB591B56C010F13F8030302E0013F13FE030F6E90B6FCDB3F8090
+27F803F80F7F922A7E007FFC07E0077F000302F890283FFE0F80037FC6D9F1F0011F4948
+7EDAF3E0DAFF3E814B153CDAF7805D92C76C496D7F14FF4A5EA24A5EA34A5EB3ADB6D8F8
+0FB66CB612F8A565367BB56E>I<D93FF0EB1FFCB591B57E030314E0030F14F892391FC0
+7FFC92397E003FFE000302F86D7EC6EBF1F04B6D7FECF3C0ECF78092C76C7F14FF5CA25C
+A45CB3ACB6D8F807B612C0A542367CB549>I<913801FFC0023F13FE91B67E010315E001
+0F018013F8903A3FFC001FFED97FF0EB07FF49486D7F48496D7F48496D7F91C8127F4883
+488349153F001F83A2003F8349151FA2007F83A400FF1880AC007F1800A3003F5F6D153F
+A2001F5FA26C6C4B5AA26C6D4A5A6C5F6C6D495B6C6D495B6D6C4990C7FCD93FFCEB1FFE
+6DB46CB45A010790B512F0010115C0D9003F49C8FC020313E039387CB642>I<D93FF8EB
+7FF0B50107B5FC031F14C0037F14F09126F9FF0013FCDAFFF8EB3FFF000302E0010F7FC6
+02806D7F92C76C7F4A824A804A6E7F85187F85A2183F85A4721380AD4E1300A44E5AA261
+18FF616E5C616E4A5B6E4A5B6F495B03E04990C7FC6FEB7FFE913AF9FE01FFF802F8B65A
+033F14C0030749C8FC030013E093CAFCB1B612F8A5414D7DB549>I<90393FF001FCB590
+380FFF804B13E0037F13F09238FE1FF89138F1F83F00019138F07FFC6CEBF3E015C0ECF7
+80A2ECFF00EE3FF84AEB1FF0EE0FE093C7FC5CA45CB3ABB612FEA52E367DB535>114
+D<903903FFC00E011FEBFC1E90B6127E000315FE3907FE003FD80FF0130F484813034848
+1301491300127F90C8127EA248153EA27FA27F01F091C7FC13FCEBFF806C13FEECFFF06C
+14FE6F7E6C15E06C816C15FC6C81C681133F010F15801301D9000F14C0EC003F030713E0
+150100F880167F6C153FA2161F7EA217C07E6D143F17807F6DEC7F0001F85C6DEB03FE90
+39FF801FFC486CB512F0D8F81F14C0D8F00791C7FC39E0007FF02B387CB634>I<147CA6
+14FCA41301A31303A21307A2130F131F133F137F13FF1203000F90B512FEB7FCA426007F
+FCC8FCB3A9EE0F80ABEE1F006D7EA2011F143E806D6D5A6DEBC1F86DEBFFF001005C023F
+1380DA03FEC7FC294D7ECB33>I<D93FF8913801FFC0B50207B5FCA50003ED001FC61607
+B3AE5FA35FA25F137F5F6D6C14F7DC01E713F06D6CD907C7EBFFC0903A0FFF801F876D90
+B51207010114FC6D6C13F0020701C091C7FC42377CB549>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fj ecrm0900 9 5
+/Fj 5 109 df<123C127E12FFA4127E123C08087A8715>46 D<EB7F803803FFF0380F80
+FC381C003E003F133F6D6C7E6E7EA26E7EEA1F00C7FCA4EB01FF131FEBFF873803FC07EA
+0FF0EA1FC0EA3F80127F13004815C05AA3140FA26C131F6C133B3A3F8071F180391FC1E1
+FF2607FFC013003900FE003C22237DA126>97 D<EA03F012FFA312071203AEEC3F80ECFF
+E09038F3C0F89038F7007E01FE7F49EB1F8049EB0FC05BED07E016F0A2150316F8AA16F0
+150716E0A2ED0FC07F6DEB1F8001ECEB3F0001CF137C90388381F8903801FFE0C76CC7FC
+25357EB32B>I<EA03F012FFA312071203AEEC1FC0EC7FF09038F1E0FC9038F3807C9038
+F7007E13FE497FA25BA25BB3486CEB7F80B538C7FFFCA326347EB32B>104
+D<EA07E012FFA3120F1207B3B3A7EA0FF0B5FCA310347EB315>108
+D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fk ecbx0900 9 7
+/Fk 7 117 df<ED1F80A24B7EA24B7EA34B7EA24A7FA34A7FA24A7F15CFA2020F7F1587
+021F801503023F80EC3E01A2027E80EC7C0002FC804A137FA20101814A133F0103814A13
+1FA249B67EA24981A290271F8000077F91C77EA24982013E80017E82017C80A201FC8249
+157FB500F0013FB512F0A43C347DB343>65 D<EB7FFE0003B512E04814F8390FF00FFC39
+1FF803FF806E138016C0157F6C5A6C5AEA0180C8FCEC7FFF010FB5FC90B6FC0003EBF07F
+000F1300EA1FF8485A485A485A5BA315FF7F007F5B6D4813E03A3FF80FBFFF000FB5121F
+0003EBFC0F39007FE00728217EA02B>97 D<EA01FC12FFA4120F1207ADEC0FF8EC7FFF01
+FDB512C09039FFF01FF09138800FF84A6C7E496D7E496D7EA2178081A217C0A91780A25D
+1700A26D495A6D495A6E485A9039F7E03FF001E1B512C0D9C07F90C7FC9038801FF02A34
+7DB331>I<903807FF80013F13F090B512FC3903FE01FE4848487EEA0FF8EA1FF0EA3FE0
+A2007F6D5A496C5A153000FF91C7FCA9127F7FA2003FEC07807F6C6C130F000FEC1F00D8
+07FE133E3903FF80FCC6EBFFF8013F13E0010790C7FC21217DA027>I<3901F81F8000FF
+EB7FF0ECFFF89038F9E3FC9038FBC7FE380FFF876C1307A213FEEC03FCEC01F8EC006049
+1300B1B512F0A41F217EA024>114 D<9038FFE1C0000713FF5A383F803F387E000F1407
+5A14037EA26C6CC7FC13FCEBFFE06C13FC806CEBFF80000F14C06C14E0C6FC010F13F0EB
+007F140F00F0130714037EA26C14E06C13076CEB0FC09038C01F8090B5120000F913FC38
+E03FE01C217DA023>I<133CA5137CA313FCA21201A212031207001FB51280B6FCA3D807
+FCC7FCB0EC03C0A79038FE078012033901FF0F006C13FEEB3FFCEB0FF01A2F7EAE22>I
+E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fl ecrm1200 12 25
+/Fl 25 122 df<121EEA7F8012FF13C0A213E0A3127FEA1E601200A413E013C0A3120113
+80120313005A1206120E5A5A5A12600B1D78891B>44 D<14FF010713E090381F81F89038
+3E007C01FC133F4848EB1F8049130F4848EB07C04848EB03E0A2000F15F0491301001F15
+F8A2003F15FCA390C8FC4815FEA54815FFB3A46C15FEA56D1301003F15FCA3001F15F8A2
+6C6CEB03F0A36C6CEB07E0000315C06D130F6C6CEB1F806C6CEB3F00013E137C90381F81
+F8903807FFE0010090C7FC28447CC131>48 D<EB03FE90381FFFC0017F13F03901F80FFC
+3903C001FE48486C7E000EC7EA7F8048EC3FC0ED1FE04815F00030140F007015F8006014
+07126CB415FC7F7F1503A46C4813076CC7FCC8FC16F8A2150F16F0151F16E0A2ED3FC0ED
+7F8016005D5D4A5A4A5A4A5A5D4A5A4A5A4AC7FC147C5C5C495A495A495A49C7120C131E
+5B013814185B5B485A4848143848C81230000E1570001FB612F0A25A5AB712E0A326427B
+C131>50 D<EC07FCEC3FFF91B512C0903903FC03E0903907E000F0D91FC0133849C71258
+017EEB01FC01FE1303491307485A485AA24848EB03F8000FEC01F092C7FC485AA3485AA3
+127FA29038007F80903801FFF090380780FC39FF0E003E49EB1F8049EB0FC049EB07E013
+6001E0EB03F04914F8150116FC5BED00FEA390C812FFA47EA57F123FA216FE121F15016D
+14FC120FED03F86C7EED07F06C6C14E06C6CEB0FC06C6CEB1F80017EEB3F0090383F80FE
+90380FFFF8010313E00100138028447CC131>54 D<16C04B7EA34B7EA34B7EA34B7EA3ED
+19FEA3ED30FFA203707FED607FA203E07FEDC03FA2020180ED801FA2DA03007F160FA202
+06801607A24A6D7EA34A6D7EA34A6D7EA20270810260147FA202E08191B7FCA249820280
+C7121FA249C87F170FA20106821707A2496F7EA3496F7EA3496F7EA201788313F8486C83
+D80FFF03037FB500E0027FEBFFC0A342477DC649>65 D<B8FC17E017FC00019039C00003
+FF6C6C4801007FEF3FC0717E717E717E84170384170184A760A21703601707604D5A4D5A
+EF7FC04DC7FCEE03FEEE3FF091B65A17FC0280C7B47EEF1FC0EF0FF0717E717E717E717E
+1980187F19C0A2183F19E0A8F07FC0A2198018FF4D1300A24D5AEF0FFC4D5AEF7FE04848
+6C903803FFC0B9C7FC17FC17C03B447CC345>I<B712FEEEFFE017F800019039C00007FE
+6C6C48903800FF80EF3FC0EF0FF0717E717EEF00FE8484F03F80F01FC0A2F00FE019F018
+0719F8A2180319FCA3F001FEA419FFAD19FEA3180319FCA319F8180719F0180F19E0A2F0
+1FC0F03F80A2F07F0018FE4D5A4D5AEF0FF0EF3FE0EFFF8048486C010790C7FCB812FC17
+E04CC8FC40447CC34A>68 D<B56C933807FFFC6E5EA20001F1FE0026006FE0EE1BF8A3D9
+67F01633A2D963F81663A3D961FC16C3A3D960FEED0183A2027FED0303A36E6C1406A36E
+6C140CA26E6C1418A36E6C1430A36E6C1460A26E6C14C0A36E6CEB0180A3037FEB0300A2
+92383F8006A36F6C5AA36F6C5AA26F6C5AA36F6C5AA36F6C5AA26FB45AA370C7FC13F0A2
+486C143ED80FFFEF0FFEB500F0011C0107B512FCA34E447BC359>77
+D<003FB912F8A3903BF0001FF8001F01806D481303003EC7150048187C0078183CA20070
+181CA30060180CA5481806A5C81600B3B3A54B7EED7FFE49B77EA33F447DC346>84
+D<B600C0010FB5FCA3000101E0C813F026007F80ED1F80F00F00A21806B3B3A7180E6D6C
+150CA2181C131F6E1518010F163818306D6C1570606D6C14016D6C5D6D6CEC0780027F4A
+C7FC6E6C131EDA1FE0137C913907FC03F00201B55A6E6C1380DB07FCC8FC40467CC349>
+I<EB07FC90383FFF809038F80FE03903C003F048C66C7E000E6D7ED80FC0137E486C137F
+6D6D7EA36F7EA26C5AEA0380C8FCA4EC0FFF49B5FC90380FFE1FEB3FC0EBFF00EA03FC48
+5A485A485A485A127F5B176048C7FCA3153FA36D137F007F14EF6D9038C7E0C0003F1301
+3A1FE00783F13B07F81E03FF802701FFFC0113003A001FE0007C2B2E7CAC31>97
+D<EC7F80903803FFF090380FC07C90383F000F01FCEB03804848EB01C00003140F4848EB
+1FE049133F120F485AA2485AED1FC0007FEC070092C7FCA290C9FC5AAB7E7FA2123F1630
+7F001F15706C6C146016E06C6C14C06C6C13010001EC03806C6CEB0700013F131E90381F
+C078903807FFF001001380242E7DAC2B>99 D<167FED3FFFA315018182B3EC7F80903803
+FFF090380FC07C90383F000E017E1307496D5AD803F87F48487F5B000F81485AA2485AA2
+127FA290C8FC5AAB7E7FA2123FA26C7EA2000F5D7F6C6C5B00035C6C6C9038077F806C6C
+010E13C0013F011C13FE90380FC0F8903803FFE09026007F0013002F467DC436>I<EB01
+FE903807FFC090381F03F090387E00FC49137E48487F485A4848EB1F80000F15C049130F
+121F484814E01507A2007F15F090C7FCA25AA390B6FCA290C9FCA67EA27FA2123F16306C
+7E1670000F15606D14E06C6C14C0000314016C6CEB03806C6CEB0700013E131E90381F80
+F8903803FFE0010090C7FC242E7DAC2B>I<EE0F80D901FCEB7FE0903A0FFF81F0F09039
+3F07E3819039FC01FF033A01F800FE014848017E13E00007027FC7FC497F000F8149131F
+001F81A9000F5D6D133F000792C7FC6D5B0003147E6C6C5B6D485A3903BF07E090380FFF
+80260701FCC8FC90CAFCA25AA37F6C7E7F90B512F86C14FF16E06C15F86C6C8048B67E3A
+07C0000FFF48481300003FC8EA3F80003E151F48ED0FC0A2481507A56C150F007C168000
+7E151F003E16006C153E6C6C5CD807E0495AD801F8EB07E0D8007FEB3F8090261FFFFEC7
+FC010113E02C427DAC31>103 D<EA01E0EA07F8A2487EA46C5AA2EA01E0C8FCADEA01FC
+12FFA3120712031201B3B0487EB512F8A315437DC21C>105 D<EA01FC12FFA312071203
+1201B3B3B3A5487EB512F8A315457DC41C>108 D<3901FC01FE00FF903807FFC091381E
+07F091383801F8000701707F0003EBE0002601FDC07F5C01FF147F91C7FCA25BA35BB3A8
+486CECFF80B5D8F83F13FEA32F2C7DAB36>110 D<EC7F80903803FFF090380FC0FC9038
+3E001F496D7E496D7E48486D7E48486D7E48486D7E000F81A24848147E003F157FA290C8
+7E481680A44816C0AA6C1680A26D147F003F1600A2001F157E6D14FE000F5D6D13010007
+5D6C6C495A6C6C495A6C6C495A013E49C7FC90381FC0FE903807FFF89038007F802A2E7D
+AC31>I<3903F803F000FFEB1FFCEC3C3EEC707F0007EBE0FF3803F9C000015B13FBEC00
+7E153C01FF13005BA45BB3A748B4FCB512FEA3202C7DAB26>114
+D<90383FE0183901FFFC383907E01F78390F0003F8001E1301481300007C1478127800F8
+1438A21518A27EA27E6C6C13006C7E13FC383FFFE06C13FC6C13FF6C14C06C14E0C614F0
+011F13F81300EC0FFC140300C0EB01FE1400157E7E153EA27EA36C143C6C147C15786C14
+F86CEB01F039F38003E039F1F00F8039E07FFE0038C00FF01F2E7DAC26>I<1306A5130E
+A4131EA3133E137EA213FE12011207001FB512F0B6FCA2C648C7FCB3A4150CAA017E131C
+017F1318A26D133890381F8030ECC070903807E0E0903801FFC09038007F001E3E7EBC26
+>I<D801FC147F00FFEC3FFFA300071401000380000181B3A85EA35DA212006D5B017E90
+38077F80017F010E13C06D011C13FE90380FC078903803FFF09026007F8013002F2D7DAB
+36>I<B539F001FFFCA3000790C7EA7FE06C48EC1F8000011600160E1200160C017F5CA2
+80013F5CA26E1370011F146080010F5CA2ECF00101075CA26D6C48C7FCA26E5A01011306
+A26D6C5AA214FF6E5AA215B8EC3FB015F06E5AA36E5AA26E5AA36EC8FC2E2C7EAA33>I<
+B539F001FFFCA3000790C7EA7FE06C48EC1F8000011600160E0000150C6D141C6D1418A2
+6E1338013F1430A26D6C5BA26E13E0010F5CA26D6C485AA2ECF803010391C7FCA2903801
+FC06A2ECFE0E0100130CA2EC7F18A215B8EC3FB0A2EC1FE0A36E5AA26E5AA36EC8FCA214
+06A35CA25CA2123C007E5BB4FC5CA25CEAFE01387C0380D87007C9FCEA3C1EEA0FFCEA03
+F02E3F7EAA33>121 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fm ecbx1200 12 47
+/Fm 47 123 df<0118140C017C143E01FC147E48485C4848495A495C4848495A4848495A
+001F140F90C75B003E4AC7FCA2003C141E007C143E0078143CA200F8147CA2481478D8F1
+F014F8D8F7FCEB7BFEB46CEB7FFF6D1580028014C0A36C80A36C806C496C13806C486D13
+006C486D5AD801F0EB00F82A2283C427>16 D<D807C0EB03E0D81FF0EB0FF8486C497E48
+6C497E486C497E6D1580A3028014C0A36C806C80D81FF7EB0FFBD807C7EB03E3D80007EB
+0003010F1407A291C71380A249140F011E1500013E5CA249143E01FC147E49147C48485C
+4848495A000714034848495A4848495A90C75B000C0206C7FC2A2281C427>I<ED0FFF4A
+B512C0020F14F0027F80903A01FFF803FC499038C000FE010FEB00034948497E49485B5C
+495A4C138001FF6E13005CA3705AEE01F893C8FCA74BB51280B9FCA5C69038E00003B3B0
+007FD9FFC1B6FCA538467EC53E>28 D<EA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA
+3FF8EA1FF0EA07C00F0F788E1F>46 D<EC3FF849B5FC010F14E0013F14F890397FF01FFC
+9039FFC007FE4890380001FF48486D1380000716C049147F000F16E049143F001F16F0A2
+003F16F8A249141F007F16FCA600FF16FEB3A3007F16FCA56C6CEC3FF8A3001F16F0A200
+0F16E06D147F000716C06D14FF6C6C4913806C6D4813006C6D485A90397FF01FFC6DB55A
+010F14E0010314809026003FF8C7FC2F427CC038>48 D<EC03C01407141F147FEB03FF13
+3FB6FCA413C3EA0003B3B3ADB712FCA5264177C038>I<ECFFE0010F13FE013F6D7E90B6
+12E0000315F82607FC0313FE3A0FE0007FFFD81F806D138048C7000F13C0488001C015E0
+01F07F00FF6E13F07F17F881A46C5A6C5A6C5AC9FC17F05DA217E05D17C04B13804B1300
+A2ED1FFC4B5A5E4B5A4B5A4A90C7FC4A5A4A5AEC0FF04A5AEC3F804AC7127814FE495A49
+4814F8D907E014F0495A495A49C8FC017C140149140348B7FC4816E05A5A5A5A5AB8FC17
+C0A42D417BC038>I<ECFFF0010713FF011F14C0017F14F049C66C7ED803F8EB3FFED807
+E06D7E81D80FF86D138013FE001F16C07FA66C5A6C4815806C485BC814005D5E4B5A4B5A
+4B5A4A5B020F1380902607FFFEC7FC15F815FF16C090C713F0ED3FFCED0FFEEEFF80816F
+13C017E0A26F13F0A217F8A3EA0FC0EA3FF0487EA2487EA217F0A25D17E06C5A494913C0
+5BD83F80491380D81FF0491300D80FFEEBFFFE6CB612F800015D6C6C14C0011F49C7FC01
+0113E02D427BC038>I<163FA25E5E5D5DA25D5D5D5DA25D92B5FCEC01F7EC03E7140715
+C7EC0F87EC1F07143E147E147C14F8EB01F0EB03E0130714C0EB0F80EB1F00133E5BA25B
+485A485A485A120F5B48C7FC123E5A12FCB91280A5C8000F90C7FCAC027FB61280A53141
+7DC038>I<0007150301E0143F01FFEB07FF91B6FC5E5E5E5E5E16804BC7FC5D15E092C8
+FC01C0C9FCAAEC3FF001C1B5FC01C714C001DF14F09039FFE03FFC9138000FFE01FC6D7E
+01F06D13804915C0497F6C4815E0C8FC6F13F0A317F8A4EA0F80EA3FE0487E12FF7FA317
+F05B5D6C4815E05B007EC74813C0123E003F4A1380D81FC0491300D80FF0495AD807FEEB
+FFFC6CB612F0C65D013F1480010F01FCC7FC010113C02D427BC038>I<4AB47E021F13F0
+027F13FC49B6FC01079038807F8090390FFC001FD93FF014C04948137F4948EBFFE04849
+5A5A1400485A120FA248486D13C0EE7F80EE1E00003F92C7FCA25B127FA2EC07FC91381F
+FF8000FF017F13E091B512F89039F9F01FFC9039FBC007FE9039FF8003FF17804A6C13C0
+5B6F13E0A24915F0A317F85BA4127FA5123FA217F07F121FA2000F4A13E0A26C6C15C06D
+4913806C018014006C6D485A6C9038E01FFC6DB55A011F5C010714C0010191C7FC903800
+3FF02D427BC038>I<121E121F13FC90B712FEA45A17FC17F817F017E017C0A248168000
+7EC8EA3F00007C157E5E00785D15014B5A00F84A5A484A5A5E151FC848C7FC157E5DA24A
+5A14035D14074A5AA2141F5D143FA2147F5D14FFA25BA35B92C8FCA35BA55BAA6D5A6D5A
+6D5A2F447AC238>I<EA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA3FF8EA1FF0EA07
+C0C7FCAEEA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA3FF8EA1FF0EA07C00F2C78AB
+1F>58 D<1A60F101F01907191FF17FC0953801FF00F007FCF01FF0F07FC04D48C7FCEF07
+FCEF3FF0EFFFC0040390C8FCEE0FFCEE3FE0EEFF80DB03FEC9FCED0FF8ED3FE0EDFF80DA
+07FECAFCEC1FF8EC7FE0903801FF80D907FCCBFCEB1FF0EB7FC04848CCFCEA07FCEA1FF0
+EA7FC048CDFCA2EA7FC0EA1FF0EA07FCEA01FF38007FC0EB1FF0EB07FC903801FF809038
+007FE0EC1FF8EC07FE913800FF80ED3FE0ED0FF8ED03FE923800FF80EE3FE0EE0FFCEE03
+FF040013C0EF3FF0EF07FCEF01FF9438007FC0F01FF0F007FCF001FF9538007FC0F11FF0
+19071901F10060444277B957>60 D<126012F812FE6C7EEA3FE0EA0FF8EA03FEC66C7EEB
+3FE0EB0FF8EB03FE903800FFC0EC3FF0EC0FFCEC03FF9138007FC0ED1FF0ED07FCED01FF
+9238007FC0EE1FF0EE07FE933801FF809338007FE0EF1FF8EF03FE943800FF80F03FE0F0
+0FF8F003FE953800FF80F13FE0F10FF0A2F13FE0F1FF80953803FE00F00FF8F03FE0F0FF
+80DD03FEC7FCEF1FF8EF7FE0933801FF80DC07FEC8FCEE1FF0EE7FC04B48C9FCED07FCED
+1FF0ED7FC0DA03FFCAFCEC0FFCEC3FF0ECFFC0D903FECBFCEB0FF8EB3FE0EBFF80D803FE
+CCFCEA0FF8EA3FE0EAFF8048CDFC12F81260444277B957>62 D<923803FFF0037FEBFF80
+0203B612F0020F15FC913A3FFC000FFFDAFFC0010013C0D903FEC8EA1FF0D907F0ED03F8
+D91FC0ED00FE4948167F017ECAEA1F8049717E4848717E49DAFF8013034848010F01F06D
+7E4848013F01FC6D7E92B6FC4848489026C07F80137C49489026001FC0133C484948D907
+E0133E001E49486D6C131E003E49480101141F023F913800FFE0003C4A82007C017F1880
+007819074A5AA300F81AC04848491603AB6C6C7F12781B801A076E7E127C003C133F003E
+6E1700021F4A5C001E6D6C5B001F6D6C49EBF01E6C6D6C011F143E6D6CD9C07F6D5A6C6C
+6C90B5383FFFF8033FD9FC0F5B6C6C010FD9F0035B6C6C0100903980007F806D91CBFC6C
+7E137E6D7E6D6CEF7FC0D907F0EE03FFD903FE043F1300902600FFC0913803FFF8DA3FFC
+49B512C0020FB748C7FC020316E0DA007F02FCC8FC030349C9FC4A477AC557>64
+D<EE1F80A24C7EA24C7EA34C7EA24B7FA34B7FA24B7FA34B7F169F031F80161F82033F80
+ED3E07037E80157C8203FC804B7E02018115F0820203814B137F0207815D173F020F814B
+7F021F8292C77EA24A82023E80027E82027FB7FCA291B87EA2498302F0C8FCA20103834A
+157F0107834A153FA249488284011F8491C97E4984133E017E82B6020FB612F0A54C457C
+C455>I<B9FC18F018FE727E19E026003FFCC700077F05017F716C7E727E727EA2721380
+A37213C0A74E1380A24E1300A24E5A4E5A4E5A4D5B05075B94B5128091B700FCC7FC18F0
+18FF19E002FCC7000113F8716C7EF01FFE727E7213801AC07213E0A27213F0A31AF8A71A
+F0A2601AE0604E13C0604E138095B5120005075BBA12F86119C04EC7FC18E045447CC350
+>I<DCFFF01470031F01FF14F04AB6EAE0010207EDF803023FEDFE0791B539E001FF0F49
+49C7EA3F9F010701F0EC0FFF4901C0804990C87E4948814948814948167F4849163F4849
+161F5A4A160F485B19074890CAFC19035A5BA2007F1801A34994C7FC12FFAE127F7F1AF0
+A2123FA27F6C18011AE06C7F19036C6D17C06E16077E6C6DEE0F806C6DEE1F006D6C5E6D
+6C167E6D6C6C5D6D6D4A5A6D01F0EC07F0010101FEEC1FE06D903AFFF001FF80023F90B6
+C7FC020715FC020115F0DA001F1480030001F8C8FC44467AC451>I<B9FC18F018FE727E
+19E026003FFEC7001F13F805017F9438003FFF060F7F727F727F727F84737E737EA2737E
+A2737EA21B80A2851BC0A51BE0AD1BC0A51B8061A21B006162193F624F5A19FF624E5B06
+075B4E5B063F90C7FC4DB45A050F13F8BA5A19C04EC8FC18F095C9FC4B447CC356>I<B7
+12E0A5D8001F90C7FCB3B3B3A4B712E0A523447DC32A>73 D<B500FE067FB512806E95B6
+FCA26F5EA2D8003F50C7FC013D6DEE03DFA2013C6DEE079FA26E6CEE0F1FA26E6C161EA2
+6E6C163CA36E6C1678A26E6C16F0A26E6DEC01E0A26E6DEC03C0A36E6DEC0780A26F6CEC
+0F00A26F6C141EA26F6C5CA36F6C5CA26F6C5CA26F6D485AA26F6D485AA26F6D485AA370
+6C48C7FCA293383FF81EA2706C5AA2706C5AA3706C5AA2705BA2705BA2705BA2B6057FB6
+128071C7FCA2173E171C61447CC36A>77 D<923807FFC092B512FE0207ECFFC0021F15F0
+91267FFE0013FC902601FFF0EB1FFF01070180010313C04990C76C7FD91FFC6E6C7E4948
+6F7E49486F7E01FF8348496F7E48496F1380A248496F13C0A24890C96C13E0A24819F049
+82003F19F8A3007F19FC49177FA400FF19FEAD007F19FC6D17FFA3003F19F8A26D5E6C19
+F0A26E5D6C19E0A26C6D4B13C06C19806E5D6C6D4B13006C6D4B5A6D6C4B5A6D6C4B5A6D
+6C4A5B6D01C001075B6D01F0011F5B010101FE90B5C7FC6D90B65A023F15F8020715C002
+004AC8FC030713C047467AC454>79 D<DAFFE0131C010701FE133C013F9038FF807C90B6
+EAE0FC4815F9489038801FFF3907FC00014848EB007F4848143F4848140F491407007F15
+035B1601160012FF177CA27FA26D153C7F7F6D92C7FC6C7EEBFFE014FE6CEBFFF015FF6C
+15E016FC6C816C6F7E6C826C826C6C81011F810107811300020F80140003077FED007F82
+040F1380828212F082A282A27EA218007EA26C5D6C5E6D14036D5D6D140701F84A5A01FF
+EC3FF002F8EBFFE0486CB65AD8FC1F92C7FCD8F80714FC48C614F0480107138031467AC4
+3E>83 D<007FBA12E0BB12F0A46C19E04406776757>95 D<903801FFE0011F13FE017F6D
+7E48B612E03A03FE007FF84848EB1FFC6D6D7E486C6D7EA26F7FA36F7F6C5A6C5AEA00F0
+90C7FCA40203B5FC91B6FC1307013F13F19038FFFC01000313E0481380381FFE00485A5B
+127F5B12FF5BA35DA26D5B6C6C5B4B13F0D83FFE013EEBFFC03A1FFF80FC7F0007EBFFF8
+6CECE01FC66CEB8007D90FFCC9FC322F7DAD36>97 D<EB7FC0B5FCA512037EB1ED0FF892
+B57E02C314E002CF14F89139DFC03FFC9139FF000FFE02FCEB03FF4A6D13804A15C04A6D
+13E05CEF7FF0A218F8173FA318FCAC18F8A2177F18F0A3EFFFE06E15C06E5B6E49138002
+7C491300496C495A903AFC1FC07FFC496CB512F0D9F00314C049C691C7FCC8EA1FF03646
+7DC43E>I<EC3FFC49B512C0010F14F0013F14FC90397FF003FE9039FFC001FF0003495A
+48494813805B120F485AA2485A6F1300007F6E5AED00784991C7FCA212FFAC6C7EA3123F
+6DEC03C0A26C6C1407000F16806D140F6C6DEB1F006C6D133E6C01F05B3A007FFC03F86D
+B55A010F14C0010391C7FC9038003FF82A2F7CAD32>I<EE03FEED07FFA5ED001F160FB1
+EC3FE0903803FFFC010FEBFF8F013F14CF9039FFF807FF48EBC00148903880007F4890C7
+123F4848141F49140F121F485AA3127F5BA212FFAC127FA37F123FA26C6C141FA26C6C14
+3F0007157F6C6C91B5FC6CD9C00314FC6C9038F01FEF6DB5128F011FEBFE0F010713F890
+26007FC0EBF80036467CC43E>I<EC3FF80103B57E010F14E0013F8090397FF83FF89039
+FFC007FC48496C7E48496C7E48486D1380485A001FED7FC05B003FED3FE0A2127F5B17F0
+161F12FFA290B7FCA401F0C9FCA5127FA27FA2123F17F06C7E16016C6C15E06C6C14036C
+6DEB07C06C6DEB0F806C01F0EB3F0090397FFE01FE011FB55A010714F0010114C0902600
+1FFEC7FC2C2F7DAD33>I<EDFF80020F13E0027F13F049B512F849EB8FFC90390FFE0FFE
+90381FFC1F14F8133FEB7FF0A2ED0FFCEBFFE0ED03F0ED00C01600ABB612F8A5C601E0C7
+FCB3B0007FEBFFE0A527467DC522>I<DAFFE0137E010F9039FE03FF80013FEBFF8F90B8
+12C048D9C07F133F489038001FF84848EB0FFC4848903907FE1F80001F9238FF0F00496D
+90C7FCA2003F82A8001F93C7FCA26D5B000F5D6C6C495A6C6C495A6C9038C07FF04890B5
+5A1680D8078F49C8FC018013E0000F90CAFCA47F7F7F90B612C016FC6CEDFF8017E06C82
+6C16FC7E000382000F82D81FF0C77ED83FC014074848020113808248C9FC177FA46D15FF
+007F17006D5C6C6C4A5A6C6C4A5AD80FFEEC3FF83B07FFC001FFF0000190B612C06C6C92
+C7FC010F14F8D9007F90C8FC32427DAC38>I<EB7FC0B5FCA512037EB1ED07FE92383FFF
+8092B512E002C114F89139C7F03FFC9138CF801F9139DF000FFE14DE14FC4A6D7E5CA25C
+A35CB3A7B60083B512FEA537457CC43E>I<137C48B4FC4813804813C0A24813E0A56C13
+C0A26C13806C1300EA007C90C7FCAAEB7FC0EA7FFFA512037EB3AFB6FCA518467CC520>
+I<EB7FC0B5FCA512037EB3B3B3A3B61280A519457CC420>108 D<90277F8007FEEC0FFC
+B590263FFFC090387FFF8092B5D8F001B512E002816E4880913D87F01FFC0FE03FF8913D
+8FC00FFE1F801FFC0003D99F009026FF3E007F6C019E6D013C130F02BC5D02F86D496D7E
+A24A5D4A5DA34A5DB3A7B60081B60003B512FEA5572D7CAC5E>I<90397F8007FEB59038
+3FFF8092B512E0028114F8913987F03FFC91388F801F000390399F000FFE6C139E14BC02
+F86D7E5CA25CA35CB3A7B60083B512FEA5372D7CAC3E>I<EC1FFC49B512C0010714F001
+1F14FC90397FF80FFF9026FFC0017F48496C7F4848C7EA3FE000078248486E7E49140F00
+1F82A2003F82491407007F82A400FF1780AA007F1700A46C6C4A5AA2001F5E6D141F000F
+5E6C6C4A5AA26C6C6CEBFFE06C6D485B27007FF80F90C7FC6DB55A010F14F8010114C090
+26001FFCC8FC312F7DAD38>I<90397FC00FF8B590B57E02C314E002CF14F89139DFC03F
+FC9139FF001FFE000301FCEB07FF6C496D13804A15C04A6D13E05C7013F0A2EF7FF8A4EF
+3FFCACEF7FF8A318F017FFA24C13E06E15C06E5B6E4913806E4913006E495A9139DFC07F
+FC02CFB512F002C314C002C091C7FCED1FF092C9FCADB67EA536407DAC3E>I<DA3FE013
+1E902603FFFC133E010F01FF137E013F1480903AFFF80FE0FE489038E003F148EBC00148
+90388000FB4890C7127F49143F001F151F485A160F5B127FA3485AAC6C7EA46C7EA26C6C
+141F163F6C6C147F6C15FF6C6D5A6C9038E003EF6C9038F01FCF6DB5128F011FEBFE0F01
+0313F89038007FC091C7FCAD0307B512FCA536407CAC3B>I<90387F807FB53881FFE002
+8313F0028F13F8ED8FFC91389F1FFE000313BE6C13BC14F8A214F0ED0FFC9138E007F8ED
+01E092C7FCA35CB3A5B612E0A5272D7DAC2E>I<90391FFC038090B51287000314FF120F
+381FF003383FC00049133F48C7121F127E00FE140FA215077EA27F01E090C7FC13FE387F
+FFF014FF6C14C015F06C14FC6C800003806C15806C7E010F14C0EB003F020313E0140000
+F0143FA26C141F150FA27EA26C15C06C141FA26DEB3F8001E0EB7F009038F803FE90B55A
+00FC5CD8F03F13E026E007FEC7FC232F7CAD2C>I<EB01E0A51303A41307A2130FA2131F
+A2133F137F13FF1203000F90B51280B7FCA4C601E0C7FCB3A3ED01E0A9150302F013C013
+7F150790393FF80F8090391FFC1F006DB5FC6D13FC01015B9038003FE023407EBE2C>I<
+D97FC049B4FCB50103B5FCA50003EC000F6C81B3A85EA25EA25E7E6E491380017FD901F7
+13FE9138F807E76DB512C7010F1407010313FE9026007FF0EBFC00372E7CAC3E>I<B690
+3803FFFCA5000101E09038003E006C163C80017F5D8017F8013F5D6E1301011F5D6E1303
+010F5D6E13076D5DED800F6D92C7FC15C05E6DEBE01E163E6D143CEDF07C027F1378EDF8
+F8023F5B15FD021F5B15FF6E5BA36E5BA26E90C8FCA26E5AA26E5AA21578362C7EAB3B>
+I<B500FE90383FFFF0A5C601F0903803E0006D6C495A6D6C495A011F4AC7FC6E5B6D6C13
+7E6DEB807C6D6D5A6DEBC1F0EDE3E06DEBF7C06EB45A806E90C8FC5D6E7E6E7F6E7FA24A
+7F4A7F8291381F3FFCEC3E1F027C7F4A6C7E49486C7F01036D7F49487E02C08049486C7F
+49C76C7E013E6E7E017E141FB500E090B512FCA5362C7EAB3B>120
+D<001FB71280A49026FC001F130001E0495A5B49495A90C7485A48495B123E4A5B4A5B00
+3C495BA24A90C7FC4A5A4A5AC7FC4A5A495B495BA2495B499038800780491300A2495A49
+48130F49481400A2485B48495B485BA248495B4890C75A48485C15034848EB1FFEB7FCA4
+292C7DAB32>122 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fn ecrm1728 17.28 8
+/Fn 8 117 df<B912F018FF19E019F8C601FCC8EA7FFED93FF892380FFF80011F04017F
+9538007FF0F11FF8737EF103FE737E737F747E747E747E1A0F87747E1A0387747EA27413
+80A2F37FC0A21CE01B3FA21CF0A21B1F1CF8A31CFCA21B0FA41CFEAF1CFCA51B1F1CF8A4
+F33FF0A21CE0A21B7F1CC01BFF1C80A2501300A2505A505AA2505A505A505A505A1AFF4F
+5B4F90C7FCF107FCF11FF8F17FF0953801FFC0013F04075BD9FFFCDB7FFEC8FCBA12F819
+E096C9FC18F0576278E167>68 D<BB12FCA4C601FCC8120FD93FF89238007FFE011F171F
+190719031900A21A7E1A3EA21A1EA21A1F86A486A6F20380A318E0A297C7FCA61701A417
+031707170F171F17FF91B7FCA402F8C7FC171F170F170717031701A41700A895C9FCB3A5
+80133F90B57EB712E0A4496278E158>70 D<EC3FE0903803FFFE010F6D7E90393FC03FE0
+90397C000FF801F0EB03FC48486D7E48486D7E48486E7E48C86C7E7F01F06E7E487E6D6E
+7EA3707EA36C5AEA03E0C9FCA6167FED7FFF020FB5FC91387FF807903801FF80903807FC
+00EB1FF0EB7FC0495AD803FEC7FC485A120F5B485A485AA24848EE01C0A312FF5BA2160F
+A3161F6D141B007F153B16736D17806C6C9138E1FC03001FEC03C16C6C903A0780FE0700
+D807FE49486C5A2701FF807CEB7FFE6C6CB4486D5A011F01E06D5A010390C7EA07E03A41
+79BF43>97 D<ED1FE0EDFFF8020313FE91380FF03F91391FC01F8091383F807F91397F00
+FFC014FE1301495A5C0107EC7F80A24948EB1E0093C7FCA2495AB3A5B712E0A426001FE0
+C8FCB3B3B0497EEB7FFC003FB512FEA42A657DE429>102 D<1378EA01FE487E487FA66C
+90C7FC6C5AEA007890C8FCB3A2EB0780EA0FFFB5FCA41203C6FCA2137FB3B3AC497E487F
+B61280A4195F7BDE25>105 D<010FEB07F8D80FFFEB1FFEB590387FFF809238F81FC091
+3801E03F913903C07FE00003EB0780C6EB0F00140E6D5A0218EB3FC00238EB1F800230EB
+0600027090C7FCA2146014E0A25CA55CB3B0497E4813F0B612F8A42B3F7BBE34>114
+D<9138FFC003010FEBF807017FEBFE0F3A01FF003F9FD803F0EB07DF48486DB4FCD80F80
+1300001F8148C8FC003E81007E81127C00FC81A4827EA27E7F6C7E6D91C7FC13F8EA3FFE
+381FFFE06C13FF15F0000314FE6C6E7E6C6C14E0011F14F801078001008002077FDA003F
+13801507030113C0ED007F00E0ED3FE0161F17F06C150F1607A36C1503A37EA26C16E016
+077E17C06D140F6D15806D141FD8FDF0EC3F00D8F8F8147E017C495A3AF01F801FF06DB5
+12C0D8E00391C7FC39C0007FF02C417CBF35>I<1470A714F0A51301A31303A21307A213
+0FA2131F133F137F13FF1203000F90B6FCB8FCA326000FF0C8FCB3AEEE01C0AE6D6CEB03
+80A316076D6C14005E6D6C130E6D6C131E6E6C5A91383FE0F86EB45A020713C0020090C7
+FC2A597ED734>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fo ecbx1728 17.28 18
+/Fo 18 117 df<BB7E1AFCF2FFC01BF01BFED8000191C8001F6D7E070014E0081F7F0807
+13FC08017F747F093F7F757F757F757F757F757F757FA2767E1E80881EC0881EE0881EF0
+A2881EF8A31EFC88A31EFEA61EFFB01EFEA61EFCA2641EF8A31EF064A21EE0641EC0641E
+80521300A2525A515B515BA2515B515B093F5B515B504848C7FC08075B081F5B97B512E0
+070F1480BC48C8FC1BF81BC008FCC9FC1A8068627BE177>68 D<942603FFF8151C94B66C
+143C040F03F0147C047F03FC14FC0303B81301030FDAC00113C0033F01F8C7381FF00392
+B500C0913807F807020349C83801FE0F020F01F89238007F1F4A01E0EE3FBF4A49EE0FFF
+91B5CA7E494983494983494983495B4949187F4B183F491A1F495B90B5CC120FA2484919
+075A4A19035A4A19015AA24A19005AA348491A7CA35A9AC8FCA35CA2B5FCB07EA26E043F
+B81280A47E96C7000701FCC7FCA26C7FA37E80A27E807E807E6C7FA26D7F6D7F7F816D7F
+6D6D5F6D7F6D6D5F6D6D7E023F6D5E6E01F05E6E6DEEFE7F020301FF923801FC3F020002
+C0913807F80F033F01FC91381FF007030F903BFFE001FFC001030391B6EA8000DB007F4B
+C7123C040F03F8140C040003C091C8FC050301F8CBFC696677E37A>71
+D<BA12E0F1FF801AF81AFF1BC0D8000191C7000114F0DE000F13FC070313FF070080083F
+7F747F747F747F747FA2747F88A28986A389A865A35091C8FCA26462646462505B505B50
+138097B5C9FC070313FC070F5B4EB512C093B8CAFC1AF81AC01AF893C7000713FE06006D
+7E073F7F7313F007077F737F87737F85888688A2747FAA88A91F707614F8A286A2746D13
+011FF086746D13037614E0B800FE6EED07C0746CEBC00F759038F07F80090F90B5120009
+035CCF6C13F80A0313E06D647BE173>82 D<001FBD12F0A59126F8000191C7123F4801C0
+060713F849C71700491A7F01F01A1F491A0F491A07A2491A03A290C81801A2007EF300FC
+A4007C1C7CA7481C3EA5C91900B3B3B3A5023FB912F8A55F617AE06C>84
+D<913803FFF0027F13FF0103B612E0010F15F890263FFC0013FED97FC090381FFF8049C7
+6C7F4801C06D7F486D6D7F6E6D7F48836E7F84177F84A36C496E7FA26C5B6C5B013FC8FC
+90C9FCA75F0307B6FC4AB7FC141F91B5EAF03F0103EBFE00010F13F0013F1380D9FFFEC7
+FC485B485B485B485B485B485BA24890C8FC1A7CA2485AA35FA394B5FC7F6C5D6EEB03DF
+6CDB07CFEBC0F86C6DEB0F8F6C6DD91F07EBF3F06C01F8017E14FF6C9027FE01FC0314E0
+C690B5D8F00114C0013F9126C0007F1380010791C7383FFE009026003FF8EC07F846437B
+C14D>97 D<903807FF80B6FCA5C6FC7F7FB3A9933801FFE0041F13FE047FEBFFC00381B6
+12F0922687FC0113FC923A9FE0003FFEDBBF8090380FFF8003FEC76C7F4B6E7F4B6E7F4B
+6E7F4B824B157F4B82737EA21B80851BC0A31BE085A41BF0AE1BE0A44F13C0A31B80A24F
+1300A262197F6F5E6F4B5A4E5B6F4A5BDAFCF84A5BDAF87E4A5B4A6C4A90C7FC9126E01F
+C0EB7FFC913BC00FF803FFF8DA8003B612E091C71580013E023F01FCC8FC90C800031380
+4C657CE356>I<ED1FFF4AB512F8020F14FF027F15C0902701FFF80013F04901E0EB0FF8
+010F0180EB03FC4990C7EA0FFE49484A7E49485C4948168048495C5A5C5A485BA2487013
+005C48705A715AEF03F04893C8FC91CBFCA4B5FCAE7EA280A27EA36C7FF003E07E6E1507
+6C18C06E150F6C18806C6D151F6C6DED3F006D6C157E6D6C15FE6D6D495A6D6D495A6D01
+F0EB0FE0010101FEEB7FC06D6CB6C7FC021F14FC020314E09126001FFEC8FC3B437BC145
+>I<ED3FFE0203B512E0021F14FC027F14FF902701FFF80F13C00107D9C0037F4990C77F
+49486E7E49486E7E49486E7E49486E7E5A48496E13805A4A16C0488219E0485B834818F0
+A34890C8FCA27113F8A3B5FCA391B8FCA491CBFCA67EA4807EA27E19F8806C17016C18F0
+806C17036C6DED07E06E16C06C170F6D6CED1F806D6CED3F006D6C6C14FE01076DEB03FC
+6D01F8EB0FF8010001FFEB7FE0023F90B51280020F4AC7FC020114F8DA000F13803D437C
+C146>101 D<EEFFE0031F13FC92B6FC02031580020F9038E03FC04A903800FFE091267F
+FE0113F04A485A49494813F84913F04913E0A25B15C05B7013F04913807013E09338007F
+80EF1E0094C7FCB1B8FCA5D8003F0180C8FCB3B3B2B712F8A535657CE42F>I<F13F8091
+2601FFF0903801FFE0021F01FF010713F091B6D8E01F13F801039238F87FCF010F903BC0
+7FFEFC0FFC903B1FFE000FFFF0D97FFC6DEBE01F49486D140F48496D13F01AF848496DEB
+F807489438FC01E096C7FC48496E7EA44883A96C5FA46C6D4A5AA26C5F6C6D495BA26C6D
+495B6D6C495B6D6C4990C8FC903A7FFFC07FFE017B90B512F801F015E0021F91C9FC0001
+010113F049CCFC1203A27FA37FA27F7F6D7E91B612FCEFFFE06C17FCF0FF806C18E0856D
+17FC6D836D837F017F188048BA12C000070180C712074848C96C13E04848160F48481603
+4982007F19F084485A197FA56D17FF007F19E0A26D5E6C6C4C13C0001F19806D5E6C6C4C
+1300000301C0ED3FFC6C01F0EDFFF826007FFC020313E090261FFFE0017F1380010790B6
+48C7FC010116F8D9001F1580DA007F01E0C8FC46607CC14D>I<EB0FE0EB3FF8497E497E
+487FA24880A76C91C7FCA26C5B6D5A6D5AEB0FE090C9FCB1903807FF80007FB5FCA5C6FC
+7F7FB3B3B0B712C0A522657CE42A>105 D<903807FF80B6FCA5C6FC7F7FB3B3B3B3AFB7
+12E0A523647CE32A>108 D<D90FFFEC7FF8B60103B5FC040F14E0043F80DC7F0113FC92
+2601F8007FC6DA03E06D7E6D49487F6D49488193C77E031E825D153803788003708215F0
+5DA35DA35DB3B3A2B7D8E03FB612F8A54D417BC056>110 D<92381FFF804AB512F8020F
+14FF023F15C09126FFFC0313F001039039E0007FFC490180EB1FFED91FFEC73807FF8049
+486E7F49486E7F49486E7F48496F7EA248496F7E4884A248496F7EA2481980A24819C091
+C97EA24819E0A5B518F0AD6C19E0A46C6D4B13C0A36C1980A26C6D4B1300A26C606E157F
+6C606C6D4B5A6C606D6C4A5B6D6C4A5B6D6C4A5B6D6C6C011F90C7FC010301E0EB7FFC6D
+9039FC03FFF86D6CB612E0020F92C8FC020114F8DA001F138044437CC14D>I<903B07FF
+8001FFE0B6011F13FE047FEBFFC00381B612F0922687FC0313FC923A9FE0007FFEC6DABF
+806D6C7E6D01FEC7000F7F6D496E7F4B824B6E7F4B6E7F4B804B82737EA21B80851BC0A2
+851BE0A4851BF0AE4F13E0A41BC061A21B80A24F1300A24F5AA26F4A5B6F4A5B626F4A5B
+6F4A5B03FE4A5B03BF027F90C7FCDB9FC0EBFFFC92268FF8075B0383B612E00380158004
+3F01FCC8FC0403138093CBFCB3A4B712E0A54C5D7CC056>I<D90FFFEB07F8B6EB3FFF4C
+13804BB512E0923903F83FF0923907E07FF8C691380F80FF6D020113FC6D131E153E153C
+1578A21570DBF00013F8EF7FF04BEB3FE0EF0F8094C7FC5DA65DB3B1B712F8A536417DC0
+3E>114 D<DA7FFC131C0107B5EAC03C011FECF0FC90B612FD489038C003FFD807FEC712
+7FD80FF8143F49140F4848140748481403A248481401A2160012FFA26D157CA27F7F7F6D
+92C7FCEBFF806C13F0ECFFC015FE6CECFFC016F86C15FE6C6F7E6C826C826C826C82013F
+81010F81010181EB003F02011580EC000F1500041F13C000F88182826C8182A26C167FA3
+7E18807F17FF6D16007F6D4A5A7F6D4A5A6DEC0FF86D6C495A3BFE1FF001FFE0486CB612
+80D8F8034AC7FC48C614F048010F90C8FC32437BC13D>I<EC07C0A6140FA5141FA3143F
+A2147FA214FF5BA25B5B5B5B137F48B5FC000F91B512F8B8FCA4D8001F01C0C7FCB3B017
+1FAD6D153E81A26D157C816D15F86D7F6D9038FC01F091397FFF07E06EEBFFC0020F1480
+0203EBFE009138003FF8305C7DDA3C>I E
+%EndDVIPSBitmapFont
+end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 600dpi
+TeXDict begin
+%%PaperSize: A4
+
+%%EndSetup
+%%Page: 1 1
+1 0 bop 290 639 a Fo(Genealogical)56 b(Represen)l(tation)e(of)f(T)-13
+b(rees)52 b(in)g(Databases)1686 822 y Fn(First)46 b(Draft)1247
+1063 y Fm(Miguel)36 b(Sofer)i(<mig@utdt.edu>)1359 1179
+y Fl(Univ)m(ersidad)33 b(T)-8 b(orcuato)33 b(Di)f(T)-8
+b(ella)1728 1295 y(Buenos)33 b(Aires)1797 1411 y(Argen)m(tina)1746
+1606 y(Ma)m(y)h(6,)e(2000)1839 1905 y Fk(Abstract)441
+2035 y Fj(blah)25 b(blah)h(.)13 b(.)g(.)118 2310 y Fi(1)131
+b(In)l(tro)t(duction)118 2491 y Fh(T)-7 b(rees)28 b(are)h(a)g(v)n(ery)f
+(frequen)n(t)h(data)f(structure.)41 b(They)30 b(are)e(the)h(natural)g
+(represen)n(tation)e(for)i(instance)g(for)f(organiza-)118
+2591 y(tional)f(c)n(harts,)g(threaded)g(discussion)g(groups,)f(some)h
+(bills)g(of)h(materials,)e(.)14 b(.)g(.)243 2691 y(A)n(t)28
+b(least)f(t)n(w)n(o)f(alternativ)n(e)h(represen)n(tations)e(for)i
+(trees)g(in)h(RDBMs)g(are)e(kno)n(wn)h(and)h(used:)220
+2857 y(1.)41 b Fg(P)m(oin)m(ters:)k Fh(a)31 b(\034eld)h(in)h(the)f(c)n
+(hild)g(record)e(references)h(the)h(paren)n(t)f(no)r(de.)50
+b(This)32 b(seems)g(to)f(b)r(e)i(the)f(canonical)326
+2956 y(represen)n(tation.)38 b(Some)29 b(DB)g(engines)f(pro)n(vide)g
+(sp)r(ecial)g(SQL)g(extensions)g(to)h(simplify)g(tree)g(searc)n(hes;)e
+(Oracle)326 3056 y(tree)d(extensions)g(are)g(an)h(example)f(\(see)h
+(for)f(instance)g([1]\);)i(DB2's)f(WITH)g(can)f(b)r(e)i(used)e(for)h
+(this)g(purp)r(ose)f(to)r(o)326 3156 y(\(see)j([3],)g(pp)h(139-162\).)
+220 3322 y(2.)41 b Fg(Nested)35 b(Sets:)43 b Fh(t)n(w)n(o)30
+b(n)n(umeric)h(\034elds)g(in)g(ev)n(ery)f(no)r(de)h(record)f(co)r(de)h
+(the)g(tree)g(structure.)47 b(I)31 b(can't)g(pro)n(vide)f(a)326
+3421 y(b)r(etter)e(or)e(briefer)h(description)g(of)h(this)g(metho)r(d)g
+(than)f(the)h(four)f(articles)g([2].)118 3587 y(These)g(t)n(w)n(o)g
+(metho)r(ds)h(o\033er)f(di\033eren)n(t)h(adv)-5 b(an)n(tages)25
+b(and)j(disadv)-5 b(an)n(tages:)243 3753 y Ff(\017)41
+b Fh(P)n(oin)n(ters)30 b(are)g(extremely)g(e\036cien)n(t)h(for)f(no)r
+(de)h(insertion)f(and/or)g(deletion,)h(but)h(require)e(recursiv)n(e)f
+(table)i(ac-)326 3853 y(cesses)e(to)h(searc)n(h)f(the)h(tree)g(\(I)h
+(do)f(not)g(kno)n(w)f(the)i(implemen)n(tation)f(details)g(of)g(the)h
+(Oracle)e(tree)g(extensions,)326 3953 y(whic)n(h)e(as)g(far)g(as)g(I)g
+(kno)n(w)g(ma)n(y)g(solv)n(e)f(this)i(problem)f(in)n(ternally;)g(they)g
+(de\034nitely)h(solv)n(e)f(it)g(for)g(the)h(end)g(user\).)243
+4119 y Ff(\017)41 b Fh(Nested)30 b(sets)g(are)f(v)n(ery)f(e\036cien)n
+(t)i(for)g(tree)f(searc)n(hes,)g(but)i(are)e(rather)f(exp)r(ensiv)n(e)i
+(for)f(no)r(de)h(insertion)f(and/or)326 4218 y(deletion:)37
+b(they)27 b(require)g(up)r(dating)g(p)r(oten)n(tially)h(man)n(y)f(no)r
+(des.)243 4384 y(W)-7 b(e)30 b(prop)r(ose)f(here)h(a)g(di\033eren)n(t)h
+(represen)n(tation,)e(based)g(on)i(no)r(de)f(iden)n(ti\034ers)g(whic)n
+(h)g(are)f(\020genealogical)f(iden)n(ti-)118 4484 y(\034ers\021:)44
+b(they)32 b(con)n(tain)f(the)h(complete)f(genealogy)f(of)h(the)h(no)r
+(de,)h(i.e.,)g(the)f(list)g(of)g(ancestors)d(up)j(to)g(the)g(ro)r(ot)f
+(of)g(the)118 4584 y(tree.)243 4683 y(This)j(allo)n(ws)f(to)i(replace)e
+(man)n(y)h(searc)n(hes)f(in)h(database)g(tables)g(with)h(string)f(op)r
+(erations)f(on)h(the)h(index.)58 b(The)118 4783 y(result,)24
+b(as)f(explained)h(in)g(Section)g(3)f(is)h(that)g(tree)f(searc)n(hes)f
+(pro)r(ceed)h(at)h(\020nested)f(sets\021)30 b(sp)r(eed,)25
+b(while)f(no)r(de)g(insertions)118 4882 y(and)k(deletions)f(are)f(as)h
+(fast)h(as)f(with)h(p)r(oin)n(ters.)243 4982 y(The)i(ob)n(vious)f(do)n
+(wnside)h(of)h(the)g(metho)r(d)g(is)f(that)h(the)g(primary)f(k)n(ey)f
+(in)i(the)g(tree)f(needs)h(to)f(b)r(e)h(a)g(v)-5 b(ariable)29
+b(size)118 5082 y(text)j(\034eld,)h(and)f(that)g(the)g(iden)n
+(ti\034ers)f(ma)n(y)g(b)r(e)i(extremelly)e(long)g(for)g(deep)h(trees.)
+49 b(W)-7 b(e)32 b(will)g(pro)n(vide)e(estimates)i(of)118
+5181 y(the)c(size)f(required)g(as)g(a)g(function)h(of)g(the)f
+(magnitude)h(of)f(the)h(tree.)1987 5653 y(1)p eop
+%%Page: 2 2
+2 1 bop 118 291 a Fi(2)131 b(Genealogical)45 b(iden)l(ti\034ers)g(for)f
+(trees)118 489 y Fm(2.1)112 b(De\034nition)118 642 y
+Fh(W)-7 b(e)28 b(de\034ne)g Fe(gene)l(alo)l(gic)l(al)k(identi\034ers)j
+Fh(recursiv)n(ely)25 b(as)i(follo)n(ws:)326 808 y Fg(De\034nition:)59
+b Fe(The)42 b(gene)l(alo)l(gic)l(al)h(identi\034er)f(\(gID\))e(of)i(a)f
+(no)l(de)h(is)f(obtaine)l(d)h(by)g(app)l(ending)g(a)f(child)326
+908 y(identi\034er)30 b(to)g(the)g(gene)l(alo)l(gic)l(al)h
+(identi\034er)g(of)f(the)g(p)l(ar)l(ent)f(no)l(de.)243
+1074 y Fh(Remark)40 b(that)h(genealogical)e(iden)n(ti\034ers)i(are)f
+(rather)g(w)n(ell)h(kno)n(wn)f(and)h(used;)48 b(common)41
+b(examples)f(are)g(the)118 1174 y(\020path+\034le-name\021)33
+b(in)28 b(a)f(computer)g(\034le)h(system)f(and)h(the)f(URLs)h(within)g
+(a)f(WWW.)243 1273 y(The)d(name)g(\020genealogical)e(iden)n
+(ti\034er\021)30 b(is)24 b(suggested)g(b)n(y)g(the)g(fact)h(that)f(the)
+h(v)-5 b(alue)24 b(of)g(the)h(iden)n(ti\034er)f(con)n(tains)f(the)118
+1373 y(complete)30 b(genealogy)d(of)j(the)g(no)r(de:)41
+b(it)30 b(con)n(tains)e(as)h(a)h(substring)f(the)h(gID)f(of)h(its)g
+(father,)g(whic)n(h)f(in)h(turn)g(con)n(tains)118 1472
+y(as)d(a)g(substring)g(the)h(gID)g(of)f(the)h(grandfather,)e(.)14
+b(.)g(.)243 1572 y(The)27 b(ro)r(ot)g(no)r(de)h(of)f(the)h(tree)f(has)g
+(a)h(gID)f(with)h(v)-5 b(alue)28 b(\021)34 b(\(the)28
+b(empt)n(y)g(string\),)f(as)g(it)h(has)f(no)g(paren)n(t.)118
+1804 y Fm(2.2)112 b(Child)36 b(iden)m(ti\034ers)118 1958
+y Fh(The)26 b(ob)n(vious)e(c)n(hild)i(iden)n(ti\034er)g(is)f(a)h
+(zero-based)d(coun)n(ter:)35 b(iden)n(tify)26 b(the)h(c)n(hild)e(b)n(y)
+h(the)g(n)n(um)n(b)r(er)f(of)h(older)f(brethren)g(it)118
+2057 y(has.)243 2157 y(W)-7 b(e)25 b(could)f(represen)n(t)g(the)h(coun)
+n(ter)f(in)h(base)f(10;)h(this)g(ho)n(w)n(ev)n(er)e(is)i(extremely)f(w)
+n(asteful)g(of)h(resources.)34 b(It)25 b(is)g(m)n(uc)n(h)118
+2257 y(b)r(etter)33 b(to)f(represen)n(t)f(the)h(coun)n(ter)g(in)g(as)g
+(large)e(a)i(base)g(as)f(p)r(ossible:)46 b(in)n(terpret)32
+b(as)f(n)n(um)n(b)r(ers)h(a)g(set)g(of)g(c)n(haracters)118
+2356 y(larger)26 b(than)h({0,1,.)14 b(.)g(.)g(9}.)243
+2456 y(As)26 b(tree)f(op)r(erations)f(will)i(in)n(v)n(olv)n(e)f(string)
+g(op)r(erations)f(on)i(the)g(indices,)g(in)g(order)f(to)g(a)n(v)n(oid)g
+(a)g(\020quoting)g(hell\021)33 b(it)26 b(is)118 2555
+y(desirable)d(to)h(a)n(v)n(oid)e(using)h(an)n(y)g(c)n(haracter)f(with)i
+(a)g(sp)r(ecial)f(meaning)h(in)g(LIKE)g(expressions)e(or)g(regular)g
+(expressions;)118 2655 y(i.e.,)28 b(w)n(e)f(will)h(not)f(use)h(an)n(y)f
+(of)g(the)h(sym)n(b)r(ols)70 b Fd(.)44 b(*)f(^)g(\\)g([)g(])g({)h(})f
+(\()g(\))g(<)g(>)71 b Fh(?)37 b(|)28 b(&)f($)243 2755
+y(W)-7 b(e)28 b(prop)r(ose)e(to)h(reserv)n(e)f(also)g(/)i(as)f(a)g
+(separator)e(\(see)i(\020V)-7 b(ariable)27 b(Sized)g(gID\021)34
+b(b)r(elo)n(w\).)243 2854 y(If)g(w)n(e)f(limit)i(ourselv)n(es)d(to)i
+(ascii)f(c)n(haracters,)g(and)h(a)n(v)n(oid)e(to)i(b)r(e)g(safe)f(a)h
+(lot)g(of)g(other)f(c)n(haracters,)g(w)n(e)g(can)h(use)118
+2954 y(n)n(um)n(b)r(ers)27 b(in)h(base)f(64)g(b)n(y)g(represen)n(ting)
+243 3120 y Ff(\017)41 b Fh(0-9)26 b(with)i('0'-'9')f(\(dec)g(ascii)g
+(co)r(de)h(48-57\))243 3286 y Ff(\017)41 b Fh(10)26 b(with)i(':')37
+b(\(dec)28 b(ascii)f(co)r(de)h(58\))243 3452 y Ff(\017)41
+b Fh(11)26 b(with)i(';')g(\(dec)g(ascii)f(co)r(de)g(59\))243
+3618 y Ff(\017)41 b Fh(12-37)25 b(with)j('A'-'Z')g(\(dec)f(ascii)g(co)r
+(de)h(65-90\))243 3784 y Ff(\017)41 b Fh(38-63)25 b(with)j('a'-'z')f
+(\(dec)h(ascii)f(co)r(de)g(97-122\))118 3950 y(By)g(using)g(base)f(64,)
+h(up)g(to)h(4096)d(c)n(hildren)i(can)f(b)r(e)i(represen)n(ted)e(using)h
+(t)n(w)n(o)f(suc)n(h)h(digits,)g(up)h(to)f(262144)d(with)k(three)118
+4050 y(digits,)g(and)f(up)h(to)f(16777216)d(with)k(four)f(digits.)243
+4149 y(If)37 b(the)g(RDBMs)g(supp)r(orts)f(in)n(ternational)g(c)n
+(haracters,)h(it)g(is)g(p)r(ossible)f(to)h(further)f(increase)g(the)h
+(base;)k(as)36 b(an)118 4249 y(example,)30 b(b)n(y)f(using)g(the)h(95)f
+(additional)g(c)n(haracters)e(of)i(the)h(latin-1)f(c)n(haracter)e(set,)
+k(w)n(e)e(could)g(co)r(de)g(n)n(um)n(b)r(ers)g(in)h(a)118
+4349 y(base)f(up)g(to)g(160)f(\025)g(remark)g(that)h(ev)n(ery)f(single)
+h(digit)g(is)g(still)h(one)e(b)n(yte)h(in)h(this)f(represen)n(tation.)
+40 b(This)29 b(means)f(that)118 4448 y(w)n(e)f(expand)h(the)f(sym)n(b)r
+(ols)g(ab)r(o)n(v)n(e)f(b)n(y)i(represen)n(ting)243 4614
+y Ff(\017)41 b Fh(64-159)25 b(with)j(dec)f(latin1)g(co)r(de)h(160-255)
+243 4780 y(In)23 b(base)g(160,)g(up)g(to)h(25600)d(c)n(hildren)i(can)f
+(b)r(e)i(represen)n(ted)e(using)h(t)n(w)n(o)g(digits,)h(up)g(to)f
+(4096000)d(with)k(three)f(digits,)118 4880 y(and)28 b(up)f(to)h
+(6.5E+08)e(with)i(four)f(digits.)243 4980 y(Remark)g(that)h(base)f(con)
+n(v)n(ersions)f(only)h(need)i(to)e(b)r(e)i(p)r(erformed)e(at)h
+(insertion)g(time,)g(when)h(the)f(index)g(of)g(a)g(new)118
+5079 y(no)r(de)g(is)f(computed.)37 b(They)28 b(will)f(therefore)g(only)
+g(ha)n(v)n(e)f(an)i(impact)f(on)h(insertion)f(timings.)1987
+5653 y(2)p eop
+%%Page: 3 3
+3 2 bop 118 291 a Fm(2.3)112 b(Coun)m(ters:)50 b(\020delimited\021)44
+b(vs.)51 b(\020\034xed)38 b(size\021)118 444 y Fh(The)33
+b(standard)g(represen)n(tation)e(of)i(gID)h(uses)e(a)h(v)-5
+b(ariable)32 b(size)h(c)n(hild)h(iden)n(ti\034er,)g(and)f(delimiters)g
+(to)h(separate)d(the)118 543 y(gID)f(of)g(the)h(c)n(hild)f(no)r(de)g
+(from)f(the)i(gID)f(of)g(its)g(paren)n(t.)43 b(F)-7 b(or)30
+b(example,)g(w)n(e)g(can)f(represen)n(t)g(the)i(\034fth)g(c)n(hild)f
+(of)g(no)r(de)118 643 y('/23/27/1')24 b(as)j('/23/27/1/4'.)32
+b(Let)c(us)f(call)g(this)h(a)f Fg(vgID)h Fh(represen)n(tation)e(\(V)-7
+b(ariable)27 b(Size)h(Genealogical)d(ID\).)243 743 y(This)30
+b(represen)n(tation)f(allo)n(ws)f(for)i(an)n(y)g(n)n(um)n(b)r(er)g(of)g
+(c)n(hildren)g(of)h(a)f(no)r(de,)h(sub)5 b(ject)30 b(only)g(to)g(the)h
+(limitations)f(the)118 842 y(RDBMS)e(ma)n(y)f(ha)n(v)n(e)f(as)h(to)h
+(the)g(length)f(of)h(a)f(v)-5 b(ariable)27 b(sized)g(string.)243
+942 y(Alternativ)n(ely)-7 b(,)24 b(w)n(e)f(could)h(c)n(ho)r(ose)f(to)h
+(limit)g(from)g(the)g(outset)g(the)g(quan)n(tit)n(y)g(of)f(c)n(hildren)
+h(that)g(a)g(no)r(de)g(ma)n(y)f(ha)n(v)n(e;)118 1042
+y(this)28 b(limit)g(w)n(ould)f(dep)r(end)i(of)e(course)f(on)i(the)g
+(application.)36 b(Let)27 b(us)h(call)f(this)h(a)f Fg(fgID)h
+Fh(represen)n(tation.)243 1141 y(F)-7 b(or)25 b(example,)h(if)g(no)g
+(no)r(de)f(is)h(allo)n(w)n(ed)f(to)g(ha)n(v)n(e)g(more)g(than)h(25600)d
+(c)n(hildren,)j(w)n(e)g(could)f(represen)n(t)g(the)h(coun)n(ters)118
+1241 y(alw)n(a)n(ys)36 b(with)i(2)f(digits.)67 b(The)38
+b(no)r(de)f(whic)n(h)h(w)n(as)f(previously)f('/23/27/1/4')d(is)k(no)n
+(w)g('23270104'.)64 b(If)38 b(w)n(e)f(require)118 1340
+y(a)g(three)g(digit)h(represen)n(tation)d(of)i(no)r(des)g(\(up)h(to)f
+(ab)r(out)h(4)f(million)g(c)n(hildren\),)j(then)d(it)h(will)g(b)r(e)f
+(represen)n(ted)f(as)118 1440 y('023027001004'.)118 1672
+y Fm(2.4)112 b(Ordering)37 b(of)h(no)s(des)118 1825 y
+Fh(F)-7 b(or)35 b(some)g(applications)g(it)h(is)f(necessary)f(to)i
+(obtain)f(subtrees)g(ordered)f(according)g(to)i(some)f(sp)r(ecial)g
+(rules.)60 b(F)-7 b(or)118 1925 y(instance:)220 2090
+y(1.)41 b(the)34 b(complete)g(subtree)f(starting)g(at)h(a)f(no)r(de)h
+(is)g(listed)g(immediately)g(after)f(the)i(no)r(de)f(in)g(question)f
+(\(\020depth)326 2189 y(\034rst\021\))220 2354 y(2.)41
+b(no)r(des)27 b(with)h(a)f(common)g(paren)n(t)g(are)g(listed)g(c)n
+(hronologically)243 2519 y(F)-7 b(or)39 b(instance,)k(the)d(displa)n(y)
+f(of)h(an)f(organization)f(c)n(hart)h(is)g(usually)h(required)e(to)i
+(satisfy)g(at)f(least)h(the)g(\034rst)118 2619 y(condition.)h(In)29
+b(a)g(threaded)f(discussion)h(group)e(one)i(wishes)g(to)f(satisfy)h(b)r
+(oth)h(conditions)e(to)h(displa)n(y)f(the)h(messages)118
+2718 y(in)20 b(a)g(thread)g(\025)f(the)i(threads)e(themselv)n(es)h
+(\(i.e.,)i(c)n(hildren)e(of)g(the)g(ro)r(ot)f(no)r(de\))i(are)e
+(usually)g(listed)i(in)f(in)n(v)n(erse)f(c)n(hronolical)118
+2818 y(order.)243 2917 y(T)-7 b(o)35 b(mak)n(e)f(a)h(particular)f
+(ordering)g(e\036cien)n(t,)j(it)f(w)n(ould)f(b)r(e)h(a)f(nice)g
+(feature)g(if)h(it)g(could)f(b)r(e)h(made)f(to)g(coincide)118
+3017 y(with)28 b(a)f(lexicographic)f(ordering)f(of)j(the)g(indices)f
+(\025i.e.,)g(as)g(pro)r(duced)g(b)n(y)h(an)f(\020ORDER)h(BY)f(id)h
+(ASC\021)35 b(in)27 b(SQL.)h(The)118 3117 y(lexicographic)d(ordering)h
+(of)h(fgID)h(satis\034es)e(b)r(oth)i(conditions.)36 b(The)27
+b(lexicographic)f(ordering)f(of)i(vgID)g(as)g(describ)r(ed)118
+3216 y(ab)r(o)n(v)n(e)34 b(satis\034es)g(the)h(\034rst)g(requisite)f
+(if)i(the)f(separator)d(has)j(the)g(minimal)g(binary)g(represen)n
+(tation)e(of)i(all)f(allo)n(w)n(ed)118 3316 y(sym)n(b)r(ols)c(in)h(an)f
+(index)h(\025)f(this)h(is)g(wh)n(y)f(w)n(e)g(reserv)n(ed)f(/)h(for)g
+(the)i(separator.)43 b(But)31 b(the)g(second)f(prop)r(ert)n(y)g(is)g
+(missing:)118 3416 y(for)d(instance,)g(the)h(index)g('/1/10')d(is)j
+(lexicographically)d(b)r(efore)i('/1/2'.)243 3515 y(If)c(the)h(second)e
+(prop)r(ert)n(y)g(is)i(also)e(required)g(for)h(vgID,)g(w)n(e)f(can)h
+(sp)r(ecify)h(the)f(c)n(hild)h(iden)n(ti\034ers)e(with)i(coun)n(ters)e
+(built)118 3615 y(in)28 b(the)g(follo)n(wing)e(w)n(a)n(y:)36
+b(represen)n(t)26 b(a)h(n)n(um)n(b)r(er)h(b)n(y)f(a)g(string)g(of)g
+(digits,)h(where)243 3779 y Ff(\017)41 b Fh(the)25 b(\034rst)g(digit)h
+Fc(D)896 3791 y Fb(0)958 3779 y Fh(represen)n(ts)e(the)i(length)f(in)h
+(digits)f(of)g(the)h(decimal)f(expansion)f(of)i(the)f(n)n(um)n(b)r(er,)
+h(min)n(us)f(one)243 3945 y Ff(\017)41 b Fh(the)28 b(follo)n(wing)e
+Fa(\()p Fc(D)920 3957 y Fb(0)976 3945 y Fa(+)18 b(1\))27
+b Fh(digits)h(are)e(the)i(decimal)g(expansion)e(of)i(the)g(n)n(um)n(b)r
+(er)118 4109 y(Let)g(us)f(call)h(these)f(iden)n(ti\034ers)g
+Fg(m-vgID)p Fh(,)g(\020m\021)34 b(for)27 b(mo)r(di\034ed.)243
+4209 y(As)e(an)f(example,)h(the)g(no)r(de)g(whic)n(h)g(w)n(as)f
+(previously)f(represen)n(ted)h(b)n(y)g(/15/3/182)d(will,)k(after)g
+(this)g(mo)r(di\034cation,)118 4309 y(ha)n(v)n(e)h(the)i(index)g
+(/115/03/2182.)243 4408 y(The)37 b(lexicographic)f(ordering)g(of)i
+(m-vgID)f(is)h(the)g(desired)f(ordering)f(of)h(the)h(tree)g(no)r(des.)
+67 b(The)38 b(cost)f(of)g(this)118 4508 y(prop)r(ert)n(y)31
+b(is)i(that)f(\(a\))h(the)g(ID)f(are)g(no)n(w)g(longer,)g(\(b\))h(no)f
+(no)r(de)g(can)g(ha)n(v)n(e)g(more)f(than)i Fa(160)3106
+4478 y Fb(160)3240 4508 y Fh(c)n(hildren)f(\(actually)-7
+b(,)118 4607 y(this)32 b(is)g(a)f(non-issue\),)h(and)f(\(c\))h(the)g
+(index)g(structure)f(is)h(redundan)n(t,)g(some)f(formally)f(correct)h
+(indices)g(are)g(in)n(v)-5 b(alid)118 4707 y(\025e.g.,)24
+b(/316/013/11.)30 b(The)24 b(third)g(issue)g(can)g(b)r(e)g(addressed)f
+(b)n(y)g(k)n(eeping)g(a)h(strict)g(con)n(trol)e(on)i(the)g(generation)f
+(of)h(new)118 4807 y(indices)k(to)f(insure)g(that)h(all)f(indices)h
+(are)e(formally)h(correct.)243 4906 y(The)32 b(issue)f(of)h(the)g(rev)n
+(erse)e(c)n(hronological)f(indexing)j(of)f(threads)h(in)g(threaded)f
+(discussion)g(groups)g(can)g(b)r(e)h(ad-)118 5006 y(dressed)d(easily)f
+(enough)h(in)h(fgID:)f(coun)n(t)g(\020do)n(wn\021)36
+b(instead)29 b(of)g(\020up\021)36 b(the)30 b(c)n(hildren)f(of)g(the)h
+(ro)r(ot)e(no)r(de)i(\025)f(this)h(implies)118 5106 y(only)e(an)g
+(inconsequen)n(tial)f(mo)r(di\034cation)h(of)g(the)g(no)r(de)h
+(insertion)e(routine,)h(as)g(sho)n(wn)f(b)r(elo)n(w.)38
+b(The)29 b(problem)e(is)h(less)118 5205 y(trivial)i(with)g(vgID;)h(in)f
+(this)h(case,)f(ma)n(yb)r(e)f(a)h(thread)g(iden)n(ti\034er)g(should)g
+(b)r(e)h(k)n(ept)f(in)g(a)g(di\033eren)n(t)g(\034eld)h(-)f(i.e.,)h
+(repre-)118 5305 y(sen)n(ting)h(the)h(structure)f(as)g(a)h(forest)f
+(rather)f(than)i(a)f(tree,)i(where)e(the)h(thread_id)f(\034eld)h
+(selects)f(the)h(\020tree\021)38 b(in)33 b(the)118 5404
+y(forest.)1987 5653 y(3)p eop
+%%Page: 4 4
+4 3 bop 118 291 a Fi(3)131 b(T)-11 b(ree)45 b(op)t(erations)e(using)h
+(genealogical)g(indices)118 472 y Fh(In)32 b(this)f(section)g(w)n(e)g
+(sho)n(w)g(ho)n(w)g(to)g(implemen)n(t)h(v)-5 b(arious)30
+b(tree)h(op)r(erations)f(using)h(gID)g(as)g(the)h(primary)e(k)n(ey)h
+(in)g(the)118 572 y(no)r(de)d(table.)243 672 y(Some)h(implemen)n
+(tation)h(issues)g(are)f(relev)-5 b(an)n(t)29 b(here,)h(esp)r(ecially)f
+(concerning)g(the)h(utilisation)g(of)g(indices)g(b)n(y)f(the)118
+771 y(DB)f(engine.)243 871 y(W)-7 b(e)28 b(discuss)f(a)g(tree)g
+(represen)n(ted)f(in)i(a)f(table)h(of)f(the)h(form)326
+1034 y Fd(CREATE)41 b(TABLE)g(tree)h(\()456 1134 y(gid)304
+b(text)42 b(PRIMARY)f(KEY,)456 1234 y(nchildren)f(integer)h(DEFAULT)f
+(0,)456 1333 y(\\ldots)h(the)i(actual)e(node)h(data)326
+1433 y(\);)118 1597 y Fh(The)26 b(\034eld)g(\020nc)n(hildren\021)32
+b(is)26 b(a)f(coun)n(ter)g(for)g(the)i(n)n(um)n(b)r(er)e(of)h(c)n
+(hildren)f(that)h(the)h(no)r(de)f(has)f Fe(ever)35 b
+Fh(had;)27 b(w)n(e)e(assume)g(here)118 1696 y(it)j(is)g(not)f(up)r
+(dated)h(when)g(no)r(des)f(or)g(subtrees)g(are)f(deleted.)243
+1796 y(Section)h(4)g(pro)n(vides)f(a)i(complete)f(implemen)n(tation)h
+(of)f(these)h(op)r(erations)e(for)h(fgID)h(in)g(P)n(ostgreSQL.)118
+2028 y Fm(3.1)112 b(Computing)37 b(the)g(lev)m(el)f(of)h(a)h(no)s(de)
+118 2181 y Fg(Cost:)f Fe(string)30 b(op)l(er)l(ations)g(\(no)g(table)g
+(ac)l(c)l(ess\))243 2280 y Fh(This)d(is)h(a)f(pure)g(string)g(op)r
+(eration,)f(no)i(table)f(access)g(is)g(required.)243
+2460 y Ff(\017)41 b Fg(vgID:)27 b Fh(coun)n(t)h(the)g(n)n(um)n(b)r(er)f
+(of)g(separators)e(\('/'\))j(in)g(the)g(PK)243 2625 y
+Ff(\017)41 b Fg(fgID:)27 b Fh(coun)n(t)g(the)h(n)n(um)n(b)r(er)g(of)f
+(c)n(haracters)e(in)j(the)g(PK,)g(divide)g(b)n(y)f(the)h(\034xed)f
+(size)h(of)f(the)h(coun)n(ters.)118 2857 y Fm(3.2)112
+b(Selecting)36 b(or)h(deleting)f(a)i(subtree)118 3010
+y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+3173 y Ff(\017)41 b Fg(vgID:)27 b Fh(The)h(subtree)f(ro)r(oted)g(at)g
+(/26/5/7)e(is)i(selected)g(b)n(y)508 3338 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('/26/5/7\045')d(AND)j(id)h(<)g('/26/5/70')243
+3503 y Ff(\017)e Fg(m-vgID:)26 b Fh(The)h(subtree)h(ro)r(oted)e(at)i
+(/126/05/07)22 b(is)28 b(selected)f(b)n(y)508 3668 y
+Fd(...)43 b(WHERE)e(id)i(LIKE)f('/126/06/07\045')243
+3833 y Ff(\017)f Fg(fgID:)27 b Fh(The)h(subtree)f(ro)r(oted)g(at)g
+(260507)e(is)i(selected)h(b)n(y)508 3997 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('260507\045')118 4229 y Fm(3.3)112
+b(Selecting)36 b(the)h(direct)f(c)m(hildren)g(of)i(a)g(no)s(de)118
+4382 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+4562 y Ff(\017)41 b Fg(vgID:)27 b Fh(The)h(direct)f(c)n(hildren)g(of)h
+(/26/5/7)c(are)j(selected)g(b)n(y)508 4727 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('/26/5/7/\045')d(AND)j(id)h(NOT)f(LIKE)g
+('26/5/7/\045/\045')243 4892 y Ff(\017)f Fg(m-vgID:)26
+b Fh(The)h(direct)h(c)n(hildren)f(of)g(/26/5/7)e(are)h(selected)i(b)n
+(y)508 5056 y Fd(...)43 b(WHERE)e(id)i(LIKE)f('/126/06/07/\045')37
+b(AND)43 b(id)f(NOT)h(LIKE)f('/126/05/07/\045/\045)o(')243
+5221 y Ff(\017)f Fg(fgID:)27 b Fh(The)h(direct)f(c)n(hildren)g(of)h
+(260507)c(are)j(selected)g(b)n(y)508 5386 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('260507\045')d(AND)k(char_length\(id\))37
+b(=)43 b(\(char_length\('26)o(05)o(07')o(\)+)o(2\))1987
+5653 y Fh(4)p eop
+%%Page: 5 5
+5 4 bop 118 291 a Fm(3.4)112 b(Inserting)37 b(a)h(no)s(de)g(or)f(a)h
+(subtree)118 444 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l
+(e)l(e)f(+)h(string)f(and)h(math)g(op)l(er)l(ations)243
+543 y Fh(Insertion)f(is)g(a)h(pro)r(cedural)e(op)r(eration.)42
+b(As)30 b(eac)n(h)f(RDBMS)h(has)f(a)h(di\033eren)n(t)f(w)n(a)n(y)g(of)g
+(de\034ning)h(pro)r(cedures,)f(w)n(e)118 643 y(will)f(just)g(describ)r
+(e)f(here)g(the)h(necessary)e(steps.)37 b(Examples)27
+b(for)g(P)n(ostgreSQL)f(are)h(pro)n(vided)f(in)i(4.)243
+743 y(In)22 b(order)f(to)h(insert)g(a)g(new)g(c)n(hild)h(of)f
+(\020daddy\021)28 b(\(either)23 b(one)f(of)g(/26/5/7,)e(/126/05/07)d
+(or)22 b(260507)d(in)k(the)f(examples)118 842 y(ab)r(o)n(v)n(e\))27
+b(y)n(ou)f(ha)n(v)n(e)h(to)220 1008 y(1.)41 b(add)27
+b(one)g(to)h(the)g(n)n(um)n(b)r(er)f(of)g(c)n(hildren)h(of)f
+(\020daddy\021)508 1174 y Fd(UPDATE)41 b(tree)h(SET)h(nchildren)c(=)k
+(\(nchildren)d(+)j(1\))g(WHERE)e(ID)i(=)g(``daddy'';)220
+1340 y Fh(2.)e(enco)r(de)27 b(the)h(n)n(um)n(b)r(er)f(of)g(c)n(hildren)
+g(of)h(\020daddy\021)33 b(in)28 b(base)f(160,)f(bring)h(it)h(to)f(the)h
+(correct)e(format)h(dep)r(ending)h(on)326 1440 y(the)c(v)-5
+b(arian)n(t)23 b(of)h(gID)g(\(pad)g(with)h(0)e(or)g(not,)i(prep)r(end)f
+(a)g(digit)g(coun)n(ter)f(or)g(not,)i(prep)r(end)f(/)g(or)f(not,)i
+(coun)n(t)e(do)n(wn)326 1540 y(or)j(up,)i(.)14 b(.)g(.)g(\))37
+b(and)28 b(app)r(end)f(it)h(to)g(daddy's)f(gID)g(to)h(obtain)f(the)h
+(new)g(no)r(de's)f(gID.)220 1706 y(3.)41 b(insert)27
+b(the)h(new)f(no)r(de)243 1872 y(When)35 b(inserting)g(a)f(subtree,)j
+(the)e(index)g(of)g(the)h(ro)r(ot)e(of)h(the)g(subtree)g(has)f(to)h(b)r
+(e)h(computed)f(as)f(ab)r(o)n(v)n(e,)i(and)118 1971 y(prep)r(ended)28
+b(to)f(the)h(index)g(of)f(eac)n(h)g(no)r(de)h(of)f(the)h(subtree)f(b)r
+(efore)h(insertion.)243 2071 y(Remark)e(that)i(only)f(the)h(paren)n(t)f
+(no)r(de)h(has)f(to)g(b)r(e)h(up)r(dated)g(on)f(insertion.)118
+2303 y Fm(3.5)112 b(Selecting)36 b(the)h(ancestors)h(of)g(a)g(no)s(de)
+118 2457 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+2556 y Fh(Y)-7 b(ou)27 b(can)g(sp)r(ecify)h(all)g(ancestors)d(of)j(a)f
+(no)r(de)h(in)f(a)h(single)f(SQL)g(statemen)n(t;)g(for)g(instance)h
+(for)f(vgID)326 2722 y Fd(...)42 b(WHERE)f('/25/6/7')f(LIKE)i(\(id)g
+(||)h('/\045'\))f(AND)g(id)h(<)g('/25/6/7')118 2888 y
+Fh(The)31 b(second)e(part)h(of)h(the)g(clause,)f(while)h(logically)e
+(redundan)n(t,)h(is)h(a)f(\020hin)n(t\021)37 b(to)30
+b(the)h(optimizer.)45 b(A)n(t)31 b(least)f(in)g(P)n(ost-)118
+2988 y(greSQL,)c(without)i(it)g(the)g(optimizer)f(will)h(c)n(ho)r(ose)e
+(a)i(sequen)n(tial)e(scan)h(of)h(the)g(table)f(and)h(disregard)d(the)j
+(index.)118 3220 y Fm(3.6)112 b(Selecting)36 b(all)g(lea)m(v)m(es)118
+3374 y Fg(Cost:)h Fe(sc)l(an)30 b(of)g(the)g(tr)l(e)l(e)243
+3473 y Fh(A)e(leaf)f(is)g(a)h(no)r(de)f(without)h(descendan)n(ts:)36
+b(it)28 b(has)f(0)g(c)n(hildren.)37 b(Hence)326 3639
+y Fd(...)42 b(WHERE)f(nchildren)f(=)j(0)118 3805 y Fh(If)28
+b(this)g(t)n(yp)r(e)g(of)f(query)g(is)h(often)f(necessary)-7
+b(,)26 b(y)n(ou)h(ma)n(y)g(b)r(e)h(w)n(ell)f(advised)g(to)g(k)n(eep)g
+(an)h(index)f(on)h(tree\(nc)n(hildren\).)118 4038 y Fm(3.7)112
+b(Determining)35 b(if)i(A)g(is)g(a)h(descendan)m(t)g(of)g(B)118
+4191 y Fg(Cost:)f Fe(string)30 b(op)l(er)l(ations,)h(no)f(table)g(ac)l
+(c)l(ess)243 4291 y Fh(This)d(is)h(a)f(pure)g(string)g(op)r(eration)f
+(on)i(the)g(indices,)f(no)g(table)h(access)e(is)i(necessary)-7
+b(.)118 4565 y Fi(4)131 b(Putting)45 b(it)f(all)h(together:)57
+b(a)44 b(P)l(ostgreSQL)f(implemen)l(tation)118 4747 y
+Fh(h)n(ttp://www.p)r(ostgresql.org/mhonarc/pgsq)o(l-sql/)o(20)o(00)o
+(-0)o(4/)o(msg0)o(02)o(67)o(.h)n(tml)243 4847 y(W)-7
+b(e)30 b(describ)r(e)g(here)g(a)g(small)f(pac)n(k)-5
+b(age)29 b(that)i(can)e(b)r(e)i(used)f(for)g(implemen)n(ting)g(gID)g
+(on)g(P)n(ostgreSQL.)f(It)i(can)e(b)r(e)118 4946 y(found)f(at)f(<h)n
+(ttp://...>)243 5046 y(The)21 b(pac)n(k)-5 b(age)21 b(uses)g(the)h(pro)
+r(cedural)e(language)h(PL/PGsql.)35 b(A)22 b(b)r(etter)g(implemen)n
+(tation)g(w)n(ould)f(probably)g(de\034ne)118 5145 y(the)28
+b(gID)g(as)f(new)g(P)n(ostgres)f(t)n(yp)r(es,)i(and)f(co)r(de)g(all)h
+(this)g(in)f(C.)243 5245 y(The)g(\034les)h(should)f(b)r(e)h(loaded)f
+(in)h(alphab)r(etical)f(order.)1987 5653 y(5)p eop
+%%Page: 6 6
+6 5 bop 118 291 a Fm(4.1)112 b(tree0_enco)s(ding.sql)118
+444 y Fh(This)28 b(\034le)f(de\034nes)h(and)f(p)r(opulates)h(the)f
+(table)h(_b160_digits)d(of)j(\020digits\021)33 b(in)28
+b(base)f(160,)326 604 y Fd(CREATE)41 b(TABLE)g(\\_b160\\_digits)d
+(\(deci)j(integer,)f(code)i(char\);)118 764 y Fh(and)28
+b(the)f(t)n(w)n(o)g(functions)326 924 y Fd(CREATE)41
+b(FUNCTION)f(\\_b160\\_encode\(i)o(nt)o(eg)o(er\))d(RETURNS)j(string)
+413 1024 y(AS)j('....')e(LANGUAGE)f('plpgsql';)326 1124
+y(CREATE)h(FUNCTION)f(\\_b160\\_encode\(i)o(nt)o(eg)o(er,)o(in)o(te)o
+(ger)o(\))d(RETURNS)k(string)413 1223 y(AS)i('....')e(LANGUAGE)f
+('plpgsql';)118 1384 y Fh(The)22 b(\034rst)h(function)f(returns)g(a)g
+(v)-5 b(ariable)21 b(size)h(enco)r(ding;)i(the)f(second)e(a)h(\034xed)h
+(size)f(enco)r(ding)g(\(the)h(second)e(parameter)118
+1483 y(is)g(the)h(size\),)g(and)f(raises)e(an)i(exception)g(if)h(the)f
+(n)n(um)n(b)r(er)g(is)g(to)r(o)g(large)e(to)i(b)r(e)h(represen)n(ted)e
+(with)h(the)h(requested)e(n)n(um)n(b)r(er)118 1583 y(of)28
+b(digits.)118 1814 y Fm(4.2)112 b(tree1_de\034ne.sql)118
+1967 y Fh(This)28 b(\034le)f(pro)n(vides)f(a)i(function)326
+2127 y Fd(CREATE)41 b(FUNCTION)f(_tree_create\(tex)o(t,)o(in)o(teg)o
+(er)o(,t)o(ext)o(,t)o(ex)o(t\))d(RETURNS)k(bpchar)413
+2227 y(AS)i('....')e(LANGUAGE)f('plpgsql';)118 2387 y
+Fh(that)e(creates)f(a)h(tree)f(infrastructure)g(of)h(either)g(fgID)g
+(or)f(vgID.)h(Assuming)f(y)n(ou)g(ha)n(v)n(e)g(a)h(table)f(\020m)n
+(ytable\021)44 b(with)118 2487 y(primary)26 b(k)n(ey)h(\020m)n
+(yid\021,)g(then)h(calling)326 2647 y Fd(SELECT)41 b(_tree_create\('m)o
+(yt)o(ree)o(',)o(2,')o(my)o(ta)o(ble)o(',)o('m)o(yid)o('\))o(;)118
+2807 y Fh(will)28 b(cause:)220 2967 y(1.)41 b(the)28
+b(creation)e(of)i(a)f(table)508 3131 y Fd(CREATE)41 b(TABLE)h
+(mytree_bkg\()683 3230 y(gid)g(text)g(PRIMARY)e(KEY,)683
+3330 y(nchildren)f(int,)683 3429 y(sid)j(integer)f(REFERENCES)e
+(mytable\(myid\))508 3529 y(\);)508 3629 y(CREATE)i(UNIQUE)g(INDEX)h
+(mytree_bkg_sid)37 b(ON)43 b(mytree_bkg\(sid\);)326 3792
+y Fh(for)27 b(the)h(tree)f(structure.)220 3955 y(2.)41
+b(the)28 b(creation)e(of)i(a)f(view)508 4118 y Fd(CREATE)41
+b(VIEW)h(mytree)f(AS)639 4218 y(SELECT)g(t.gid,n.*)900
+4317 y(FROM)h(mytable)f(n,)i(mytree_bkg)c(t)900 4417
+y(WHERE)j(t.sid=n.myid;)326 4580 y Fh(with:)35 b(a)23
+b(trigger)e(on)i(UPD)n(A)-7 b(TE)25 b(that)e(blo)r(c)n(ks)g(up)r
+(dating)g(the)h(gid)f(and)g(allo)n(ws)f(up)r(dating)h(the)g(no)r(de)h
+(data,)f(a)g(rule)326 4680 y(on)k(DELETE)i(that)f(deletes)f(the)h
+(corresp)r(onding)e(en)n(try)h(b)r(oth)h(in)g(m)n(ytree_bkg)d(and)j(m)n
+(ytable,)f(and)g(a)g(trigger)326 4779 y(ON)h(INSER)-7
+b(T)30 b(that)f(raises)e(an)h(exception)g(and)g(informs)h(the)f(user)g
+(to)h(use)f(the)h(insertion)f(function)h(describ)r(ed)326
+4879 y(b)r(elo)n(w.)220 5042 y(3.)41 b(t)n(w)n(o)26 b(insertion)h
+(functions)h(that)g(compute)g(automatically)e(the)i(gID)g(of)f(the)h
+(new)g(no)r(de:)425 5205 y Ff(\017)41 b Fh(a)27 b(function)i(m)n
+(ytree_insert\(text,text,in)n(teger,text\))d(for)h(insertion)g(sim)n
+(ultaneosly)f(in)i(b)r(oth)g(tables:)508 5305 y(m)n
+(ytree_insert\('2201','hello',0,'not)15 b(m)n(uc)n(h'\))j(inserts)g(a)g
+(new)g(c)n(hild)h(of)f(2201)f(with)h(data1='hello',)h(data2=0)508
+5404 y(and)28 b(data3='not)e(m)n(uc)n(h')1987 5653 y(6)p
+eop
+%%Page: 7 7
+7 6 bop 425 291 a Ff(\017)41 b Fh(a)27 b(function)i(m)n
+(ytree_insert_no)r(de\(text,in)n(teger\))c(for)i(insertion)g(in)h(m)n
+(ytree_bkg)508 390 y(m)n(ytree_insert\('2201',25\))c(inserts)j(in)h(m)n
+(ytree_bkg)e(a)h(new)h(c)n(hild)f(of)h(2201)d(with)j(sid=25)220
+556 y(4.)41 b(a)27 b(function)h(m)n(ytree_mo)n(v)n(e\(text,text\))e
+(that)i(mo)n(v)n(es)e(subtrees:)326 656 y(m)n(ytree_mo)n(v)n
+(e\('2201','23'\))d(mo)n(v)n(es)j(the)i(subtree)f(ro)r(oted)g(at)g
+(2201)f(to)h(a)h(place)f(b)r(elo)n(w)g(23)f(\(ma)n(yb)r(e)i(2307\))220
+822 y(5.)41 b(a)c(function)g(m)n(ytree_len\(\))g(that)h(returns)e(the)i
+(length)f(of)g(the)h(enco)r(dings)f(used)g(in)h(the)f(gID)g(\(2)h
+(here;)j(0)c(if)326 922 y(v)-5 b(ariable)26 b(size\).)118
+1196 y Fi(5)131 b(Non-tree)44 b(hierarc)l(hies)118 1378
+y Fh(sequence)22 b(as)f(id,)j(table)e(with)h(\(id,g-index\))f(with)g(p)
+r(ossibly)g(man)n(y)g(g-indices)f(for)h(eac)n(h)f(id)h(\(if)h(TOO)f
+(man)n(y)-7 b(,)23 b(bad)f(mo)r(del:)118 1478 y(list)28
+b(all)f(genealogies,)f(i.e.,)h(paths)h(from)f(the)h(ro)r(ot\))118
+1752 y Fi(References)160 1934 y Fh([1])41 b(Philip)28
+b(Greenspun,)g Fe(T)-6 b(r)l(e)l(es)29 b(in)h(Or)l(acle)g(SQL)p
+Fh(,)d(in)h Fg(SQL)k(for)g(W)-8 b(eb)31 b(Nerds)289 2033
+y Fh(<h)n(ttp://photo.net/sql/trees.h)n(tml>)160 2200
+y([2])41 b(Jo)r(e)27 b(Celk)n(o,)f Fe(SQL)j(for)i(Smarties)p
+Fh(,)d(in)g Fg(DBMS)j(Online)p Fh(,)26 b(Marc)n(h)h(to)g(June)h(1996)
+289 2299 y(<h)n(ttp://www.dbmsmag.com/9603d06.h)n(tml>)289
+2399 y(<h)n(ttp://www.dbmsmag.com/9604d06.h)n(tml>)289
+2498 y(<h)n(ttp://www.dbmsmag.com/9605d06.h)n(tml>)289
+2598 y(<h)n(ttp://www.dbmsmag.com/9606d06.h)n(tml>)160
+2764 y([3])41 b(Graeme)26 b(Birc)n(hall,)h Fg(DB2)32
+b(UDB)g(V6.1)f(SQL)h(Co)s(okb)s(o)s(ok)p Fh(,)289 2864
+y(<h)n(ttp://ourw)n(orld.compuserv)n(e.com/homepag)o(es/)o(Gra)o
+(eme_Bir)o(c)n(ha)o(ll/HTM_CO)o(OK)o(.HTM>)1987 5653
+y(7)p eop
+%%Trailer
+end
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/source3/lib/ldb/ldb_tdb/ldb_cache.c b/source3/lib/ldb/ldb_tdb/ldb_cache.c
new file mode 100644 (file)
index 0000000..d6d66dd
--- /dev/null
@@ -0,0 +1,545 @@
+/* 
+   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 cache functions
+ *
+ *  Description: cache special records in a ldb/tdb
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+#define LTDB_FLAG_CASE_INSENSITIVE (1<<0)
+#define LTDB_FLAG_INTEGER          (1<<1)
+#define LTDB_FLAG_HIDDEN           (1<<2)
+#define LTDB_FLAG_OBJECTCLASS      (1<<3)
+
+/* valid attribute flags */
+static const struct {
+       const char *name;
+       int value;
+} ltdb_valid_attr_flags[] = {
+       { "CASE_INSENSITIVE", LTDB_FLAG_CASE_INSENSITIVE },
+       { "INTEGER", LTDB_FLAG_INTEGER },
+       { "HIDDEN", LTDB_FLAG_HIDDEN },
+       { "NONE", 0 },
+       { NULL, 0 }
+};
+
+
+/*
+  de-register any special handlers for @ATTRIBUTES
+*/
+static void ltdb_attributes_unload(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg;
+       int i;
+
+       if (ltdb->cache->attributes == NULL) {
+               /* no previously loaded attributes */
+               return;
+       }
+
+       msg = ltdb->cache->attributes;
+       for (i=0;i<msg->num_elements;i++) {
+               const struct ldb_attrib_handler *h;
+               /* this is rather ugly - a consequence of const handling */
+               h = ldb_attrib_handler(module->ldb, msg->elements[i].name);
+               ldb_remove_attrib_handler(module->ldb, msg->elements[i].name);
+               if (strcmp(h->attr, msg->elements[i].name) == 0) {
+                       talloc_steal(msg, h->attr);
+               }
+       }
+
+       talloc_free(ltdb->cache->attributes);
+       ltdb->cache->attributes = NULL;
+}
+
+/*
+  add up the attrib flags for a @ATTRIBUTES element
+*/
+static int ltdb_attributes_flags(struct ldb_message_element *el, unsigned *v)
+{
+       int i;
+       unsigned value = 0;
+       for (i=0;i<el->num_values;i++) {
+               int j;
+               for (j=0;ltdb_valid_attr_flags[j].name;j++) {
+                       if (strcmp(ltdb_valid_attr_flags[j].name, 
+                                  (char *)el->values[i].data) == 0) {
+                               value |= ltdb_valid_attr_flags[j].value;
+                               break;
+                       }
+               }
+               if (ltdb_valid_attr_flags[j].name == NULL) {
+                       return -1;
+               }
+       }
+       *v = value;
+       return 0;
+}
+
+/*
+  register any special handlers from @ATTRIBUTES
+*/
+static int ltdb_attributes_load(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg = ltdb->cache->attributes;
+       struct ldb_dn *dn;
+       int i;
+
+       dn = ldb_dn_explode(module->ldb, LTDB_ATTRIBUTES);
+       if (dn == NULL) goto failed;
+
+       if (ltdb_search_dn1(module, dn, msg) == -1) {
+               talloc_free(dn);
+               goto failed;
+       }
+       talloc_free(dn);
+       /* mapping these flags onto ldap 'syntaxes' isn't strictly correct,
+          but its close enough for now */
+       for (i=0;i<msg->num_elements;i++) {
+               unsigned flags;
+               const char *syntax;
+               const struct ldb_attrib_handler *h;
+               struct ldb_attrib_handler h2;
+
+               if (ltdb_attributes_flags(&msg->elements[i], &flags) != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'\n", msg->elements[i].name);
+                       goto failed;
+               }
+               switch (flags & ~LTDB_FLAG_HIDDEN) {
+               case 0:
+                       syntax = LDB_SYNTAX_OCTET_STRING;
+                       break;
+               case LTDB_FLAG_CASE_INSENSITIVE:
+                       syntax = LDB_SYNTAX_DIRECTORY_STRING;
+                       break;
+               case LTDB_FLAG_INTEGER:
+                       syntax = LDB_SYNTAX_INTEGER;
+                       break;
+               default:
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, 
+                                 "Invalid flag combination 0x%x for '%s' in @ATTRIBUTES\n",
+                                 flags, msg->elements[i].name);
+                       goto failed;
+               }
+
+               h = ldb_attrib_handler_syntax(module->ldb, syntax);
+               if (h == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, 
+                                 "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n",
+                                 syntax, msg->elements[i].name);
+                       goto failed;
+               }
+               h2 = *h;
+               h2.attr = talloc_strdup(module, msg->elements[i].name);
+               if (ldb_set_attrib_handlers(module->ldb, &h2, 1) != 0) {
+                       goto failed;
+               }
+       }
+
+       return 0;
+failed:
+       return -1;
+}
+
+
+/*
+  register any subclasses from @SUBCLASSES
+*/
+static int ltdb_subclasses_load(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg = ltdb->cache->subclasses;
+       struct ldb_dn *dn;
+       int i, j;
+
+       dn = ldb_dn_explode(module->ldb, LTDB_SUBCLASSES);
+       if (dn == NULL) goto failed;
+
+       if (ltdb_search_dn1(module, dn, msg) == -1) {
+               talloc_free(dn);
+               goto failed;
+       }
+       talloc_free(dn);
+
+       for (i=0;i<msg->num_elements;i++) {
+               struct ldb_message_element *el = &msg->elements[i];
+               for (j=0;j<el->num_values;j++) {
+                       if (ldb_subclass_add(module->ldb, el->name, 
+                                            (char *)el->values[j].data) != 0) {
+                               goto failed;
+                       }
+               }
+       }
+
+       return 0;
+failed:
+       return -1;
+}
+
+
+/*
+  de-register any @SUBCLASSES
+*/
+static void ltdb_subclasses_unload(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg;
+       int i;
+
+       if (ltdb->cache->subclasses == NULL) {
+               /* no previously loaded subclasses */
+               return;
+       }
+
+       msg = ltdb->cache->subclasses;
+       for (i=0;i<msg->num_elements;i++) {
+               ldb_subclass_remove(module->ldb, msg->elements[i].name);
+       }
+
+       talloc_free(ltdb->cache->subclasses);
+       ltdb->cache->subclasses = NULL;
+}
+
+
+/*
+  initialise the baseinfo record
+*/
+static int ltdb_baseinfo_init(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg;
+       struct ldb_message_element el;
+       struct ldb_val val;
+       int ret;
+       /* the initial sequence number must be different from the one
+          set in ltdb_cache_free(). Thanks to Jon for pointing this
+          out. */
+       const char *initial_sequence_number = "1";
+
+       ltdb->sequence_number = atof(initial_sequence_number);
+
+       msg = talloc(ltdb, struct ldb_message);
+       if (msg == NULL) {
+               goto failed;
+       }
+
+       msg->num_elements = 1;
+       msg->elements = &el;
+       msg->dn = ldb_dn_explode(msg, LTDB_BASEINFO);
+       if (!msg->dn) {
+               goto failed;
+       }
+       el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
+       if (!el.name) {
+               goto failed;
+       }
+       el.values = &val;
+       el.num_values = 1;
+       el.flags = 0;
+       val.data = (uint8_t *)talloc_strdup(msg, initial_sequence_number);
+       if (!val.data) {
+               goto failed;
+       }
+       val.length = 1;
+       
+       ret = ltdb_store(module, msg, TDB_INSERT);
+
+       talloc_free(msg);
+
+       return ret;
+
+failed:
+       talloc_free(msg);
+       errno = ENOMEM;
+       return -1;
+}
+
+/*
+  free any cache records
+ */
+static void ltdb_cache_free(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+
+       ltdb->sequence_number = 0;
+       talloc_free(ltdb->cache);
+       ltdb->cache = NULL;
+}
+
+/*
+  force a cache reload
+*/
+int ltdb_cache_reload(struct ldb_module *module)
+{
+       ltdb_attributes_unload(module);
+       ltdb_subclasses_unload(module);
+       ltdb_cache_free(module);
+       return ltdb_cache_load(module);
+}
+
+/*
+  load the cache records
+*/
+int ltdb_cache_load(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_dn *baseinfo_dn = NULL;
+       struct ldb_dn *indexlist_dn = NULL;
+       double seq;
+
+       if (ltdb->cache == NULL) {
+               ltdb->cache = talloc_zero(ltdb, struct ltdb_cache);
+               if (ltdb->cache == NULL) goto failed;
+               ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
+               ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
+               ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
+               if (ltdb->cache->indexlist == NULL ||
+                   ltdb->cache->subclasses == NULL ||
+                   ltdb->cache->attributes == NULL) {
+                       goto failed;
+               }
+       }
+
+       talloc_free(ltdb->cache->baseinfo);
+       ltdb->cache->baseinfo = talloc(ltdb->cache, struct ldb_message);
+       if (ltdb->cache->baseinfo == NULL) goto failed;
+
+       baseinfo_dn = ldb_dn_explode(module->ldb, LTDB_BASEINFO);
+       if (baseinfo_dn == NULL) goto failed;
+
+       if (ltdb_search_dn1(module, baseinfo_dn, ltdb->cache->baseinfo) == -1) {
+               goto failed;
+       }
+       
+       /* possibly initialise the baseinfo */
+       if (!ltdb->cache->baseinfo->dn) {
+               if (ltdb_baseinfo_init(module) != 0) {
+                       goto failed;
+               }
+               if (ltdb_search_dn1(module, baseinfo_dn, ltdb->cache->baseinfo) != 1) {
+                       goto failed;
+               }
+       }
+
+       /* if the current internal sequence number is the same as the one
+          in the database then assume the rest of the cache is OK */
+       seq = ldb_msg_find_attr_as_double(ltdb->cache->baseinfo, LTDB_SEQUENCE_NUMBER, 0);
+       if (seq == ltdb->sequence_number) {
+               goto done;
+       }
+       ltdb->sequence_number = seq;
+
+       talloc_free(ltdb->cache->last_attribute.name);
+       memset(&ltdb->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute));
+
+       ltdb_attributes_unload(module);
+       ltdb_subclasses_unload(module);
+
+       talloc_free(ltdb->cache->indexlist);
+       talloc_free(ltdb->cache->subclasses);
+
+       ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
+       ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
+       ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
+       if (ltdb->cache->indexlist == NULL ||
+           ltdb->cache->subclasses == NULL ||
+           ltdb->cache->attributes == NULL) {
+               goto failed;
+       }
+           
+       indexlist_dn = ldb_dn_explode(module->ldb, LTDB_INDEXLIST);
+       if (indexlist_dn == NULL) goto failed;
+
+       if (ltdb_search_dn1(module, indexlist_dn, ltdb->cache->indexlist) == -1) {
+               goto failed;
+       }
+
+       if (ltdb_attributes_load(module) == -1) {
+               goto failed;
+       }
+       if (ltdb_subclasses_load(module) == -1) {
+               goto failed;
+       }
+
+done:
+       talloc_free(baseinfo_dn);
+       talloc_free(indexlist_dn);
+       return 0;
+
+failed:
+       talloc_free(baseinfo_dn);
+       talloc_free(indexlist_dn);
+       return -1;
+}
+
+
+/*
+  increase the sequence number to indicate a database change
+*/
+int ltdb_increase_sequence_number(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       struct ldb_message *msg;
+       struct ldb_message_element el[2];
+       struct ldb_val val;
+       struct ldb_val val_time;
+       time_t t = time(NULL);
+       char *s = NULL;
+       int ret;
+
+       msg = talloc(ltdb, struct ldb_message);
+       if (msg == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1);
+       if (!s) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       msg->num_elements = ARRAY_SIZE(el);
+       msg->elements = el;
+       msg->dn = ldb_dn_explode(msg, LTDB_BASEINFO);
+       if (msg->dn == NULL) {
+               talloc_free(msg);
+               errno = ENOMEM;
+               return -1;
+       }
+       el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
+       if (el[0].name == NULL) {
+               talloc_free(msg);
+               errno = ENOMEM;
+               return -1;
+       }
+       el[0].values = &val;
+       el[0].num_values = 1;
+       el[0].flags = LDB_FLAG_MOD_REPLACE;
+       val.data = (uint8_t *)s;
+       val.length = strlen(s);
+
+       el[1].name = talloc_strdup(msg, LTDB_MOD_TIMESTAMP);
+       if (el[1].name == NULL) {
+               talloc_free(msg);
+               errno = ENOMEM;
+               return -1;
+       }
+       el[1].values = &val_time;
+       el[1].num_values = 1;
+       el[1].flags = LDB_FLAG_MOD_REPLACE;
+
+       s = ldb_timestring(msg, t);
+       if (s == NULL) {
+               return -1;
+       }
+
+       val_time.data = (uint8_t *)s;
+       val_time.length = strlen(s);
+
+       ret = ltdb_modify_internal(module, msg);
+
+       talloc_free(msg);
+
+       if (ret == 0) {
+               ltdb->sequence_number += 1;
+       }
+
+       return ret;
+}
+
+
+/*
+  return the attribute flags from the @ATTRIBUTES record 
+  for the given attribute
+*/
+int ltdb_attribute_flags(struct ldb_module *module, const char *attr_name)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       const struct ldb_message_element *attr_el;
+       int i, j, ret=0;
+
+       if (ltdb->cache->last_attribute.name &&
+           ldb_attr_cmp(ltdb->cache->last_attribute.name, attr_name) == 0) {
+               return ltdb->cache->last_attribute.flags;
+       }
+
+       /* objectclass is a special default case */
+       if (ldb_attr_cmp(attr_name, LTDB_OBJECTCLASS) == 0) {
+               ret = LTDB_FLAG_OBJECTCLASS | LTDB_FLAG_CASE_INSENSITIVE;
+       }
+
+       attr_el = ldb_msg_find_element(ltdb->cache->attributes, attr_name);
+
+       if (!attr_el) {
+               /* check if theres a wildcard attribute */
+               attr_el = ldb_msg_find_element(ltdb->cache->attributes, "*");
+
+               if (!attr_el) {
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < attr_el->num_values; i++) {
+               for (j=0; ltdb_valid_attr_flags[j].name; j++) {
+                       if (strcmp(ltdb_valid_attr_flags[j].name, 
+                                  (char *)attr_el->values[i].data) == 0) {
+                               ret |= ltdb_valid_attr_flags[j].value;
+                       }
+               }
+       }
+
+       talloc_free(ltdb->cache->last_attribute.name);
+
+       ltdb->cache->last_attribute.name = talloc_strdup(ltdb->cache, attr_name);
+       ltdb->cache->last_attribute.flags = ret;
+
+       return ret;
+}
+
+int ltdb_check_at_attributes_values(const struct ldb_val *value)
+{
+       int i;
+
+       for (i = 0; ltdb_valid_attr_flags[i].name != NULL; i++) {
+               if ((strcmp(ltdb_valid_attr_flags[i].name, (char *)value->data) == 0)) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
diff --git a/source3/lib/ldb/ldb_tdb/ldb_index.c b/source3/lib/ldb/ldb_tdb/ldb_index.c
new file mode 100644 (file)
index 0000000..59c1645
--- /dev/null
@@ -0,0 +1,1159 @@
+/* 
+   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"
+#include "ldb/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+/*
+  find an element in a list, using the given comparison function and
+  assuming that the list is already sorted using comp_fn
+
+  return -1 if not found, or the index of the first occurance of needle if found
+*/
+static int ldb_list_find(const void *needle, 
+                        const void *base, size_t nmemb, size_t size, 
+                        comparison_fn_t comp_fn)
+{
+       const char *base_p = base;
+       size_t min_i, max_i, test_i;
+
+       if (nmemb == 0) {
+               return -1;
+       }
+
+       min_i = 0;
+       max_i = nmemb-1;
+
+       while (min_i < max_i) {
+               int r;
+
+               test_i = (min_i + max_i) / 2;
+               /* the following cast looks strange, but is
+                correct. The key to understanding it is that base_p
+                is a pointer to an array of pointers, so we have to
+                dereference it after casting to void **. The strange
+                const in the middle gives us the right type of pointer
+                after the dereference  (tridge) */
+               r = comp_fn(needle, *(void * const *)(base_p + (size * test_i)));
+               if (r == 0) {
+                       /* scan back for first element */
+                       while (test_i > 0 &&
+                              comp_fn(needle, *(void * const *)(base_p + (size * (test_i-1)))) == 0) {
+                               test_i--;
+                       }
+                       return test_i;
+               }
+               if (r < 0) {
+                       if (test_i == 0) {
+                               return -1;
+                       }
+                       max_i = test_i - 1;
+               }
+               if (r > 0) {
+                       min_i = test_i + 1;
+               }
+       }
+
+       if (comp_fn(needle, *(void * const *)(base_p + (size * min_i))) == 0) {
+               return min_i;
+       }
+
+       return -1;
+}
+
+struct dn_list {
+       unsigned int count;
+       char **dn;
+};
+
+/*
+  return the dn key to be used for an index
+  caller frees
+*/
+static struct ldb_dn *ldb_dn_key(struct ldb_context *ldb,
+                       const char *attr, const struct ldb_val *value)
+{
+       struct ldb_dn *ret;
+       char *dn;
+       struct ldb_val v;
+       const struct ldb_attrib_handler *h;
+       char *attr_folded;
+
+       attr_folded = ldb_attr_casefold(ldb, attr);
+       if (!attr_folded) {
+               return NULL;
+       }
+
+       h = ldb_attrib_handler(ldb, attr);
+       if (h->canonicalise_fn(ldb, ldb, value, &v) != 0) {
+               /* canonicalisation can be refused. For example, 
+                  a attribute that takes wildcards will refuse to canonicalise
+                  if the value contains a wildcard */
+               talloc_free(attr_folded);
+               return NULL;
+       }
+       if (ldb_should_b64_encode(&v)) {
+               char *vstr = ldb_base64_encode(ldb, (char *)v.data, v.length);
+               if (!vstr) return NULL;
+               dn = talloc_asprintf(ldb, "%s:%s::%s", LTDB_INDEX, attr_folded, vstr);
+               talloc_free(vstr);
+               if (v.data != value->data) {
+                       talloc_free(v.data);
+               }
+               talloc_free(attr_folded);
+               if (dn == NULL) return NULL;
+               goto done;
+       }
+
+       dn = talloc_asprintf(ldb, "%s:%s:%.*s", 
+                             LTDB_INDEX, attr_folded, (int)v.length, (char *)v.data);
+
+       if (v.data != value->data) {
+               talloc_free(v.data);
+       }
+       talloc_free(attr_folded);
+
+done:
+       ret = ldb_dn_explode(ldb, dn);
+       talloc_free(dn);
+       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,
+                           unsigned int *v_idx, const char *key)
+{
+       unsigned int i, j;
+       for (i=0;i<msg->num_elements;i++) {
+               if (ldb_attr_cmp(msg->elements[i].name, key) == 0) {
+                       const struct ldb_message_element *el = 
+                               &msg->elements[i];
+                       for (j=0;j<el->num_values;j++) {
+                               if (ldb_attr_cmp((char *)el->values[j].data, attr) == 0) {
+                                       if (v_idx) {
+                                               *v_idx = j;
+                                       }
+                                       return i;
+                               }
+                       }
+               }
+       }
+       return -1;
+}
+
+/* used in sorting dn lists */
+static int list_cmp(const char **s1, const char **s2)
+{
+       return strcmp(*s1, *s2);
+}
+
+/*
+  return a list of dn's that might match a simple indexed search or
+ */
+static int ltdb_index_dn_simple(struct ldb_module *module, 
+                               const struct ldb_parse_tree *tree,
+                               const struct ldb_message *index_list,
+                               struct dn_list *list)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct ldb_dn *dn;
+       int ret;
+       unsigned int i, j;
+       struct ldb_message *msg;
+
+       list->count = 0;
+       list->dn = NULL;
+
+       /* 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.equality.attr, NULL, LTDB_IDXATTR) == -1) {
+               return -1;
+       }
+
+       /* the attribute is indexed. Pull the list of DNs that match the 
+          search criterion */
+       dn = ldb_dn_key(ldb, tree->u.equality.attr, &tree->u.equality.value);
+       if (!dn) return -1;
+
+       msg = talloc(list, struct ldb_message);
+       if (msg == NULL) {
+               return -1;
+       }
+
+       ret = ltdb_search_dn1(module, dn, msg);
+       talloc_free(dn);
+       if (ret == 0 || ret == -1) {
+               return ret;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               struct ldb_message_element *el;
+
+               if (strcmp(msg->elements[i].name, LTDB_IDX) != 0) {
+                       continue;
+               }
+
+               el = &msg->elements[i];
+
+               list->dn = talloc_array(list, char *, el->num_values);
+               if (!list->dn) {
+                       break;          
+               }
+
+               for (j=0;j<el->num_values;j++) {
+                       list->dn[list->count] = 
+                               talloc_strdup(list->dn, (char *)el->values[j].data);
+                       if (!list->dn[list->count]) {
+                               return -1;
+                       }
+                       list->count++;
+               }
+       }
+
+       talloc_free(msg);
+
+       if (list->count > 1) {
+               qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) list_cmp);
+       }
+
+       return 1;
+}
+
+
+static int list_union(struct ldb_context *, struct dn_list *, const struct dn_list *);
+
+/*
+  return a list of dn's that might match a simple indexed search on
+  the special objectclass attribute
+ */
+static int ltdb_index_dn_objectclass(struct ldb_module *module, 
+                                    const struct ldb_parse_tree *tree,
+                                    const struct ldb_message *index_list,
+                                    struct dn_list *list)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int ret;
+       const char *target = (const char *)tree->u.equality.value.data;
+       const char **subclasses;
+
+       list->count = 0;
+       list->dn = NULL;
+
+       ret = ltdb_index_dn_simple(module, tree, index_list, list);
+
+       subclasses = ldb_subclass_list(module->ldb, target);
+
+       if (subclasses == NULL) {
+               return ret;
+       }
+
+       for (i=0;subclasses[i];i++) {
+               struct ldb_parse_tree tree2;
+               struct dn_list *list2;
+               tree2.operation = LDB_OP_EQUALITY;
+               tree2.u.equality.attr = LTDB_OBJECTCLASS;
+               if (!tree2.u.equality.attr) {
+                       return -1;
+               }
+               tree2.u.equality.value.data = 
+                       (uint8_t *)talloc_strdup(list, subclasses[i]);
+               if (tree2.u.equality.value.data == NULL) {
+                       return -1;                      
+               }
+               tree2.u.equality.value.length = strlen(subclasses[i]);
+               list2 = talloc(list, struct dn_list);
+               if (list2 == NULL) {
+                       talloc_free(tree2.u.equality.value.data);
+                       return -1;
+               }
+               if (ltdb_index_dn_objectclass(module, &tree2, 
+                                             index_list, list2) == 1) {
+                       if (list->count == 0) {
+                               *list = *list2;
+                               ret = 1;
+                       } else {
+                               list_union(ldb, list, list2);
+                               talloc_free(list2);
+                       }
+               }
+               talloc_free(tree2.u.equality.value.data);
+       }
+
+       return ret;
+}
+
+/*
+  return a list of dn's that might match a leaf indexed search
+ */
+static int ltdb_index_dn_leaf(struct ldb_module *module, 
+                             const struct ldb_parse_tree *tree,
+                             const struct ldb_message *index_list,
+                             struct dn_list *list)
+{
+       if (ldb_attr_cmp(tree->u.equality.attr, LTDB_OBJECTCLASS) == 0) {
+               return ltdb_index_dn_objectclass(module, tree, index_list, list);
+       }
+       if (ldb_attr_dn(tree->u.equality.attr) == 0) {
+               list->dn = talloc_array(list, char *, 1);
+               if (list->dn == NULL) {
+                       ldb_oom(module->ldb);
+                       return -1;
+               }
+               list->dn[0] = talloc_strdup(list, (char *)tree->u.equality.value.data);
+               if (list->dn[0] == NULL) {
+                       ldb_oom(module->ldb);
+                       return -1;
+               }
+               list->count = 1;
+               return 1;
+       }
+       return ltdb_index_dn_simple(module, tree, index_list, list);
+}
+
+
+/*
+  list intersection
+  list = list & list2
+  relies on the lists being sorted
+*/
+static int list_intersect(struct ldb_context *ldb,
+                         struct dn_list *list, const struct dn_list *list2)
+{
+       struct dn_list *list3;
+       unsigned int i;
+
+       if (list->count == 0 || list2->count == 0) {
+               /* 0 & X == 0 */
+               return 0;
+       }
+
+       list3 = talloc(ldb, struct dn_list);
+       if (list3 == NULL) {
+               return -1;
+       }
+
+       list3->dn = talloc_array(list3, char *, list->count);
+       if (!list3->dn) {
+               talloc_free(list3);
+               return -1;
+       }
+       list3->count = 0;
+
+       for (i=0;i<list->count;i++) {
+               if (ldb_list_find(list->dn[i], list2->dn, list2->count, 
+                             sizeof(char *), (comparison_fn_t)strcmp) != -1) {
+                       list3->dn[list3->count] = talloc_move(list3->dn, &list->dn[i]);
+                       list3->count++;
+               } else {
+                       talloc_free(list->dn[i]);
+               }               
+       }
+
+       talloc_free(list->dn);
+       list->dn = talloc_move(list, &list3->dn);
+       list->count = list3->count;
+       talloc_free(list3);
+
+       return 0;
+}
+
+
+/*
+  list union
+  list = list | list2
+  relies on the lists being sorted
+*/
+static int list_union(struct ldb_context *ldb, 
+                     struct dn_list *list, const struct dn_list *list2)
+{
+       unsigned int i;
+       char **d;
+       unsigned int count = list->count;
+
+       if (list->count == 0 && list2->count == 0) {
+               /* 0 | 0 == 0 */
+               return 0;
+       }
+
+       d = talloc_realloc(list, list->dn, char *, list->count + list2->count);
+       if (!d) {
+               return -1;
+       }
+       list->dn = d;
+
+       for (i=0;i<list2->count;i++) {
+               if (ldb_list_find(list2->dn[i], list->dn, count, 
+                             sizeof(char *), (comparison_fn_t)strcmp) == -1) {
+                       list->dn[list->count] = talloc_strdup(list->dn, list2->dn[i]);
+                       if (!list->dn[list->count]) {
+                               return -1;
+                       }
+                       list->count++;
+               }               
+       }
+
+       if (list->count != count) {
+               qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)list_cmp);
+       }
+
+       return 0;
+}
+
+static int ltdb_index_dn(struct ldb_module *module, 
+                        const 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_module *module, 
+                           const struct ldb_parse_tree *tree,
+                           const struct ldb_message *index_list,
+                           struct dn_list *list)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int ret;
+       
+       ret = -1;
+       list->dn = NULL;
+       list->count = 0;
+
+       for (i=0;i<tree->u.list.num_elements;i++) {
+               struct dn_list *list2;
+               int v;
+
+               list2 = talloc(module, struct dn_list);
+               if (list2 == NULL) {
+                       return -1;
+               }
+
+               v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2);
+
+               if (v == 0) {
+                       /* 0 || X == X */
+                       if (ret == -1) {
+                               ret = 0;
+                       }
+                       talloc_free(list2);
+                       continue;
+               }
+
+               if (v == -1) {
+                       /* 1 || X == 1 */
+                       talloc_free(list->dn);
+                       talloc_free(list2);
+                       return -1;
+               }
+
+               if (ret == -1) {
+                       ret = 1;
+                       list->dn = talloc_move(list, &list2->dn);
+                       list->count = list2->count;
+               } else {
+                       if (list_union(ldb, list, list2) == -1) {
+                               talloc_free(list2);
+                               return -1;
+                       }
+                       ret = 1;
+               }
+               talloc_free(list2);
+       }
+
+       if (list->count == 0) {
+               return 0;
+       }
+
+       return ret;
+}
+
+
+/*
+  NOT an index results
+ */
+static int ltdb_index_dn_not(struct ldb_module *module, 
+                            const 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_module *module, 
+                            const struct ldb_parse_tree *tree,
+                            const struct ldb_message *index_list,
+                            struct dn_list *list)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int ret;
+       
+       ret = -1;
+       list->dn = NULL;
+       list->count = 0;
+
+       for (i=0;i<tree->u.list.num_elements;i++) {
+               struct dn_list *list2;
+               int v;
+
+               list2 = talloc(module, struct dn_list);
+               if (list2 == NULL) {
+                       return -1;
+               }
+
+               v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2);
+
+               if (v == 0) {
+                       /* 0 && X == 0 */
+                       talloc_free(list->dn);
+                       talloc_free(list2);
+                       return 0;
+               }
+
+               if (v == -1) {
+                       talloc_free(list2);
+                       continue;
+               }
+
+               if (ret == -1) {
+                       ret = 1;
+                       talloc_free(list->dn);
+                       list->dn = talloc_move(list, &list2->dn);
+                       list->count = list2->count;
+               } else {
+                       if (list_intersect(ldb, list, list2) == -1) {
+                               talloc_free(list2);
+                               return -1;
+                       }
+               }
+
+               talloc_free(list2);
+
+               if (list->count == 0) {
+                       talloc_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_module *module, 
+                        const struct ldb_parse_tree *tree,
+                        const struct ldb_message *index_list,
+                        struct dn_list *list)
+{
+       int ret = -1;
+
+       switch (tree->operation) {
+       case LDB_OP_AND:
+               ret = ltdb_index_dn_and(module, tree, index_list, list);
+               break;
+
+       case LDB_OP_OR:
+               ret = ltdb_index_dn_or(module, tree, index_list, list);
+               break;
+
+       case LDB_OP_NOT:
+               ret = ltdb_index_dn_not(module, tree, index_list, list);
+               break;
+
+       case LDB_OP_EQUALITY:
+               ret = ltdb_index_dn_leaf(module, tree, index_list, list);
+               break;
+
+       case LDB_OP_SUBSTRING:
+       case LDB_OP_GREATER:
+       case LDB_OP_LESS:
+       case LDB_OP_PRESENT:
+       case LDB_OP_APPROX:
+       case LDB_OP_EXTENDED:
+               /* we can't index with fancy bitops yet */
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+  filter a candidate dn_list from an indexed search into a set of results
+  extracting just the given attributes
+*/
+static int ltdb_index_filter(const struct dn_list *dn_list, 
+                            struct ldb_handle *handle)
+{
+       struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
+       struct ldb_reply *ares = NULL;
+       unsigned int i;
+
+       for (i = 0; i < dn_list->count; i++) {
+               struct ldb_dn *dn;
+               int ret;
+
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       handle->state = LDB_ASYNC_DONE;
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ares->message = ldb_msg_new(ares);
+               if (!ares->message) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       handle->state = LDB_ASYNC_DONE;
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+
+               dn = ldb_dn_explode(ares->message, dn_list->dn[i]);
+               if (dn == NULL) {
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ret = ltdb_search_dn1(ac->module, dn, ares->message);
+               talloc_free(dn);
+               if (ret == 0) {
+                       /* the record has disappeared? yes, this can happen */
+                       talloc_free(ares);
+                       continue;
+               }
+
+               if (ret == -1) {
+                       /* an internal error */
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               if (!ldb_match_msg(ac->module->ldb, ares->message, ac->tree, ac->base, ac->scope)) {
+                       talloc_free(ares);
+                       continue;
+               }
+
+               /* filter the attributes that the user wants */
+               ret = ltdb_filter_attrs(ares->message, ac->attrs);
+
+               if (ret == -1) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       handle->state = LDB_ASYNC_DONE;
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ares->type = LDB_REPLY_ENTRY;
+               handle->state = LDB_ASYNC_PENDING;
+               handle->status = ac->callback(ac->module->ldb, ac->context, ares);
+
+               if (handle->status != LDB_SUCCESS) {
+                       handle->state = LDB_ASYNC_DONE;
+                       return handle->status;
+               }
+       }
+
+       return LDB_SUCCESS;
+}
+
+/*
+  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_handle *handle)
+{
+       struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
+       struct ltdb_private *ltdb = talloc_get_type(ac->module->private_data, struct ltdb_private);
+       struct dn_list *dn_list;
+       int ret;
+
+       if (ltdb->cache->indexlist->num_elements == 0 && 
+           ac->scope != LDB_SCOPE_BASE) {
+               /* no index list? must do full search */
+               return -1;
+       }
+
+       dn_list = talloc(handle, struct dn_list);
+       if (dn_list == NULL) {
+               return -1;
+       }
+
+       if (ac->scope == LDB_SCOPE_BASE) {
+               /* with BASE searches only one DN can match */
+               dn_list->dn = talloc_array(dn_list, char *, 1);
+               if (dn_list->dn == NULL) {
+                       ldb_oom(ac->module->ldb);
+                       return -1;
+               }
+               dn_list->dn[0] = ldb_dn_linearize(dn_list, ac->base);
+               if (dn_list->dn[0] == NULL) {
+                       ldb_oom(ac->module->ldb);
+                       return -1;
+               }
+               dn_list->count = 1;
+               ret = 1;
+       } else {
+               ret = ltdb_index_dn(ac->module, ac->tree, ltdb->cache->indexlist, dn_list);
+       }
+
+       if (ret == 1) {
+               /* we've got a candidate list - now filter by the full tree
+                  and extract the needed attributes */
+               ret = ltdb_index_filter(dn_list, handle);
+               handle->status = ret;
+               handle->state = LDB_ASYNC_DONE;
+       }
+
+       talloc_free(dn_list);
+
+       return ret;
+}
+
+/*
+  add a index element where this is the first indexed DN for this value
+*/
+static int ltdb_index_add1_new(struct ldb_context *ldb, 
+                              struct ldb_message *msg,
+                              struct ldb_message_element *el,
+                              const char *dn)
+{
+       struct ldb_message_element *el2;
+
+       /* add another entry */
+       el2 = talloc_realloc(msg, msg->elements, 
+                              struct ldb_message_element, msg->num_elements+1);
+       if (!el2) {
+               return -1;
+       }
+
+       msg->elements = el2;
+       msg->elements[msg->num_elements].name = talloc_strdup(msg->elements, LTDB_IDX);
+       if (!msg->elements[msg->num_elements].name) {
+               return -1;
+       }
+       msg->elements[msg->num_elements].num_values = 0;
+       msg->elements[msg->num_elements].values = talloc(msg->elements, struct ldb_val);
+       if (!msg->elements[msg->num_elements].values) {
+               return -1;
+       }
+       msg->elements[msg->num_elements].values[0].length = strlen(dn);
+       msg->elements[msg->num_elements].values[0].data = discard_const_p(uint8_t, dn);
+       msg->elements[msg->num_elements].num_values = 1;
+       msg->num_elements++;
+
+       return 0;
+}
+
+
+/*
+  add a index element where this is not the first indexed DN for this
+  value
+*/
+static int ltdb_index_add1_add(struct ldb_context *ldb, 
+                              struct ldb_message *msg,
+                              struct ldb_message_element *el,
+                              int idx,
+                              const char *dn)
+{
+       struct ldb_val *v2;
+       unsigned int i;
+
+       /* for multi-valued attributes we can end up with repeats */
+       for (i=0;i<msg->elements[idx].num_values;i++) {
+               if (strcmp(dn, (char *)msg->elements[idx].values[i].data) == 0) {
+                       return 0;
+               }
+       }
+
+       v2 = talloc_realloc(msg->elements, msg->elements[idx].values,
+                             struct ldb_val, 
+                             msg->elements[idx].num_values+1);
+       if (!v2) {
+               return -1;
+       }
+       msg->elements[idx].values = v2;
+
+       msg->elements[idx].values[msg->elements[idx].num_values].length = strlen(dn);
+       msg->elements[idx].values[msg->elements[idx].num_values].data = discard_const_p(uint8_t, dn);
+       msg->elements[idx].num_values++;
+
+       return 0;
+}
+
+/*
+  add an index entry for one message element
+*/
+static int ltdb_index_add1(struct ldb_module *module, const char *dn, 
+                          struct ldb_message_element *el, int v_idx)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct ldb_message *msg;
+       struct ldb_dn *dn_key;
+       int ret;
+       unsigned int i;
+
+       msg = talloc(module, struct ldb_message);
+       if (msg == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       dn_key = ldb_dn_key(ldb, el->name, &el->values[v_idx]);
+       if (!dn_key) {
+               talloc_free(msg);
+               errno = ENOMEM;
+               return -1;
+       }
+       talloc_steal(msg, dn_key);
+
+       ret = ltdb_search_dn1(module, dn_key, msg);
+       if (ret == -1) {
+               talloc_free(msg);
+               return -1;
+       }
+
+       if (ret == 0) {
+               msg->dn = dn_key;
+               msg->num_elements = 0;
+               msg->elements = NULL;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               if (strcmp(LTDB_IDX, msg->elements[i].name) == 0) {
+                       break;
+               }
+       }
+
+       if (i == msg->num_elements) {
+               ret = ltdb_index_add1_new(ldb, msg, el, dn);
+       } else {
+               ret = ltdb_index_add1_add(ldb, msg, el, i, dn);
+       }
+
+       if (ret == 0) {
+               ret = ltdb_store(module, msg, TDB_REPLACE);
+       }
+
+       talloc_free(msg);
+
+       return ret;
+}
+
+static int ltdb_index_add0(struct ldb_module *module, const char *dn,
+                          struct ldb_message_element *elements, int num_el)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       int ret;
+       unsigned int i, j;
+
+       if (dn[0] == '@') {
+               return 0;
+       }
+
+       if (ltdb->cache->indexlist->num_elements == 0) {
+               /* no indexed fields */
+               return 0;
+       }
+
+       for (i = 0; i < num_el; i++) {
+               ret = ldb_msg_find_idx(ltdb->cache->indexlist, elements[i].name, 
+                                      NULL, LTDB_IDXATTR);
+               if (ret == -1) {
+                       continue;
+               }
+               for (j = 0; j < elements[i].num_values; j++) {
+                       ret = ltdb_index_add1(module, dn, &elements[i], j);
+                       if (ret == -1) {
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+  add the index entries for a new record
+  return -1 on failure
+*/
+int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       char *dn;
+       int ret;
+
+       dn = ldb_dn_linearize(ltdb, msg->dn);
+       if (dn == NULL) {
+               return -1;
+       }
+
+       ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements);
+
+       talloc_free(dn);
+
+       return ret;
+}
+
+
+/*
+  delete an index entry for one message element
+*/
+int ltdb_index_del_value(struct ldb_module *module, const char *dn, 
+                        struct ldb_message_element *el, int v_idx)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct ldb_message *msg;
+       struct ldb_dn *dn_key;
+       int ret, i;
+       unsigned int j;
+
+       if (dn[0] == '@') {
+               return 0;
+       }
+
+       dn_key = ldb_dn_key(ldb, el->name, &el->values[v_idx]);
+       if (!dn_key) {
+               return -1;
+       }
+
+       msg = talloc(dn_key, struct ldb_message);
+       if (msg == NULL) {
+               talloc_free(dn_key);
+               return -1;
+       }
+
+       ret = ltdb_search_dn1(module, dn_key, msg);
+       if (ret == -1) {
+               talloc_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 */
+               talloc_free(dn_key);
+               return 0;
+       }
+
+       i = ldb_msg_find_idx(msg, dn, &j, LTDB_IDX);
+       if (i == -1) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR,
+                               "ERROR: dn %s not found in %s\n", dn,
+                               ldb_dn_linearize(dn_key, dn_key));
+               /* it ain't there. hmmm */
+               talloc_free(dn_key);
+               return 0;
+       }
+
+       if (j != msg->elements[i].num_values - 1) {
+               memmove(&msg->elements[i].values[j], 
+                       &msg->elements[i].values[j+1], 
+                       (msg->elements[i].num_values-(j+1)) * 
+                       sizeof(msg->elements[i].values[0]));
+       }
+       msg->elements[i].num_values--;
+
+       if (msg->elements[i].num_values == 0) {
+               ret = ltdb_delete_noindex(module, dn_key);
+       } else {
+               ret = ltdb_store(module, msg, TDB_REPLACE);
+       }
+
+       talloc_free(dn_key);
+
+       return ret;
+}
+
+/*
+  delete the index entries for a record
+  return -1 on failure
+*/
+int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       int ret;
+       char *dn;
+       unsigned int i, j;
+
+       if (ldb_dn_is_special(msg->dn)) {
+               return 0;
+       }
+
+       dn = ldb_dn_linearize(ltdb, msg->dn);
+       if (dn == NULL) {
+               return -1;
+       }
+
+       /* find the list of indexed fields */   
+       if (ltdb->cache->indexlist->num_elements == 0) {
+               /* no indexed fields */
+               return 0;
+       }
+
+       for (i = 0; i < msg->num_elements; i++) {
+               ret = ldb_msg_find_idx(ltdb->cache->indexlist, msg->elements[i].name, 
+                                      NULL, LTDB_IDXATTR);
+               if (ret == -1) {
+                       continue;
+               }
+               for (j = 0; j < msg->elements[i].num_values; j++) {
+                       ret = ltdb_index_del_value(module, dn, &msg->elements[i], j);
+                       if (ret == -1) {
+                               talloc_free(dn);
+                               return -1;
+                       }
+               }
+       }
+
+       talloc_free(dn);
+       return 0;
+}
+
+
+/*
+  traversal function that deletes all @INDEX records
+*/
+static int delete_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+       const char *dn = "DN=" LTDB_INDEX ":";
+       if (strncmp((char *)key.dptr, dn, strlen(dn)) == 0) {
+               return tdb_delete(tdb, key);
+       }
+       return 0;
+}
+
+/*
+  traversal function that adds @INDEX records during a re index
+*/
+static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+       struct ldb_module *module = state;
+       struct ldb_message *msg;
+       char *dn = NULL;
+       int ret;
+       TDB_DATA key2;
+
+       if (strncmp((char *)key.dptr, "DN=@", 4) == 0 ||
+           strncmp((char *)key.dptr, "DN=", 3) != 0) {
+               return 0;
+       }
+
+       msg = talloc(module, struct ldb_message);
+       if (msg == NULL) {
+               return -1;
+       }
+
+       ret = ltdb_unpack_data(module, &data, msg);
+       if (ret != 0) {
+               talloc_free(msg);
+               return -1;
+       }
+
+       /* check if the DN key has changed, perhaps due to the 
+          case insensitivity of an element changing */
+       key2 = ltdb_key(module, msg->dn);
+       if (key2.dptr == NULL) {
+               /* probably a corrupt record ... darn */
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Invalid DN in re_index: %s\n",
+                                                       ldb_dn_linearize(msg, msg->dn));
+               talloc_free(msg);
+               return 0;
+       }
+       if (strcmp((char *)key2.dptr, (char *)key.dptr) != 0) {
+               tdb_delete(tdb, key);
+               tdb_store(tdb, key2, data, 0);
+       }
+       talloc_free(key2.dptr);
+
+       if (msg->dn == NULL) {
+               dn = (char *)key.dptr + 3;
+       } else {
+               dn = ldb_dn_linearize(msg->dn, msg->dn);
+       }
+
+       ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements);
+
+       talloc_free(msg);
+
+       return ret;
+}
+
+/*
+  force a complete reindex of the database
+*/
+int ltdb_reindex(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       int ret;
+
+       if (ltdb_cache_reload(module) != 0) {
+               return -1;
+       }
+
+       /* first traverse the database deleting any @INDEX records */
+       ret = tdb_traverse(ltdb->tdb, delete_index, NULL);
+       if (ret == -1) {
+               return -1;
+       }
+
+       /* now traverse adding any indexes for normal LDB records */
+       ret = tdb_traverse(ltdb->tdb, re_index, module);
+       if (ret == -1) {
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/source3/lib/ldb/ldb_tdb/ldb_pack.c b/source3/lib/ldb/ldb_tdb/ldb_pack.c
new file mode 100644 (file)
index 0000000..c6edf66
--- /dev/null
@@ -0,0 +1,294 @@
+/* 
+   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/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+/* change this if the data format ever changes */
+#define LTDB_PACKING_FORMAT 0x26011967
+
+/* old packing formats */
+#define LTDB_PACKING_FORMAT_NODN 0x26011966
+
+/* use a portable integer format */
+static void put_uint32(uint8_t *p, int ofs, unsigned int val)
+{
+       p += ofs;
+       p[0] = val&0xFF;
+       p[1] = (val>>8)  & 0xFF;
+       p[2] = (val>>16) & 0xFF;
+       p[3] = (val>>24) & 0xFF;
+}
+
+static unsigned int pull_uint32(uint8_t *p, int ofs)
+{
+       p += ofs;
+       return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+}
+
+static int attribute_storable_values(const struct ldb_message_element *el)
+{
+       if (el->num_values == 0) return 0;
+
+       if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
+
+       if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
+
+       return el->num_values;
+}
+
+/*
+  pack a ldb message into a linear buffer in a TDB_DATA
+
+  note that this routine avoids saving elements with zero values,
+  as these are equivalent to having no element
+
+  caller frees the data buffer after use
+*/
+int ltdb_pack_data(struct ldb_module *module,
+                  const struct ldb_message *message,
+                  struct TDB_DATA *data)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i, j, real_elements=0;
+       size_t size;
+       char *dn;
+       uint8_t *p;
+       size_t len;
+
+       dn = ldb_dn_linearize(ldb, message->dn);
+       if (dn == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       /* work out how big it needs to be */
+       size = 8;
+
+       size += 1 + strlen(dn);
+
+       for (i=0;i<message->num_elements;i++) {
+               if (attribute_storable_values(&message->elements[i]) == 0) {
+                       continue;
+               }
+
+               real_elements++;
+
+               size += 1 + strlen(message->elements[i].name) + 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       size += 4 + message->elements[i].values[j].length + 1;
+               }
+       }
+
+       /* allocate it */
+       data->dptr = talloc_array(ldb, uint8_t, size);
+       if (!data->dptr) {
+               talloc_free(dn);
+               errno = ENOMEM;
+               return -1;
+       }
+       data->dsize = size;
+
+       p = data->dptr;
+       put_uint32(p, 0, LTDB_PACKING_FORMAT); 
+       put_uint32(p, 4, real_elements); 
+       p += 8;
+
+       /* the dn needs to be packed so we can be case preserving
+          while hashing on a case folded dn */
+       len = strlen(dn);
+       memcpy(p, dn, len+1);
+       p += len + 1;
+       
+       for (i=0;i<message->num_elements;i++) {
+               if (attribute_storable_values(&message->elements[i]) == 0) {
+                       continue;
+               }
+               len = strlen(message->elements[i].name);
+               memcpy(p, message->elements[i].name, len+1);
+               p += len + 1;
+               put_uint32(p, 0, message->elements[i].num_values);
+               p += 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       put_uint32(p, 0, message->elements[i].values[j].length);
+                       memcpy(p+4, message->elements[i].values[j].data, 
+                              message->elements[i].values[j].length);
+                       p[4+message->elements[i].values[j].length] = 0;
+                       p += 4 + message->elements[i].values[j].length + 1;
+               }
+       }
+
+       talloc_free(dn);
+       return 0;
+}
+
+/*
+  unpack a ldb message from a linear buffer in TDB_DATA
+
+  Free with ltdb_unpack_data_free()
+*/
+int ltdb_unpack_data(struct ldb_module *module,
+                    const struct TDB_DATA *data,
+                    struct ldb_message *message)
+{
+       struct ldb_context *ldb = module->ldb;
+       uint8_t *p;
+       unsigned int remaining;
+       unsigned int i, j;
+       unsigned format;
+       size_t len;
+
+       message->elements = NULL;
+
+       p = data->dptr;
+       if (data->dsize < 8) {
+               errno = EIO;
+               goto failed;
+       }
+
+       format = pull_uint32(p, 0);
+       message->num_elements = pull_uint32(p, 4);
+       p += 8;
+
+       remaining = data->dsize - 8;
+
+       switch (format) {
+       case LTDB_PACKING_FORMAT_NODN:
+               message->dn = NULL;
+               break;
+
+       case LTDB_PACKING_FORMAT:
+               len = strnlen((char *)p, remaining);
+               if (len == remaining) {
+                       errno = EIO;
+                       goto failed;
+               }
+               message->dn = ldb_dn_explode(message, (char *)p);
+               if (message->dn == NULL) {
+                       errno = ENOMEM;
+                       goto failed;
+               }
+               remaining -= len + 1;
+               p += len + 1;
+               break;
+
+       default:
+               errno = EIO;
+               goto failed;
+       }
+
+       if (message->num_elements == 0) {
+               message->elements = NULL;
+               return 0;
+       }
+       
+       if (message->num_elements > remaining / 6) {
+               errno = EIO;
+               goto failed;
+       }
+
+       message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
+       if (!message->elements) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       memset(message->elements, 0, 
+              message->num_elements * sizeof(struct ldb_message_element));
+
+       for (i=0;i<message->num_elements;i++) {
+               if (remaining < 10) {
+                       errno = EIO;
+                       goto failed;
+               }
+               len = strnlen((char *)p, remaining-6);
+               if (len == remaining-6) {
+                       errno = EIO;
+                       goto failed;
+               }
+               message->elements[i].flags = 0;
+               message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
+               if (message->elements[i].name == NULL) {
+                       errno = ENOMEM;
+                       goto failed;
+               }
+               remaining -= len + 1;
+               p += len + 1;
+               message->elements[i].num_values = pull_uint32(p, 0);
+               message->elements[i].values = NULL;
+               if (message->elements[i].num_values != 0) {
+                       message->elements[i].values = talloc_array(message->elements,
+                                                                    struct ldb_val, 
+                                                                    message->elements[i].num_values);
+                       if (!message->elements[i].values) {
+                               errno = ENOMEM;
+                               goto failed;
+                       }
+               }
+               p += 4;
+               remaining -= 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       len = pull_uint32(p, 0);
+                       if (len > remaining-5) {
+                               errno = EIO;
+                               goto failed;
+                       }
+
+                       message->elements[i].values[j].length = len;
+                       message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
+                       if (message->elements[i].values[j].data == NULL) {
+                               errno = ENOMEM;
+                               goto failed;
+                       }
+                       memcpy(message->elements[i].values[j].data, p+4, len);
+                       message->elements[i].values[j].data[len] = 0;
+       
+                       remaining -= len+4+1;
+                       p += len+4+1;
+               }
+       }
+
+       if (remaining != 0) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, 
+                         "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
+       }
+
+       return 0;
+
+failed:
+       talloc_free(message->elements);
+       return -1;
+}
diff --git a/source3/lib/ldb/ldb_tdb/ldb_search.c b/source3/lib/ldb/ldb_tdb/ldb_search.c
new file mode 100644 (file)
index 0000000..7cdb2b6
--- /dev/null
@@ -0,0 +1,543 @@
+/* 
+   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/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+/*
+  add one element to a message
+*/
+static int msg_add_element(struct ldb_message *ret, 
+                          const struct ldb_message_element *el,
+                          int check_duplicates)
+{
+       unsigned int i;
+       struct ldb_message_element *e2, *elnew;
+
+       if (check_duplicates && ldb_msg_find_element(ret, el->name)) {
+               /* its already there */
+               return 0;
+       }
+
+       e2 = talloc_realloc(ret, ret->elements, struct ldb_message_element, ret->num_elements+1);
+       if (!e2) {
+               return -1;
+       }
+       ret->elements = e2;
+       
+       elnew = &e2[ret->num_elements];
+
+       elnew->name = talloc_strdup(ret->elements, el->name);
+       if (!elnew->name) {
+               return -1;
+       }
+
+       if (el->num_values) {
+               elnew->values = talloc_array(ret->elements, struct ldb_val, el->num_values);
+               if (!elnew->values) {
+                       return -1;
+               }
+       } else {
+               elnew->values = NULL;
+       }
+
+       for (i=0;i<el->num_values;i++) {
+               elnew->values[i] = ldb_val_dup(elnew->values, &el->values[i]);
+               if (elnew->values[i].length != el->values[i].length) {
+                       return -1;
+               }
+       }
+
+       elnew->num_values = el->num_values;
+
+       ret->num_elements++;
+
+       return 0;
+}
+
+/*
+  add the special distinguishedName element
+*/
+static int msg_add_distinguished_name(struct ldb_message *msg)
+{
+       struct ldb_message_element el;
+       struct ldb_val val;
+       int ret;
+
+       el.flags = 0;
+       el.name = "distinguishedName";
+       el.num_values = 1;
+       el.values = &val;
+       val.data = (uint8_t *)ldb_dn_linearize(msg, msg->dn);
+       val.length = strlen((char *)val.data);
+       
+       ret = msg_add_element(msg, &el, 1);
+       return ret;
+}
+
+/*
+  add all elements from one message into another
+ */
+static int msg_add_all_elements(struct ldb_module *module, struct ldb_message *ret,
+                               const struct ldb_message *msg)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int check_duplicates = (ret->num_elements != 0);
+
+       if (msg_add_distinguished_name(ret) != 0) {
+               return -1;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               const struct ldb_attrib_handler *h;
+               h = ldb_attrib_handler(ldb, msg->elements[i].name);
+               if (h->flags & LDB_ATTR_FLAG_HIDDEN) {
+                       continue;
+               }
+               if (msg_add_element(ret, &msg->elements[i],
+                                   check_duplicates) != 0) {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+  pull the specified list of attributes from a message
+ */
+static struct ldb_message *ltdb_pull_attrs(struct ldb_module *module, 
+                                          TALLOC_CTX *mem_ctx, 
+                                          const struct ldb_message *msg, 
+                                          const char * const *attrs)
+{
+       struct ldb_message *ret;
+       int i;
+
+       ret = talloc(mem_ctx, struct ldb_message);
+       if (!ret) {
+               return NULL;
+       }
+
+       ret->dn = ldb_dn_copy(ret, msg->dn);
+       if (!ret->dn) {
+               talloc_free(ret);
+               return NULL;
+       }
+
+       ret->num_elements = 0;
+       ret->elements = NULL;
+
+       if (!attrs) {
+               if (msg_add_all_elements(module, ret, msg) != 0) {
+                       talloc_free(ret);
+                       return NULL;
+               }
+               return ret;
+       }
+
+       for (i=0;attrs[i];i++) {
+               struct ldb_message_element *el;
+
+               if (strcmp(attrs[i], "*") == 0) {
+                       if (msg_add_all_elements(module, ret, msg) != 0) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
+                       if (msg_add_distinguished_name(ret) != 0) {
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               el = ldb_msg_find_element(msg, attrs[i]);
+               if (!el) {
+                       continue;
+               }
+               if (msg_add_element(ret, el, 1) != 0) {
+                       talloc_free(ret);
+                       return NULL;                            
+               }
+       }
+
+       return ret;
+}
+
+
+/*
+  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_module *module, const struct ldb_dn *dn, struct ldb_message *msg)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       int ret;
+       TDB_DATA tdb_key, tdb_data;
+
+       memset(msg, 0, sizeof(*msg));
+
+       /* form the key */
+       tdb_key = ltdb_key(module, dn);
+       if (!tdb_key.dptr) {
+               return -1;
+       }
+
+       tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+       talloc_free(tdb_key.dptr);
+       if (!tdb_data.dptr) {
+               return 0;
+       }
+
+       msg->num_elements = 0;
+       msg->elements = NULL;
+
+       ret = ltdb_unpack_data(module, &tdb_data, msg);
+       free(tdb_data.dptr);
+       if (ret == -1) {
+               return -1;              
+       }
+
+       if (!msg->dn) {
+               msg->dn = ldb_dn_copy(msg, dn);
+       }
+       if (!msg->dn) {
+               return -1;
+       }
+
+       return 1;
+}
+
+/* the lock key for search locking. Note that this is not a DN, its
+   just an arbitrary key to give to tdb. Also note that as we and
+   using transactions for all write operations and transactions take
+   care of their own locks, we don't need to do any locking anywhere
+   other than in ldb_search() */
+#define LDBLOCK        "INT_LDBLOCK"
+
+/*
+  lock the database for read - use by ltdb_search
+*/
+static int ltdb_lock_read(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA key;
+
+       key.dptr = discard_const(LDBLOCK);
+       key.dsize = strlen(LDBLOCK);
+
+       return tdb_chainlock_read(ltdb->tdb, key);
+}
+
+/*
+  unlock the database after a ltdb_lock_read()
+*/
+static int ltdb_unlock_read(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA key;
+
+       key.dptr = discard_const(LDBLOCK);
+       key.dsize = strlen(LDBLOCK);
+
+       return tdb_chainunlock_read(ltdb->tdb, key);
+}
+
+/*
+  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_module *module, 
+                         TALLOC_CTX *mem_ctx, 
+                         struct ldb_message *msg,
+                         const char * const 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(module, mem_ctx, msg, attrs);
+       if (!msg2) {
+               return -1;
+       }
+
+       /* add to the results list */
+       res2 = talloc_realloc(mem_ctx, *res, struct ldb_message *, (*count)+2);
+       if (!res2) {
+               talloc_free(msg2);
+               return -1;
+       }
+
+       (*res) = res2;
+
+       (*res)[*count] = talloc_move(*res, &msg2);
+       (*res)[(*count)+1] = NULL;
+       (*count)++;
+
+       return 0;
+}
+
+
+
+/*
+  filter the specified list of attributes from a message
+  removing not requested attrs.
+ */
+int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs)
+{
+       int i, keep_all = 0;
+
+       if (attrs) {
+               /* check for special attrs */
+               for (i = 0; attrs[i]; i++) {
+                       if (strcmp(attrs[i], "*") == 0) {
+                               keep_all = 1;
+                               break;
+                       }
+
+                       if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
+                               if (msg_add_distinguished_name(msg) != 0) {
+                                       return -1;
+                               }
+                       }
+               }
+       } else {
+               keep_all = 1;
+       }
+       
+       if (keep_all) {
+               if (msg_add_distinguished_name(msg) != 0) {
+                       return -1;
+               }
+               return 0;
+       }
+
+       for (i = 0; i < msg->num_elements; i++) {
+               int j, found;
+               
+               for (j = 0, found = 0; attrs[j]; j++) {
+                       if (ldb_attr_cmp(msg->elements[i].name, attrs[j]) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       ldb_msg_remove_attr(msg, msg->elements[i].name);
+                       i--;
+               }
+       }
+
+       return 0;
+}
+
+/*
+  search function for a non-indexed search
+ */
+static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+       struct ldb_handle *handle = talloc_get_type(state, struct ldb_handle);
+       struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
+       struct ldb_reply *ares = NULL;
+       int ret;
+
+       if (key.dsize < 4 || 
+           strncmp((char *)key.dptr, "DN=", 3) != 0) {
+               return 0;
+       }
+
+       ares = talloc_zero(ac, struct ldb_reply);
+       if (!ares) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               handle->state = LDB_ASYNC_DONE;
+               return -1;
+       }
+
+       ares->message = ldb_msg_new(ares);
+       if (!ares->message) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               handle->state = LDB_ASYNC_DONE;
+               talloc_free(ares);
+               return -1;
+       }
+
+       /* unpack the record */
+       ret = ltdb_unpack_data(ac->module, &data, ares->message);
+       if (ret == -1) {
+               talloc_free(ares);
+               return -1;
+       }
+
+       if (!ares->message->dn) {
+               ares->message->dn = ldb_dn_explode(ares->message, (char *)key.dptr + 3);
+               if (ares->message->dn == NULL) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       handle->state = LDB_ASYNC_DONE;
+                       talloc_free(ares);
+                       return -1;
+               }
+       }
+
+       /* see if it matches the given expression */
+       if (!ldb_match_msg(ac->module->ldb, ares->message, ac->tree, 
+                              ac->base, ac->scope)) {
+               talloc_free(ares);
+               return 0;
+       }
+
+       /* filter the attributes that the user wants */
+       ret = ltdb_filter_attrs(ares->message, ac->attrs);
+
+       if (ret == -1) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               handle->state = LDB_ASYNC_DONE;
+               talloc_free(ares);
+               return -1;
+       }
+
+       ares->type = LDB_REPLY_ENTRY;
+        handle->state = LDB_ASYNC_PENDING;
+       handle->status = ac->callback(ac->module->ldb, ac->context, ares);
+
+       if (handle->status != LDB_SUCCESS) {
+               /* don't try to free ares here, the callback is in charge of that */
+               return -1;
+       }       
+
+       return 0;
+}
+
+
+/*
+  search the database with a LDAP-like expression.
+  this is the "full search" non-indexed variant
+*/
+static int ltdb_search_full(struct ldb_handle *handle)
+{
+       struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
+       struct ltdb_private *ltdb = talloc_get_type(ac->module->private_data, struct ltdb_private);
+       int ret;
+
+       ret = tdb_traverse_read(ltdb->tdb, search_func, handle);
+
+       if (ret == -1) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       handle->state = LDB_ASYNC_DONE;
+       return LDB_SUCCESS;
+}
+
+/*
+  search the database with a LDAP-like expression.
+  choses a search method
+*/
+int ltdb_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
+       struct ltdb_context *ltdb_ac;
+       struct ldb_reply *ares;
+       int ret;
+
+       if ((req->op.search.base == NULL || req->op.search.base->comp_num == 0) &&
+           (req->op.search.scope == LDB_SCOPE_BASE || req->op.search.scope == LDB_SCOPE_ONELEVEL))
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       if (ltdb_lock_read(module) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ltdb_cache_load(module) != 0) {
+               ltdb_unlock_read(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (req->op.search.tree == NULL) {
+               ltdb_unlock_read(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->handle = init_ltdb_handle(ltdb, module, req->context, req->callback);
+       if (req->handle == NULL) {
+               ltdb_unlock_read(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
+
+       ltdb_ac->tree = req->op.search.tree;
+       ltdb_ac->scope = req->op.search.scope;
+       ltdb_ac->base = req->op.search.base;
+       ltdb_ac->attrs = req->op.search.attrs;
+
+       ret = ltdb_search_indexed(req->handle);
+       if (ret == -1) {
+               ret = ltdb_search_full(req->handle);
+       }
+       if (ret != LDB_SUCCESS) {
+               ldb_set_errstring(module->ldb, "Indexed and full searches both failed!\n");
+               req->handle->state = LDB_ASYNC_DONE;
+               req->handle->status = ret;
+       }
+
+       /* Finally send an LDB_REPLY_DONE packet when searching is finished */
+
+       ares = talloc_zero(req, struct ldb_reply);
+       if (!ares) {
+               ltdb_unlock_read(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->handle->state = LDB_ASYNC_DONE;
+       ares->type = LDB_REPLY_DONE;
+
+       ret = req->callback(module->ldb, req->context, ares);
+       req->handle->status = ret;
+
+       ltdb_unlock_read(module);
+
+       return LDB_SUCCESS;
+}
+
diff --git a/source3/lib/ldb/ldb_tdb/ldb_tdb.c b/source3/lib/ldb/ldb_tdb/ldb_tdb.c
new file mode 100644 (file)
index 0000000..8f67665
--- /dev/null
@@ -0,0 +1,1065 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+   Copyright (C) Stefan Metzmacher  2004
+   Copyright (C) Simo Sorce       2006
+   
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb_tdb
+ *
+ *  Component: ldb tdb backend
+ *
+ *  Description: core functions for tdb backend
+ *
+ *  Author: Andrew Tridgell
+ *  Author: Stefan Metzmacher
+ *
+ *  Modifications:
+ *
+ *  - description: make the module use asyncronous calls
+ *    date: Feb 2006
+ *    Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+
+/*
+  map a tdb error code to a ldb error code
+*/
+static int ltdb_err_map(enum TDB_ERROR tdb_code)
+{
+       switch (tdb_code) {
+       case TDB_SUCCESS:
+               return LDB_SUCCESS;
+       case TDB_ERR_CORRUPT:
+       case TDB_ERR_OOM:
+       case TDB_ERR_EINVAL:
+               return LDB_ERR_OPERATIONS_ERROR;
+       case TDB_ERR_IO:
+               return LDB_ERR_PROTOCOL_ERROR;
+       case TDB_ERR_LOCK:
+       case TDB_ERR_NOLOCK:
+               return LDB_ERR_BUSY;
+       case TDB_ERR_LOCK_TIMEOUT:
+               return LDB_ERR_TIME_LIMIT_EXCEEDED;
+       case TDB_ERR_EXISTS:
+               return LDB_ERR_ENTRY_ALREADY_EXISTS;
+       case TDB_ERR_NOEXIST:
+               return LDB_ERR_NO_SUCH_OBJECT;
+       case TDB_ERR_RDONLY:
+               return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+       return LDB_ERR_OTHER;
+}
+
+
+struct ldb_handle *init_ltdb_handle(struct ltdb_private *ltdb, struct ldb_module *module,
+                                         void *context,
+                                         int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct ltdb_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(ltdb, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct ltdb_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->context = context;
+       ac->callback = callback;
+
+       return h;
+}
+
+/*
+  form a TDB_DATA for a record key
+  caller frees
+
+  note that the key for a record can depend on whether the 
+  dn refers to a case sensitive index record or not
+*/
+struct TDB_DATA ltdb_key(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       struct ldb_context *ldb = module->ldb;
+       TDB_DATA key;
+       char *key_str = NULL;
+       char *dn_folded = NULL;
+
+       /*
+         most DNs are case insensitive. The exception is index DNs for
+         case sensitive attributes
+
+         there are 3 cases dealt with in this code:
+
+         1) if the dn doesn't start with @ then uppercase the attribute
+             names and the attributes values of case insensitive attributes
+         2) if the dn starts with @ then leave it alone - the indexing code handles
+            the rest
+       */
+
+       dn_folded = ldb_dn_linearize_casefold(ldb, dn);
+       if (!dn_folded) {
+               goto failed;
+       }
+
+       key_str = talloc_asprintf(ldb, "DN=%s", dn_folded);
+
+       talloc_free(dn_folded);
+
+       if (!key_str) {
+               goto failed;
+       }
+
+       key.dptr = (uint8_t *)key_str;
+       key.dsize = strlen(key_str) + 1;
+
+       return key;
+
+failed:
+       errno = ENOMEM;
+       key.dptr = NULL;
+       key.dsize = 0;
+       return key;
+}
+
+/*
+  check special dn's have valid attributes
+  currently only @ATTRIBUTES is checked
+*/
+int ltdb_check_special_dn(struct ldb_module *module, const struct ldb_message *msg)
+{
+       int i, j;
+       if (! ldb_dn_is_special(msg->dn) ||
+           ! ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) {
+               return 0;
+       }
+
+       /* we have @ATTRIBUTES, let's check attributes are fine */
+       /* should we check that we deny multivalued attributes ? */
+       for (i = 0; i < msg->num_elements; i++) {
+               for (j = 0; j < msg->elements[i].num_values; j++) {
+                       if (ltdb_check_at_attributes_values(&msg->elements[i].values[j]) != 0) {
+                               ldb_set_errstring(module->ldb, "Invalid attribute value in an @ATTRIBUTES entry");
+                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+  we've made a modification to a dn - possibly reindex and 
+  update sequence number
+*/
+static int ltdb_modified(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       int ret = 0;
+
+       if (ldb_dn_is_special(dn) &&
+           (ldb_dn_check_special(dn, LTDB_INDEXLIST) ||
+            ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) {
+               ret = ltdb_reindex(module);
+       }
+
+       if (ret == 0 &&
+           !(ldb_dn_is_special(dn) &&
+             ldb_dn_check_special(dn, LTDB_BASEINFO)) ) {
+               ret = ltdb_increase_sequence_number(module);
+       }
+
+       return ret;
+}
+
+/*
+  store a record into the db
+*/
+int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA tdb_key, tdb_data;
+       int ret;
+
+       tdb_key = ltdb_key(module, msg->dn);
+       if (!tdb_key.dptr) {
+               return LDB_ERR_OTHER;
+       }
+
+       ret = ltdb_pack_data(module, msg, &tdb_data);
+       if (ret == -1) {
+               talloc_free(tdb_key.dptr);
+               return LDB_ERR_OTHER;
+       }
+
+       ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs);
+       if (ret == -1) {
+               ret = ltdb_err_map(tdb_error(ltdb->tdb));
+               goto done;
+       }
+       
+       ret = ltdb_index_add(module, msg);
+       if (ret == -1) {
+               tdb_delete(ltdb->tdb, tdb_key);
+       }
+
+done:
+       talloc_free(tdb_key.dptr);
+       talloc_free(tdb_data.dptr);
+
+       return ret;
+}
+
+
+static int ltdb_add_internal(struct ldb_module *module, const struct ldb_message *msg)
+{
+       int ret;
+       
+       ret = ltdb_check_special_dn(module, msg);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       
+       if (ltdb_cache_load(module) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ltdb_store(module, msg, TDB_INSERT);
+
+       if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+               char *dn;
+
+               dn = ldb_dn_linearize(module, msg->dn);
+               if (!dn) {
+                       return ret;
+               }
+               ldb_asprintf_errstring(module->ldb, "Entry %s already exists", dn);
+               talloc_free(dn);
+               return ret;
+       }
+       
+       if (ret == LDB_SUCCESS) {
+               ret = ltdb_modified(module, msg->dn);
+               if (ret != LDB_SUCCESS) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       }
+
+       return ret;
+}
+
+/*
+  add a record to the database
+*/
+static int ltdb_add(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
+       struct ltdb_context *ltdb_ac;
+       int tret, ret = LDB_SUCCESS;
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_tdb backend!\n");
+               if (check_critical_controls(req->controls)) {
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+       }
+       
+       req->handle = init_ltdb_handle(ltdb, module, req->context, req->callback);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
+
+       tret = ltdb_add_internal(module, req->op.add.message);
+       if (tret != LDB_SUCCESS) {
+               req->handle->status = tret;
+               goto done;
+       }
+       
+       if (ltdb_ac->callback) {
+               ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL);
+       }
+done:
+       req->handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+/*
+  delete a record from the database, not updating indexes (used for deleting
+  index records)
+*/
+int ltdb_delete_noindex(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA tdb_key;
+       int ret;
+
+       tdb_key = ltdb_key(module, dn);
+       if (!tdb_key.dptr) {
+               return LDB_ERR_OTHER;
+       }
+
+       ret = tdb_delete(ltdb->tdb, tdb_key);
+       talloc_free(tdb_key.dptr);
+
+       if (ret != 0) {
+               ret = ltdb_err_map(tdb_error(ltdb->tdb));
+       }
+
+       return ret;
+}
+
+static int ltdb_delete_internal(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       struct ldb_message *msg;
+       int ret;
+
+       msg = talloc(module, struct ldb_message);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* in case any attribute of the message was indexed, we need
+          to fetch the old record */
+       ret = ltdb_search_dn1(module, dn, msg);
+       if (ret != 1) {
+               /* not finding the old record is an error */
+               talloc_free(msg);
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       ret = ltdb_delete_noindex(module, dn);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+
+       /* remove any indexed attributes */
+       ret = ltdb_index_del(module, msg);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(msg);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ltdb_modified(module, dn);
+       if (ret != LDB_SUCCESS) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       talloc_free(msg);
+       return LDB_SUCCESS;
+}
+
+/*
+  delete a record from the database
+*/
+static int ltdb_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
+       struct ltdb_context *ltdb_ac;
+       int tret, ret = LDB_SUCCESS;
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_tdb backend!\n");
+               if (check_critical_controls(req->controls)) {
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+       }
+       
+       req->handle = NULL;
+
+       if (ltdb_cache_load(module) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->handle = init_ltdb_handle(ltdb, module, req->context, req->callback);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
+
+       tret = ltdb_delete_internal(module, req->op.del.dn);
+       if (tret != LDB_SUCCESS) {
+               req->handle->status = tret; 
+               goto done;
+       }
+
+       if (ltdb_ac->callback) {
+               ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL);
+       }
+done:
+       req->handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+/*
+  find an element by attribute name. At the moment this does a linear search, it should
+  be re-coded to use a binary search once all places that modify records guarantee
+  sorted order
+
+  return the index of the first matching element if found, otherwise -1
+*/
+static int find_element(const struct ldb_message *msg, const char *name)
+{
+       unsigned int i;
+       for (i=0;i<msg->num_elements;i++) {
+               if (ldb_attr_cmp(msg->elements[i].name, name) == 0) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+
+/*
+  add an element to an existing record. Assumes a elements array that we
+  can call re-alloc on, and assumed that we can re-use the data pointers from the 
+  passed in additional values. Use with care!
+
+  returns 0 on success, -1 on failure (and sets errno)
+*/
+static int msg_add_element(struct ldb_context *ldb,
+                          struct ldb_message *msg, struct ldb_message_element *el)
+{
+       struct ldb_message_element *e2;
+       unsigned int i;
+
+       e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element, 
+                             msg->num_elements+1);
+       if (!e2) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       msg->elements = e2;
+
+       e2 = &msg->elements[msg->num_elements];
+
+       e2->name = el->name;
+       e2->flags = el->flags;
+       e2->values = NULL;
+       if (el->num_values != 0) {
+               e2->values = talloc_array(msg->elements, struct ldb_val, el->num_values);
+               if (!e2->values) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+       }
+       for (i=0;i<el->num_values;i++) {
+               e2->values[i] = el->values[i];
+       }
+       e2->num_values = el->num_values;
+
+       msg->num_elements++;
+
+       return 0;
+}
+
+/*
+  delete all elements having a specified attribute name
+*/
+static int msg_delete_attribute(struct ldb_module *module,
+                               struct ldb_context *ldb,
+                               struct ldb_message *msg, const char *name)
+{
+       char *dn;
+       unsigned int i, j;
+
+       dn = ldb_dn_linearize(ldb, msg->dn);
+       if (dn == NULL) {
+               return -1;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               if (ldb_attr_cmp(msg->elements[i].name, name) == 0) {
+                       for (j=0;j<msg->elements[i].num_values;j++) {
+                               ltdb_index_del_value(module, dn, &msg->elements[i], j);
+                       }
+                       talloc_free(msg->elements[i].values);
+                       if (msg->num_elements > (i+1)) {
+                               memmove(&msg->elements[i], 
+                                       &msg->elements[i+1], 
+                                       sizeof(struct ldb_message_element)*
+                                       (msg->num_elements - (i+1)));
+                       }
+                       msg->num_elements--;
+                       i--;
+                       msg->elements = talloc_realloc(msg, msg->elements, 
+                                                        struct ldb_message_element, 
+                                                        msg->num_elements);
+               }
+       }
+
+       talloc_free(dn);
+       return 0;
+}
+
+/*
+  delete all elements matching an attribute name/value 
+
+  return 0 on success, -1 on failure
+*/
+static int msg_delete_element(struct ldb_module *module,
+                             struct ldb_message *msg, 
+                             const char *name,
+                             const struct ldb_val *val)
+{
+       struct ldb_context *ldb = module->ldb;
+       unsigned int i;
+       int found;
+       struct ldb_message_element *el;
+       const struct ldb_attrib_handler *h;
+
+       found = find_element(msg, name);
+       if (found == -1) {
+               return -1;
+       }
+
+       el = &msg->elements[found];
+
+       h = ldb_attrib_handler(ldb, el->name);
+
+       for (i=0;i<el->num_values;i++) {
+               if (h->comparison_fn(ldb, ldb, &el->values[i], val) == 0) {
+                       if (i<el->num_values-1) {
+                               memmove(&el->values[i], &el->values[i+1],
+                                       sizeof(el->values[i])*(el->num_values-(i+1)));
+                       }
+                       el->num_values--;
+                       if (el->num_values == 0) {
+                               return msg_delete_attribute(module, ldb, msg, name);
+                       }
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+/*
+  modify a record - internal interface
+
+  yuck - this is O(n^2). Luckily n is usually small so we probably
+  get away with it, but if we ever have really large attribute lists 
+  then we'll need to look at this again
+*/
+int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct ltdb_private *ltdb = module->private_data;
+       TDB_DATA tdb_key, tdb_data;
+       struct ldb_message *msg2;
+       unsigned i, j;
+       int ret;
+
+       tdb_key = ltdb_key(module, msg->dn);
+       if (!tdb_key.dptr) {
+               return LDB_ERR_OTHER;
+       }
+
+       tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+       if (!tdb_data.dptr) {
+               talloc_free(tdb_key.dptr);
+               return ltdb_err_map(tdb_error(ltdb->tdb));
+       }
+
+       msg2 = talloc(tdb_key.dptr, struct ldb_message);
+       if (msg2 == NULL) {
+               talloc_free(tdb_key.dptr);
+               return LDB_ERR_OTHER;
+       }
+
+       ret = ltdb_unpack_data(module, &tdb_data, msg2);
+       if (ret == -1) {
+               ret = LDB_ERR_OTHER;
+               goto failed;
+       }
+
+       if (!msg2->dn) {
+               msg2->dn = msg->dn;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               struct ldb_message_element *el = &msg->elements[i];
+               struct ldb_message_element *el2;
+               struct ldb_val *vals;
+               char *dn;
+
+               switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+
+               case LDB_FLAG_MOD_ADD:
+                       /* add this element to the message. fail if it
+                          already exists */
+                       ret = find_element(msg2, el->name);
+
+                       if (ret == -1) {
+                               if (msg_add_element(ldb, msg2, el) != 0) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+                               continue;
+                       }
+
+                       el2 = &msg2->elements[ret];
+
+                       /* An attribute with this name already exists, add all
+                        * values if they don't already exist. */
+
+                       for (j=0;j<el->num_values;j++) {
+                               if (ldb_msg_find_val(el2, &el->values[j])) {
+                                       ldb_set_errstring(module->ldb, "Type or value exists");
+                                       ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+                                       goto failed;
+                               }
+                       }
+
+                       vals = talloc_realloc(msg2->elements, el2->values, struct ldb_val,
+                                               el2->num_values + el->num_values);
+
+                       if (vals == NULL) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+
+                       for (j=0;j<el->num_values;j++) {
+                               vals[el2->num_values + j] =
+                                       ldb_val_dup(vals, &el->values[j]);
+                       }
+
+                       el2->values = vals;
+                       el2->num_values += el->num_values;
+
+                       break;
+
+               case LDB_FLAG_MOD_REPLACE:
+                       /* replace all elements of this attribute name with the elements
+                          listed. The attribute not existing is not an error */
+                       msg_delete_attribute(module, ldb, msg2, msg->elements[i].name);
+
+                       /* add the replacement element, if not empty */
+                       if (msg->elements[i].num_values != 0 &&
+                           msg_add_element(ldb, msg2, &msg->elements[i]) != 0) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+                       break;
+
+               case LDB_FLAG_MOD_DELETE:
+
+                       dn = ldb_dn_linearize(msg2, msg->dn);
+                       if (dn == NULL) {
+                               ret = LDB_ERR_OTHER;
+                               goto failed;
+                       }
+
+                       /* we could be being asked to delete all
+                          values or just some values */
+                       if (msg->elements[i].num_values == 0) {
+                               if (msg_delete_attribute(module, ldb, msg2, 
+                                                        msg->elements[i].name) != 0) {
+                                       ldb_asprintf_errstring(module->ldb, "No such attribute: %s for delete on %s", msg->elements[i].name, dn);
+                                       ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
+                                       goto failed;
+                               }
+                               break;
+                       }
+                       for (j=0;j<msg->elements[i].num_values;j++) {
+                               if (msg_delete_element(module,
+                                                      msg2, 
+                                                      msg->elements[i].name,
+                                                      &msg->elements[i].values[j]) != 0) {
+                                       ldb_asprintf_errstring(module->ldb, "No matching attribute value when deleting attribute: %s on %s", msg->elements[i].name, dn);
+                                       ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
+                                       goto failed;
+                               }
+                               if (ltdb_index_del_value(module, dn, &msg->elements[i], j) != 0) {
+                                       ret = LDB_ERR_OTHER;
+                                       goto failed;
+                               }
+                       }
+                       break;
+               default:
+                       ldb_asprintf_errstring(module->ldb, "Invalid ldb_modify flags on %s: 0x%x", 
+                                                            msg->elements[i].name, 
+                                                            msg->elements[i].flags & LDB_FLAG_MOD_MASK);
+                       ret = LDB_ERR_PROTOCOL_ERROR;
+                       goto failed;
+               }
+       }
+
+       /* we've made all the mods - save the modified record back into the database */
+       ret = ltdb_store(module, msg2, TDB_MODIFY);
+       if (ret != LDB_SUCCESS) {
+               goto failed;
+       }
+
+       if (ltdb_modified(module, msg->dn) != LDB_SUCCESS) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto failed;
+       }
+
+       talloc_free(tdb_key.dptr);
+       free(tdb_data.dptr);
+       return ret;
+
+failed:
+       talloc_free(tdb_key.dptr);
+       free(tdb_data.dptr);
+       return ret;
+}
+
+/*
+  modify a record
+*/
+static int ltdb_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
+       struct ltdb_context *ltdb_ac;
+       int tret, ret = LDB_SUCCESS;
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_tdb backend!\n");
+               if (check_critical_controls(req->controls)) {
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+       }
+       
+       req->handle = NULL;
+
+       req->handle = init_ltdb_handle(ltdb, module, req->context, req->callback);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
+
+       tret = ltdb_check_special_dn(module, req->op.mod.message);
+       if (tret != LDB_SUCCESS) {
+               req->handle->status = tret;
+               goto done;
+       }
+       
+       if (ltdb_cache_load(module) != 0) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       tret = ltdb_modify_internal(module, req->op.mod.message);
+       if (tret != LDB_SUCCESS) {
+               req->handle->status = tret;
+               goto done;
+       }
+
+       if (ltdb_ac->callback) {
+               ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL);
+       }
+done:
+       req->handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+/*
+  rename a record
+*/
+static int ltdb_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
+       struct ltdb_context *ltdb_ac;
+       struct ldb_message *msg;
+       int tret, ret = LDB_SUCCESS;
+
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_tdb backend!\n");
+               if (check_critical_controls(req->controls)) {
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+       }
+       
+       req->handle = NULL;
+
+       if (ltdb_cache_load(module) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->handle = init_ltdb_handle(ltdb, module, req->context, req->callback);
+       if (req->handle == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
+
+       msg = talloc(ltdb_ac, struct ldb_message);
+       if (msg == NULL) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       /* in case any attribute of the message was indexed, we need
+          to fetch the old record */
+       tret = ltdb_search_dn1(module, req->op.rename.olddn, msg);
+       if (tret != 1) {
+               /* not finding the old record is an error */
+               req->handle->status = LDB_ERR_NO_SUCH_OBJECT;
+               goto done;
+       }
+
+       msg->dn = ldb_dn_copy(msg, req->op.rename.newdn);
+       if (!msg->dn) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       tret = ltdb_add_internal(module, msg);
+       if (tret != LDB_SUCCESS) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       tret = ltdb_delete_internal(module, req->op.rename.olddn);
+       if (tret != LDB_SUCCESS) {
+               ltdb_delete_internal(module, req->op.rename.newdn);
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       if (ltdb_ac->callback) {
+               ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL);
+       }
+done:
+       req->handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+static int ltdb_start_trans(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+
+       if (tdb_transaction_start(ltdb->tdb) != 0) {
+               return ltdb_err_map(tdb_error(ltdb->tdb));
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int ltdb_end_trans(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+
+       if (tdb_transaction_commit(ltdb->tdb) != 0) {
+               return ltdb_err_map(tdb_error(ltdb->tdb));
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int ltdb_del_trans(struct ldb_module *module)
+{
+       struct ltdb_private *ltdb = module->private_data;
+
+       if (tdb_transaction_cancel(ltdb->tdb) != 0) {
+               return ltdb_err_map(tdb_error(ltdb->tdb));
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int ltdb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       return handle->status;
+}
+
+static int ltdb_request(struct ldb_module *module, struct ldb_request *req)
+{
+       /* check for oustanding critical controls and return an error if found */
+       if (req->controls != NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls should not reach the ldb_tdb backend!\n");
+               if (check_critical_controls(req->controls)) {
+                       return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+               }
+       }
+       
+       /* search, add, modify, delete, rename are handled by their own, no other op supported */
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+  return sequenceNumber from @BASEINFO
+*/
+static int ltdb_sequence_number(struct ldb_module *module, struct ldb_request *req)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(req);
+       struct ldb_message *msg = NULL;
+       struct ldb_dn *dn = ldb_dn_explode(tmp_ctx, LTDB_BASEINFO);
+       int tret;
+
+       if (tmp_ctx == NULL) {
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg = talloc(tmp_ctx, struct ldb_message);
+       if (msg == NULL) {
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->op.seq_num.flags = 0;
+
+       tret = ltdb_search_dn1(module, dn, msg);
+       if (tret != 1) {
+               talloc_free(tmp_ctx);
+               req->op.seq_num.seq_num = 0;
+               /* zero is as good as anything when we don't know */
+               return LDB_SUCCESS;
+       }
+
+       switch (req->op.seq_num.type) {
+       case LDB_SEQ_HIGHEST_SEQ:
+               req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+               break;
+       case LDB_SEQ_NEXT:
+               req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+               req->op.seq_num.seq_num++;
+               break;
+       case LDB_SEQ_HIGHEST_TIMESTAMP:
+       {
+               const char *date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL);
+               if (date) {
+                       req->op.seq_num.seq_num = ldb_string_to_time(date);
+               } else {
+                       req->op.seq_num.seq_num = 0;
+                       /* zero is as good as anything when we don't know */
+               }
+               break;
+       }
+       }
+       talloc_free(tmp_ctx);
+       return LDB_SUCCESS;
+}
+
+static const struct ldb_module_ops ltdb_ops = {
+       .name              = "tdb",
+       .search            = ltdb_search,
+       .add               = ltdb_add,
+       .modify            = ltdb_modify,
+       .del               = ltdb_delete,
+       .rename            = ltdb_rename,
+       .request           = ltdb_request,
+       .start_transaction = ltdb_start_trans,
+       .end_transaction   = ltdb_end_trans,
+       .del_transaction   = ltdb_del_trans,
+       .wait              = ltdb_wait,
+       .sequence_number   = ltdb_sequence_number
+};
+
+/*
+  connect to the database
+*/
+static int ltdb_connect(struct ldb_context *ldb, const char *url, 
+                       unsigned int flags, const char *options[],
+                       struct ldb_module **module)
+{
+       const char *path;
+       int tdb_flags, open_flags;
+       struct ltdb_private *ltdb;
+
+       /* parse the url */
+       if (strchr(url, ':')) {
+               if (strncmp(url, "tdb://", 6) != 0) {
+                       ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid tdb URL '%s'", url);
+                       return -1;
+               }
+               path = url+6;
+       } else {
+               path = url;
+       }
+
+       tdb_flags = TDB_DEFAULT;
+
+       /* check for the 'nosync' option */
+       if (flags & LDB_FLG_NOSYNC) {
+               tdb_flags |= TDB_NOSYNC;
+       }
+
+       if (flags & LDB_FLG_RDONLY) {
+               open_flags = O_RDONLY;
+       } else {
+               open_flags = O_CREAT | O_RDWR;
+       }
+
+       ltdb = talloc_zero(ldb, struct ltdb_private);
+       if (!ltdb) {
+               ldb_oom(ldb);
+               return -1;
+       }
+
+       /* note that we use quite a large default hash size */
+       ltdb->tdb = ltdb_wrap_open(ltdb, path, 10000, 
+                                  tdb_flags, open_flags, 0666, ldb);
+       if (!ltdb->tdb) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "Unable to open tdb '%s'\n", path);
+               talloc_free(ltdb);
+               return -1;
+       }
+
+       ltdb->sequence_number = 0;
+
+       *module = talloc(ldb, struct ldb_module);
+       if (!module) {
+               ldb_oom(ldb);
+               talloc_free(ltdb);
+               return -1;
+       }
+       (*module)->ldb = ldb;
+       (*module)->prev = (*module)->next = NULL;
+       (*module)->private_data = ltdb;
+       (*module)->ops = &ltdb_ops;
+
+       return 0;
+}
+
+int ldb_tdb_init(void)
+{
+       return ldb_register_backend("tdb", ltdb_connect);
+}
diff --git a/source3/lib/ldb/ldb_tdb/ldb_tdb.h b/source3/lib/ldb/ldb_tdb/ldb_tdb.h
new file mode 100644 (file)
index 0000000..670d3b6
--- /dev/null
@@ -0,0 +1,127 @@
+
+#ifdef _SAMBA_BUILD_
+#include "system/filesys.h"
+#endif
+
+#if (_SAMBA_BUILD_ >= 4)
+#include "lib/tdb/include/tdb.h"
+#elif defined(_SAMBA_BUILD_)
+#include "tdb/include/tdb.h"
+#else
+#include "tdb.h"
+#endif
+
+/* this private structure is used by the ltdb backend in the
+   ldb_context */
+struct ltdb_private {
+       TDB_CONTEXT *tdb;
+       unsigned int connect_flags;
+       
+       /* a double is used for portability and ease of string
+          handling. It has plenty of digits of precision */
+       unsigned long long sequence_number;
+
+       struct ltdb_cache {
+               struct ldb_message *baseinfo;
+               struct ldb_message *indexlist;
+               struct ldb_message *attributes;
+               struct ldb_message *subclasses;
+
+               struct {
+                       char *name;
+                       int flags;
+               } last_attribute;
+       } *cache;
+};
+
+/*
+  the async local context
+  holds also internal search state during a full db search
+*/
+struct ltdb_context {
+       struct ldb_module *module;
+
+       /* search stuff */
+       const struct ldb_parse_tree *tree;
+       const struct ldb_dn *base;
+       enum ldb_scope scope;
+       const char * const *attrs;
+
+       /* async stuff */
+       void *context;
+       int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
+};
+
+/* special record types */
+#define LTDB_INDEX      "@INDEX"
+#define LTDB_INDEXLIST  "@INDEXLIST"
+#define LTDB_IDX        "@IDX"
+#define LTDB_IDXATTR    "@IDXATTR"
+#define LTDB_BASEINFO   "@BASEINFO"
+#define LTDB_ATTRIBUTES "@ATTRIBUTES"
+#define LTDB_SUBCLASSES "@SUBCLASSES"
+
+/* special attribute types */
+#define LTDB_SEQUENCE_NUMBER "sequenceNumber"
+#define LTDB_MOD_TIMESTAMP "whenChanged"
+#define LTDB_OBJECTCLASS "objectClass"
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_cache.c  */
+
+int ltdb_cache_reload(struct ldb_module *module);
+int ltdb_cache_load(struct ldb_module *module);
+int ltdb_increase_sequence_number(struct ldb_module *module);
+int ltdb_check_at_attributes_values(const struct ldb_val *value);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_index.c  */
+
+struct ldb_parse_tree;
+
+int ltdb_search_indexed(struct ldb_handle *handle);
+int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg);
+int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg);
+int ltdb_reindex(struct ldb_module *module);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_pack.c  */
+
+int ltdb_pack_data(struct ldb_module *module,
+                  const struct ldb_message *message,
+                  struct TDB_DATA *data);
+void ltdb_unpack_data_free(struct ldb_module *module,
+                          struct ldb_message *message);
+int ltdb_unpack_data(struct ldb_module *module,
+                    const struct TDB_DATA *data,
+                    struct ldb_message *message);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_search.c  */
+
+int ltdb_has_wildcard(struct ldb_module *module, const char *attr_name, 
+                     const struct ldb_val *val);
+void ltdb_search_dn1_free(struct ldb_module *module, struct ldb_message *msg);
+int ltdb_search_dn1(struct ldb_module *module, const struct ldb_dn *dn, struct ldb_message *msg);
+int ltdb_add_attr_results(struct ldb_module *module,
+                         TALLOC_CTX *mem_ctx, 
+                         struct ldb_message *msg,
+                         const char * const attrs[], 
+                         unsigned int *count, 
+                         struct ldb_message ***res);
+int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs);
+int ltdb_search(struct ldb_module *module, struct ldb_request *req);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_tdb.c  */
+struct ldb_handle *init_ltdb_handle(struct ltdb_private *ltdb, struct ldb_module *module,
+                                         void *context,
+                                         int (*callback)(struct ldb_context *, void *, struct ldb_reply *));
+struct TDB_DATA ltdb_key(struct ldb_module *module, const struct ldb_dn *dn);
+int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs);
+int ltdb_delete_noindex(struct ldb_module *module, const struct ldb_dn *dn);
+int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg);
+
+int ltdb_index_del_value(struct ldb_module *module, const char *dn, 
+                        struct ldb_message_element *el, int v_idx);
+
+struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
+                                  const char *path, int hash_size, int tdb_flags,
+                                  int open_flags, mode_t mode,
+                                  struct ldb_context *ldb);
+
diff --git a/source3/lib/ldb/ldb_tdb/ldb_tdb_wrap.c b/source3/lib/ldb/ldb_tdb/ldb_tdb_wrap.c
new file mode 100644 (file)
index 0000000..b28bf77
--- /dev/null
@@ -0,0 +1,174 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/ldb_tdb/ldb_tdb.h"
+
+/*
+  the purpose of this code is to work around the braindead posix locking
+  rules, to allow us to have a ldb open more than once while allowing 
+  locking to work
+*/
+
+struct ltdb_wrap {
+       struct ltdb_wrap *next, *prev;
+       struct tdb_context *tdb;
+       dev_t device;
+       ino_t inode;
+};
+
+static struct ltdb_wrap *tdb_list;
+
+/* destroy the last connection to a tdb */
+static int ltdb_wrap_destructor(struct ltdb_wrap *w)
+{
+       tdb_close(w->tdb);
+       if (w->next) {
+               w->next->prev = w->prev;
+       }
+       if (w->prev) {
+               w->prev->next = w->next;
+       }
+       if (w == tdb_list) {
+               tdb_list = w->next;
+       }
+       return 0;
+}                               
+
+#if defined(_SAMBA_BUILD_) && (_SAMBA_BUILD_ <= 3)
+static void ltdb_log_fn(struct tdb_context *tdb, int level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+static void ltdb_log_fn(struct tdb_context *tdb, int level, const char *fmt, ...)
+{
+       /* until we merge the tdb debug changes into samba3, we don't know 
+          how serious the error is, and we can't go via the ldb loggin code */
+       va_list ap;
+       const char *name = tdb_name(tdb);
+       char *message; 
+       va_start(ap, fmt);
+       message = talloc_vasprintf(NULL, fmt, ap);
+       va_end(ap);
+       DEBUG(3, ("ltdb: tdb(%s): %s", name, message));
+       talloc_free(message);
+}
+#else
+static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+       va_list ap;
+       const char *name = tdb_name(tdb);
+       struct ldb_context *ldb = talloc_get_type(tdb_get_logging_private(tdb), struct ldb_context);
+       enum ldb_debug_level ldb_level;
+       char *message; 
+       va_start(ap, fmt);
+       message = talloc_vasprintf(ldb, fmt, ap);
+       va_end(ap);
+
+       switch (level) {
+       case TDB_DEBUG_FATAL:
+               ldb_level = LDB_DEBUG_FATAL;
+               break;
+       case TDB_DEBUG_ERROR:
+               ldb_level = LDB_DEBUG_ERROR;
+               break;
+       case TDB_DEBUG_WARNING:
+               ldb_level = LDB_DEBUG_WARNING;
+               break;
+       case TDB_DEBUG_TRACE:
+               ldb_level = LDB_DEBUG_TRACE;
+               break;
+       default:
+               ldb_level = LDB_DEBUG_FATAL;
+       }
+
+       ldb_debug(ldb, ldb_level, "ltdb: tdb(%s): %s", name, message);
+       talloc_free(message);
+}
+#endif
+
+/*
+  wrapped connection to a tdb database. The caller should _not_ free
+  this as it is not a talloc structure (as tdb does not use talloc
+  yet). It will auto-close when the caller frees the mem_ctx that is
+  passed to this call
+ */
+struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
+                                  const char *path, int hash_size, 
+                                  int tdb_flags,
+                                  int open_flags, mode_t mode, 
+                                  struct ldb_context *ldb)
+{
+       struct ltdb_wrap *w;
+       struct stat st;
+#if defined(_SAMBA_BUILD_) && (_SAMBA_BUILD_ <= 3)
+       tdb_log_func log_ctx_p = ltdb_log_fn;
+#else
+       struct tdb_logging_context log_ctx;
+       const struct tdb_logging_context *log_ctx_p = &log_ctx;
+       log_ctx.log_fn = ltdb_log_fn;
+       log_ctx.log_private = ldb;
+#endif
+
+       if (stat(path, &st) == 0) {
+               for (w=tdb_list;w;w=w->next) {
+                       if (st.st_dev == w->device && st.st_ino == w->inode) {
+                               talloc_reference(mem_ctx, w);
+                               return w->tdb;
+                       }
+               }
+       }
+
+       w = talloc(mem_ctx, struct ltdb_wrap);
+       if (w == NULL) {
+               return NULL;
+       }
+
+       w->tdb = tdb_open_ex(path, hash_size, tdb_flags, open_flags, mode, log_ctx_p, NULL);
+       if (w->tdb == NULL) {
+               talloc_free(w);
+               return NULL;
+       }
+
+       if (fstat(tdb_fd(w->tdb), &st) != 0) {
+               tdb_close(w->tdb);
+               talloc_free(w);
+               return NULL;
+       }
+
+       w->device = st.st_dev;
+       w->inode  = st.st_ino;
+
+       talloc_set_destructor(w, ltdb_wrap_destructor);
+
+       w->next = tdb_list;
+       w->prev = NULL;
+       if (tdb_list) {
+               tdb_list->prev = w;
+       }
+       tdb_list = w;
+       
+       return w->tdb;
+}
+
diff --git a/source3/lib/ldb/libldb.m4 b/source3/lib/ldb/libldb.m4
new file mode 100644 (file)
index 0000000..845563b
--- /dev/null
@@ -0,0 +1,33 @@
+SMB_ENABLE(ldb_sqlite3,$with_sqlite3_support)
+
+AC_MSG_CHECKING([for Python])
+
+PYTHON=
+AC_ARG_WITH(python,
+[  --with-python=PYTHONNAME  build Python libraries],
+[ case "${withval-python}" in
+  yes)
+        PYTHON=python
+        ;;
+  no)
+        PYTHON=
+        ;;
+  *)
+        PYTHON=${withval-python}
+        ;;
+  esac ])
+
+if test x"$PYTHON" != "x"; then
+       incdir=`python -c 'import sys; print "%s/include/python%d.%d" % (sys.prefix, sys.version_info[[0]], sys.version_info[[1]])'`
+       CPPFLAGS="$CPPFLAGS -I $incdir"
+fi
+
+if test x"$PYTHON" != "x"; then
+       AC_MSG_RESULT([${withval-python}])
+else
+       AC_MSG_RESULT(no)
+       SMB_ENABLE(swig_ldb, NO)
+fi
+
+AC_SUBST(PYTHON)
diff --git a/source3/lib/ldb/mainpage.dox b/source3/lib/ldb/mainpage.dox
new file mode 100644 (file)
index 0000000..bbd8d9c
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+
+\mainpage ldb
+
+\section Overview
+
+ldb is a LDAP-like embedded database. It is not at all LDAP standards
+compliant, so if you want a standards compliant database then please
+see the excellent <a href="http://www.openldap.org/">OpenLDAP</a>
+project.<p>
+
+What ldb does is provide a fast database with an LDAP-like API
+designed to be used within an application. In some ways it can be seen
+as a intermediate solution between key-value pair databases and a real
+LDAP database.<p>
+
+ldb is the database engine used in Samba4.
+
+\section Features
+
+The main features that separate ldb from other solutions are:
+ - Safe multi-reader, multi-writer, using byte range locking
+ - LDAP-like API
+ - fast operation
+ - choice of local tdb, local sqlite3 or remote LDAP backends
+ - integration with <a href="http://talloc.samba.org">talloc</a>
+ - schema-less operation, for trivial setup
+ - modules for extensions (such as schema support)
+ - easy setup of indexes and attribute properties
+ - ldbedit tool for database editing (reminiscent of 'vipw')
+ - ldif for import/export
+
+\section Documentation
+
+ldb has limited programmer and administrator documentation:
+ - a list of <a href="globals_func.html">functions</a>
+ - a list of <a href="examples.html">examples</a>
+ - a list of <a href="annotated.html">data structures</a>
+ - a list of <a href="globals_defs.html">constants</a>
+
+If you need more information than is presented in this document, you
+may wish to look at the source code, especially the source code in the
+<a href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/tools/">tools directory</a>. 
+
+ldb makes use of the LDAP Data Interchange Format (LDIF), which is
+documented in <a href="http://www.ietf.org/rfc/rfc2849.txt">RFC
+2849</a>. 
+
+\section Support
+
+ldb does not currently have its own mailing list or bug tracking
+system. For now, please use the <a
+href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba
+bugzilla</a> bug tracking system.
+
+\section Download
+
+You can download the latest release either via rsync or anonymous
+svn. To fetch via svn use the following commands:
+
+\verbatim
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
+\endverbatim
+
+To fetch via rsync use these commands:
+
+\verbatim
+  rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/ldb .
+  rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/tdb .
+  rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/talloc .
+\endverbatim
+
+\section Credits
+
+ldb is another product of the prolific <a href="http://samba.org/~tridge/">Andrew Tridgell</a>.
+
+*/
diff --git a/source3/lib/ldb/man/ad2oLschema.1.xml b/source3/lib/ldb/man/ad2oLschema.1.xml
new file mode 100644 (file)
index 0000000..6ae8996
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ad2oLschema.1">
+
+<refmeta>
+       <refentrytitle>ad2oLschema</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ad2oLschema</refname>
+       <refpurpose>Converts AC-like LDAP schemas to OpenLDAP
+       compatible schema files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ad2oLschema</command>
+               <arg choice="opt">-I INPUT-FILE</arg>
+               <arg choice="opt">-O OUTPUT-FILE</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ad2oLschema is a simple tool that converts AD-like LDIF
+       schema files into OpenLDAP schema files.</para>
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-H url</term>
+               <listitem><para>URL to an LDB or LDAP server with an AD schema to read.                 </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>-I input-file</term> <listitem><para>AD schema
+               to read. If neither this nor -H is specified, the
+               schema file will be read from standard input.
+               </para></listitem>
+               </varlistentry>
+
+       <varlistentry>
+               <term>-O output-file</term>
+               <listitem><para>File to write OpenLDAP version of schema to.
+               </para></listitem>
+       </varlistentry>
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+               ad2oLschema was written by <ulink
+                url="http://samba.org/~abartlet/">Andrew Bartlett</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldb.3.xml b/source3/lib/ldb/man/ldb.3.xml
new file mode 100644 (file)
index 0000000..6a3a789
--- /dev/null
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldb.3">
+
+<refmeta>
+       <refentrytitle>ldb</refentrytitle>
+       <manvolnum>3</manvolnum>
+</refmeta>
+
+<refnamediv>
+       <refname>ldb</refname>
+       <refclass>The Samba Project</refclass>
+       <refpurpose>A light-weight database library</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <synopsis>#include &lt;ldb.h&gt;</synopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>description</title>
+
+       <para>
+ldb is a light weight embedded database library and API. With a
+programming interface that is very similar to LDAP, ldb can store its
+data either in a tdb(3) database or in a real LDAP database.
+       </para>
+
+       <para>
+When used with the tdb backend ldb does not require any database
+daemon. Instead, ldb function calls are processed immediately by the
+ldb library, which does IO directly on the database, while allowing
+multiple readers/writers using operating system byte range locks. This
+leads to an API with very low overheads, often resulting in speeds of
+more than 10x what can be achieved with a more traditional LDAP
+architecture.
+       </para>
+
+       <para>
+It a taxonomy of databases ldb would sit half way between key/value
+pair databases (such as berkley db or tdb) and a full LDAP
+database. With a structured attribute oriented API like LDAP and good
+indexing capabilities, ldb can be used for quite sophisticated
+applications that need a light weight database, without the
+administrative overhead of a full LDAP installation.
+       </para>
+
+       <para>
+Included with ldb are a number of useful command line tools for
+manipulating a ldb database. These tools are similar in style to the
+equivalent ldap command line tools.
+       </para>
+
+       <para>
+In its default mode of operation with a tdb backend, ldb can also be
+seen as a "schema-less LDAP". By default ldb does not require a
+schema, which greatly reduces the complexity of getting started with
+ldb databases. As the complexity of you application grows you can take
+advantage of some of the optional schema-like attributes that ldb
+offers, or you can migrate to using the full LDAP api while keeping
+your exiting ldb code.
+       </para>
+
+       <para>
+If you are new to ldb, then I suggest starting with the manual pages
+for ldbsearch(1) and ldbedit(1), and experimenting with a local
+database. Then I suggest you look at the ldb_connect(3) and
+ldb_search(3) manual pages.
+       </para>
+</refsect1>
+
+<refsect1>
+       <title>TOOLS</title>
+
+       <itemizedlist>
+               <listitem><para>
+                       <application>ldbsearch(1)</application>
+                         - command line ldb search utility
+               </para></listitem>
+
+               <listitem><para>
+                       <application>ldbedit(1)</application>
+                        - edit all or part of a ldb database using your favourite editor
+               </para></listitem>
+
+               <listitem><para>
+                       <application>ldbadd(1)</application>
+                        - add records to a ldb database using LDIF formatted input
+               </para></listitem>
+
+               <listitem><para>
+                       <application>ldbdel(1)</application>
+                        - delete records from a ldb database
+               </para></listitem>
+
+               <listitem><para>
+                       <application>ldbmodify(1)</application>
+                        - modify records in a ldb database using LDIF formatted input
+               </para></listitem>
+       </itemizedlist>
+</refsect1>
+
+<refsect1>
+       <title>FUNCTIONS</title>
+
+       <itemizedlist>
+               <listitem><para>
+                       <function>ldb_connect(3)</function>
+                        - connect to a ldb backend
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_search(3)</function>
+                        - perform a database search
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_add(3)</function>
+                        - add a record to the database
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_delete(3)</function>
+                        - delete a record from the database
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_modify(3)</function>
+                        - modify a record in the database
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_errstring(3)</function>
+                        - retrieve extended error information from the last operation
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_write(3)</function>
+                        - write a LDIF formatted message
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_write_file(3)</function>
+                        - write a LDIF formatted message to a file
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_read(3)</function>
+                        - read a LDIF formatted message
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_read_free(3)</function>
+                        - free the result of a ldb_ldif_read()
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_read_file(3)</function>
+                        - read a LDIF message from a file
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_ldif_read_string(3)</function>
+                        - read a LDIF message from a string
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_msg_find_element(3)</function>
+                        - find an element in a ldb_message
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_val_equal_exact(3)</function>
+                        - compare two ldb_val structures
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_msg_find_val(3)</function>
+                        - find an element by value
+               </para></listitem>
+
+               <listitem><para>
+                       <function>ldb_msg_add_empty(3)</function>
+                        - add an empty message element to a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_add(3)</function>
+                        - add a non-empty message element to a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_element_compare(3)</function>
+                        - compare two ldb_message_element structures
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_find_int(3)</function>
+                        - return an integer value from a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_find_uint(3)</function>
+                        - return an unsigned integer value from a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_find_double(3)</function>
+                        - return a double value from a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_msg_find_string(3)</function>
+                        - return a string value from a ldb_message
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_set_alloc(3)</function>
+                        - set the memory allocation function to be used by ldb
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_set_debug(3)</function>
+                        - set a debug handler to be used by ldb
+               </para></listitem>
+
+
+               <listitem><para>
+                       <function>ldb_set_debug_stderr(3)</function>
+                        - set a debug handler for stderr output
+               </para></listitem>
+       </itemizedlist>
+</refsect1>
+
+<refsect1>
+       <title>Author</title>
+
+       <para>
+               ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>
+ldb is released under the GNU Lesser General Public License version 2
+or later. Please see the file COPYING for license details.
+       </para>
+</refsect1>
+</refentry>
diff --git a/source3/lib/ldb/man/ldbadd.1.xml b/source3/lib/ldb/man/ldbadd.1.xml
new file mode 100644 (file)
index 0000000..7ad0f83
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbadd.1">
+
+<refmeta>
+       <refentrytitle>ldbadd</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ldbadd</refname>
+       <refpurpose>Command-line utility for adding records to an LDB</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ldbadd</command>
+               <arg choice="opt">-h</arg>
+               <arg choice="opt">-H LDB-URL</arg>
+               <arg choice="opt">ldif-file1</arg>
+               <arg choice="opt">ldif-file2</arg>
+               <arg choice="opt">...</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ldbadd adds records to an ldb(7) database. It reads 
+               the ldif(5) files specified on the command line and adds 
+               the records from these files to the LDB database, which is specified 
+               by the -H option or the LDB_URL environment variable.
+       </para>
+
+       <para>If - is specified as a ldb file, the ldif input is read from 
+               standard input.</para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Show list of available options.</para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-H &lt;ldb-url&gt;</term>
+                       <listitem><para>
+                               LDB URL to connect to. See ldb(7) for details.
+                       </para></listitem>
+               </varlistentry>
+               
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+               <varlistentry><term>LDB_URL</term>
+                       <listitem><para>LDB URL to connect to (can be overrided by using the 
+                                       -H command-line option.)</para></listitem>
+               </varlistentry>
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>This manpage was written by Jelmer Vernooij.</para>
+       
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldbdel.1.xml b/source3/lib/ldb/man/ldbdel.1.xml
new file mode 100644 (file)
index 0000000..7dfc736
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbdel.1">
+
+<refmeta>
+       <refentrytitle>ldbdel</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ldbdel</refname>
+       <refpurpose>Command-line program for deleting LDB records</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ldbdel</command>
+               <arg choice="opt">-h</arg>
+               <arg choice="opt">-H LDB-URL</arg>
+               <arg choice="opt">dn</arg>
+               <arg choice="opt">...</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ldbdel deletes records from an ldb(7) database. 
+               It deletes the records identified by the dn's specified 
+               on the command-line. </para>
+
+       <para>ldbdel uses either the database that is specified with 
+               the -H option or the database specified by the LDB_URL environment
+               variable.</para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Show list of available options.</para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-H &lt;ldb-url&gt;</term>
+                       <listitem><para>
+                               LDB URL to connect to. See ldb(7) for details.
+                       </para></listitem>
+               </varlistentry>
+               
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+               <varlistentry><term>LDB_URL</term>
+                       <listitem><para>LDB URL to connect to (can be overrided by using the 
+                                       -H command-line option.)</para></listitem>
+               </varlistentry>
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbmodify, ldbadd, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+               <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>ldbdel was written by Andrew Tridgell.</para>
+
+       <para>This manpage was written by Jelmer Vernooij.</para>
+       
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldbedit.1.xml b/source3/lib/ldb/man/ldbedit.1.xml
new file mode 100644 (file)
index 0000000..15c69b1
--- /dev/null
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbedit.1">
+
+    <refmeta>
+       <refentrytitle>ldbedit</refentrytitle>
+       <manvolnum>1</manvolnum>
+    </refmeta>
+
+
+    <refnamediv>
+       <refname>ldbedit</refname>
+       <refpurpose>Edit LDB databases using your preferred editor</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+       <cmdsynopsis>
+           <command>ldbedit</command>
+           <arg choice="opt">-?</arg>
+           <arg choice="opt">--usage</arg>
+           <arg choice="opt">-s base|one|sub</arg>
+           <arg choice="opt">-b basedn</arg>
+           <arg choice="opt">-a</arg>
+           <arg choice="opt">-e editor</arg>
+           <arg choice="opt">-H LDB-URL</arg>
+           <arg choice="opt">expression</arg>
+           <arg rep="repeat" choice="opt">attributes</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ldbedit is a utility that allows you to edit LDB entries (in 
+           tdb files, sqlite files or LDAP servers) using your preferred editor.
+           ldbedit generates an LDIF file based on your query, allows you to edit
+           the LDIF, and then merges that LDIF back into the LDB backend.
+       </para>
+
+</refsect1>
+
+
+    <refsect1>
+       <title>OPTIONS</title>
+       
+       <variablelist>
+           <varlistentry>
+               <term>-?</term>
+               <term>--help</term>
+               <listitem>
+                   <para>
+                       Show list of available options, and a phrase describing what that option
+                       does.
+                   </para>
+               </listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>--usage</term>
+               <listitem>
+                   <para>
+                       Show list of available options. This is similar to the help option, 
+                       however it does not provide any description, and is hence shorter.
+                   </para>
+               </listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-H &lt;ldb-url&gt;</term>
+               <listitem>
+                   <para>
+                       LDB URL to connect to. For a tdb database,
+                       this will be of the form
+                       tdb://<replaceable>filename</replaceable>.
+                       For a LDAP connection over unix domain
+                       sockets, this will be of the form
+                       ldapi://<replaceable>socket</replaceable>. For
+                       a (potentially remote) LDAP connection over
+                       TCP, this will be of the form
+                       ldap://<replaceable>hostname</replaceable>. For
+                       an SQLite database, this will be of the form
+                       sqlite://<replaceable>filename</replaceable>.
+                   </para>
+               </listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-s one|sub|base</term>
+               <listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-a</term>
+               <term>-all</term>
+               <listitem>
+                   <para>Edit all records. This allows you to
+                       apply the same change to a number of records
+                       at once. You probably want to combine this
+                       with an expression of the form
+                       "objectclass=*".
+                   </para>
+               </listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-e editor</term>
+               <term>--editor editor</term>
+               <listitem>
+                   <para>Specify the editor that should be used (overrides 
+                       the VISUAL and EDITOR environment
+                       variables). If this option is not used, and
+                       neither VISUAL nor EDITOR environment variables
+                       are set, then the vi editor will be used.
+                   </para>
+               </listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-b basedn</term>
+               <listitem><para>Specify Base Distinguished Name to use.</para></listitem>
+           </varlistentry>
+
+           <varlistentry>
+               <term>-v</term>
+               <term>--verbose</term>
+               <listitem>
+                   <para>Make ldbedit more verbose about the
+                       operations that are being performed. Without
+                       this option, ldbedit will only provide a
+                       summary change line.
+                   </para>
+               </listitem>
+           </varlistentry>
+               
+       </variablelist>
+       
+    </refsect1>
+
+    <refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+           <varlistentry>
+               <term>LDB_URL</term>
+               <listitem>
+                   <para>LDB URL to connect to. This can be
+                   overridden by using the -H command-line option.)
+                   </para>
+               </listitem>
+           </varlistentry>
+           <varlistentry>
+               <term>VISUAL and EDITOR</term>
+               <listitem>
+                   <para>
+                       Environment variables used to determine what 
+                       editor to use. VISUAL takes precedence over
+                       EDITOR, and both are overridden by the
+                       -e command-line option.
+                   </para>
+               </listitem>
+           </varlistentry>
+       </variablelist>
+       
+    </refsect1>
+
+    <refsect1>
+       <title>VERSION</title>
+       
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+    </refsect1>
+
+    <refsect1>
+       <title>SEE ALSO</title>
+       
+       <para>ldb(7), ldbmodify(1), ldbdel(1), ldif(5), vi(1)</para>
+
+    </refsect1>
+
+    <refsect1>
+       <title>AUTHOR</title>
+
+       <para>
+           ldb was written by 
+           <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+           If you wish to report a problem or make a suggestion then please see
+           the <ulink url="http://ldb.samba.org/"/> web site for
+           current contact and maintainer information.
+       </para>
+
+       <para>
+           This manpage was written by Jelmer Vernooij and updated
+           by Brad Hards.
+       </para>
+       
+    </refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldbmodify.1.xml b/source3/lib/ldb/man/ldbmodify.1.xml
new file mode 100644 (file)
index 0000000..bc19647
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbmodify.1">
+
+<refmeta>
+       <refentrytitle>ldbmodify</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ldbmodify</refname>
+       <refpurpose>Modify records in a LDB database</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ldbmodify</command>
+               <arg choice="opt">-H LDB-URL</arg>
+               <arg choice="opt">ldif-file</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>
+               ldbmodify changes, adds and deletes records in a LDB database. 
+               The changes that should be made to the LDB database are read from 
+               the specified LDIF-file. If - is specified as the filename, input is read from stdin.
+       </para>
+
+       <para>For now, see ldapmodify(1) for details on the LDIF file format.</para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+                       <term>-H &lt;ldb-url&gt;</term>
+                       <listitem><para>
+                               LDB URL to connect to. See ldb(7) for details.
+                       </para></listitem>
+               </varlistentry>
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+               <varlistentry><term>LDB_URL</term>
+                       <listitem><para>LDB URL to connect to (can be overrided by using the 
+                                       -H command-line option.)</para></listitem>
+               </varlistentry>
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbedit</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>This manpage was written by Jelmer Vernooij.</para>
+       
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldbrename.1.xml b/source3/lib/ldb/man/ldbrename.1.xml
new file mode 100644 (file)
index 0000000..391ec84
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbrename.1">
+
+<refmeta>
+       <refentrytitle>ldbrename</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ldbrename</refname>
+       <refpurpose>Edit LDB databases using your favorite editor</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ldbrename</command>
+               <arg choice="opt">-h</arg>
+               <arg choice="opt">-o options</arg>
+               <arg choice="req">olddn</arg>
+               <arg choice="req">newdb</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ldbrename is a utility that allows you to rename trees in 
+               an LDB database based by DN. This utility takes 
+               two arguments: the original 
+               DN name of the top element and the DN to change it to.
+       </para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Show list of available options.</para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-H &lt;ldb-url&gt;</term>
+                       <listitem><para>
+                               LDB URL to connect to. See ldb(7) for details.
+                       </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-o options</term>
+                       <listitem><para>Extra ldb options, such as 
+                       modules.</para></listitem>
+               </varlistentry>
+               
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+               <varlistentry><term>LDB_URL</term>
+                       <listitem><para>LDB URL to connect to (can be overrided by using the 
+                                       -H command-line option.)</para></listitem>
+               </varlistentry>
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>This manpage was written by Jelmer Vernooij.</para>
+       
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/ldbsearch.1.xml b/source3/lib/ldb/man/ldbsearch.1.xml
new file mode 100644 (file)
index 0000000..ed3749b
--- /dev/null
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbsearch.1">
+
+<refmeta>
+       <refentrytitle>ldbsearch</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>ldbsearch</refname>
+       <refpurpose>Search for records in a LDB database</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>ldbsearch</command>
+               <arg choice="opt">-h</arg>
+               <arg choice="opt">-s base|one|sub</arg>
+               <arg choice="opt">-b basedn</arg>
+               <arg chioce="opt">-i</arg>
+               <arg choice="opt">-H LDB-URL</arg>
+               <arg choice="opt">expression</arg>
+               <arg choice="opt">attributes</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>ldbsearch searches a LDB database for records matching the 
+               specified expression (see the ldapsearch(1) manpage for 
+               a description of the expression format). For each 
+               record, the specified attributes are printed.
+       </para>
+
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-h</term>
+               <listitem><para>
+               Show list of available options.</para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-H &lt;ldb-url&gt;</term>
+                       <listitem><para>
+                               LDB URL to connect to. See ldb(7) for details.
+                       </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-s one|sub|base</term>
+                       <listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-i</term>
+                       <listitem><para>Read search expressions from stdin. </para></listitem>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>-b basedn</term>
+                       <listitem><para>Specify Base DN to use.</para></listitem>
+               </varlistentry>
+               
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>ENVIRONMENT</title>
+
+       <variablelist>
+               <varlistentry><term>LDB_URL</term>
+                       <listitem><para>LDB URL to connect to (can be overrided by using the 
+                                       -H command-line option.)</para></listitem>
+               </varlistentry>
+       </variablelist>
+       
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbedit(1)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+       <para>This manpage was written by Jelmer Vernooij.</para>
+       
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/man/oLschema2ldif.1.xml b/source3/lib/ldb/man/oLschema2ldif.1.xml
new file mode 100644 (file)
index 0000000..b1e681b
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="oLschema2ldif.1">
+
+<refmeta>
+       <refentrytitle>oLschema2ldif</refentrytitle>
+       <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+       <refname>oLschema2ldif</refname>
+       <refpurpose>Converts LDAP schema's to LDB-compatible LDIF</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+       <cmdsynopsis>
+               <command>oLschema2ldif</command>
+               <arg choice="opt">-I INPUT-FILE</arg>
+               <arg choice="opt">-O OUTPUT-FILE</arg>
+       </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+
+       <para>oLschema2ldif is a simple tool that converts standard OpenLDAP schema files to a LDIF format that is understood by LDB.</para>
+</refsect1>
+
+
+<refsect1>
+       <title>OPTIONS</title>
+
+       <variablelist>
+               <varlistentry>
+               <term>-I input-file</term>
+               <listitem><para>OpenLDAP schema to read. If none are specified, 
+the schema file will be read from standard input.
+               </para></listitem>
+               </varlistentry>
+
+       <varlistentry>
+               <term>-O output-file</term>
+               <listitem><para>File to write ldif version of schema to.
+               </para></listitem>
+       </varlistentry>
+       </variablelist>
+</refsect1>
+
+<refsect1>
+       <title>VERSION</title>
+
+       <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+       <para> ldb was written by 
+                <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+               oLschema2ldif was written by <ulink url="mailto:idra@samba.org">Simo Sorce</ulink>.
+       </para>
+
+       <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+       </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source3/lib/ldb/modules/asq.c b/source3/lib/ldb/modules/asq.c
new file mode 100644 (file)
index 0000000..229a6ea
--- /dev/null
@@ -0,0 +1,471 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb attribute scoped query control module
+ *
+ *  Description: this module searches all the the objects pointed
+ *              by the DNs contained in the references attribute
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct asq_context {
+
+       enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
+
+       struct ldb_module *module;
+       void *up_context;
+       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+       const char * const *req_attrs;
+       char *req_attribute;
+       enum {
+               ASQ_CTRL_SUCCESS                        = 0,
+               ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX       = 21,
+               ASQ_CTRL_UNWILLING_TO_PERFORM           = 53,
+               ASQ_CTRL_AFFECTS_MULTIPLE_DSA           = 71
+       } asq_ret;
+
+       struct ldb_request *base_req;
+       struct ldb_reply *base_res;
+
+       struct ldb_request **reqs;
+       int num_reqs;
+       int cur_req;
+
+       struct ldb_control **controls;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct asq_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(mem_ctx, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct asq_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->up_context = context;
+       ac->up_callback = callback;
+
+       return h;
+}
+
+static int asq_terminate(struct ldb_handle *handle)
+{
+       struct asq_context *ac;
+       struct ldb_reply *ares;
+       struct ldb_asq_control *asq;
+       int i;
+
+       ac = talloc_get_type(handle->private_data, struct asq_context);
+
+       handle->status = LDB_SUCCESS;
+       handle->state = LDB_ASYNC_DONE;
+
+       ares = talloc_zero(ac, struct ldb_reply);
+       if (ares == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       ares->type = LDB_REPLY_DONE;
+
+       if (ac->controls) {
+               for (i = 0; ac->controls[i]; i++);
+               ares->controls = talloc_move(ares, &ac->controls);
+       } else {
+               i = 0;
+       }
+
+       ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, i + 2);
+       
+       if (ares->controls == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       ares->controls[i] = talloc(ares->controls, struct ldb_control);
+       if (ares->controls[i] == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       ares->controls[i]->oid = LDB_CONTROL_ASQ_OID;
+       ares->controls[i]->critical = 0;
+
+       asq = talloc_zero(ares->controls[i], struct ldb_asq_control);
+       if (asq == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       asq->result = ac->asq_ret;
+       
+       ares->controls[i]->data = asq;
+
+       ares->controls[i + 1] = NULL;
+
+       ac->up_callback(ac->module->ldb, ac->up_context, ares);
+
+       return LDB_SUCCESS;
+}
+
+static int asq_base_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct asq_context *ac;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }
+
+       ac = talloc_get_type(context, struct asq_context);
+
+       /* we are interested only in the single reply (base search) we receive here */
+       if (ares->type == LDB_REPLY_ENTRY) {
+               ac->base_res = talloc_move(ac, &ares);
+       } else {
+               talloc_free(ares);
+       }
+
+       return LDB_SUCCESS;
+error:
+       talloc_free(ares);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_reqs_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct asq_context *ac;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }
+
+       ac = talloc_get_type(context, struct asq_context);
+
+       /* we are interested only in the single reply (base search) we receive here */
+       if (ares->type == LDB_REPLY_ENTRY) {
+
+               /* pass the message up to the original callback as we
+                * do not have to elaborate on it any further */
+               return ac->up_callback(ac->module->ldb, ac->up_context, ares);
+               
+       } else { /* ignore any REFERRAL or DONE reply */
+               talloc_free(ares);
+       }
+
+       return LDB_SUCCESS;
+error:
+       talloc_free(ares);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_control *control;
+       struct ldb_asq_control *asq_ctrl;
+       struct asq_context *ac;
+       struct ldb_handle *h;
+       char **base_attrs;
+       int ret;
+
+       /* check if there's a paged request control */
+       control = get_control_from_list(req->controls, LDB_CONTROL_ASQ_OID);
+       if (control == NULL) {
+               /* not found go on */
+               return ldb_next_request(module, req);
+       }
+
+       req->handle = NULL;
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb,
+                                 "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
+       if (!asq_ctrl) {
+               return LDB_ERR_PROTOCOL_ERROR;
+       }
+
+       h = init_handle(req, module, req->context, req->callback);
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct asq_context);
+
+       req->handle = h;
+
+       /* check the search is well formed */
+       if (req->op.search.scope != LDB_SCOPE_BASE) {
+               ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
+               return asq_terminate(h);
+       }
+
+       ac->req_attrs = req->op.search.attrs;
+       ac->req_attribute = talloc_strdup(ac, asq_ctrl->source_attribute);
+       if (ac->req_attribute == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       /* get the object to retrieve the DNs to search */
+       ac->base_req = talloc_zero(req, struct ldb_request);
+       if (ac->base_req == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+       ac->base_req->operation = req->operation;
+       ac->base_req->op.search.base = req->op.search.base;
+       ac->base_req->op.search.scope = LDB_SCOPE_BASE;
+       ac->base_req->op.search.tree = req->op.search.tree;
+       base_attrs = talloc_array(ac->base_req, char *, 2);
+       if (base_attrs == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+       base_attrs[0] = talloc_strdup(base_attrs, asq_ctrl->source_attribute);
+       if (base_attrs[0] == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+       base_attrs[1] = NULL;
+       ac->base_req->op.search.attrs = (const char * const *)base_attrs;
+
+       ac->base_req->context = ac;
+       ac->base_req->callback = asq_base_callback;
+       ldb_set_timeout_from_prev_req(module->ldb, req, ac->base_req);
+
+       ac->step = ASQ_SEARCH_BASE;
+
+       ret = ldb_request(module->ldb, ac->base_req);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int asq_requests(struct ldb_handle *handle) {
+       struct asq_context *ac;
+       struct ldb_message_element *el;
+       int i;
+
+       ac = talloc_get_type(handle->private_data, struct asq_context);
+
+       /* look up the DNs */
+       if (ac->base_res == NULL) {
+               return LDB_ERR_NO_SUCH_OBJECT;
+       }
+       el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
+       /* no values found */
+       if (el == NULL) {
+               ac->asq_ret = ASQ_CTRL_SUCCESS;
+               return asq_terminate(handle);
+       }
+
+       /* build up the requests call chain */
+       ac->num_reqs = el->num_values;
+       ac->cur_req = 0;
+       ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
+       if (ac->reqs == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       for (i = 0; i < el->num_values; i++) {
+
+               ac->reqs[i] = talloc_zero(ac->reqs, struct ldb_request);
+               if (ac->reqs[i] == NULL)
+                       return LDB_ERR_OPERATIONS_ERROR;
+               ac->reqs[i]->operation = LDB_SEARCH;
+               ac->reqs[i]->op.search.base = ldb_dn_explode(ac->reqs[i], (const char *)el->values[i].data);
+               if (ac->reqs[i]->op.search.base == NULL) {
+                       ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
+                       return asq_terminate(handle);
+               }
+               ac->reqs[i]->op.search.scope = LDB_SCOPE_BASE;
+               ac->reqs[i]->op.search.tree = ac->base_req->op.search.tree;
+               ac->reqs[i]->op.search.attrs = ac->req_attrs;
+
+               ac->reqs[i]->context = ac;
+               ac->reqs[i]->callback = asq_reqs_callback;
+               ldb_set_timeout_from_prev_req(ac->module->ldb, ac->base_req, ac->reqs[i]);
+       }
+
+       ac->step = ASQ_SEARCH_MULTI;
+
+       return LDB_SUCCESS;
+}
+
+static int asq_wait_none(struct ldb_handle *handle)
+{
+       struct asq_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct asq_context);
+
+
+       switch (ac->step) {
+       case ASQ_SEARCH_BASE:
+               ret = ldb_wait(ac->base_req->handle, LDB_WAIT_NONE);
+               
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+
+               if (ac->base_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->base_req->handle->status;
+                       goto done;
+               }
+               if (ac->base_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               ret = asq_requests(handle);
+
+       case ASQ_SEARCH_MULTI:
+
+               if (ac->reqs[ac->cur_req]->handle == NULL) {
+                       ret = ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
+
+               ret = ldb_wait(ac->reqs[ac->cur_req]->handle, LDB_WAIT_NONE);
+               
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->reqs[ac->cur_req]->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->reqs[ac->cur_req]->handle->status;
+               }
+
+               if (ac->reqs[ac->cur_req]->handle->state == LDB_ASYNC_DONE) {
+                       ac->cur_req++;
+               }
+
+               if (ac->cur_req < ac->num_reqs) {
+                       return LDB_SUCCESS;
+               }
+
+               return asq_terminate(handle);
+
+       default:
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       ret = LDB_SUCCESS;
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+static int asq_wait_all(struct ldb_handle *handle)
+{
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = asq_wait_none(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return asq_wait_all(handle);
+       } else {
+               return asq_wait_none(handle);
+       }
+}
+
+static int asq_init(struct ldb_module *module)
+{
+       struct ldb_request *req;
+       int ret;
+
+       req = talloc_zero(module, struct ldb_request);
+       if (req == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Out of memory!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_REGISTER_CONTROL;
+       req->op.reg_control.oid = LDB_CONTROL_ASQ_OID;
+
+       ret = ldb_request(module->ldb, req);
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Unable to register control with rootdse!\n");
+               return LDB_ERR_OTHER;
+       }
+
+       return ldb_next_init(module);
+}
+
+
+static const struct ldb_module_ops asq_ops = {
+       .name              = "asq",
+       .search            = asq_search,
+       .wait              = asq_wait,
+       .init_context      = asq_init
+};
+
+int ldb_asq_init(void)
+{
+       return ldb_register_module(&asq_ops);
+}
diff --git a/source3/lib/ldb/modules/ldb_map.c b/source3/lib/ldb/modules/ldb_map.c
new file mode 100644 (file)
index 0000000..0c58687
--- /dev/null
@@ -0,0 +1,1361 @@
+/*
+   ldb database mapping module
+
+   Copyright (C) Jelmer Vernooij 2005
+   Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+   * NOTICE: this module is NOT released under the GNU LGPL license as
+   * other ldb code. This module is release under the GNU GPL v2 or
+   * later license.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* 
+ *  Name: ldb
+ *
+ *  Component: ldb ldb_map module
+ *
+ *  Description: Map portions of data into a different format on a
+ *  remote partition.
+ *
+ *  Author: Jelmer Vernooij, Martin Kuehl
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+/* Description of the provided ldb requests:
+ - special attribute 'isMapped'
+
+ - search:
+     - if parse tree can be split
+         - search remote records w/ remote attrs and parse tree
+     - otherwise
+         - enumerate all remote records
+     - for each remote result
+         - map remote result to local message
+         - search local result
+         - is present
+             - merge local into remote result
+             - run callback on merged result
+         - otherwise
+             - run callback on remote result
+
+ - add:
+     - split message into local and remote part
+     - if local message is not empty
+         - add isMapped to local message
+         - add local message
+     - add remote message
+
+ - modify:
+     - split message into local and remote part
+     - if local message is not empty
+         - add isMapped to local message
+         - search for local record
+         - if present
+             - modify local record
+         - otherwise
+             - add local message
+     - modify remote record
+
+ - delete:
+     - search for local record
+     - if present
+         - delete local record
+     - delete remote record
+
+ - rename:
+     - search for local record
+     - if present
+         - rename local record
+         - modify local isMapped
+     - rename remote record
+*/
+
+
+
+/* Private data structures
+ * ======================= */
+
+/* Global private data */
+/* Extract mappings from private data. */
+const struct ldb_map_context *map_get_context(struct ldb_module *module)
+{
+       const struct map_private *data = talloc_get_type(module->private_data, struct map_private);
+       return data->context;
+}
+
+/* Create a generic request context. */
+static struct map_context *map_init_context(struct ldb_handle *h, struct ldb_request *req)
+{
+       struct map_context *ac;
+
+       ac = talloc_zero(h, struct map_context);
+       if (ac == NULL) {
+               map_oom(h->module);
+               return NULL;
+       }
+
+       ac->module = h->module;
+       ac->orig_req = req;
+
+       return ac;
+}
+
+/* Create a search request context. */
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares)
+{
+       struct map_search_context *sc;
+
+       sc = talloc_zero(ac, struct map_search_context);
+       if (sc == NULL) {
+               map_oom(ac->module);
+               return NULL;
+       }
+
+       sc->ac = ac;
+       sc->local_res = NULL;
+       sc->remote_res = ares;
+
+       return sc;
+}
+
+/* Create a request context and handle. */
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module)
+{
+       struct map_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(req, struct ldb_handle);
+       if (h == NULL) {
+               map_oom(module);
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = map_init_context(h, req);
+       if (ac == NULL) {
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       return h;
+}
+
+
+/* Dealing with DNs for different partitions
+ * ========================================= */
+
+/* Check whether any data should be stored in the local partition. */
+BOOL map_check_local_db(struct ldb_module *module)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+
+       if (!data->remote_base_dn || !data->local_base_dn) {
+               return False;
+       }
+
+       return True;
+}
+
+/* WARK: verbatim copy from ldb_dn.c */
+static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+{
+       struct ldb_dn_component dst;
+
+       memset(&dst, 0, sizeof(dst));
+
+       if (src == NULL) {
+               return dst;
+       }
+
+       dst.value = ldb_val_dup(mem_ctx, &(src->value));
+       if (dst.value.data == NULL) {
+               return dst;
+       }
+
+       dst.name = talloc_strdup(mem_ctx, src->name);
+       if (dst.name == NULL) {
+               talloc_free(dst.value.data);
+       }
+
+       return dst;
+}
+
+/* Copy a DN but replace the old with the new base DN. */
+static struct ldb_dn *ldb_dn_rebase(void *mem_ctx, const struct ldb_dn *old, const struct ldb_dn *old_base, const struct ldb_dn *new_base)
+{
+       struct ldb_dn *new;
+       int i, offset;
+
+       /* Perhaps we don't need to rebase at all? */
+       if (!old_base || !new_base) {
+               return ldb_dn_copy(mem_ctx, old);
+       }
+
+       offset = old->comp_num - old_base->comp_num;
+       new = ldb_dn_copy_partial(mem_ctx, new_base, offset + new_base->comp_num);
+       for (i = 0; i < offset; i++) {
+               new->components[i] = ldb_dn_copy_component(new->components, &(old->components[i]));
+       }
+
+       return new;
+}
+
+/* Copy a DN with the base DN of the local partition. */
+static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, const struct ldb_dn *dn)
+{
+       return ldb_dn_rebase(mem_ctx, dn, data->remote_base_dn, data->local_base_dn);
+}
+
+/* Copy a DN with the base DN of the remote partition. */
+static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, const struct ldb_dn *dn)
+{
+       return ldb_dn_rebase(mem_ctx, dn, data->local_base_dn, data->remote_base_dn);
+}
+
+/* Run a request and make sure it targets the remote partition. */
+/* TODO: free old DNs and messages? */
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       struct ldb_message *msg;
+
+       switch (request->operation) {
+       case LDB_SEARCH:
+               if (request->op.search.base) {
+                       request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
+               } else {
+                       request->op.search.base = data->remote_base_dn;
+                       /* TODO: adjust scope? */
+               }
+               break;
+
+       case LDB_ADD:
+               msg = ldb_msg_copy_shallow(request, request->op.add.message);
+               msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+               request->op.add.message = msg;
+               break;
+
+       case LDB_MODIFY:
+               msg = ldb_msg_copy_shallow(request, request->op.mod.message);
+               msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+               request->op.mod.message = msg;
+               break;
+
+       case LDB_DELETE:
+               request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
+               break;
+
+       case LDB_RENAME:
+               request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
+               request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
+               break;
+
+       default:
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                         "Invalid remote request!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return ldb_next_request(module, request);
+}
+
+
+/* Finding mappings for attributes and objectClasses
+ * ================================================= */
+
+/* Find an objectClass mapping by the local name. */
+static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
+{
+       int i;
+
+       for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
+               if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
+                       return &data->objectclass_maps[i];
+               }
+       }
+
+       return NULL;
+}
+
+/* Find an objectClass mapping by the remote name. */
+static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
+{
+       int i;
+
+       for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
+               if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
+                       return &data->objectclass_maps[i];
+               }
+       }
+
+       return NULL;
+}
+
+/* Find an attribute mapping by the local name. */
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
+{
+       int i;
+
+       for (i = 0; data->attribute_maps[i].local_name; i++) {
+               if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
+                       return &data->attribute_maps[i];
+               }
+       }
+       for (i = 0; data->attribute_maps[i].local_name; i++) {
+               if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
+                       return &data->attribute_maps[i];
+               }
+       }
+
+       return NULL;
+}
+
+/* Find an attribute mapping by the remote name. */
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
+{
+       const struct ldb_map_attribute *map;
+       const struct ldb_map_attribute *wildcard = NULL;
+       int i, j;
+
+       for (i = 0; data->attribute_maps[i].local_name; i++) {
+               map = &data->attribute_maps[i];
+               if (ldb_attr_cmp(map->local_name, "*") == 0) {
+                       wildcard = &data->attribute_maps[i];
+               }
+
+               switch (map->type) {
+               case MAP_IGNORE:
+                       break;
+
+               case MAP_KEEP:
+                       if (ldb_attr_cmp(map->local_name, name) == 0) {
+                               return map;
+                       }
+                       break;
+
+               case MAP_RENAME:
+               case MAP_CONVERT:
+                       if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
+                               return map;
+                       }
+                       break;
+
+               case MAP_GENERATE:
+                       for (j = 0; map->u.generate.remote_names && map->u.generate.remote_names[j]; j++) {
+                               if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
+                                       return map;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       /* We didn't find it, so return the wildcard record if one was configured */
+       return wildcard;
+}
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Check whether an attribute will be mapped into the remote partition. */
+BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
+{
+       const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
+
+       if (map == NULL) {
+               return False;
+       }
+       if (map->type == MAP_IGNORE) {
+               return False;
+       }
+
+       return True;
+}
+
+/* Map an attribute name into the remote partition. */
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+       if (map == NULL) {
+               return talloc_strdup(mem_ctx, attr);
+       }
+
+       switch (map->type) {
+       case MAP_KEEP:
+               return talloc_strdup(mem_ctx, attr);
+
+       case MAP_RENAME:
+       case MAP_CONVERT:
+               return talloc_strdup(mem_ctx, map->u.rename.remote_name);
+
+       default:
+               return NULL;
+       }
+}
+
+/* Map an attribute name back into the local partition. */
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+       if (map == NULL) {
+               return talloc_strdup(mem_ctx, attr);
+       }
+
+       if (map->type == MAP_KEEP) {
+               return talloc_strdup(mem_ctx, attr);
+       }
+
+       return talloc_strdup(mem_ctx, map->local_name);
+}
+
+
+/* Merge two lists of attributes into a single one. */
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs)
+{
+       int i, j, k;
+
+       for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
+       for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
+
+       *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
+       if (*attrs == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       for (k = 0; k < j; k++) {
+               (*attrs)[i+k] = more_attrs[k];
+       }
+
+       (*attrs)[i+k] = NULL;
+
+       return 0;
+}
+
+/* Mapping ldb values
+ * ================== */
+
+/* Map an ldb value into the remote partition. */
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val)
+{
+       if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_local)) {
+               return map->u.convert.convert_local(module, mem_ctx, &val);
+       }
+
+       return ldb_val_dup(mem_ctx, &val);
+}
+
+/* Map an ldb value back into the local partition. */
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val)
+{
+       if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_remote)) {
+               return map->u.convert.convert_remote(module, mem_ctx, &val);
+       }
+
+       return ldb_val_dup(mem_ctx, &val);
+}
+
+
+/* Mapping DNs
+ * =========== */
+
+/* Check whether a DN is below the local baseDN. */
+BOOL ldb_dn_check_local(struct ldb_module *module, const struct ldb_dn *dn)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+
+       if (!data->local_base_dn) {
+               return True;
+       }
+
+       return ldb_dn_compare_base(module->ldb, data->local_base_dn, dn) == 0;
+}
+
+/* Map a DN into the remote partition. */
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       struct ldb_dn *newdn;
+       struct ldb_dn_component *old, *new;
+       const struct ldb_map_attribute *map;
+       enum ldb_map_attr_type map_type;
+       int i;
+
+       if (dn == NULL) {
+               return NULL;
+       }
+
+       newdn = ldb_dn_copy(mem_ctx, dn);
+       if (newdn == NULL) {
+               map_oom(module);
+               return NULL;
+       }
+
+       /* For each RDN, map the component name and possibly the value */
+       for (i = 0; i < newdn->comp_num; i++) {
+               old = &dn->components[i];
+               new = &newdn->components[i];
+               map = map_attr_find_local(data, old->name);
+
+               /* Unknown attribute - leave this RDN as is and hope the best... */
+               if (map == NULL) {
+                       map_type = MAP_KEEP;
+               } else {
+                       map_type = map->type;
+               }
+
+               switch (map_type) {
+               case MAP_IGNORE:
+               case MAP_GENERATE:
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                 "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+                                 "used in DN!\n", old->name);
+                       goto failed;
+
+               case MAP_CONVERT:
+                       if (map->u.convert.convert_local == NULL) {
+                               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                         "'convert_local' not set for attribute '%s' "
+                                         "used in DN!\n", old->name);
+                               goto failed;
+                       }
+                       /* fall through */
+               case MAP_KEEP:
+               case MAP_RENAME:
+                       new->name = discard_const_p(char, map_attr_map_local(newdn->components, map, old->name));
+                       new->value = ldb_val_map_local(module, newdn->components, map, old->value);
+                       break;
+               }
+       }
+
+       return newdn;
+
+failed:
+       talloc_free(newdn);
+       return NULL;
+}
+
+/* Map a DN into the local partition. */
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       struct ldb_dn *newdn;
+       struct ldb_dn_component *old, *new;
+       const struct ldb_map_attribute *map;
+       enum ldb_map_attr_type map_type;
+       int i;
+
+       if (dn == NULL) {
+               return NULL;
+       }
+
+       newdn = ldb_dn_copy(mem_ctx, dn);
+       if (newdn == NULL) {
+               map_oom(module);
+               return NULL;
+       }
+
+       /* For each RDN, map the component name and possibly the value */
+       for (i = 0; i < newdn->comp_num; i++) {
+               old = &dn->components[i];
+               new = &newdn->components[i];
+               map = map_attr_find_remote(data, old->name);
+
+               /* Unknown attribute - leave this RDN as is and hope the best... */
+               if (map == NULL) {
+                       map_type = MAP_KEEP;
+               } else {
+                       map_type = map->type;
+               }
+
+               switch (map_type) {
+               case MAP_IGNORE:
+               case MAP_GENERATE:
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                 "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+                                 "used in DN!\n", old->name);
+                       goto failed;
+
+               case MAP_CONVERT:
+                       if (map->u.convert.convert_remote == NULL) {
+                               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                         "'convert_remote' not set for attribute '%s' "
+                                         "used in DN!\n", old->name);
+                               goto failed;
+                       }
+                       /* fall through */
+               case MAP_KEEP:
+               case MAP_RENAME:
+                       new->name = discard_const_p(char, map_attr_map_remote(newdn->components, map, old->name));
+                       new->value = ldb_val_map_remote(module, newdn->components, map, old->value);
+                       break;
+               }
+       }
+
+       return newdn;
+
+failed:
+       talloc_free(newdn);
+       return NULL;
+}
+
+/* Map a DN and its base into the local partition. */
+/* TODO: This should not be required with GUIDs. */
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       struct ldb_dn *dn1, *dn2;
+
+       dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
+       dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
+
+       talloc_free(dn1);
+       return dn2;
+}
+
+
+/* Converting DNs and objectClasses (as ldb values)
+ * ================================================ */
+
+/* Map a DN contained in an ldb value into the remote partition. */
+static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+       struct ldb_dn *dn, *newdn;
+       struct ldb_val newval;
+
+       dn = ldb_dn_explode(mem_ctx, (char *)val->data);
+       newdn = ldb_dn_map_local(module, mem_ctx, dn);
+       talloc_free(dn);
+
+       newval.length = 0;
+       newval.data = (uint8_t *)ldb_dn_linearize(mem_ctx, newdn);
+       if (newval.data) {
+               newval.length = strlen((char *)newval.data);
+       }
+       talloc_free(newdn);
+
+       return newval;
+}
+
+/* Map a DN contained in an ldb value into the local partition. */
+static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+       struct ldb_dn *dn, *newdn;
+       struct ldb_val newval;
+
+       dn = ldb_dn_explode(mem_ctx, (char *)val->data);
+       newdn = ldb_dn_map_remote(module, mem_ctx, dn);
+       talloc_free(dn);
+
+       newval.length = 0;
+       newval.data = (uint8_t *)ldb_dn_linearize(mem_ctx, newdn);
+       if (newval.data) {
+               newval.length = strlen((char *)newval.data);
+       }
+       talloc_free(newdn);
+
+       return newval;
+}
+
+/* Map an objectClass into the remote partition. */
+static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const char *name = (char *)val->data;
+       const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
+       struct ldb_val newval;
+
+       if (map) {
+               newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
+               newval.length = strlen((char *)newval.data);
+               return newval;
+       }
+
+       return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a remote message with a mapped objectClass. */
+static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local)
+{
+       struct ldb_message_element *el, *oc;
+       struct ldb_val val;
+       BOOL found_extensibleObject = False;
+       int i;
+
+       /* Find old local objectClass */
+       oc = ldb_msg_find_element(old, local_attr);
+       if (oc == NULL) {
+               return;
+       }
+
+       /* Prepare new element */
+       el = talloc_zero(remote, struct ldb_message_element);
+       if (el == NULL) {
+               ldb_oom(module->ldb);
+               return;                 /* TODO: fail? */
+       }
+
+       /* Copy local objectClass element, reverse space for an extra value */
+       el->num_values = oc->num_values + 1;
+       el->values = talloc_array(el, struct ldb_val, el->num_values);
+       if (el->values == NULL) {
+               talloc_free(el);
+               ldb_oom(module->ldb);
+               return;                 /* TODO: fail? */
+       }
+
+       /* Copy local element name "objectClass" */
+       el->name = talloc_strdup(el, local_attr);
+
+       /* Convert all local objectClasses */
+       for (i = 0; i < el->num_values - 1; i++) {
+               el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
+               if (ldb_attr_cmp((char *)el->values[i].data, "extensibleObject") == 0) {
+                       found_extensibleObject = True;
+               }
+       }
+
+       if (!found_extensibleObject) {
+               val.data = (uint8_t *)talloc_strdup(el->values, "extensibleObject");
+               val.length = strlen((char *)val.data);
+
+               /* Append additional objectClass "extensibleObject" */
+               el->values[i] = val;
+       } else {
+               el->num_values--;
+       }
+
+       /* Add new objectClass to remote message */
+       ldb_msg_add(remote, el, 0);
+}
+
+/* Map an objectClass into the local partition. */
+static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const char *name = (char *)val->data;
+       const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
+       struct ldb_val newval;
+
+       if (map) {
+               newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
+               newval.length = strlen((char *)newval.data);
+               return newval;
+       }
+
+       return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a local message with a mapped objectClass. */
+static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *remote_attr, const struct ldb_message *remote)
+{
+       struct ldb_message_element *el, *oc;
+       struct ldb_val val;
+       int i;
+
+       /* Find old remote objectClass */
+       oc = ldb_msg_find_element(remote, remote_attr);
+       if (oc == NULL) {
+               return NULL;
+       }
+
+       /* Prepare new element */
+       el = talloc_zero(mem_ctx, struct ldb_message_element);
+       if (el == NULL) {
+               ldb_oom(module->ldb);
+               return NULL;
+       }
+
+       /* Copy remote objectClass element */
+       el->num_values = oc->num_values;
+       el->values = talloc_array(el, struct ldb_val, el->num_values);
+       if (el->values == NULL) {
+               talloc_free(el);
+               ldb_oom(module->ldb);
+               return NULL;
+       }
+
+       /* Copy remote element name "objectClass" */
+       el->name = talloc_strdup(el, remote_attr);
+
+       /* Convert all remote objectClasses */
+       for (i = 0; i < el->num_values; i++) {
+               el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
+       }
+
+       val.data = (uint8_t *)talloc_strdup(el->values, "extensibleObject");
+       val.length = strlen((char *)val.data);
+
+       /* Remove last value if it was "extensibleObject" */
+       if (ldb_val_equal_exact(&val, &el->values[i-1])) {
+               el->num_values--;
+               el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
+               if (el->values == NULL) {
+                       talloc_free(el);
+                       ldb_oom(module->ldb);
+                       return NULL;
+               }
+       }
+
+       return el;
+}
+
+/* Mappings for searches on objectClass= assuming a one-to-one
+ * mapping.  Needed because this is a generate operator for the
+ * add/modify code */
+static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx, 
+                                           struct ldb_parse_tree **new, const struct ldb_parse_tree *tree) 
+{
+       
+       static const struct ldb_map_attribute objectclass_map = {
+               .local_name = "objectClass",
+               .type = MAP_CONVERT,
+               .u = {
+                       .convert = {
+                                .remote_name = "objectClass",
+                                .convert_local = map_objectclass_convert_local,
+                                .convert_remote = map_objectclass_convert_remote,
+                        },
+               },
+       };
+
+       return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_map);
+}
+
+/* Auxiliary request construction
+ * ============================== */
+
+/* Store the DN of a single search result in context. */
+static int map_search_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct map_context *ac;
+
+       if (context == NULL || ares == NULL) {
+               ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = talloc_get_type(context, struct map_context);
+
+       /* We are interested only in the single reply */
+       if (ares->type != LDB_REPLY_ENTRY) {
+               talloc_free(ares);
+               return LDB_SUCCESS;
+       }
+
+       /* We have already found a remote DN */
+       if (ac->local_dn) {
+               ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results to base search"));
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Store local DN */
+       ac->local_dn = ares->message->dn;
+
+       return LDB_SUCCESS;
+}
+
+/* Build a request to search a record by its DN. */
+struct ldb_request *map_search_base_req(struct map_context *ac, const struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback)
+{
+       struct ldb_request *req;
+
+       req = talloc_zero(ac, struct ldb_request);
+       if (req == NULL) {
+               map_oom(ac->module);
+               return NULL;
+       }
+
+       req->operation = LDB_SEARCH;
+       req->op.search.base = dn;
+       req->op.search.scope = LDB_SCOPE_BASE;
+       req->op.search.attrs = attrs;
+
+       if (tree) {
+               req->op.search.tree = tree;
+       } else {
+               req->op.search.tree = ldb_parse_tree(req, NULL);
+               if (req->op.search.tree == NULL) {
+                       talloc_free(req);
+                       return NULL;
+               }
+       }
+
+       req->controls = NULL;
+       req->context = context;
+       req->callback = callback;
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
+
+       return req;
+}
+
+/* Build a request to search the local record by its DN. */
+struct ldb_request *map_search_self_req(struct map_context *ac, const struct ldb_dn *dn)
+{
+       /* attrs[] is returned from this function in
+        * ac->search_req->op.search.attrs, so it must be static, as
+        * otherwise the compiler can put it on the stack */
+       static const char * const attrs[] = { IS_MAPPED, NULL };
+       struct ldb_parse_tree *tree;
+
+       /* Limit search to records with 'IS_MAPPED' present */
+       /* TODO: `tree = ldb_parse_tree(ac, IS_MAPPED);' won't do. */
+       tree = talloc_zero(ac, struct ldb_parse_tree);
+       if (tree == NULL) {
+               map_oom(ac->module);
+               return NULL;
+       }
+
+       tree->operation = LDB_OP_PRESENT;
+       tree->u.present.attr = talloc_strdup(tree, IS_MAPPED);
+
+       return map_search_base_req(ac, dn, attrs, tree, ac, map_search_self_callback);
+}
+
+/* Build a request to update the 'IS_MAPPED' attribute */
+struct ldb_request *map_build_fixup_req(struct map_context *ac, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
+{
+       struct ldb_request *req;
+       struct ldb_message *msg;
+       const char *dn;
+
+       /* Prepare request */
+       req = talloc_zero(ac, struct ldb_request);
+       if (req == NULL) {
+               map_oom(ac->module);
+               return NULL;
+       }
+
+       /* Prepare message */
+       msg = ldb_msg_new(req);
+       if (msg == NULL) {
+               map_oom(ac->module);
+               goto failed;
+       }
+
+       /* Update local 'IS_MAPPED' to the new remote DN */
+       msg->dn = discard_const_p(struct ldb_dn, olddn);
+       dn = ldb_dn_linearize(msg, newdn);
+       if (dn == NULL) {
+               goto failed;
+       }
+       if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE) != 0) {
+               goto failed;
+       }
+       if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+               goto failed;
+       }
+
+       req->operation = LDB_MODIFY;
+       req->op.mod.message = msg;
+       req->controls = NULL;
+       req->handle = NULL;
+       req->context = NULL;
+       req->callback = NULL;
+
+       return req;
+
+failed:
+       talloc_free(req);
+       return NULL;
+}
+
+
+/* Asynchronous call structure
+ * =========================== */
+
+/* Figure out which request is currently pending. */
+static struct ldb_request *map_get_req(struct map_context *ac)
+{
+       switch (ac->step) {
+       case MAP_SEARCH_SELF_MODIFY:
+       case MAP_SEARCH_SELF_DELETE:
+       case MAP_SEARCH_SELF_RENAME:
+               return ac->search_req;
+
+       case MAP_ADD_REMOTE:
+       case MAP_MODIFY_REMOTE:
+       case MAP_DELETE_REMOTE:
+       case MAP_RENAME_REMOTE:
+               return ac->remote_req;
+
+       case MAP_RENAME_FIXUP:
+               return ac->down_req;
+
+       case MAP_ADD_LOCAL:
+       case MAP_MODIFY_LOCAL:
+       case MAP_DELETE_LOCAL:
+       case MAP_RENAME_LOCAL:
+               return ac->local_req;
+
+       case MAP_SEARCH_REMOTE:
+               /* Can't happen */
+               break;
+       }
+
+       return NULL;            /* unreachable; silences a warning */
+}
+
+typedef int (*map_next_function)(struct ldb_handle *handle);
+
+/* Figure out the next request to run. */
+static map_next_function map_get_next(struct map_context *ac)
+{
+       switch (ac->step) {
+       case MAP_SEARCH_REMOTE:
+               return NULL;
+
+       case MAP_ADD_LOCAL:
+               return map_add_do_remote;
+       case MAP_ADD_REMOTE:
+               return NULL;
+
+       case MAP_SEARCH_SELF_MODIFY:
+               return map_modify_do_local;
+       case MAP_MODIFY_LOCAL:
+               return map_modify_do_remote;
+       case MAP_MODIFY_REMOTE:
+               return NULL;
+
+       case MAP_SEARCH_SELF_DELETE:
+               return map_delete_do_local;
+       case MAP_DELETE_LOCAL:
+               return map_delete_do_remote;
+       case MAP_DELETE_REMOTE:
+               return NULL;
+
+       case MAP_SEARCH_SELF_RENAME:
+               return map_rename_do_local;
+       case MAP_RENAME_LOCAL:
+               return map_rename_do_fixup;
+       case MAP_RENAME_FIXUP:
+               return map_rename_do_remote;
+       case MAP_RENAME_REMOTE:
+               return NULL;
+       }
+
+       return NULL;            /* unreachable; silences a warning */
+}
+
+/* Wait for the current pending request to finish and continue with the next. */
+static int map_wait_next(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+       struct ldb_request *req;
+       map_next_function next;
+       int ret;
+
+       if (handle == NULL || handle->private_data == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       if (ac->step == MAP_SEARCH_REMOTE) {
+               int i;
+               for (i = 0; i < ac->num_searches; i++) {
+                       req = ac->search_reqs[i];
+                       ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+                       if (ret != LDB_SUCCESS) {
+                               handle->status = ret;
+                               goto done;
+                       }
+                       if (req->handle->status != LDB_SUCCESS) {
+                               handle->status = req->handle->status;
+                               goto done;
+                       }
+                       if (req->handle->state != LDB_ASYNC_DONE) {
+                               return LDB_SUCCESS;
+                       }
+               }
+       } else {
+
+               req = map_get_req(ac);
+
+               ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (req->handle->status != LDB_SUCCESS) {
+                       handle->status = req->handle->status;
+                       goto done;
+               }
+               if (req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               next = map_get_next(ac);
+               if (next) {
+                       return next(handle);
+               }
+       }
+
+       ret = LDB_SUCCESS;
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+/* Wait for all current pending requests to finish. */
+static int map_wait_all(struct ldb_handle *handle)
+{
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = map_wait_next(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+/* Wait for pending requests to finish. */
+static int map_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return map_wait_all(handle);
+       } else {
+               return map_wait_next(handle);
+       }
+}
+
+
+/* Module initialization
+ * ===================== */
+
+/* Provided module operations */
+static const struct ldb_module_ops map_ops = {
+       .name           = "ldb_map",
+       .add            = map_add,
+       .modify         = map_modify,
+       .del            = map_delete,
+       .rename         = map_rename,
+       .search         = map_search,
+       .wait           = map_wait,
+};
+
+/* Builtin mappings for DNs and objectClasses */
+static const struct ldb_map_attribute builtin_attribute_maps[] = {
+       {
+               .local_name = "dn",
+               .type = MAP_CONVERT,
+               .u = {
+                       .convert = {
+                                .remote_name = "dn",
+                                .convert_local = ldb_dn_convert_local,
+                                .convert_remote = ldb_dn_convert_remote,
+                        },
+               },
+       },
+       {
+               .local_name = "objectClass",
+               .type = MAP_GENERATE,
+               .convert_operator = map_objectclass_convert_operator,
+               .u = {
+                       .generate = {
+                                .remote_names = { "objectClass", NULL },
+                                .generate_local = map_objectclass_generate_local,
+                                .generate_remote = map_objectclass_generate_remote,
+                        },
+               },
+       },
+       {
+               .local_name = NULL,
+       }
+};
+
+/* Find the special 'MAP_DN_NAME' record and store local and remote
+ * base DNs in private data. */
+static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
+{
+       static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
+       struct ldb_dn *dn;
+       struct ldb_message *msg;
+       struct ldb_result *res;
+       int ret;
+
+       if (!name) {
+               data->local_base_dn = NULL;
+               data->remote_base_dn = NULL;
+               return LDB_SUCCESS;
+       }
+
+       dn = ldb_dn_string_compose(data, NULL, "%s=%s", MAP_DN_NAME, name);
+       if (dn == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                         "Failed to construct '%s' DN!\n", MAP_DN_NAME);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = ldb_search(module->ldb, dn, LDB_SCOPE_BASE, NULL, attrs, &res);
+       talloc_free(dn);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       if (res->count == 0) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                         "No results for '%s=%s'!\n", MAP_DN_NAME, name);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+       if (res->count > 1) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                         "Too many results for '%s=%s'!\n", MAP_DN_NAME, name);
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
+       msg = res->msgs[0];
+       data->local_base_dn = ldb_msg_find_attr_as_dn(data, msg, MAP_DN_FROM);
+       data->remote_base_dn = ldb_msg_find_attr_as_dn(data, msg, MAP_DN_TO);
+       talloc_free(res);
+
+       return LDB_SUCCESS;
+}
+
+/* Store attribute maps and objectClass maps in private data. */
+static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data, 
+                        const struct ldb_map_attribute *attrs, 
+                        const struct ldb_map_objectclass *ocls, 
+                        const char * const *wildcard_attributes)
+{
+       int i, j, last;
+       last = 0;
+
+       /* Count specified attribute maps */
+       for (i = 0; attrs[i].local_name; i++) /* noop */ ;
+       /* Count built-in attribute maps */
+       for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
+
+       /* Store list of attribute maps */
+       data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+1);
+       if (data->attribute_maps == NULL) {
+               map_oom(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Specified ones go first */
+       for (i = 0; attrs[i].local_name; i++) {
+               data->attribute_maps[last] = attrs[i];
+               last++;
+       }
+
+       /* Built-in ones go last */
+       for (i = 0; builtin_attribute_maps[i].local_name; i++) {
+               data->attribute_maps[last] = builtin_attribute_maps[i];
+               last++;
+       }
+
+       /* Ensure 'local_name == NULL' for the last entry */
+       memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
+
+       /* Store list of objectClass maps */
+       data->objectclass_maps = ocls;
+
+       data->wildcard_attributes = wildcard_attributes;
+
+       return LDB_SUCCESS;
+}
+
+/* Copy the list of provided module operations. */
+struct ldb_module_ops ldb_map_get_ops(void)
+{
+       return map_ops;
+}
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs, 
+                const struct ldb_map_objectclass *ocls,
+                const char * const *wildcard_attributes,
+                const char *name)
+{
+       struct map_private *data;
+       int ret;
+
+       /* Prepare private data */
+       data = talloc_zero(module, struct map_private);
+       if (data == NULL) {
+               map_oom(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       module->private_data = data;
+
+       data->context = talloc_zero(data, struct ldb_map_context);
+       if (!data->context) {
+               map_oom(module);
+               return LDB_ERR_OPERATIONS_ERROR;                
+       }
+
+       /* Store local and remote baseDNs */
+       ret = map_init_dns(module, data->context, name);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(data);
+               return ret;
+       }
+
+       /* Store list of attribute and objectClass maps */
+       ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(data);
+               return ret;
+       }
+
+       return LDB_SUCCESS;
+}
+
+/* Usage note for initialization of this module:
+ *
+ * ldb_map is meant to be used from a different module that sets up
+ * the mappings and gets registered in ldb.
+ *
+ * 'ldb_map_init' initializes the private data of this module and
+ * stores the attribute and objectClass maps in there. It also looks
+ * up the '@MAP' special DN so requests can be redirected to the
+ * remote partition.
+ *
+ * This function should be called from the 'init_context' op of the
+ * module using ldb_map.
+ *
+ * 'ldb_map_get_ops' returns a copy of ldb_maps module operations.
+ *
+ * It should be called from the initialize function of the using
+ * module, which should then override the 'init_context' op with a
+ * function making the appropriate calls to 'ldb_map_init'.
+ */
diff --git a/source3/lib/ldb/modules/ldb_map.h b/source3/lib/ldb/modules/ldb_map.h
new file mode 100644 (file)
index 0000000..c5c455b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+   ldb database mapping module
+
+   Copyright (C) Jelmer Vernooij 2005
+   Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+   * NOTICE: this module is NOT released under the GNU LGPL license as
+   * other ldb code. This module is release under the GNU GPL v2 or
+   * later license.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __LDB_MAP_H__
+#define __LDB_MAP_H__
+
+/* ldb_map is a skeleton LDB module that can be used for any other modules
+ * that need to map attributes.
+ *
+ * The term 'remote' in this header refers to the connection where the 
+ * original schema is used on while 'local' means the local connection 
+ * that any upper layers will use.
+ *
+ * All local attributes will have to have a definition. Not all remote 
+ * attributes need a definition as LDB is a lot less strict than LDAP 
+ * (in other words, sending unknown attributes to an LDAP server hurts us, 
+ * while returning too many attributes in ldb_search() doesn't)
+ */
+
+
+/* Name of the internal attribute pointing from the local to the
+ * remote part of a record */
+#define IS_MAPPED "isMapped"
+
+
+struct ldb_map_context;
+
+/* convert a local ldb_val to a remote ldb_val */
+typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
+
+#define LDB_MAP_MAX_REMOTE_NAMES 10
+
+/* map from local to remote attribute */
+struct ldb_map_attribute {
+       const char *local_name; /* local name */
+
+       enum ldb_map_attr_type { 
+               MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely.  */
+               MAP_KEEP,   /* Keep as is. Same name locally and remotely. */
+               MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
+               MAP_CONVERT, /* Rename + convert data */
+               MAP_GENERATE /* Use generate function for generating new name/data. 
+                                               Used for generating attributes based on 
+                                               multiple remote attributes. */
+       } type;
+       
+       /* if set, will be called for search expressions that contain this attribute */
+       int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *);
+
+       union { 
+               struct {
+                       const char *remote_name;
+               } rename;
+               
+               struct {
+                       const char *remote_name;
+
+                       /* Convert local to remote data */
+                       ldb_map_convert_func convert_local;
+
+                       /* Convert remote to local data */
+                       /* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name 
+                        * that is non-NULL before it. */
+                       ldb_map_convert_func convert_remote;
+               } convert;
+       
+               struct {
+                       /* Generate the local attribute from remote message */
+                       struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
+
+                       /* Update remote message with information from local message */
+                       void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
+
+                       /* Name(s) for this attribute on the remote server. This is an array since 
+                        * one local attribute's data can be split up into several attributes 
+                        * remotely */
+                       const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
+
+                       /* Names of additional remote attributes
+                        * required for the generation.  NULL
+                        * indicates that `local_attr' suffices. */
+                       /*
+#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
+                       const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
+                       */
+               } generate;
+       } u;
+};
+
+
+#define LDB_MAP_MAX_SUBCLASSES 10
+#define LDB_MAP_MAX_MUSTS              10
+#define LDB_MAP_MAX_MAYS               50
+
+/* map from local to remote objectClass */
+struct ldb_map_objectclass {
+       const char *local_name;
+       const char *remote_name;
+       const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
+       const char *musts[LDB_MAP_MAX_MUSTS];
+       const char *mays[LDB_MAP_MAX_MAYS];
+};
+
+
+/* private context data */
+struct ldb_map_context {
+       struct ldb_map_attribute *attribute_maps;
+       /* NOTE: Always declare base classes first here */
+       const struct ldb_map_objectclass *objectclass_maps;
+
+       /* Remote (often operational) attributes that should be added
+        * to any wildcard search */
+       const char * const *wildcard_attributes;
+
+       /* struct ldb_context *mapped_ldb; */
+       const struct ldb_dn *local_base_dn;
+       const struct ldb_dn *remote_base_dn;
+};
+
+/* Global private data */
+struct map_private {
+       void *caller_private;
+       struct ldb_map_context *context;
+};
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs, 
+                const struct ldb_map_objectclass *ocls,
+                const char * const *wildcard_attributes,
+                const char *name);
+
+/* get copy of map_ops */
+struct ldb_module_ops
+ldb_map_get_ops(void);
+
+#endif /* __LDB_MAP_H__ */
diff --git a/source3/lib/ldb/modules/ldb_map_inbound.c b/source3/lib/ldb/modules/ldb_map_inbound.c
new file mode 100644 (file)
index 0000000..404b2ce
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+   ldb database mapping module
+
+   Copyright (C) Jelmer Vernooij 2005
+   Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+   * NOTICE: this module is NOT released under the GNU LGPL license as
+   * other ldb code. This module is release under the GNU GPL v2 or
+   * later license.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+
+/* Mapping message elements
+ * ======================== */
+
+/* Map a message element into the remote partition. */
+static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+       struct ldb_message_element *el;
+       int i;
+
+       el = talloc_zero(mem_ctx, struct ldb_message_element);
+       if (el == NULL) {
+               map_oom(module);
+               return NULL;
+       }
+
+       el->num_values = old->num_values;
+       el->values = talloc_array(el, struct ldb_val, el->num_values);
+       if (el->values == NULL) {
+               talloc_free(el);
+               map_oom(module);
+               return NULL;
+       }
+
+       el->name = map_attr_map_local(el, map, old->name);
+
+       for (i = 0; i < el->num_values; i++) {
+               el->values[i] = ldb_val_map_local(module, el->values, map, old->values[i]);
+       }
+
+       return el;
+}
+
+/* Add a message element either to a local or to a remote message,
+ * depending on whether it goes into the local or remote partition. */
+static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
+       struct ldb_message_element *el=NULL;
+
+       /* Unknown attribute: ignore */
+       if (map == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                         "Not mapping attribute '%s': no mapping found\n",
+                         old->name);
+               goto local;
+       }
+
+       switch (map->type) {
+       case MAP_IGNORE:
+               goto local;
+
+       case MAP_CONVERT:
+               if (map->u.convert.convert_local == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                                 "Not mapping attribute '%s': "
+                                 "'convert_local' not set\n",
+                                 map->local_name);
+                       goto local;
+               }
+               /* fall through */
+       case MAP_KEEP:
+       case MAP_RENAME:
+               el = ldb_msg_el_map_local(module, remote, map, old);
+               break;
+
+       case MAP_GENERATE:
+               if (map->u.generate.generate_remote == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                                 "Not mapping attribute '%s': "
+                                 "'generate_remote' not set\n",
+                                 map->local_name);
+                       goto local;
+               }
+
+               /* TODO: if this attr requires context:
+                *       make sure all context attrs are mappable (in 'names')
+                *       make sure all context attrs have already been mapped?
+                *       maybe postpone generation until they have been mapped?
+                */
+
+               map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
+               return 0;
+       }
+
+       if (el == NULL) {
+               return -1;
+       }
+
+       return ldb_msg_add(remote, el, old->flags);
+
+local:
+       el = talloc(local, struct ldb_message_element);
+       if (el == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       *el = *old;                     /* copy the old element */
+
+       return ldb_msg_add(local, el, old->flags);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Check whether a message will be (partially) mapped into the remote partition. */
+static BOOL ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       BOOL ret;
+       int i;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               ret = map_attr_check_remote(data, msg->elements[i].name);
+               if (ret) {
+                       return ret;
+               }
+       }
+
+       return False;
+}
+
+/* Split message elements that stay in the local partition from those
+ * that are mapped into the remote partition. */
+static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
+{
+       /* const char * const names[]; */
+       int i, ret;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               /* Skip 'IS_MAPPED' */
+               if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                                 "Skipping attribute '%s'\n",
+                                 msg->elements[i].name);
+                       continue;
+               }
+
+               ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
+               if (ret) {
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+
+/* Inbound requests: add, modify, rename, delete
+ * ============================================= */
+
+/* Add the remote record. */
+int map_add_do_remote(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+       ac->step = MAP_ADD_REMOTE;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Add the local record. */
+int map_add_do_local(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+       ac->step = MAP_ADD_LOCAL;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Add a record. */
+int map_add(struct ldb_module *module, struct ldb_request *req)
+{
+       const struct ldb_message *msg = req->op.add.message;
+       struct ldb_handle *h;
+       struct map_context *ac;
+       struct ldb_message *local, *remote;
+       const char *dn;
+
+       /* Do not manipulate our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+       if (!ldb_dn_check_local(module, msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping needed, fail */
+       if (!ldb_msg_check_remote(module, msg)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Prepare context and handle */
+       h = map_init_handle(req, module);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct map_context);
+
+       /* Prepare the local operation */
+       ac->local_req = talloc(ac, struct ldb_request);
+       if (ac->local_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->local_req) = *req;        /* copy the request */
+
+       ac->local_req->context = NULL;
+       ac->local_req->callback = NULL;
+
+       /* Prepare the remote operation */
+       ac->remote_req = talloc(ac, struct ldb_request);
+       if (ac->remote_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->remote_req) = *req;       /* copy the request */
+
+       ac->remote_req->context = NULL;
+       ac->remote_req->callback = NULL;
+
+       /* Prepare the local message */
+       local = ldb_msg_new(ac->local_req);
+       if (local == NULL) {
+               goto oom;
+       }
+       local->dn = msg->dn;
+
+       /* Prepare the remote message */
+       remote = ldb_msg_new(ac->remote_req);
+       if (remote == NULL) {
+               goto oom;
+       }
+       remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+       /* Split local from remote message */
+       ldb_msg_partition(module, local, remote, msg);
+       ac->local_req->op.add.message = local;
+       ac->remote_req->op.add.message = remote;
+
+       if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+               /* No local data or db, just run the remote request */
+               talloc_free(ac->local_req);
+               req->handle = h;        /* return our own handle to deal with this call */
+               return map_add_do_remote(h);
+       }
+
+       /* Store remote DN in 'IS_MAPPED' */
+       /* TODO: use GUIDs here instead */
+       dn = ldb_dn_linearize(local, remote->dn);
+       if (ldb_msg_add_string(local, IS_MAPPED, dn) != 0) {
+               goto failed;
+       }
+
+       req->handle = h;                /* return our own handle to deal with this call */
+       return map_add_do_local(h);
+
+oom:
+       map_oom(module);
+failed:
+       talloc_free(h);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Modify the remote record. */
+int map_modify_do_remote(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+       ac->step = MAP_MODIFY_REMOTE;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Modify the local record. */
+int map_modify_do_local(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+       struct ldb_message *msg;
+       char *dn;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       if (ac->local_dn == NULL) {
+               /* No local record present, add it instead */
+               msg = discard_const_p(struct ldb_message, ac->local_req->op.mod.message);
+
+               /* Add local 'IS_MAPPED' */
+               /* TODO: use GUIDs here instead */
+               dn = ldb_dn_linearize(msg, ac->remote_req->op.mod.message->dn);
+               if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD) != 0) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               /* Turn request into 'add' */
+               ac->local_req->operation = LDB_ADD;
+               ac->local_req->op.add.message = msg;
+               /* TODO: Could I just leave msg in there?  I think so,
+                *       but it looks clearer this way. */
+       }
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+       ac->step = MAP_MODIFY_LOCAL;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Modify a record. */
+int map_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       const struct ldb_message *msg = req->op.mod.message;
+       struct ldb_handle *h;
+       struct map_context *ac;
+       struct ldb_message *local, *remote;
+
+       /* Do not manipulate our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+       if (!ldb_dn_check_local(module, msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping needed, skip to next module */
+       /* TODO: What if the remote part exists, the local doesn't,
+        *       and this request wants to modify local data and thus
+        *       add the local record? */
+       if (!ldb_msg_check_remote(module, msg)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Prepare context and handle */
+       h = map_init_handle(req, module);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct map_context);
+
+       /* Prepare the local operation */
+       ac->local_req = talloc(ac, struct ldb_request);
+       if (ac->local_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->local_req) = *req;        /* copy the request */
+
+       ac->local_req->context = NULL;
+       ac->local_req->callback = NULL;
+
+       /* Prepare the remote operation */
+       ac->remote_req = talloc(ac, struct ldb_request);
+       if (ac->remote_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->remote_req) = *req;       /* copy the request */
+
+       ac->remote_req->context = NULL;
+       ac->remote_req->callback = NULL;
+
+       /* Prepare the local message */
+       local = ldb_msg_new(ac->local_req);
+       if (local == NULL) {
+               goto oom;
+       }
+       local->dn = msg->dn;
+
+       /* Prepare the remote message */
+       remote = ldb_msg_new(ac->remote_req);
+       if (remote == NULL) {
+               goto oom;
+       }
+       remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+       /* Split local from remote message */
+       ldb_msg_partition(module, local, remote, msg);
+       ac->local_req->op.mod.message = local;
+       ac->remote_req->op.mod.message = remote;
+
+       if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+               /* No local data or db, just run the remote request */
+               talloc_free(ac->local_req);
+               req->handle = h;        /* return our own handle to deal with this call */
+               return map_modify_do_remote(h);
+       }
+
+       /* prepare the search operation */
+       ac->search_req = map_search_self_req(ac, msg->dn);
+       if (ac->search_req == NULL) {
+               goto failed;
+       }
+
+       ac->step = MAP_SEARCH_SELF_MODIFY;
+
+       req->handle = h;                /* return our own handle to deal with this call */
+       return ldb_next_request(module, ac->search_req);
+
+oom:
+       map_oom(module);
+failed:
+       talloc_free(h);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Delete the remote record. */
+int map_delete_do_remote(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+       ac->step = MAP_DELETE_REMOTE;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Delete the local record. */
+int map_delete_do_local(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       /* No local record, continue remotely */
+       if (ac->local_dn == NULL) {
+               return map_delete_do_remote(handle);
+       }
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+       ac->step = MAP_DELETE_LOCAL;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Delete a record. */
+int map_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_handle *h;
+       struct map_context *ac;
+
+       /* Do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.del.dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+       if (!ldb_dn_check_local(module, req->op.del.dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* Prepare context and handle */
+       h = map_init_handle(req, module);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct map_context);
+
+       /* Prepare the local operation */
+       ac->local_req = talloc(ac, struct ldb_request);
+       if (ac->local_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->local_req) = *req;        /* copy the request */
+       ac->local_req->op.del.dn = req->op.del.dn;
+
+       ac->local_req->context = NULL;
+       ac->local_req->callback = NULL;
+
+       /* Prepare the remote operation */
+       ac->remote_req = talloc(ac, struct ldb_request);
+       if (ac->remote_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->remote_req) = *req;       /* copy the request */
+       ac->remote_req->op.del.dn = ldb_dn_map_local(module, ac->remote_req, req->op.del.dn);
+
+       /* No local db, just run the remote request */
+       if (!map_check_local_db(ac->module)) {
+               req->handle = h;        /* return our own handle to deal with this call */
+               return map_delete_do_remote(h);
+       }
+
+       ac->remote_req->context = NULL;
+       ac->remote_req->callback = NULL;
+
+       /* Prepare the search operation */
+       ac->search_req = map_search_self_req(ac, req->op.del.dn);
+       if (ac->search_req == NULL) {
+               goto failed;
+       }
+
+       req->handle = h;                /* return our own handle to deal with this call */
+
+       ac->step = MAP_SEARCH_SELF_DELETE;
+
+       return ldb_next_request(module, ac->search_req);
+
+oom:
+       map_oom(module);
+failed:
+       talloc_free(h);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Rename the remote record. */
+int map_rename_do_remote(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+       ac->step = MAP_RENAME_REMOTE;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Update the local 'IS_MAPPED' attribute. */
+int map_rename_do_fixup(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
+
+       ac->step = MAP_RENAME_FIXUP;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_request(ac->module, ac->down_req);
+}
+
+/* Rename the local record. */
+int map_rename_do_local(struct ldb_handle *handle)
+{
+       struct map_context *ac;
+
+       ac = talloc_get_type(handle->private_data, struct map_context);
+
+       /* No local record, continue remotely */
+       if (ac->local_dn == NULL) {
+               return map_rename_do_remote(handle);
+       }
+
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+       ac->step = MAP_RENAME_LOCAL;
+
+       handle->state = LDB_ASYNC_INIT;
+       handle->status = LDB_SUCCESS;
+
+       return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Rename a record. */
+int map_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_handle *h;
+       struct map_context *ac;
+
+       /* Do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.rename.olddn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+       if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
+           (!ldb_dn_check_local(module, req->op.rename.newdn))) {
+               return ldb_next_request(module, req);
+       }
+
+       /* Rename into/out of the mapped partition requested, bail out */
+       if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
+           !ldb_dn_check_local(module, req->op.rename.newdn)) {
+               return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+       }
+
+       /* Prepare context and handle */
+       h = map_init_handle(req, module);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct map_context);
+
+       /* Prepare the local operation */
+       ac->local_req = talloc(ac, struct ldb_request);
+       if (ac->local_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->local_req) = *req;        /* copy the request */
+       ac->local_req->op.rename.olddn = req->op.rename.olddn;
+       ac->local_req->op.rename.newdn = req->op.rename.newdn;
+
+       ac->local_req->context = NULL;
+       ac->local_req->callback = NULL;
+
+       /* Prepare the remote operation */
+       ac->remote_req = talloc(ac, struct ldb_request);
+       if (ac->remote_req == NULL) {
+               goto oom;
+       }
+
+       *(ac->remote_req) = *req;       /* copy the request */
+       ac->remote_req->op.rename.olddn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.olddn);
+       ac->remote_req->op.rename.newdn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.newdn);
+
+       ac->remote_req->context = NULL;
+       ac->remote_req->callback = NULL;
+
+       /* No local db, just run the remote request */
+       if (!map_check_local_db(ac->module)) {
+               req->handle = h;        /* return our own handle to deal with this call */
+               return map_rename_do_remote(h);
+       }
+
+       /* Prepare the fixup operation */
+       /* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
+       ac->down_req = map_build_fixup_req(ac, req->op.rename.newdn, ac->remote_req->op.rename.newdn);
+       if (ac->down_req == NULL) {
+               goto failed;
+       }
+
+       /* Prepare the search operation */
+       ac->search_req = map_search_self_req(ac, req->op.rename.olddn);
+       if (ac->search_req == NULL) {
+               goto failed;
+       }
+
+       req->handle = h;                /* return our own handle to deal with this call */
+
+       ac->step = MAP_SEARCH_SELF_RENAME;
+
+       return ldb_next_request(module, ac->search_req);
+
+oom:
+       map_oom(module);
+failed:
+       talloc_free(h);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/source3/lib/ldb/modules/ldb_map_outbound.c b/source3/lib/ldb/modules/ldb_map_outbound.c
new file mode 100644 (file)
index 0000000..e1b207f
--- /dev/null
@@ -0,0 +1,1155 @@
+/*
+   ldb database mapping module
+
+   Copyright (C) Jelmer Vernooij 2005
+   Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+   * NOTICE: this module is NOT released under the GNU LGPL license as
+   * other ldb code. This module is release under the GNU GPL v2 or
+   * later license.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Select attributes that stay in the local partition. */
+static const char **map_attrs_select_local(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const char **result;
+       int i, last;
+
+       if (attrs == NULL)
+               return NULL;
+
+       last = 0;
+       result = talloc_array(mem_ctx, const char *, 1);
+       if (result == NULL) {
+               goto failed;
+       }
+       result[0] = NULL;
+
+       for (i = 0; attrs[i]; i++) {
+               /* Wildcards and ignored attributes are kept locally */
+               if ((ldb_attr_cmp(attrs[i], "*") == 0) ||
+                   (!map_attr_check_remote(data, attrs[i]))) {
+                       result = talloc_realloc(mem_ctx, result, const char *, last+2);
+                       if (result == NULL) {
+                               goto failed;
+                       }
+
+                       result[last] = talloc_strdup(result, attrs[i]);
+                       result[last+1] = NULL;
+                       last++;
+               }
+       }
+
+       return result;
+
+failed:
+       talloc_free(result);
+       map_oom(module);
+       return NULL;
+}
+
+/* Collect attributes that are mapped into the remote partition. */
+static const char **map_attrs_collect_remote(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const char **result;
+       const struct ldb_map_attribute *map;
+       const char *name=NULL;
+       int i, j, last;
+       int ret;
+
+       last = 0;
+       result = talloc_array(mem_ctx, const char *, 1);
+       if (result == NULL) {
+               goto failed;
+       }
+       result[0] = NULL;
+
+       for (i = 0; attrs[i]; i++) {
+               /* Wildcards are kept remotely, too */
+               if (ldb_attr_cmp(attrs[i], "*") == 0) {
+                       const char **new_attrs = NULL;
+                       ret = map_attrs_merge(module, mem_ctx, &new_attrs, attrs);
+                       if (ret != LDB_SUCCESS) {
+                               goto failed;
+                       }
+                       ret = map_attrs_merge(module, mem_ctx, &new_attrs, data->wildcard_attributes);
+                       if (ret != LDB_SUCCESS) {
+                               goto failed;
+                       }
+
+                       attrs = new_attrs;
+                       break;
+               }
+       }
+
+       for (i = 0; attrs[i]; i++) {
+               /* Wildcards are kept remotely, too */
+               if (ldb_attr_cmp(attrs[i], "*") == 0) {
+                       /* Add all 'include in wildcard' attributes */
+                       name = attrs[i];
+                       goto named;
+               }
+
+               /* Add remote names of mapped attrs */
+               map = map_attr_find_local(data, attrs[i]);
+               if (map == NULL) {
+                       continue;
+               }
+
+               switch (map->type) {
+               case MAP_IGNORE:
+                       continue;
+
+               case MAP_KEEP:
+                       name = attrs[i];
+                       goto named;
+
+               case MAP_RENAME:
+               case MAP_CONVERT:
+                       name = map->u.rename.remote_name;
+                       goto named;
+
+               case MAP_GENERATE:
+                       /* Add all remote names of "generate" attrs */
+                       for (j = 0; map->u.generate.remote_names[j]; j++) {
+                               result = talloc_realloc(mem_ctx, result, const char *, last+2);
+                               if (result == NULL) {
+                                       goto failed;
+                               }
+
+                               result[last] = talloc_strdup(result, map->u.generate.remote_names[j]);
+                               result[last+1] = NULL;
+                               last++;
+                       }
+                       continue;
+               }
+
+       named:  /* We found a single remote name, add that */
+               result = talloc_realloc(mem_ctx, result, const char *, last+2);
+               if (result == NULL) {
+                       goto failed;
+               }
+
+               result[last] = talloc_strdup(result, name);
+               result[last+1] = NULL;
+               last++;
+       }
+
+       return result;
+
+failed:
+       talloc_free(result);
+       map_oom(module);
+       return NULL;
+}
+
+/* Split attributes that stay in the local partition from those that
+ * are mapped into the remote partition. */
+static int map_attrs_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs)
+{
+       *local_attrs = map_attrs_select_local(module, local_ctx, attrs);
+       *remote_attrs = map_attrs_collect_remote(module, remote_ctx, attrs);
+
+       return 0;
+}
+
+/* Mapping message elements
+ * ======================== */
+
+/* Add an element to a message, overwriting any old identically named elements. */
+static int ldb_msg_replace(struct ldb_message *msg, const struct ldb_message_element *el)
+{
+       struct ldb_message_element *old;
+
+       old = ldb_msg_find_element(msg, el->name);
+
+       /* no local result, add as new element */
+       if (old == NULL) {
+               if (ldb_msg_add_empty(msg, el->name, 0) != 0) {
+                       return -1;
+               }
+
+               old = ldb_msg_find_element(msg, el->name);
+               if (old == NULL) {
+                       return -1;
+               }
+       }
+
+       *old = *el;                     /* copy new element */
+
+       return 0;
+}
+
+/* Map a message element back into the local partition. */
+static struct ldb_message_element *ldb_msg_el_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+       struct ldb_message_element *el;
+       int i;
+
+       el = talloc_zero(mem_ctx, struct ldb_message_element);
+       if (el == NULL) {
+               map_oom(module);
+               return NULL;
+       }
+
+       el->num_values = old->num_values;
+       el->values = talloc_array(el, struct ldb_val, el->num_values);
+       if (el->values == NULL) {
+               talloc_free(el);
+               map_oom(module);
+               return NULL;
+       }
+
+       el->name = map_attr_map_remote(el, map, old->name);
+
+       for (i = 0; i < el->num_values; i++) {
+               el->values[i] = ldb_val_map_remote(module, el->values, map, old->values[i]);
+       }
+
+       return el;
+}
+
+/* Merge a remote message element into a local message. */
+static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const char *attr_name, const struct ldb_message_element *old)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const struct ldb_map_attribute *map = map_attr_find_remote(data, attr_name);
+       struct ldb_message_element *el=NULL;
+
+       /* Unknown attribute in remote message:
+        * skip, attribute was probably auto-generated */
+       if (map == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                         "Skipping attribute '%s': no mapping found\n",
+                         old->name);
+               return 0;
+       }
+
+       switch (map->type) {
+       case MAP_IGNORE:
+               return -1;
+
+       case MAP_CONVERT:
+               if (map->u.convert.convert_remote == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                 "Skipping attribute '%s': "
+                                 "'convert_remote' not set\n",
+                                 old->name);
+                       return 0;
+               }
+               /* fall through */
+       case MAP_KEEP:
+       case MAP_RENAME:
+               el = ldb_msg_el_map_remote(module, local, map, old);
+               break;
+
+       case MAP_GENERATE:
+               if (map->u.generate.generate_local == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+                                 "Skipping attribute '%s': "
+                                 "'generate_local' not set\n",
+                                 old->name);
+                       return 0;
+               }
+
+               el = map->u.generate.generate_local(module, local, old->name, remote);
+               break;
+       }
+
+       if (el == NULL) {
+               return -1;
+       }
+
+       return ldb_msg_replace(local, el);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Merge two local messages into a single one. */
+static int ldb_msg_merge_local(struct ldb_module *module, struct ldb_message *msg1, struct ldb_message *msg2)
+{
+       int i, ret;
+
+       for (i = 0; i < msg2->num_elements; i++) {
+               ret = ldb_msg_replace(msg1, &msg2->elements[i]);
+               if (ret) {
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/* Merge a local and a remote message into a single local one. */
+static int ldb_msg_merge_remote(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote)
+{
+       int i, ret;
+
+       /* Try to map each attribute back;
+        * Add to local message is possible,
+        * Overwrite old local attribute if necessary */
+       for (i = 0; i < remote->num_elements; i++) {
+               ret = ldb_msg_el_merge(module, local, remote, remote->elements[i].name, &remote->elements[i]);
+               if (ret) {
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/* Mapping search results
+ * ====================== */
+
+/* Map a search result back into the local partition. */
+static int map_reply_remote(struct ldb_module *module, struct ldb_reply *ares)
+{
+       struct ldb_message *msg;
+       struct ldb_dn *dn;
+       int ret;
+
+       /* There is no result message, skip */
+       if (ares->type != LDB_REPLY_ENTRY) {
+               return 0;
+       }
+
+       /* Create a new result message */
+       msg = ldb_msg_new(ares);
+       if (msg == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       /* Merge remote message into new message */
+       ret = ldb_msg_merge_remote(module, msg, ares->message);
+       if (ret) {
+               talloc_free(msg);
+               return ret;
+       }
+
+       /* Create corresponding local DN */
+       dn = ldb_dn_map_rebase_remote(module, msg, ares->message->dn);
+       if (dn == NULL) {
+               talloc_free(msg);
+               return -1;
+       }
+       msg->dn = dn;
+
+       /* Store new message with new DN as the result */
+       talloc_free(ares->message);
+       ares->message = msg;
+
+       return 0;
+}
+
+/* Mapping parse trees
+ * =================== */
+
+/* Check whether a parse tree can safely be split in two. */
+static BOOL ldb_parse_tree_check_splittable(const struct ldb_parse_tree *tree)
+{
+       const struct ldb_parse_tree *subtree = tree;
+       BOOL negate = False;
+
+       while (subtree) {
+               switch (subtree->operation) {
+               case LDB_OP_NOT:
+                       negate = !negate;
+                       subtree = subtree->u.isnot.child;
+                       continue;
+
+               case LDB_OP_AND:
+                       return !negate; /* if negate: False */
+
+               case LDB_OP_OR:
+                       return negate;  /* if negate: True */
+
+               default:
+                       return True;    /* simple parse tree */
+               }
+       }
+
+       return True;                    /* no parse tree */
+}
+
+/* Collect a list of attributes required to match a given parse tree. */
+static int ldb_parse_tree_collect_attrs(struct ldb_module *module, void *mem_ctx, const char ***attrs, const struct ldb_parse_tree *tree)
+{
+       const char **new_attrs;
+       int i, ret;
+
+       if (tree == NULL) {
+               return 0;
+       }
+
+       switch (tree->operation) {
+       case LDB_OP_OR:
+       case LDB_OP_AND:                /* attributes stored in list of subtrees */
+               for (i = 0; i < tree->u.list.num_elements; i++) {
+                       ret = ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.list.elements[i]);
+                       if (ret) {
+                               return ret;
+                       }
+               }
+               return 0;
+
+       case LDB_OP_NOT:                /* attributes stored in single subtree */
+               return ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.isnot.child);
+
+       default:                        /* single attribute in tree */
+               new_attrs = ldb_attr_list_copy_add(mem_ctx, *attrs, tree->u.equality.attr);
+               talloc_free(*attrs);
+               *attrs = new_attrs;
+               return 0;
+       }
+
+       return -1;
+}
+
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Select a negated subtree that queries attributes in the local partition */
+static int map_subtree_select_local_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       struct ldb_parse_tree *child;
+       int ret;
+
+       /* Prepare new tree */
+       *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       /* Generate new subtree */
+       ret = map_subtree_select_local(module, *new, &child, tree->u.isnot.child);
+       if (ret) {
+               talloc_free(*new);
+               return ret;
+       }
+
+       /* Prune tree without subtree */
+       if (child == NULL) {
+               talloc_free(*new);
+               *new = NULL;
+               return 0;
+       }
+
+       (*new)->u.isnot.child = child;
+
+       return ret;
+}
+
+/* Select a list of subtrees that query attributes in the local partition */
+static int map_subtree_select_local_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       int i, j, ret=0;
+
+       /* Prepare new tree */
+       *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       /* Prepare list of subtrees */
+       (*new)->u.list.num_elements = 0;
+       (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+       if ((*new)->u.list.elements == NULL) {
+               map_oom(module);
+               talloc_free(*new);
+               return -1;
+       }
+
+       /* Generate new list of subtrees */
+       j = 0;
+       for (i = 0; i < tree->u.list.num_elements; i++) {
+               struct ldb_parse_tree *child;
+               ret = map_subtree_select_local(module, *new, &child, tree->u.list.elements[i]);
+               if (ret) {
+                       talloc_free(*new);
+                       return ret;
+               }
+
+               if (child) {
+                       (*new)->u.list.elements[j] = child;
+                       j++;
+               }
+       }
+
+       /* Prune tree without subtrees */
+       if (j == 0) {
+               talloc_free(*new);
+               *new = NULL;
+               return 0;
+       }
+
+       /* Fix subtree list size */
+       (*new)->u.list.num_elements = j;
+       (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+       return ret;
+}
+
+/* Select a simple subtree that queries attributes in the local partition */
+static int map_subtree_select_local_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       /* Prepare new tree */
+       *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Select subtrees that query attributes in the local partition */
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+
+       if (tree == NULL) {
+               return 0;
+       }
+
+       if (tree->operation == LDB_OP_NOT) {
+               return map_subtree_select_local_not(module, mem_ctx, new, tree);
+       }
+
+       if (tree->operation == LDB_OP_AND || tree->operation == LDB_OP_OR) {
+               return map_subtree_select_local_list(module, mem_ctx, new, tree);
+       }
+
+       if (map_attr_check_remote(data, tree->u.equality.attr)) {
+               *new = NULL;
+               return 0;
+       }
+
+       return map_subtree_select_local_simple(module, mem_ctx, new, tree);
+}
+
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Collect a negated subtree that queries attributes in the remote partition */
+static int map_subtree_collect_remote_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       struct ldb_parse_tree *child;
+       int ret;
+
+       /* Prepare new tree */
+       *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       /* Generate new subtree */
+       ret = map_subtree_collect_remote(module, *new, &child, tree->u.isnot.child);
+       if (ret) {
+               talloc_free(*new);
+               return ret;
+       }
+
+       /* Prune tree without subtree */
+       if (child == NULL) {
+               talloc_free(*new);
+               *new = NULL;
+               return 0;
+       }
+
+       (*new)->u.isnot.child = child;
+
+       return ret;
+}
+
+/* Collect a list of subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       int i, j, ret=0;
+
+       /* Prepare new tree */
+       *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+
+       /* Prepare list of subtrees */
+       (*new)->u.list.num_elements = 0;
+       (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+       if ((*new)->u.list.elements == NULL) {
+               map_oom(module);
+               talloc_free(*new);
+               return -1;
+       }
+
+       /* Generate new list of subtrees */
+       j = 0;
+       for (i = 0; i < tree->u.list.num_elements; i++) {
+               struct ldb_parse_tree *child;
+               ret = map_subtree_collect_remote(module, *new, &child, tree->u.list.elements[i]);
+               if (ret) {
+                       talloc_free(*new);
+                       return ret;
+               }
+
+               if (child) {
+                       (*new)->u.list.elements[j] = child;
+                       j++;
+               }
+       }
+
+       /* Prune tree without subtrees */
+       if (j == 0) {
+               talloc_free(*new);
+               *new = NULL;
+               return 0;
+       }
+
+       /* Fix subtree list size */
+       (*new)->u.list.num_elements = j;
+       (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+       return ret;
+}
+
+/* Collect a simple subtree that queries attributes in the remote partition */
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map)
+{
+       const char *attr;
+
+       /* Prepare new tree */
+       *new = talloc(mem_ctx, struct ldb_parse_tree);
+       if (*new == NULL) {
+               map_oom(module);
+               return -1;
+       }
+       **new = *tree;
+       
+       if (map->type == MAP_KEEP) {
+               /* Nothing to do here */
+               return 0;
+       }
+
+       /* Store attribute and value in new tree */
+       switch (tree->operation) {
+       case LDB_OP_PRESENT:
+               attr = map_attr_map_local(*new, map, tree->u.present.attr);
+               (*new)->u.present.attr = attr;
+               break;
+       case LDB_OP_SUBSTRING:
+       {
+               attr = map_attr_map_local(*new, map, tree->u.substring.attr);
+               (*new)->u.substring.attr = attr;
+               break;
+       }
+       case LDB_OP_EQUALITY:
+               attr = map_attr_map_local(*new, map, tree->u.equality.attr);
+               (*new)->u.equality.attr = attr;
+               break;
+       case LDB_OP_LESS:
+       case LDB_OP_GREATER:
+       case LDB_OP_APPROX:
+               attr = map_attr_map_local(*new, map, tree->u.comparison.attr);
+               (*new)->u.comparison.attr = attr;
+               break;
+       case LDB_OP_EXTENDED:
+               attr = map_attr_map_local(*new, map, tree->u.extended.attr);
+               (*new)->u.extended.attr = attr;
+               break;
+       default:                        /* unknown kind of simple subtree */
+               talloc_free(*new);
+               return -1;
+       }
+
+       if (attr == NULL) {
+               talloc_free(*new);
+               *new = NULL;
+               return 0;
+       }
+
+       if (map->type == MAP_RENAME) {
+               /* Nothing more to do here, the attribute has been renamed */
+               return 0;
+       }
+
+       /* Store attribute and value in new tree */
+       switch (tree->operation) {
+       case LDB_OP_PRESENT:
+               break;
+       case LDB_OP_SUBSTRING:
+       {
+               int i;
+               /* Map value */
+               (*new)->u.substring.chunks = NULL;
+               for (i=0; tree->u.substring.chunks[i]; i++) {
+                       (*new)->u.substring.chunks = talloc_realloc(*new, (*new)->u.substring.chunks, struct ldb_val *, i+2);
+                       if (!(*new)->u.substring.chunks) {
+                               talloc_free(*new);
+                               *new = NULL;
+                               return 0;
+                       }
+                       (*new)->u.substring.chunks[i] = talloc(*new, struct ldb_val);
+                       if (!(*new)->u.substring.chunks[i]) {
+                               talloc_free(*new);
+                               *new = NULL;
+                               return 0;
+                       }
+                       *(*new)->u.substring.chunks[i] = ldb_val_map_local(module, *new, map, *tree->u.substring.chunks[i]);
+                       (*new)->u.substring.chunks[i+1] = NULL;
+               }
+               break;
+       }
+       case LDB_OP_EQUALITY:
+               (*new)->u.equality.value = ldb_val_map_local(module, *new, map, tree->u.equality.value);
+               break;
+       case LDB_OP_LESS:
+       case LDB_OP_GREATER:
+       case LDB_OP_APPROX:
+               (*new)->u.comparison.value = ldb_val_map_local(module, *new, map, tree->u.comparison.value);
+               break;
+       case LDB_OP_EXTENDED:
+               (*new)->u.extended.value = ldb_val_map_local(module, *new, map, tree->u.extended.value);
+               (*new)->u.extended.rule_id = talloc_strdup(*new, tree->u.extended.rule_id);
+               break;
+       default:                        /* unknown kind of simple subtree */
+               talloc_free(*new);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Collect subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+       const struct ldb_map_context *data = map_get_context(module);
+       const struct ldb_map_attribute *map;
+
+       if (tree == NULL) {
+               return 0;
+       }
+
+       if (tree->operation == LDB_OP_NOT) {
+               return map_subtree_collect_remote_not(module, mem_ctx, new, tree);
+       }
+
+       if ((tree->operation == LDB_OP_AND) || (tree->operation == LDB_OP_OR)) {
+               return map_subtree_collect_remote_list(module, mem_ctx, new, tree);
+       }
+
+       if (!map_attr_check_remote(data, tree->u.equality.attr)) {
+               *new = NULL;
+               return 0;
+       }
+
+       map = map_attr_find_local(data, tree->u.equality.attr);
+       if (map->convert_operator) {
+               return map->convert_operator(module, mem_ctx, new, tree);
+       }
+
+       if (map->type == MAP_GENERATE) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+                         "Skipping attribute '%s': "
+                         "'convert_operator' not set\n",
+                         tree->u.equality.attr);
+               *new = NULL;
+               return 0;
+       }
+
+       return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, map);
+}
+
+/* Split subtrees that query attributes in the local partition from
+ * those that query the remote partition. */
+static int ldb_parse_tree_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, struct ldb_parse_tree **local_tree, struct ldb_parse_tree **remote_tree, const struct ldb_parse_tree *tree)
+{
+       int ret;
+
+       *local_tree = NULL;
+       *remote_tree = NULL;
+
+       /* No original tree */
+       if (tree == NULL) {
+               return 0;
+       }
+
+       /* Generate local tree */
+       ret = map_subtree_select_local(module, local_ctx, local_tree, tree);
+       if (ret) {
+               return ret;
+       }
+
+       /* Generate remote tree */
+       ret = map_subtree_collect_remote(module, remote_ctx, remote_tree, tree);
+       if (ret) {
+               talloc_free(*local_tree);
+               return ret;
+       }
+
+       return 0;
+}
+
+/* Collect a list of attributes required either explicitly from a
+ * given list or implicitly  from a given parse tree; split the
+ * collected list into local and remote parts. */
+static int map_attrs_collect_and_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *search_attrs, const struct ldb_parse_tree *tree)
+{
+       void *tmp_ctx;
+       const char **tree_attrs;
+       int ret;
+
+       /* Clear initial lists of partitioned attributes */
+       *local_attrs = NULL;
+       *remote_attrs = NULL;
+
+       /* There is no tree, just partition the searched attributes */
+       if (tree == NULL) {
+               return map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, search_attrs);
+       }
+
+       /* Create context for temporary memory */
+       tmp_ctx = talloc_new(local_ctx);
+       if (tmp_ctx == NULL) {
+               goto oom;
+       }
+
+       /* Prepare list of attributes from tree */
+       tree_attrs = talloc_array(tmp_ctx, const char *, 1);
+       if (tree_attrs == NULL) {
+               talloc_free(tmp_ctx);
+               goto oom;
+       }
+       tree_attrs[0] = NULL;
+
+       /* Collect attributes from tree */
+       ret = ldb_parse_tree_collect_attrs(module, tmp_ctx, &tree_attrs, tree);
+       if (ret) {
+               goto done;
+       }
+
+       /* Merge attributes from search operation */
+       ret = map_attrs_merge(module, tmp_ctx, &tree_attrs, search_attrs);
+       if (ret) {
+               goto done;
+       }
+
+       /* Split local from remote attributes */
+       ret = map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, tree_attrs);
+
+done:
+       /* Free temporary memory */
+       talloc_free(tmp_ctx);
+       return ret;
+
+oom:
+       map_oom(module);
+       return -1;
+}
+
+
+/* Outbound requests: search
+ * ========================= */
+
+/* Pass a merged search result up the callback chain. */
+int map_up_callback(struct ldb_context *ldb, const struct ldb_request *req, struct ldb_reply *ares)
+{
+       int i;
+
+       /* No callback registered, stop */
+       if (req->callback == NULL) {
+               return LDB_SUCCESS;
+       }
+
+       /* Only records need special treatment */
+       if (ares->type != LDB_REPLY_ENTRY) {
+               return req->callback(ldb, req->context, ares);
+       }
+
+       /* Merged result doesn't match original query, skip */
+       if (!ldb_match_msg(ldb, ares->message, req->op.search.tree, req->op.search.base, req->op.search.scope)) {
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: "
+                         "Skipping record '%s': "
+                         "doesn't match original search\n",
+                         ldb_dn_linearize(ldb, ares->message->dn));
+               return LDB_SUCCESS;
+       }
+
+       /* Limit result to requested attrs */
+       if ((req->op.search.attrs) && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
+               for (i = 0; i < ares->message->num_elements; i++) {
+                       const struct ldb_message_element *el = &ares->message->elements[i];
+                       if (!ldb_attr_in_list(req->op.search.attrs, el->name)) {
+                               ldb_msg_remove_attr(ares->message, el->name);
+                       }
+               }
+       }
+
+       return req->callback(ldb, req->context, ares);
+}
+
+/* Merge the remote and local parts of a search result. */
+int map_local_merge_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct map_search_context *sc;
+       int ret;
+
+       if (context == NULL || ares == NULL) {
+               ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+                                                      "NULL Context or Result in `map_local_merge_callback`"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       sc = talloc_get_type(context, struct map_search_context);
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               /* We have already found a local record */
+               if (sc->local_res) {
+                       ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+                                                              "Too many results to base search for local entry"));
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               /* Store local result */
+               sc->local_res = ares;
+
+               /* Merge remote into local message */
+               ret = ldb_msg_merge_local(sc->ac->module, ares->message, sc->remote_res->message);
+               if (ret) {
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               return map_up_callback(ldb, sc->ac->orig_req, ares);
+
+       case LDB_REPLY_DONE:
+               /* No local record found, continue with remote record */
+               if (sc->local_res == NULL) {
+                       return map_up_callback(ldb, sc->ac->orig_req, sc->remote_res);
+               }
+               return LDB_SUCCESS;
+
+       default:
+               ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+                                                      "Unexpected result type in base search for local entry"));
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+}
+
+/* Search the local part of a remote search result. */
+int map_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct map_context *ac;
+       struct map_search_context *sc;
+       struct ldb_request *req;
+       int ret;
+
+       if (context == NULL || ares == NULL) {
+               ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+                                                      "NULL Context or Result in `map_remote_search_callback`"));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = talloc_get_type(context, struct map_context);
+
+       /* It's not a record, stop searching */
+       if (ares->type != LDB_REPLY_ENTRY) {
+               return map_up_callback(ldb, ac->orig_req, ares);
+       }
+
+       /* Map result record into a local message */
+       ret = map_reply_remote(ac->module, ares);
+       if (ret) {
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* There is no local db, stop searching */
+       if (!map_check_local_db(ac->module)) {
+               return map_up_callback(ldb, ac->orig_req, ares);
+       }
+
+       /* Prepare local search context */
+       sc = map_init_search_context(ac, ares);
+       if (sc == NULL) {
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* Prepare local search request */
+       /* TODO: use GUIDs here instead? */
+
+       ac->search_reqs = talloc_realloc(ac, ac->search_reqs, struct ldb_request *, ac->num_searches + 2);
+       if (ac->search_reqs == NULL) {
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->search_reqs[ac->num_searches]
+               = req = map_search_base_req(ac, ares->message->dn, 
+                                           NULL, NULL, sc, map_local_merge_callback);
+       if (req == NULL) {
+               talloc_free(sc);
+               talloc_free(ares);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->num_searches++;
+       ac->search_reqs[ac->num_searches] = NULL;
+
+       return ldb_next_request(ac->module, req);
+}
+
+/* Search a record. */
+int map_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_handle *h;
+       struct map_context *ac;
+       struct ldb_parse_tree *local_tree, *remote_tree;
+       const char **local_attrs, **remote_attrs;
+       int ret;
+
+       const char *wildcard[] = { "*", NULL };
+       const char * const *attrs;
+
+       /* Do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.search.base))
+               return ldb_next_request(module, req);
+
+       /* No mapping requested, skip to next module */
+       if ((req->op.search.base) && (!ldb_dn_check_local(module, req->op.search.base))) {
+               return ldb_next_request(module, req);
+       }
+
+       /* TODO: How can we be sure about which partition we are
+        *       targetting when there is no search base? */
+
+       /* Prepare context and handle */
+       h = map_init_handle(req, module);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct map_context);
+
+       ac->search_reqs = talloc_array(ac, struct ldb_request *, 2);
+       if (ac->search_reqs == NULL) {
+               talloc_free(h);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->num_searches = 1;
+       ac->search_reqs[1] = NULL;
+
+       /* Prepare the remote operation */
+       ac->search_reqs[0] = talloc(ac, struct ldb_request);
+       if (ac->search_reqs[0] == NULL) {
+               goto oom;
+       }
+
+       *(ac->search_reqs[0]) = *req;   /* copy the request */
+
+       ac->search_reqs[0]->handle = h; /* return our own handle to deal with this call */
+
+       ac->search_reqs[0]->context = ac;
+       ac->search_reqs[0]->callback = map_remote_search_callback;
+
+       /* It is easier to deal with the two different ways of
+        * expressing the wildcard in the same codepath */
+       attrs = req->op.search.attrs;
+       if (attrs == NULL) {
+               attrs = wildcard;
+       }
+
+       /* Split local from remote attrs */
+       ret = map_attrs_collect_and_partition(module, ac, ac->search_reqs[0], &local_attrs, &remote_attrs, attrs, req->op.search.tree);
+       if (ret) {
+               goto failed;
+       }
+
+       ac->local_attrs = local_attrs;
+       ac->search_reqs[0]->op.search.attrs = remote_attrs;
+
+       /* Split local from remote tree */
+       ret = ldb_parse_tree_partition(module, ac, ac->search_reqs[0], &local_tree, &remote_tree, req->op.search.tree);
+       if (ret) {
+               goto failed;
+       }
+
+       if (((local_tree != NULL) && (remote_tree != NULL)) &&
+           (!ldb_parse_tree_check_splittable(req->op.search.tree))) {
+               /* The query can't safely be split, enumerate the remote partition */
+               local_tree = NULL;
+               remote_tree = NULL;
+       }
+
+       if (local_tree == NULL) {
+               /* Construct default local parse tree */
+               local_tree = talloc_zero(ac, struct ldb_parse_tree);
+               if (local_tree == NULL) {
+                       map_oom(ac->module);
+                       goto failed;
+               }
+
+               local_tree->operation = LDB_OP_PRESENT;
+               local_tree->u.present.attr = talloc_strdup(local_tree, IS_MAPPED);
+       }
+       if (remote_tree == NULL) {
+               /* Construct default remote parse tree */
+               remote_tree = ldb_parse_tree(ac->search_reqs[0], NULL);
+               if (remote_tree == NULL) {
+                       goto failed;
+               }
+       }
+
+       ac->local_tree = local_tree;
+       ac->search_reqs[0]->op.search.tree = remote_tree;
+
+       ldb_set_timeout_from_prev_req(module->ldb, req, ac->search_reqs[0]);
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->step = MAP_SEARCH_REMOTE;
+
+       ret = ldb_next_remote_request(module, ac->search_reqs[0]);
+       if (ret == LDB_SUCCESS) {
+               req->handle = h;
+       }
+       return ret;
+
+oom:
+       map_oom(module);
+failed:
+       talloc_free(h);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/source3/lib/ldb/modules/ldb_map_private.h b/source3/lib/ldb/modules/ldb_map_private.h
new file mode 100644 (file)
index 0000000..7fb2745
--- /dev/null
@@ -0,0 +1,115 @@
+
+/* A handy macro to report Out of Memory conditions */
+#define map_oom(module) ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+
+/* The type of search callback functions */
+typedef int (*ldb_search_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+/* The special DN from which the local and remote base DNs are fetched */
+#define MAP_DN_NAME    "@MAP"
+#define MAP_DN_FROM    "@FROM"
+#define MAP_DN_TO      "@TO"
+
+/* Private data structures
+ * ======================= */
+
+/* Context data for mapped requests */
+struct map_context {
+       enum map_step {
+               MAP_SEARCH_REMOTE,
+               MAP_ADD_REMOTE,
+               MAP_ADD_LOCAL,
+               MAP_SEARCH_SELF_MODIFY,
+               MAP_MODIFY_REMOTE,
+               MAP_MODIFY_LOCAL,
+               MAP_SEARCH_SELF_DELETE,
+               MAP_DELETE_REMOTE,
+               MAP_DELETE_LOCAL,
+               MAP_SEARCH_SELF_RENAME,
+               MAP_RENAME_REMOTE,
+               MAP_RENAME_FIXUP,
+               MAP_RENAME_LOCAL
+       } step;
+
+       struct ldb_module *module;
+
+       const struct ldb_dn *local_dn;
+       const struct ldb_parse_tree *local_tree;
+       const char * const *local_attrs;
+
+       struct ldb_request *orig_req;
+       struct ldb_request *local_req;
+       struct ldb_request *remote_req;
+       struct ldb_request *down_req;
+       struct ldb_request *search_req;
+
+       /* for search, we may have a lot of contexts */
+       int num_searches;
+       struct ldb_request **search_reqs;
+};
+
+/* Context data for mapped search requests */
+struct map_search_context {
+       struct map_context *ac;
+       struct ldb_reply *local_res;
+       struct ldb_reply *remote_res;
+};
+
+
+/* Common operations
+ * ================= */
+
+/* The following definitions come from lib/ldb/modules/ldb_map.c */
+const struct ldb_map_context *map_get_context(struct ldb_module *module);
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares);
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module);
+
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
+
+BOOL map_check_local_db(struct ldb_module *module);
+BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
+BOOL ldb_dn_check_local(struct ldb_module *module, const struct ldb_dn *dn);
+
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
+
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
+
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val);
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val);
+
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+
+struct ldb_request *map_search_base_req(struct map_context *ac, const struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback);
+struct ldb_request *map_search_self_req(struct map_context *ac, const struct ldb_dn *dn);
+struct ldb_request *map_build_fixup_req(struct map_context *ac, const struct ldb_dn *olddn, const struct ldb_dn *newdn);
+
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map);
+
+/* LDB Requests
+ * ============ */
+
+/* The following definitions come from lib/ldb/modules/ldb_map_inbound.c */
+int map_add_do_remote(struct ldb_handle *handle);
+int map_add_do_local(struct ldb_handle *handle);
+int map_add(struct ldb_module *module, struct ldb_request *req);
+
+int map_modify_do_remote(struct ldb_handle *handle);
+int map_modify_do_local(struct ldb_handle *handle);
+int map_modify(struct ldb_module *module, struct ldb_request *req);
+
+int map_delete_do_remote(struct ldb_handle *handle);
+int map_delete_do_local(struct ldb_handle *handle);
+int map_delete(struct ldb_module *module, struct ldb_request *req);
+
+int map_rename_do_remote(struct ldb_handle *handle);
+int map_rename_do_fixup(struct ldb_handle *handle);
+int map_rename_do_local(struct ldb_handle *handle);
+int map_rename(struct ldb_module *module, struct ldb_request *req);
+
+/* The following definitions come from lib/ldb/modules/ldb_map_outbound.c */
+int map_search(struct ldb_module *module, struct ldb_request *req);
diff --git a/source3/lib/ldb/modules/objectclass.c b/source3/lib/ldb/modules/objectclass.c
new file mode 100644 (file)
index 0000000..493ecda
--- /dev/null
@@ -0,0 +1,694 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2006
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: objectClass sorting module
+ *
+ *  Description: sort the objectClass attribute into the class hierarchy
+ *
+ *  Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct oc_context {
+
+       enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
+
+       struct ldb_module *module;
+       struct ldb_request *orig_req;
+
+       struct ldb_request *down_req;
+
+       struct ldb_request *search_req;
+       struct ldb_reply *search_res;
+
+       struct ldb_request *mod_req;
+};
+
+struct class_list {
+       struct class_list *prev, *next;
+       const char *objectclass;
+};
+
+static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
+{
+       struct oc_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(req, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct oc_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->orig_req = req;
+
+       return h;
+}
+
+static int objectclass_sort(struct ldb_module *module,
+                           TALLOC_CTX *mem_ctx,
+                           struct ldb_message_element *objectclass_element,
+                           struct class_list **sorted_out) 
+{
+       int i;
+       int layer;
+       struct class_list *sorted = NULL, *parent_class = NULL,
+               *subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
+       /* DESIGN:
+        *
+        * We work on 4 different 'bins' (implemented here as linked lists):
+        *
+        * * sorted:       the eventual list, in the order we wish to push
+        *                 into the database.  This is the only ordered list.
+        *
+        * * parent_class: The current parent class 'bin' we are
+        *                 trying to find subclasses for
+        *
+        * * subclass:     The subclasses we have found so far
+        *
+        * * unsorted:     The remaining objectClasses
+        *
+        * The process is a matter of filtering objectClasses up from
+        * unsorted into sorted.  Order is irrelevent in the later 3 'bins'.
+        * 
+        * We start with 'top' (found and promoted to parent_class
+        * initially).  Then we find (in unsorted) all the direct
+        * subclasses of 'top'.  parent_classes is concatenated onto
+        * the end of 'sorted', and subclass becomes the list in
+        * parent_class.
+        *
+        * We then repeat, until we find no more subclasses.  Any left
+        * over classes are added to the end.
+        *
+        */
+
+       /* Firstly, dump all the objectClass elements into the
+        * unsorted bin, except for 'top', which is special */
+       for (i=0; i < objectclass_element->num_values; i++) {
+               current = talloc(mem_ctx, struct class_list);
+               if (!current) {
+                       ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list");
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               current->objectclass = (const char *)objectclass_element->values[i].data;
+
+               /* this is the root of the tree.  We will start
+                * looking for subclasses from here */
+               if (ldb_attr_cmp("top", current->objectclass) == 0) {
+                       DLIST_ADD(parent_class, current);
+               } else {
+                       DLIST_ADD(unsorted, current);
+               }
+       }
+
+       /* DEBUGGING aid:  how many layers are we down now? */
+       layer = 0;
+       do {
+               layer++;
+               /* Find all the subclasses of classes in the
+                * parent_classes.  Push them onto the subclass list */
+
+               /* Ensure we don't bother if there are no unsorted entries left */
+               for (current = parent_class; unsorted && current; current = current->next) {
+                       const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass);
+
+                       /* Walk the list of possible subclasses in unsorted */
+                       for (poss_subclass = unsorted; poss_subclass; ) {
+                               struct class_list *next;
+                               
+                               /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
+                               next = poss_subclass->next;
+
+                               for (i = 0; subclasses && subclasses[i]; i++) {
+                                       if (ldb_attr_cmp(poss_subclass->objectclass, subclasses[i]) == 0) {
+                                               DLIST_REMOVE(unsorted, poss_subclass);
+                                               DLIST_ADD(subclass, poss_subclass);
+
+                                               break;
+                                       }
+                               }
+                               poss_subclass = next;
+                       }
+               }
+
+               /* Now push the parent_classes as sorted, we are done with
+               these.  Add to the END of the list by concatenation */
+               DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
+
+               /* and now find subclasses of these */
+               parent_class = subclass;
+               subclass = NULL;
+
+               /* If we didn't find any subclasses we will fall out
+                * the bottom here */
+       } while (parent_class);
+
+       /* This shouldn't happen, and would break MMC, but we can't
+        * afford to loose objectClasses.  Perhaps there was no 'top',
+        * or some other schema error? 
+        *
+        * Detecting schema errors is the job of the schema module, so
+        * at this layer we just try not to loose data
+        */
+       DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
+
+       *sorted_out = sorted;
+       return LDB_SUCCESS;
+}
+
+static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_message_element *objectclass_element;
+       struct class_list *sorted, *current;
+       struct ldb_request *down_req;
+       struct ldb_message *msg;
+       int ret;
+       TALLOC_CTX *mem_ctx;
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
+
+       if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
+               return ldb_next_request(module, req);
+       }
+       
+       objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass");
+
+       /* If no part of this add has an objectClass, then we don't
+        * need to make any changes. cn=rootdse doesn't have an objectClass */
+       if (!objectclass_element) {
+               return ldb_next_request(module, req);
+       }
+
+       mem_ctx = talloc_new(req);
+       if (mem_ctx == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /* prepare the first operation */
+       down_req = talloc(req, struct ldb_request);
+       if (down_req == NULL) {
+               ldb_set_errstring(module->ldb, "Out of memory!");
+               talloc_free(mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *down_req = *req; /* copy the request */
+
+       down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+
+       if (down_req->op.add.message == NULL) {
+               talloc_free(mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ldb_msg_remove_attr(msg, "objectClass");
+       ret = ldb_msg_add_empty(msg, "objectClass", 0);
+       
+       if (ret != LDB_SUCCESS) {
+               talloc_free(mem_ctx);
+               return ret;
+       }
+
+       /* We must completely replace the existing objectClass entry,
+        * because we need it sorted */
+
+       /* Move from the linked list back into an ldb msg */
+       for (current = sorted; current; current = current->next) {
+               ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+               if (ret != LDB_SUCCESS) {
+                       ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+                       talloc_free(mem_ctx);
+                       return ret;
+               }
+       }
+
+       talloc_free(mem_ctx);
+       ret = ldb_msg_sanity_check(module->ldb, msg);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /* go on with the call chain */
+       ret = ldb_next_request(module, down_req);
+
+       /* do not free down_req as the call results may be linked to it,
+        * it will be freed when the upper level request get freed */
+       if (ret == LDB_SUCCESS) {
+               req->handle = down_req->handle;
+       }
+       return ret;
+}
+
+static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_message_element *objectclass_element;
+       struct ldb_message *msg;
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
+
+       if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+               return ldb_next_request(module, req);
+       }
+       
+       objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
+
+       /* If no part of this touches the objectClass, then we don't
+        * need to make any changes.  */
+       /* If the only operation is the deletion of the objectClass then go on */
+       if (!objectclass_element) {
+               return ldb_next_request(module, req);
+       }
+
+       switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
+       case LDB_FLAG_MOD_DELETE:
+               /* Delete everything?  Probably totally illigal, but hey! */
+               if (objectclass_element->num_values == 0) {
+                       return ldb_next_request(module, req);
+               }
+               break;
+       case LDB_FLAG_MOD_REPLACE:
+       {
+               struct ldb_request *down_req;
+               struct class_list *sorted, *current;
+               TALLOC_CTX *mem_ctx;
+               int ret;
+               mem_ctx = talloc_new(req);
+               if (mem_ctx == NULL) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               /* prepare the first operation */
+               down_req = talloc(req, struct ldb_request);
+               if (down_req == NULL) {
+                       ldb_set_errstring(module->ldb, "Out of memory!");
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               
+               *down_req = *req; /* copy the request */
+               
+               down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
+               
+               if (down_req->op.add.message == NULL) {
+                       talloc_free(mem_ctx);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               
+               ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+
+               /* We must completely replace the existing objectClass entry,
+                * because we need it sorted */
+               
+               ldb_msg_remove_attr(msg, "objectClass");
+               ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
+               
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(mem_ctx);
+                       return ret;
+               }
+
+               /* Move from the linked list back into an ldb msg */
+               for (current = sorted; current; current = current->next) {
+                       ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+                       if (ret != LDB_SUCCESS) {
+                               ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+                               talloc_free(mem_ctx);
+                               return ret;
+                       }
+               }
+               
+               talloc_free(mem_ctx);
+
+               ret = ldb_msg_sanity_check(module->ldb, msg);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(mem_ctx);
+                       return ret;
+               }
+               
+               /* go on with the call chain */
+               ret = ldb_next_request(module, down_req);
+               
+               /* do not free down_req as the call results may be linked to it,
+                * it will be freed when the upper level request get freed */
+               if (ret == LDB_SUCCESS) {
+                       req->handle = down_req->handle;
+               }
+               return ret;
+       }
+       }
+
+       {
+               struct ldb_handle *h;
+               struct oc_context *ac;
+               
+               h = oc_init_handle(req, module);
+               if (!h) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               ac = talloc_get_type(h->private_data, struct oc_context);
+               
+               /* return or own handle to deal with this call */
+               req->handle = h;
+               
+               /* prepare the first operation */
+               ac->down_req = talloc(ac, struct ldb_request);
+               if (ac->down_req == NULL) {
+                       ldb_set_errstring(module->ldb, "Out of memory!");
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               
+               *(ac->down_req) = *req; /* copy the request */
+               
+               ac->down_req->context = NULL;
+               ac->down_req->callback = NULL;
+               ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
+               
+               ac->step = OC_DO_REQ;
+
+               return ldb_next_request(module, ac->down_req);
+       }
+}
+
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct oc_context *ac;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = talloc_get_type(context, struct oc_context);
+
+       /* we are interested only in the single reply (base search) we receive here */
+       if (ares->type == LDB_REPLY_ENTRY) {
+               if (ac->search_res != NULL) {
+                       ldb_set_errstring(ldb, "Too many results");
+                       talloc_free(ares);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ac->search_res = talloc_move(ac, &ares);
+       } else {
+               talloc_free(ares);
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int objectclass_search_self(struct ldb_handle *h) {
+
+       struct oc_context *ac;
+       static const char * const attrs[] = { "objectClass", NULL };
+
+       ac = talloc_get_type(h->private_data, struct oc_context);
+
+       /* prepare the search operation */
+       ac->search_req = talloc_zero(ac, struct ldb_request);
+       if (ac->search_req == NULL) {
+               ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->search_req->operation = LDB_SEARCH;
+       ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
+       ac->search_req->op.search.scope = LDB_SCOPE_BASE;
+       ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
+       if (ac->search_req->op.search.tree == NULL) {
+               ldb_set_errstring(ac->module->ldb, "objectclass: Internal error producing null search");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->search_req->op.search.attrs = attrs;
+       ac->search_req->controls = NULL;
+       ac->search_req->context = ac;
+       ac->search_req->callback = get_self_callback;
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
+
+       ac->step = OC_SEARCH_SELF;
+
+       return ldb_next_request(ac->module, ac->search_req);
+}
+
+static int objectclass_do_mod(struct ldb_handle *h) {
+
+       struct oc_context *ac;
+       struct ldb_message_element *objectclass_element;
+       struct ldb_message *msg;
+       TALLOC_CTX *mem_ctx;
+       struct class_list *sorted, *current;
+       int ret;
+      
+       ac = talloc_get_type(h->private_data, struct oc_context);
+
+       mem_ctx = talloc_new(ac);
+       if (mem_ctx == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->mod_req = talloc(ac, struct ldb_request);
+       if (ac->mod_req == NULL) {
+               talloc_free(mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->mod_req->operation = LDB_MODIFY;
+       ac->mod_req->controls = NULL;
+       ac->mod_req->context = ac;
+       ac->mod_req->callback = NULL;
+       ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
+       
+       /* use a new message structure */
+       ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+       if (msg == NULL) {
+               ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg");
+               talloc_free(mem_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* This is now the objectClass list from the database */
+       objectclass_element = ldb_msg_find_element(ac->search_res->message, 
+                                                  "objectClass");
+       if (!objectclass_element) {
+               /* Where did it go?  Move along now, nothing to see here */
+               talloc_free(mem_ctx);
+               return LDB_SUCCESS;
+       }
+       
+       /* modify dn */
+       msg->dn = ac->orig_req->op.mod.message->dn;
+
+       ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /* We must completely replace the existing objectClass entry.
+        * We could do a constrained add/del, but we are meant to be
+        * in a transaction... */
+
+       ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
+       if (ret != LDB_SUCCESS) {
+               ldb_set_errstring(ac->module->ldb, "objectclass: could not clear objectclass in modify msg");
+               talloc_free(mem_ctx);
+               return ret;
+       }
+       
+       /* Move from the linked list back into an ldb msg */
+       for (current = sorted; current; current = current->next) {
+               ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+               if (ret != LDB_SUCCESS) {
+                       ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+                       talloc_free(mem_ctx);
+                       return ret;
+               }
+       }
+
+       ret = ldb_msg_sanity_check(ac->module->ldb, msg);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(mem_ctx);
+               return ret;
+       }
+
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->step = OC_DO_MOD;
+
+       talloc_free(mem_ctx);
+       /* perform the search */
+       return ldb_next_request(ac->module, ac->mod_req);
+}
+
+static int oc_wait(struct ldb_handle *handle) {
+       struct oc_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct oc_context);
+
+       switch (ac->step) {
+       case OC_DO_REQ:
+               ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->down_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->down_req->handle->status;
+                       goto done;
+               }
+
+               if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               /* mods done, go on */
+               return objectclass_search_self(handle);
+
+       case OC_SEARCH_SELF:
+               ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
+
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->search_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->search_req->handle->status;
+                       goto done;
+               }
+
+               if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               /* self search done, go on */
+               return objectclass_do_mod(handle);
+
+       case OC_DO_MOD:
+               ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
+
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->mod_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->mod_req->handle->status;
+                       goto done;
+               }
+
+               if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               break;
+               
+       default:
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       ret = LDB_SUCCESS;
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+static int oc_wait_all(struct ldb_handle *handle) {
+
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = oc_wait(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return oc_wait_all(handle);
+       } else {
+               return oc_wait(handle);
+       }
+}
+
+static const struct ldb_module_ops objectclass_ops = {
+       .name              = "objectclass",
+       .add           = objectclass_add,
+       .modify        = objectclass_modify,
+       .wait          = objectclass_wait
+};
+
+int ldb_objectclass_init(void)
+{
+       return ldb_register_module(&objectclass_ops);
+}
+
diff --git a/source3/lib/ldb/modules/operational.c b/source3/lib/ldb/modules/operational.c
new file mode 100644 (file)
index 0000000..c327a96
--- /dev/null
@@ -0,0 +1,312 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell 2005
+   Copyright (C) Simo Sorce 2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*
+  handle operational attributes
+ */
+
+/*
+  createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
+  modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
+
+     for the above two, we do the search as normal, and if
+     createTimestamp or modifyTimestamp is asked for, then do
+     additional searches for whenCreated and whenChanged and fill in
+     the resulting values
+
+     we also need to replace these with the whenCreated/whenChanged
+     equivalent in the search expression trees
+
+  whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+  whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+
+     on init we need to setup attribute handlers for these so
+     comparisons are done correctly. The resolution is 1 second.
+
+     on add we need to add both the above, for current time
+
+     on modify we need to change whenChanged
+
+
+  subschemaSubentry: HIDDEN, not-searchable, 
+                     points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
+
+     for this one we do the search as normal, then add the static
+     value if requested. How do we work out the $BASEDN from inside a
+     module?
+     
+
+  structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
+
+     for this one we do the search as normal, then if requested ask
+     for objectclass, change the attribute name, and add it
+
+  allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
+     list of attributes that can be modified - requires schema lookup
+
+
+  attributeTypes: in schema only
+  objectClasses: in schema only
+  matchingRules: in schema only
+  matchingRuleUse: in schema only
+  creatorsName: not supported by w2k3?
+  modifiersName: not supported by w2k3?
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+  construct a canonical name from a message
+*/
+static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
+{
+       char *canonicalName;
+       canonicalName = ldb_dn_canonical_string(msg, msg->dn);
+       if (canonicalName == NULL) {
+               return -1;
+       }
+       return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
+}
+
+/*
+  a list of attribute names that should be substituted in the parse
+  tree before the search is done
+*/
+static const struct {
+       const char *attr;
+       const char *replace;
+} parse_tree_sub[] = {
+       { "createTimestamp", "whenCreated" },
+       { "modifyTimestamp", "whenChanged" }
+};
+
+
+/*
+  a list of attribute names that are hidden, but can be searched for
+  using another (non-hidden) name to produce the correct result
+*/
+static const struct {
+       const char *attr;
+       const char *replace;
+       int (*constructor)(struct ldb_module *, struct ldb_message *);
+} search_sub[] = {
+       { "createTimestamp", "whenCreated", NULL },
+       { "modifyTimestamp", "whenChanged", NULL },
+       { "structuralObjectClass", "objectClass", NULL },
+       { "canonicalName", "distinguishedName", construct_canonical_name }
+};
+
+/*
+  post process a search result record. For any search_sub[] attributes that were
+  asked for, we need to call the appropriate copy routine to copy the result
+  into the message, then remove any attributes that we added to the search but were
+  not asked for by the user
+*/
+static int operational_search_post_process(struct ldb_module *module,
+                                          struct ldb_message *msg, 
+                                          const char * const *attrs)
+{
+       int i, a=0;
+
+       for (a=0;attrs && attrs[a];a++) {
+               for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+                       if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
+                               continue;
+                       }
+
+                       /* construct the new attribute, using either a supplied 
+                          constructor or a simple copy */
+                       if (search_sub[i].constructor) {
+                               if (search_sub[i].constructor(module, msg) != 0) {
+                                       goto failed;
+                               }
+                       } else if (ldb_msg_copy_attr(msg,
+                                                    search_sub[i].replace,
+                                                    search_sub[i].attr) != 0) {
+                               goto failed;
+                       }
+
+                       /* remove the added search attribute, unless it was asked for 
+                          by the user */
+                       if (search_sub[i].replace == NULL ||
+                           ldb_attr_in_list(attrs, search_sub[i].replace) ||
+                           ldb_attr_in_list(attrs, "*")) {
+                               continue;
+                       }
+
+                       ldb_msg_remove_attr(msg, search_sub[i].replace);
+               }
+       }
+
+       return 0;
+
+failed:
+       ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
+                     "operational_search_post_process failed for attribute '%s'\n", 
+                     attrs[a]);
+       return -1;
+}
+
+
+/*
+  hook search operations
+*/
+
+struct operational_context {
+
+       struct ldb_module *module;
+       void *up_context;
+       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+       const char * const *attrs;
+};
+
+static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct operational_context *ac;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }
+
+       ac = talloc_get_type(context, struct operational_context);
+
+       if (ares->type == LDB_REPLY_ENTRY) {
+               /* for each record returned post-process to add any derived
+                  attributes that have been asked for */
+               if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
+                       goto error;
+               }
+       }
+
+       return ac->up_callback(ldb, ac->up_context, ares);
+
+error:
+       talloc_free(ares);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int operational_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct operational_context *ac;
+       struct ldb_request *down_req;
+       const char **search_attrs = NULL;
+       int i, a, ret;
+
+       req->handle = NULL;
+
+       ac = talloc(req, struct operational_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->module = module;
+       ac->up_context = req->context;
+       ac->up_callback = req->callback;
+       ac->attrs = req->op.search.attrs;
+
+       down_req = talloc_zero(req, struct ldb_request);
+       if (down_req == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       down_req->operation = req->operation;
+       down_req->op.search.base = req->op.search.base;
+       down_req->op.search.scope = req->op.search.scope;
+       down_req->op.search.tree = req->op.search.tree;
+
+       /*  FIXME: I hink we should copy the tree and keep the original
+        *  unmodified. SSS */
+       /* replace any attributes in the parse tree that are
+          searchable, but are stored using a different name in the
+          backend */
+       for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
+               ldb_parse_tree_attr_replace(req->op.search.tree, 
+                                           parse_tree_sub[i].attr, 
+                                           parse_tree_sub[i].replace);
+       }
+
+       /* in the list of attributes we are looking for, rename any
+          attributes to the alias for any hidden attributes that can
+          be fetched directly using non-hidden names */
+       for (a=0;ac->attrs && ac->attrs[a];a++) {
+               for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+                       if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
+                           search_sub[i].replace) {
+                               if (!search_attrs) {
+                                       search_attrs = ldb_attr_list_copy(req, ac->attrs);
+                                       if (search_attrs == NULL) {
+                                               return LDB_ERR_OPERATIONS_ERROR;
+                                       }
+                               }
+                               search_attrs[a] = search_sub[i].replace;
+                       }
+               }
+       }
+       
+       /* use new set of attrs if any */
+       if (search_attrs) down_req->op.search.attrs = search_attrs;
+       else down_req->op.search.attrs = req->op.search.attrs;
+       
+       down_req->controls = req->controls;
+
+       down_req->context = ac;
+       down_req->callback = operational_callback;
+       ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
+
+       /* perform the search */
+       ret = ldb_next_request(module, down_req);
+
+       /* do not free down_req as the call results may be linked to it,
+        * it will be freed when the upper level request get freed */
+       if (ret == LDB_SUCCESS) {
+               req->handle = down_req->handle;
+       }
+
+       return ret;
+}
+
+static int operational_init(struct ldb_module *ctx)
+{
+       /* setup some standard attribute handlers */
+       ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
+       ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
+       ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
+       ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
+
+       return ldb_next_init(ctx);
+}
+
+static const struct ldb_module_ops operational_ops = {
+       .name              = "operational",
+       .search            = operational_search,
+       .init_context      = operational_init
+};
+
+int ldb_operational_init(void)
+{
+       return ldb_register_module(&operational_ops);
+}
diff --git a/source3/lib/ldb/modules/paged_results.c b/source3/lib/ldb/modules/paged_results.c
new file mode 100644 (file)
index 0000000..3ab575e
--- /dev/null
@@ -0,0 +1,567 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: paged_result
+ *
+ *  Component: ldb paged results control module
+ *
+ *  Description: this module caches a complete search and sends back
+ *              results in chunks as asked by the client
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct message_store {
+       /* keep the whole ldb_reply as an optimization
+        * instead of freeing and talloc-ing the container
+        * on each result */
+       struct ldb_reply *r;
+       struct message_store *next;
+};
+
+struct private_data;
+
+struct results_store {
+
+       struct private_data *priv;
+
+       char *cookie;
+       time_t timestamp;
+
+       struct results_store *prev;
+       struct results_store *next;
+       
+       struct message_store *first;
+       struct message_store *last;
+       int num_entries;
+
+       struct message_store *first_ref;
+       struct message_store *last_ref;
+
+       struct ldb_control **controls;
+
+       struct ldb_request *req;
+};
+
+struct private_data {
+
+       int next_free_id;
+       struct results_store *store;
+       
+};
+
+int store_destructor(struct results_store *store)
+{
+       if (store->prev) {
+               store->prev->next = store->next;
+       }
+       if (store->next) {
+               store->next->prev = store->prev;
+       }
+
+       if (store == store->priv->store) {
+               store->priv->store = NULL;
+       }
+
+       return 0;
+}
+
+static struct results_store *new_store(struct private_data *priv)
+{
+       struct results_store *newr;
+       int new_id = priv->next_free_id++;
+
+       /* TODO: we should have a limit on the number of
+        * outstanding paged searches
+        */
+
+       newr = talloc(priv, struct results_store);
+       if (!newr) return NULL;
+
+       newr->priv = priv;
+
+       newr->cookie = talloc_asprintf(newr, "%d", new_id);
+       if (!newr->cookie) {
+               talloc_free(newr);
+               return NULL;
+       }
+
+       newr->timestamp = time(NULL);
+
+       newr->first = NULL;
+       newr->num_entries = 0;
+       newr->first_ref = NULL;
+       newr->controls = NULL;
+
+       /* put this entry as first */
+       newr->prev = NULL;
+       newr->next = priv->store;
+       if (priv->store != NULL) priv->store->prev = newr;
+       priv->store = newr;
+
+       talloc_set_destructor(newr, store_destructor);
+
+       return newr;
+}
+
+struct paged_context {
+       struct ldb_module *module;
+       void *up_context;
+       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+       int size;
+
+       struct results_store *store;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct paged_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(mem_ctx, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct paged_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->up_context = context;
+       ac->up_callback = callback;
+
+       return h;
+}
+
+static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct paged_context *ac = NULL;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }
+
+       ac = talloc_get_type(context, struct paged_context);
+
+       if (ares->type == LDB_REPLY_ENTRY) {
+               if (ac->store->first == NULL) {
+                       ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
+               } else {
+                       ac->store->last->next = talloc(ac->store, struct message_store);
+                       ac->store->last = ac->store->last->next;
+               }
+               if (ac->store->last == NULL) {
+                       goto error;
+               }
+
+               ac->store->num_entries++;
+
+               ac->store->last->r = talloc_steal(ac->store->last, ares);
+               ac->store->last->next = NULL;
+       }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               if (ac->store->first_ref == NULL) {
+                       ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
+               } else {
+                       ac->store->last_ref->next = talloc(ac->store, struct message_store);
+                       ac->store->last_ref = ac->store->last_ref->next;
+               }
+               if (ac->store->last_ref == NULL) {
+                       goto error;
+               }
+
+               ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
+               ac->store->last_ref->next = NULL;
+       }
+
+       if (ares->type == LDB_REPLY_DONE) {
+               ac->store->controls = talloc_move(ac->store, &ares->controls);
+               talloc_free(ares);
+       }
+
+       return LDB_SUCCESS;
+
+error:
+       talloc_free(ares);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int paged_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_control *control;
+       struct private_data *private_data;
+       struct ldb_paged_control *paged_ctrl;
+       struct ldb_control **saved_controls;
+       struct paged_context *ac;
+       struct ldb_handle *h;
+       int ret;
+
+       /* check if there's a paged request control */
+       control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
+       if (control == NULL) {
+               /* not found go on */
+               return ldb_next_request(module, req);
+       }
+
+       private_data = talloc_get_type(module->private_data, struct private_data);
+
+       req->handle = NULL;
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb,
+                                 "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
+       if (!paged_ctrl) {
+               return LDB_ERR_PROTOCOL_ERROR;
+       }
+
+       h = init_handle(req, module, req->context, req->callback);
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct paged_context);
+
+       ac->size = paged_ctrl->size;
+
+       /* check if it is a continuation search the store */
+       if (paged_ctrl->cookie_len == 0) {
+               
+               ac->store = new_store(private_data);
+               if (ac->store == NULL) {
+                       talloc_free(h);
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               }
+
+               ac->store->req = talloc(ac->store, struct ldb_request);
+               if (!ac->store->req)
+                       return LDB_ERR_OPERATIONS_ERROR;
+
+               ac->store->req->operation = req->operation;
+               ac->store->req->op.search.base = req->op.search.base;
+               ac->store->req->op.search.scope = req->op.search.scope;
+               ac->store->req->op.search.tree = req->op.search.tree;
+               ac->store->req->op.search.attrs = req->op.search.attrs;
+               ac->store->req->controls = req->controls;
+
+               /* save it locally and remove it from the list */
+               /* we do not need to replace them later as we
+                * are keeping the original req intact */
+               if (!save_controls(control, ac->store->req, &saved_controls)) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ac->store->req->context = ac;
+               ac->store->req->callback = paged_search_callback;
+               ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
+
+               ret = ldb_next_request(module, ac->store->req);
+
+       } else {
+               struct results_store *current = NULL;
+
+               for (current = private_data->store; current; current = current->next) {
+                       if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
+                               current->timestamp = time(NULL);
+                               break;
+                       }
+               }
+               if (current == NULL) {
+                       talloc_free(h);
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               }
+
+               ac->store = current;
+               ret = LDB_SUCCESS;
+       }
+
+       req->handle = h;
+
+       /* check if it is an abandon */
+       if (ac->size == 0) {
+               talloc_free(ac->store);
+               h->status = LDB_SUCCESS;
+               h->state = LDB_ASYNC_DONE;
+               return LDB_SUCCESS;
+       }
+
+       /* TODO: age out old outstanding requests */
+
+       return ret;
+
+}
+
+static int paged_results(struct ldb_handle *handle)
+{
+       struct paged_context *ac;
+       struct ldb_paged_control *paged;
+       struct ldb_reply *ares;
+       struct message_store *msg;
+       int i, num_ctrls, ret;
+
+       ac = talloc_get_type(handle->private_data, struct paged_context);
+
+       if (ac->store == NULL)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       while (ac->store->num_entries > 0 && ac->size > 0) {
+               msg = ac->store->first;
+               ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       handle->state = LDB_ASYNC_DONE;
+                       return ret;
+               }
+
+               ac->store->first = msg->next;
+               talloc_free(msg);
+               ac->store->num_entries--;
+               ac->size--;
+       }
+
+       handle->state = LDB_ASYNC_DONE;
+
+       while (ac->store->first_ref != NULL) {
+               msg = ac->store->first_ref;
+               ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       handle->state = LDB_ASYNC_DONE;
+                       return ret;
+               }
+
+               ac->store->first_ref = msg->next;
+               talloc_free(msg);
+       }
+
+       ares = talloc_zero(ac->store, struct ldb_reply);
+       if (ares == NULL) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+       num_ctrls = 2;
+       i = 0;
+
+       if (ac->store->controls != NULL) {
+               ares->controls = ac->store->controls;
+               while (ares->controls[i]) i++; /* counting */
+
+               ares->controls = talloc_move(ares, &ac->store->controls);
+               num_ctrls += i;
+       }
+
+       ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
+       if (ares->controls == NULL) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+
+       ares->controls[i] = talloc(ares->controls, struct ldb_control);
+       if (ares->controls[i] == NULL) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+
+       ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
+       if (ares->controls[i]->oid == NULL) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+               
+       ares->controls[i]->critical = 0;
+       ares->controls[i + 1] = NULL;
+
+       paged = talloc(ares->controls[i], struct ldb_paged_control);
+       if (paged == NULL) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+       
+       ares->controls[i]->data = paged;
+
+       if (ac->size > 0) {
+               paged->size = 0;
+               paged->cookie = NULL;
+               paged->cookie_len = 0;
+       } else {
+               paged->size = ac->store->num_entries;
+               paged->cookie = talloc_strdup(paged, ac->store->cookie);
+               paged->cookie_len = strlen(paged->cookie) + 1;
+       }
+
+       ares->type = LDB_REPLY_DONE;
+
+       ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+
+       handle->status = ret;
+
+       return ret;
+}
+
+static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       struct paged_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+
+       ac = talloc_get_type(handle->private_data, struct paged_context);
+
+       if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
+               /* if lower level is finished we do not need to call it anymore */
+               /* return all we have until size == 0 or we empty storage */
+               ret = paged_results(handle);
+
+               /* we are done, if num_entries is zero free the storage
+                * as that mean we delivered the last batch */
+               if (ac->store->num_entries == 0) {
+                       talloc_free(ac->store);
+               }
+
+               return ret;
+       }
+
+       if (type == LDB_WAIT_ALL) {
+               while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
+                       ret = ldb_wait(ac->store->req->handle, type);
+                       if (ret != LDB_SUCCESS) {
+                               handle->state = LDB_ASYNC_DONE;
+                               handle->status = ret;
+                               return ret;
+                       }
+               }
+
+               ret = paged_results(handle);
+
+               /* we are done, if num_entries is zero free the storage
+                * as that mean we delivered the last batch */
+               if (ac->store->num_entries == 0) {
+                       talloc_free(ac->store);
+               }
+
+               return ret;
+       }
+
+       ret = ldb_wait(ac->store->req->handle, type);
+       if (ret != LDB_SUCCESS) {
+               handle->state = LDB_ASYNC_DONE;
+               handle->status = ret;
+               return ret;
+       }
+
+       handle->status = ret;
+
+       if (ac->store->num_entries >= ac->size ||
+           ac->store->req->handle->state == LDB_ASYNC_DONE) {
+
+               ret = paged_results(handle);
+
+               /* we are done, if num_entries is zero free the storage
+                * as that mean we delivered the last batch */
+               if (ac->store->num_entries == 0) {
+                       talloc_free(ac->store);
+               }
+       }
+
+       return ret;
+}
+
+static int paged_request_init(struct ldb_module *module)
+{
+       struct private_data *data;
+       struct ldb_request *req;
+       int ret;
+
+       data = talloc(module, struct private_data);
+       if (data == NULL) {
+               return LDB_ERR_OTHER;
+       }
+       
+       data->next_free_id = 1;
+       data->store = NULL;
+       module->private_data = data;
+
+       req = talloc(module, struct ldb_request);
+       if (req == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_REGISTER_CONTROL;
+       req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
+       req->controls = NULL;
+
+       ret = ldb_request(module->ldb, req);
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "paged_request: Unable to register control with rootdse!\n");
+               talloc_free(req);
+               return LDB_ERR_OTHER;
+       }
+
+       talloc_free(req);
+       return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops paged_ops = {
+       .name           = "paged_results",
+       .search         = paged_search,
+       .wait           = paged_wait,
+       .init_context   = paged_request_init
+};
+
+int ldb_paged_results_init(void)
+{
+       return ldb_register_module(&paged_ops);
+}
+
diff --git a/source3/lib/ldb/modules/paged_searches.c b/source3/lib/ldb/modules/paged_searches.c
new file mode 100644 (file)
index 0000000..c7d1633
--- /dev/null
@@ -0,0 +1,468 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005-2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: paged_searches
+ *
+ *  Component: ldb paged searches module
+ *
+ *  Description: this module detects if the remote ldap server supports
+ *               paged results and use them to transparently access all objects
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define PS_DEFAULT_PAGE_SIZE 500
+/* 500 objects per query seem to be a decent compromise
+ * the default AD limit per request is 1000 entries */
+
+struct private_data {
+
+       bool paged_supported;
+};
+
+struct ps_context {
+       struct ldb_module *module;
+       void *up_context;
+       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+       struct ldb_request *orig_req;
+
+       struct ldb_request *new_req;
+
+       bool pending;
+
+       char **saved_referrals;
+       int num_referrals;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct ps_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(mem_ctx, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct ps_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->up_context = context;
+       ac->up_callback = callback;
+
+       ac->pending = False;
+       ac->saved_referrals = NULL;
+       ac->num_referrals = 0;
+
+       return h;
+}
+
+static int check_ps_continuation(struct ldb_reply *ares, struct ps_context *ac)
+{
+       struct ldb_paged_control *rep_control, *req_control;
+
+       /* look up our paged control */
+       if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) {
+               /* something wrong here */
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control);
+       if (rep_control->cookie_len == 0) {
+               /* we are done */
+               ac->pending = False;
+               return LDB_SUCCESS;
+       }
+
+       /* more processing required */
+       /* let's fill in the request control with the new cookie */
+       /* if there's a reply control we must find a request
+        * control matching it */
+
+       if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ac->new_req->controls[0]->oid) != 0) {
+               /* something wrong here */
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req_control = talloc_get_type(ac->new_req->controls[0]->data, struct ldb_paged_control);
+
+       if (req_control->cookie) {
+               talloc_free(req_control->cookie);
+       }
+
+       req_control->cookie = talloc_memdup(req_control,
+                                           rep_control->cookie,
+                                           rep_control->cookie_len);
+       req_control->cookie_len = rep_control->cookie_len;
+
+       ac->pending = True;
+       return LDB_SUCCESS;
+}
+
+static int store_referral(char *referral, struct ps_context *ac)
+{
+       ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
+       if (!ac->saved_referrals) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
+       if (!ac->saved_referrals[ac->num_referrals]) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->num_referrals++;
+       ac->saved_referrals[ac->num_referrals] = NULL;
+
+       return LDB_SUCCESS;
+}
+
+static int send_referrals(struct ldb_context *ldb, struct ps_context *ac)
+{
+       struct ldb_reply *ares;
+       int i;
+
+       for (i = 0; i < ac->num_referrals; i++) {
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               ares->type = LDB_REPLY_REFERRAL;
+               ares->referral = ac->saved_referrals[i];
+
+               ac->up_callback(ldb, ac->up_context, ares);
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int ps_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct ps_context *ac = NULL;
+       int ret = LDB_ERR_OPERATIONS_ERROR;
+
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }
+
+       ac = talloc_get_type(context, struct ps_context);
+
+       switch (ares->type) {
+       case LDB_REPLY_ENTRY:
+               ac->up_callback(ldb, ac->up_context, ares);
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               ret = store_referral(ares->referral, ac);
+               if (ret != LDB_SUCCESS) {
+                       goto error;
+               }
+               break;
+
+       case LDB_REPLY_DONE:
+               ret = check_ps_continuation(ares, ac);
+               if (ret != LDB_SUCCESS) {
+                       goto error;
+               }
+               if (!ac->pending) {
+                       /* send referrals */
+                       ret = send_referrals(ldb, ac);
+                       if (ret != LDB_SUCCESS) {
+                               goto error;
+                       }
+
+                       /* send REPLY_DONE */
+                       ac->up_callback(ldb, ac->up_context, ares);
+               }
+               break;
+       default:
+               goto error;
+       }
+
+       return LDB_SUCCESS;
+
+error:
+       talloc_free(ares);
+       return ret;
+}
+
+static int ps_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct private_data *private_data;
+       struct ldb_paged_control *control;
+       struct ps_context *ac;
+       struct ldb_handle *h;
+
+       private_data = talloc_get_type(module->private_data, struct private_data);
+
+       /* check if paging is supported and if there is a any control */
+       if (!private_data || !private_data->paged_supported || req->controls) {
+               /* do not touch this request paged controls not
+                * supported or explicit controls have been set or we
+                * are just not setup yet */
+               return ldb_next_request(module, req);
+       }
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb,
+                                 "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       h = init_handle(req, module, req->context, req->callback);
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct ps_context);
+
+       ac->new_req = talloc(ac, struct ldb_request);
+       if (!ac->new_req) return LDB_ERR_OPERATIONS_ERROR;
+
+       ac->new_req->controls = talloc_array(ac->new_req, struct ldb_control *, 2);
+       if (!ac->new_req->controls) return LDB_ERR_OPERATIONS_ERROR;
+
+       ac->new_req->controls[0] = talloc(ac->new_req->controls, struct ldb_control);
+       if (!ac->new_req->controls[0]) return LDB_ERR_OPERATIONS_ERROR;
+
+       control = talloc(ac->new_req->controls[0], struct ldb_paged_control);
+       if (!control) return LDB_ERR_OPERATIONS_ERROR;
+
+       control->size = PS_DEFAULT_PAGE_SIZE;
+       control->cookie = NULL;
+       control->cookie_len = 0;
+
+       ac->new_req->controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+       ac->new_req->controls[0]->critical = 1;
+       ac->new_req->controls[0]->data = control;
+
+       ac->new_req->controls[1] = NULL;
+
+       ac->new_req->operation = req->operation;
+       ac->new_req->op.search.base = req->op.search.base;
+       ac->new_req->op.search.scope = req->op.search.scope;
+       ac->new_req->op.search.tree = req->op.search.tree;
+       ac->new_req->op.search.attrs = req->op.search.attrs;
+       ac->new_req->context = ac;
+       ac->new_req->callback = ps_callback;
+       ldb_set_timeout_from_prev_req(module->ldb, req, ac->new_req);
+
+       req->handle = h;
+
+       return ldb_next_request(module, ac->new_req);
+}
+
+static int ps_continuation(struct ldb_handle *handle)
+{
+       struct ps_context *ac;
+
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = talloc_get_type(handle->private_data, struct ps_context);
+
+       /* reset the requests handle */
+       ac->new_req->handle = NULL;
+
+       return ldb_next_request(handle->module, ac->new_req);
+}
+
+static int ps_wait_none(struct ldb_handle *handle)
+{
+       struct ps_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct ps_context);
+
+       ret = ldb_wait(ac->new_req->handle, LDB_WAIT_NONE);
+
+       if (ret != LDB_SUCCESS) {
+               handle->status = ret;
+               goto done;
+       }
+
+       if (ac->new_req->handle->status != LDB_SUCCESS) {
+               handle->status = ac->new_req->handle->status;
+               goto done;
+       }
+
+       if (ac->new_req->handle->state != LDB_ASYNC_DONE) {
+               return LDB_SUCCESS;
+       } 
+
+       /* see if we need to send another request for the next batch */
+       if (ac->pending) {
+               ret = ps_continuation(handle);
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+
+               /* continue the search with the next request */
+               return LDB_SUCCESS;
+       }
+
+       ret = LDB_SUCCESS;
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+static int ps_wait_all(struct ldb_handle *handle)
+{
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = ps_wait_none(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+static int ps_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return ps_wait_all(handle);
+       } else {
+               return ps_wait_none(handle);
+       }
+}
+
+static int check_supported_paged(struct ldb_context *ldb, void *context, 
+                                struct ldb_reply *ares) 
+{
+       struct private_data *data;
+       data = talloc_get_type(context,
+                              struct private_data);
+       if (ares->type == LDB_REPLY_ENTRY) {
+               if (ldb_msg_check_string_attribute(ares->message,
+                                                  "supportedControl",
+                                                  LDB_CONTROL_PAGED_RESULTS_OID)) {
+                       data->paged_supported = True;
+               }
+       }
+       return LDB_SUCCESS;
+}
+
+
+static int ps_init(struct ldb_module *module)
+{
+       static const char *attrs[] = { "supportedControl", NULL };
+       struct private_data *data;
+       int ret;
+       struct ldb_request *req;
+
+       data = talloc(module, struct private_data);
+       if (data == NULL) {
+               return LDB_ERR_OTHER;
+       }
+       module->private_data = data;
+       data->paged_supported = False;
+
+       req = talloc(module, struct ldb_request);
+       if (req == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_SEARCH;
+       req->op.search.base = ldb_dn_new(req);
+       req->op.search.scope = LDB_SCOPE_BASE;
+
+       req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
+       if (req->op.search.tree == NULL) {
+               ldb_set_errstring(module->ldb, "Unable to parse search expression");
+               talloc_free(req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->op.search.attrs = attrs;
+       req->controls = NULL;
+       req->context = data;
+       req->callback = check_supported_paged;
+       ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
+
+       ret = ldb_next_request(module, req);
+       
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+       
+       talloc_free(req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ps_ops = {
+       .name           = "paged_searches",
+       .search         = ps_search,
+       .wait           = ps_wait,
+       .init_context   = ps_init
+};
+
+int ldb_paged_searches_init(void)
+{
+       return ldb_register_module(&ps_ops);
+}
+
diff --git a/source3/lib/ldb/modules/rdn_name.c b/source3/lib/ldb/modules/rdn_name.c
new file mode 100644 (file)
index 0000000..fce1d34
--- /dev/null
@@ -0,0 +1,337 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Bartlet 2005
+   Copyright (C) Simo Sorce     2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: rdb_name
+ *
+ *  Component: ldb rdn name module
+ *
+ *  Description: keep a consistent name attribute on objects manpulations
+ *
+ *  Author: Andrew Bartlet
+ *
+ *  Modifications:
+ *    - made the module async
+ *      Simo Sorce Mar 2006
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
+{
+       int i;
+
+       for (i = 0; i < msg->num_elements; i++) {
+               if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
+                       return &msg->elements[i];
+               }
+       }
+
+       return NULL;
+}
+
+static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_request *down_req;
+       struct ldb_message *msg;
+       struct ldb_message_element *attribute;
+       struct ldb_dn_component *rdn;
+       int i, ret;
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
+
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.add.message->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       down_req = talloc(req, struct ldb_request);
+       if (down_req == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *down_req = *req;
+
+       down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       rdn = ldb_dn_get_rdn(msg, msg->dn);
+       if (rdn == NULL) {
+               talloc_free(down_req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       /* Perhaps someone above us tried to set this? */
+       if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
+               attribute->num_values = 0;
+       }
+
+       if (ldb_msg_add_value(msg, "name", &rdn->value) != 0) {
+               talloc_free(down_req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       attribute = rdn_name_find_attribute(msg, rdn->name);
+
+       if (!attribute) {
+               if (ldb_msg_add_value(msg, rdn->name, &rdn->value) != 0) {
+                       talloc_free(down_req);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       } else {
+               const struct ldb_attrib_handler *handler = ldb_attrib_handler(module->ldb, rdn->name);
+
+               for (i = 0; i < attribute->num_values; i++) {
+                       if (handler->comparison_fn(module->ldb, msg, &rdn->value, &attribute->values[i]) == 0) {
+                               /* overwrite so it matches in case */
+                               attribute->values[i] = rdn->value;
+                               break;
+                       }
+               }
+               if (i == attribute->num_values) {
+                       ldb_debug_set(module->ldb, LDB_DEBUG_FATAL, 
+                                     "RDN mismatch on %s: %s", 
+                                     ldb_dn_linearize(msg, msg->dn), rdn->name);
+                       talloc_free(down_req);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       }
+
+       /* go on with the call chain */
+       ret = ldb_next_request(module, down_req);
+
+       /* do not free down_req as the call results may be linked to it,
+        * it will be freed when the upper level request get freed */
+       if (ret == LDB_SUCCESS) {
+               req->handle = down_req->handle;
+       }
+
+       return ret;
+}
+
+struct rename_context {
+
+       enum {RENAME_RENAME, RENAME_MODIFY} step;
+       struct ldb_request *orig_req;
+       struct ldb_request *down_req;
+       struct ldb_request *mod_req;
+};
+
+static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_handle *h;
+       struct rename_context *ac;
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n");
+
+       /* do not manipulate our control entries */
+       if (ldb_dn_is_special(req->op.rename.newdn)) {
+               return ldb_next_request(module, req);
+       }
+
+       h = talloc_zero(req, struct ldb_handle);
+       if (h == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct rename_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->orig_req = req;
+       ac->down_req = talloc(req, struct ldb_request);
+       if (ac->down_req == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *(ac->down_req) = *req;
+
+       ac->step = RENAME_RENAME;
+
+       req->handle = h;
+
+       /* rename first, modify "name" if rename is ok */
+       return ldb_next_request(module, ac->down_req);
+}
+
+static int rdn_name_rename_do_mod(struct ldb_handle *h) {
+
+       struct rename_context *ac;
+       struct ldb_dn_component *rdn;
+       struct ldb_message *msg;
+
+       ac = talloc_get_type(h->private_data, struct rename_context);
+
+       rdn = ldb_dn_get_rdn(ac, ac->orig_req->op.rename.newdn);
+       if (rdn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       ac->mod_req = talloc_zero(ac, struct ldb_request);
+
+       ac->mod_req->operation = LDB_MODIFY;
+       ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+       if (msg == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       msg->dn = ldb_dn_copy(msg, ac->orig_req->op.rename.newdn);
+       if (msg->dn == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ldb_msg_add_empty(msg, rdn->name, LDB_FLAG_MOD_REPLACE) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (ldb_msg_add_value(msg, rdn->name, &rdn->value) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (ldb_msg_add_value(msg, "name", &rdn->value) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ldb_set_timeout_from_prev_req(h->module->ldb, ac->orig_req, ac->mod_req);
+
+       ac->step = RENAME_MODIFY;
+
+       /* do the mod call */
+       return ldb_request(h->module->ldb, ac->mod_req);
+}
+
+static int rename_wait(struct ldb_handle *handle)
+{
+       struct rename_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct rename_context);
+
+       switch(ac->step) {
+       case RENAME_RENAME:
+               ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->down_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->down_req->handle->status;
+                       goto done;
+               }
+
+               if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               /* rename operation done */
+               return rdn_name_rename_do_mod(handle);
+
+       case RENAME_MODIFY:
+               ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
+               if (ret != LDB_SUCCESS) {
+                       handle->status = ret;
+                       goto done;
+               }
+               if (ac->mod_req->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->mod_req->handle->status;
+                       goto done;
+               }
+
+               if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+
+               break;
+
+       default:
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto done;
+       }
+
+       ret = LDB_SUCCESS;
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+}
+
+static int rename_wait_all(struct ldb_handle *handle) {
+
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = rename_wait(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+static int rdn_name_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return rename_wait_all(handle);
+       } else {
+               return rename_wait(handle);
+       }
+}
+
+static const struct ldb_module_ops rdn_name_ops = {
+       .name              = "rdn_name",
+       .add               = rdn_name_add,
+       .rename            = rdn_name_rename,
+       .wait              = rdn_name_wait
+};
+
+
+int ldb_rdn_name_init(void)
+{
+       return ldb_register_module(&rdn_name_ops);
+}
diff --git a/source3/lib/ldb/modules/schema.c b/source3/lib/ldb/modules/schema.c
new file mode 100644 (file)
index 0000000..556a350
--- /dev/null
@@ -0,0 +1,488 @@
+/* 
+   ldb database library
+
+   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
+     ** 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 schema module
+ *
+ *  Description: add schema check functionality
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define SCHEMA_FLAG_RESET      0
+#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 schema_attribute {
+       int flags;
+       char *name;
+};
+
+struct schema_attribute_list {
+       struct schema_attribute *attr;
+       int num;
+};
+
+struct schema_structures {
+       struct schema_attribute_list entry_attrs;
+       struct schema_attribute_list objectclasses;
+       struct schema_attribute_list required_attrs;
+       struct schema_attribute_list optional_attrs;
+};
+
+static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
+{
+       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 NULL;
+}
+
+/* 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)
+{
+       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, cnum = ss->objectclasses.num; j < msg->elements[i].num_values; j++) {
+                               ss->objectclasses.attr[cnum+j].name = (char *)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;
+               }
+       }
+       ss->entry_attrs.num += msg->num_elements;
+
+       return 0;
+}
+
+static int get_entry_attributes(struct ldb_context *ldb, const struct ldb_dn *dn, struct schema_structures *ss)
+{
+       struct ldb_result *srch;
+       int ret;
+
+       ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &srch);
+       if (ret != 1) {
+               return ret;
+       }
+       talloc_steal(ss, srch);
+
+       /* set flags to 0 as flags on search have undefined values */
+       ret = get_msg_attributes(ss, *(srch->msgs), 0);
+       if (ret != 0) {
+               talloc_free(srch);
+               return ret;
+       }
+
+       return 0;
+}
+
+/* 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->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->attr[c].name);
+                       if (len == el->values[i].length) {
+                               if (ldb_attr_cmp(list->attr[c].name, 
+                                                (char *)el->values[i].data) == 0) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+               }
+               if (!found) {
+                       list->attr[j + list->num].name = (char *)el->values[i].data;
+                       list->attr[j + list->num].flags = flags;
+                       j++;
+               }
+       }
+       list->num += j;
+
+       return 0;
+}
+
+
+/* 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 ldb_result *srch;
+       int i, j;
+       int ret;
+
+       for (i = 0; i < schema_struct->objectclasses.num; i++) {
+               char *filter;
+
+               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->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_set(module->ldb, LDB_DEBUG_ERROR, 
+                                     "Error retrieving Objectclass %s.\n", 
+                                     schema_struct->objectclasses.attr[i].name);
+                       return -1;
+               }
+               if (ret > 1) {
+                       /* Schema DB Error: Too Many Records */
+                       ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                     "Too many records found retrieving Objectclass %s.\n", 
+                                     schema_struct->objectclasses.attr[i].name);
+                       return -1;
+               }
+
+               /* Add inherited classes eliminating duplicates */
+               /* fill in required_attrs and optional_attrs attribute lists */
+               for (j = 0; j < srch->msgs[0]->num_elements; j++) {
+                       int is_aux, is_class;
+
+                       is_aux = 0;
+                       is_class = 0;
+                       if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "systemAuxiliaryclass") == 0) {
+                               is_aux = SCHEMA_FLAG_AUXILIARY;
+                               is_class = 1;
+                       }
+                       if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "auxiliaryClass") == 0) {
+                               is_aux = SCHEMA_FLAG_AUXILIARY;
+                               is_class = 1;
+                       }
+                       if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "subClassOf") == 0) {
+                               is_class = 1;
+                       }
+
+                       if (is_class) {
+                               if (add_attribute_uniq(schema_struct,
+                                                       &schema_struct->objectclasses,
+                                                       is_aux,
+                                                       &srch->msgs[0]->elements[j]) != 0) {
+                                       return -1;
+                               }
+                       } else {
+
+                               if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mustContain") == 0 ||
+                                       ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMustContain") == 0) {
+                                       if (add_attribute_uniq(schema_struct,
+                                                               &schema_struct->required_attrs,
+                                                               SCHEMA_FLAG_RESET,
+                                                               &srch->msgs[0]->elements[j]) != 0) {
+                                               return -1;
+                                       }
+                               }
+
+                               if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mayContain") == 0 ||
+                                   ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMayContain") == 0) {
+
+                                       if (add_attribute_uniq(schema_struct,
+                                                               &schema_struct->optional_attrs,
+                                                               SCHEMA_FLAG_RESET,
+                                                               &srch->msgs[0]->elements[j]) != 0) {
+                                               return -1;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* add_record */
+static int schema_add(struct ldb_module *module, struct ldb_request *req)
+{
+       const struct ldb_message *msg = req->op.add.message;
+       struct schema_structures *entry_structs;
+       unsigned int i;
+       int ret;
+
+       /* First implementation:
+               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
+       */
+
+       /* do not check on our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* TODO: check parent exists */
+
+       entry_structs = talloc_zero(module, struct schema_structures);
+       if (!entry_structs) {
+               return -1;
+       }
+
+       ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
+       if (ret != 0) {
+               talloc_free(entry_structs);
+               return ret;
+       }
+
+       ret = get_attr_list_recursive(module, entry_structs);
+       if (ret != 0) {
+               talloc_free(entry_structs);
+               return ret;
+       }
+
+       /* now check all required_attrs attributes are present */
+       for (i = 0; i < entry_structs->required_attrs.num; i++) {
+               struct schema_attribute *attr;
+
+               attr = schema_find_attribute(&entry_structs->entry_attrs,
+                                            entry_structs->required_attrs.attr[i].name);
+
+               if (attr == NULL) { /* not found */
+                       ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                     "The required_attrs attribute %s is missing.\n",
+                                     entry_structs->required_attrs.attr[i].name);
+                       talloc_free(entry_structs);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+               }
+
+               /* mark the attribute as checked */
+               attr->flags = SCHEMA_FLAG_CHECKED;
+       }
+
+       /* now check all others atribs are at least optional_attrs */
+       for (i = 0; i < entry_structs->entry_attrs.num; i++) {
+
+               if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+                       struct schema_attribute *attr;
+
+                       attr = schema_find_attribute(&entry_structs->optional_attrs,
+                                                    entry_structs->entry_attrs.attr[i].name);
+
+                       if (attr == NULL) { /* not found */
+                               ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                             "The attribute %s is not referenced by any objectclass.\n",
+                                             entry_structs->entry_attrs.attr[i].name);
+                               talloc_free(entry_structs);
+                               return LDB_ERR_OBJECT_CLASS_VIOLATION;
+                       }
+               }
+       }
+
+       talloc_free(entry_structs);
+
+       return ldb_next_request(module, req);
+}
+
+/* modify_record */
+static int schema_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       const struct ldb_message *msg = req->op.mod.message;
+       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 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.
+       */
+
+       /* do not check on our control entries */
+       if (ldb_dn_is_special(msg->dn)) {
+               return ldb_next_request(module, req);
+       }
+
+       /* allocate object structs */
+       entry_structs = talloc_zero(module, struct schema_structures);
+       if (!entry_structs) {
+               return -1;
+       }
+
+       /* 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;
+       }
+
+       /* 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;
+       }
+
+       ret = get_attr_list_recursive(module, entry_structs);
+       if (ret != 0) {
+               talloc_free(entry_structs);
+               return ret;
+       }
+
+       /* now check all required_attrs attributes are present */
+       for (i = 0; i < entry_structs->required_attrs.num; i++) {
+               struct schema_attribute *attr;
+
+               attr = schema_find_attribute(&entry_structs->entry_attrs,
+                                            entry_structs->required_attrs.attr[i].name);
+
+               if (attr == NULL) { /* not found */
+                       ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                     "The required_attrs attribute %s is missing.\n",
+                                     entry_structs->required_attrs.attr[i].name);
+                       talloc_free(entry_structs);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+               }
+
+               /* check we are not trying to delete a required attribute */
+               /* TODO: consider multivalued attrs */
+               if ((attr->flags & SCHEMA_FLAG_MOD_DELETE) != 0) {
+                       ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                     "Trying to delete the required attribute %s.\n",
+                                     attr->name);
+                       talloc_free(entry_structs);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+               }
+
+               /* mark the attribute as checked */
+               attr->flags = SCHEMA_FLAG_CHECKED;
+       }
+
+       /* now check all others atribs are at least optional_attrs */
+       for (i = 0; i < entry_structs->entry_attrs.num; i++) {
+
+               if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+                       struct schema_attribute *attr;
+
+                       attr = schema_find_attribute(&entry_structs->optional_attrs,
+                                                    entry_structs->entry_attrs.attr[i].name);
+
+                       if (attr == NULL) { /* not found */
+                               ldb_debug_set(module->ldb, LDB_DEBUG_ERROR, 
+                                             "The attribute %s is not referenced by any objectclass.\n",
+                                             entry_structs->entry_attrs.attr[i].name);
+                               talloc_free(entry_structs);
+                               return LDB_ERR_OBJECT_CLASS_VIOLATION;
+                       }
+               }
+       }
+
+       talloc_free(entry_structs);
+
+       return ldb_next_request(module, req);
+}
+
+static int schema_request(struct ldb_module *module, struct ldb_request *req)
+{
+       switch (req->operation) {
+
+       case LDB_ADD:
+               return schema_add(module, req);
+
+       case LDB_MODIFY:
+               return schema_modify(module, req);
+
+       default:
+               return ldb_next_request(module, req);
+
+       }
+}
+
+static const struct ldb_module_ops schema_ops = {
+       .name              = "schema",
+       .request           = schema_request
+};
+
+int ldb_schema_init(void)
+{
+       return ldb_register_module(&schema_ops);
+}
diff --git a/source3/lib/ldb/modules/skel.c b/source3/lib/ldb/modules/skel.c
new file mode 100644 (file)
index 0000000..2adef58
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2004
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb skel module
+ *
+ *  Description: example module
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct private_data {
+
+       char *some_private_data;
+};
+
+/* search */
+static int skel_search(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* add */
+static int skel_add(struct ldb_module *module, struct ldb_request *req){
+       return ldb_next_request(module, req);
+}
+
+/* modify */
+static int skel_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* delete */
+static int skel_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* rename */
+static int skel_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* start a transaction */
+static int skel_start_trans(struct ldb_module *module)
+{
+       return ldb_next_start_trans(module);
+}
+
+/* end a transaction */
+static int skel_end_trans(struct ldb_module *module)
+{
+       return ldb_next_end_trans(module);
+}
+
+/* delete a transaction */
+static int skel_del_trans(struct ldb_module *module)
+{
+       return ldb_next_del_trans(module);
+}
+
+static int skel_destructor(struct ldb_module *ctx)
+{
+       struct private_data *data = talloc_get_type(ctx->private_data, struct private_data);
+       /* put your clean-up functions here */
+       if (data->some_private_data) talloc_free(data->some_private_data);
+       return 0;
+}
+
+static int skel_request(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+static int skel_init(struct ldb_module *ctx)
+{
+       struct private_data *data;
+
+       data = talloc(ctx, struct private_data);
+       if (data == NULL) {
+               return 1;
+       }
+
+       data->some_private_data = NULL;
+       ctx->private_data = data;
+
+       talloc_set_destructor (ctx, skel_destructor);
+
+       return ldb_next_init(ctx);
+}
+
+static const struct ldb_module_ops skel_ops = {
+       .name              = "skel",
+       .init_context      = skel_init,
+       .search            = skel_search,
+       .add               = skel_add,
+       .modify            = skel_modify,
+       .del               = skel_delete,
+       .rename            = skel_rename,
+       .request           = skel_request,
+       .start_transaction = skel_start_trans,
+       .end_transaction   = skel_end_trans,
+       .del_transaction   = skel_del_trans,
+};
+
+int ldb_skel_init(void)
+{
+       return ldb_register_module(&skel_ops);
+}
diff --git a/source3/lib/ldb/modules/sort.c b/source3/lib/ldb/modules/sort.c
new file mode 100644 (file)
index 0000000..3a0598c
--- /dev/null
@@ -0,0 +1,445 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb server side sort control module
+ *
+ *  Description: this module sorts the results of a search
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct opaque {
+       struct ldb_context *ldb;
+       const struct ldb_attrib_handler *h;
+       const char *attribute;
+       int reverse;
+       int result;
+};
+
+struct sort_context {
+       struct ldb_module *module;
+       void *up_context;
+       int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+       char *attributeName;
+       char *orderingRule;
+       int reverse;
+
+       struct ldb_request *req;
+       struct ldb_message **msgs;
+       char **referrals;
+       struct ldb_control **controls;
+       int num_msgs;
+       int num_refs;
+
+       const struct ldb_attrib_handler *h;
+       int sort_result;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+                                           void *context,
+                                           int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+       struct sort_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(mem_ctx, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct sort_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = (void *)ac;
+
+       h->state = LDB_ASYNC_INIT;
+       h->status = LDB_SUCCESS;
+
+       ac->module = module;
+       ac->up_context = context;
+       ac->up_callback = callback;
+
+       return h;
+}
+
+static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
+{
+       struct ldb_control **controls;
+       struct ldb_sort_resp_control *resp;
+       int i;
+
+       if (*ctrls) {
+               controls = *ctrls;
+               for (i = 0; controls[i]; i++);
+               controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
+       } else {
+               i = 0;
+               controls = talloc_array(mem_ctx, struct ldb_control *, 2);
+       }
+       if (! controls )
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       *ctrls = controls;
+
+       controls[i+1] = NULL;
+       controls[i] = talloc(controls, struct ldb_control);
+       if (! controls[i] )
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
+       controls[i]->critical = 0;
+
+       resp = talloc(controls[i], struct ldb_sort_resp_control);
+       if (! resp )
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       resp->result = result;
+       resp->attr_desc = talloc_strdup(resp, desc);
+
+       if (! resp->attr_desc )
+               return LDB_ERR_OPERATIONS_ERROR;
+       
+       controls[i]->data = resp;
+
+       return LDB_SUCCESS;
+}
+
+static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
+{
+       struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
+       struct ldb_message_element *el1, *el2;
+
+       if (ac->sort_result != 0) {
+               /* an error occurred previously,
+                * let's exit the sorting by returning always 0 */
+               return 0;
+       }
+
+       el1 = ldb_msg_find_element(*msg1, ac->attributeName);
+       el2 = ldb_msg_find_element(*msg2, ac->attributeName);
+
+       if (!el1 || !el2) {
+               /* the attribute was not found return and
+                * set an error */
+               ac->sort_result = 53;
+               return 0;
+       }
+
+       if (ac->reverse)
+               return ac->h->comparison_fn(ac->module->ldb, ac, &el2->values[0], &el1->values[0]);
+
+       return ac->h->comparison_fn(ac->module->ldb, ac, &el1->values[0], &el2->values[0]);
+}
+
+static int server_sort_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct sort_context *ac = NULL;
+       
+       if (!context || !ares) {
+               ldb_set_errstring(ldb, "NULL Context or Result in callback");
+               goto error;
+       }       
+
+               ac = talloc_get_type(context, struct sort_context);
+
+       if (ares->type == LDB_REPLY_ENTRY) {
+               ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
+               if (! ac->msgs) {
+                       goto error;
+               }
+
+               ac->msgs[ac->num_msgs + 1] = NULL;
+
+               ac->msgs[ac->num_msgs] = talloc_move(ac->msgs, &ares->message);
+               ac->num_msgs++;
+       }
+
+       if (ares->type == LDB_REPLY_REFERRAL) {
+               ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
+               if (! ac->referrals) {
+                       goto error;
+               }
+
+               ac->referrals[ac->num_refs + 1] = NULL;
+               ac->referrals[ac->num_refs] = talloc_move(ac->referrals, &ares->referral);
+
+               ac->num_refs++;
+       }
+
+       if (ares->type == LDB_REPLY_DONE) {
+               ac->controls = talloc_move(ac, &ares->controls);
+       }
+
+       talloc_free(ares);
+       return LDB_SUCCESS;
+
+error:
+       talloc_free(ares);
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_control *control;
+       struct ldb_server_sort_control **sort_ctrls;
+       struct ldb_control **saved_controls;
+       struct sort_context *ac;
+       struct ldb_handle *h;
+       int ret;
+
+       /* check if there's a paged request control */
+       control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
+       if (control == NULL) {
+               /* not found go on */
+               return ldb_next_request(module, req);
+       }
+
+       req->handle = NULL;
+
+       if (!req->callback || !req->context) {
+               ldb_set_errstring(module->ldb,
+                                 "Async interface called with NULL callback function or NULL context");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       h = init_handle(req, module, req->context, req->callback);
+       if (!h) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac = talloc_get_type(h->private_data, struct sort_context);
+
+       sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
+       if (!sort_ctrls) {
+               return LDB_ERR_PROTOCOL_ERROR;
+       }
+
+       /* FIXME: we do not support more than one attribute for sorting right now */
+       /* FIXME: we need to check if the attribute type exist or return an error */
+               
+       if (sort_ctrls[1] != NULL) {
+               if (control->critical) {
+                       struct ldb_reply *ares;
+
+                       ares = talloc_zero(req, struct ldb_reply);
+                       if (!ares)
+                               return LDB_ERR_OPERATIONS_ERROR;
+
+                       /* 53 = unwilling to perform */
+                       ares->type = LDB_REPLY_DONE;
+                       if ((ret = build_response(ares, &ares->controls, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
+                               return ret;
+                       }
+
+                       h->status = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+                       h->state = LDB_ASYNC_DONE;
+                       ret = ac->up_callback(module->ldb, ac->up_context, ares);
+
+                       return ret;
+               } else {
+                       /* just pass the call down and don't do any sorting */
+                       ldb_next_request(module, req);
+               }
+       }
+
+       ac->attributeName = sort_ctrls[0]->attributeName;
+       ac->orderingRule = sort_ctrls[0]->orderingRule;
+       ac->reverse = sort_ctrls[0]->reverse;
+
+       ac->req = talloc(req, struct ldb_request);
+       if (!ac->req)
+               return LDB_ERR_OPERATIONS_ERROR;
+
+       ac->req->operation = req->operation;
+       ac->req->op.search.base = req->op.search.base;
+       ac->req->op.search.scope = req->op.search.scope;
+       ac->req->op.search.tree = req->op.search.tree;
+       ac->req->op.search.attrs = req->op.search.attrs;
+       ac->req->controls = req->controls;
+
+       /* save it locally and remove it from the list */
+       /* we do not need to replace them later as we
+        * are keeping the original req intact */
+       if (!save_controls(control, ac->req, &saved_controls)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac->req->context = ac;
+       ac->req->callback = server_sort_search_callback;
+       ldb_set_timeout_from_prev_req(module->ldb, req, ac->req);
+
+       req->handle = h;
+
+       return ldb_next_request(module, ac->req);
+}
+
+static int server_sort_results(struct ldb_handle *handle)
+{
+       struct sort_context *ac;
+       struct ldb_reply *ares;
+       int i, ret;
+
+       ac = talloc_get_type(handle->private_data, struct sort_context);
+
+       ac->h = ldb_attrib_handler(ac->module->ldb, ac->attributeName);
+       ac->sort_result = 0;
+
+       ldb_qsort(ac->msgs, ac->num_msgs,
+                 sizeof(struct ldb_message *),
+                 ac, (ldb_qsort_cmp_fn_t)sort_compare);
+
+       for (i = 0; i < ac->num_msgs; i++) {
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       return handle->status;
+               }
+
+               ares->type = LDB_REPLY_ENTRY;
+               ares->message = talloc_move(ares, &ac->msgs[i]);
+               
+               handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+               if (handle->status != LDB_SUCCESS) {
+                       return handle->status;
+               }
+       }
+
+       for (i = 0; i < ac->num_refs; i++) {
+               ares = talloc_zero(ac, struct ldb_reply);
+               if (!ares) {
+                       handle->status = LDB_ERR_OPERATIONS_ERROR;
+                       return handle->status;
+               }
+
+               ares->type = LDB_REPLY_REFERRAL;
+               ares->referral = talloc_move(ares, &ac->referrals[i]);
+               
+               handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+               if (handle->status != LDB_SUCCESS) {
+                       return handle->status;
+               }
+       }
+
+       ares = talloc_zero(ac, struct ldb_reply);
+       if (!ares) {
+               handle->status = LDB_ERR_OPERATIONS_ERROR;
+               return handle->status;
+       }
+
+       ares->type = LDB_REPLY_DONE;
+       ares->controls = talloc_move(ares, &ac->controls);
+               
+       handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+       if (handle->status != LDB_SUCCESS) {
+               return handle->status;
+       }
+
+       if ((ret = build_response(ac, &ac->controls, ac->sort_result, "sort control is not complete yet")) != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int server_sort_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       struct sort_context *ac;
+       int ret;
+    
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ac = talloc_get_type(handle->private_data, struct sort_context);
+
+       ret = ldb_wait(ac->req->handle, type);
+
+       if (ret != LDB_SUCCESS) {
+               handle->status = ret;
+               return ret;
+       }
+               
+       handle->state = ac->req->handle->state;
+       handle->status = ac->req->handle->status;
+
+       if (handle->status != LDB_SUCCESS) {
+               return handle->status;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               ret = server_sort_results(handle);
+       }
+
+       return ret;
+}
+
+static int server_sort_init(struct ldb_module *module)
+{
+       struct ldb_request *req;
+       int ret;
+
+       req = talloc(module, struct ldb_request);
+       if (req == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       req->operation = LDB_REQ_REGISTER_CONTROL;
+       req->op.reg_control.oid = LDB_CONTROL_SERVER_SORT_OID;
+       req->controls = NULL;
+
+       ret = ldb_request(module->ldb, req);
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(module->ldb, LDB_DEBUG_ERROR, "server_sort: Unable to register control with rootdse!\n");
+               talloc_free(req);
+               return LDB_ERR_OTHER;
+       }
+
+       talloc_free(req);
+       return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops server_sort_ops = {
+       .name              = "server_sort",
+       .search            = server_sort_search,
+       .wait              = server_sort_wait,
+       .init_context      = server_sort_init
+};
+
+int ldb_sort_init(void)
+{
+       return ldb_register_module(&server_sort_ops);
+}
diff --git a/source3/lib/ldb/samba/README b/source3/lib/ldb/samba/README
new file mode 100644 (file)
index 0000000..3fa4715
--- /dev/null
@@ -0,0 +1,7 @@
+This directory contains Samba specific extensions to ldb. It also
+serves as example code on how to extend ldb for your own application.
+
+The main extension Samba uses is to provide ldif encode/decode
+routines for specific attributes, so users can get nice pretty
+printing of attributes in ldbedit, while the attributes are stored in
+the standard NDR format in the database.
diff --git a/source3/lib/ldb/samba/ldif_handlers.c b/source3/lib/ldb/samba/ldif_handlers.c
new file mode 100644 (file)
index 0000000..b490c8f
--- /dev/null
@@ -0,0 +1,480 @@
+/* 
+   ldb database library - ldif handlers for Samba
+
+   Copyright (C) Andrew Tridgell 2005
+   Copyright (C) Andrew Bartlett 2006
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+
+/*
+  convert a ldif formatted objectSid to a NDR formatted blob
+*/
+static int ldif_read_objectSid(struct ldb_context *ldb, void *mem_ctx,
+                              const struct ldb_val *in, struct ldb_val *out)
+{
+       struct dom_sid *sid;
+       NTSTATUS status;
+       sid = dom_sid_parse_talloc(mem_ctx, (const char *)in->data);
+       if (sid == NULL) {
+               return -1;
+       }
+       status = ndr_push_struct_blob(out, mem_ctx, sid, 
+                                     (ndr_push_flags_fn_t)ndr_push_dom_sid);
+       talloc_free(sid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  convert a NDR formatted blob to a ldif formatted objectSid
+*/
+static int ldif_write_objectSid(struct ldb_context *ldb, void *mem_ctx,
+                               const struct ldb_val *in, struct ldb_val *out)
+{
+       struct dom_sid *sid;
+       NTSTATUS status;
+       sid = talloc(mem_ctx, struct dom_sid);
+       if (sid == NULL) {
+               return -1;
+       }
+       status = ndr_pull_struct_blob(in, sid, sid, 
+                                     (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(sid);
+               return -1;
+       }
+       out->data = (uint8_t *)dom_sid_string(mem_ctx, sid);
+       talloc_free(sid);
+       if (out->data == NULL) {
+               return -1;
+       }
+       out->length = strlen((const char *)out->data);
+       return 0;
+}
+
+static BOOL ldb_comparision_objectSid_isString(const struct ldb_val *v)
+{
+       /* see if the input if null-terninated */
+       if (v->data[v->length] != '\0') return False;
+       
+       if (strncmp("S-", (const char *)v->data, 2) != 0) return False;
+       return True;
+}
+
+/*
+  compare two objectSids
+*/
+static int ldb_comparison_objectSid(struct ldb_context *ldb, void *mem_ctx,
+                                   const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       if (ldb_comparision_objectSid_isString(v1) && ldb_comparision_objectSid_isString(v2)) {
+               return strcmp((const char *)v1->data, (const char *)v2->data);
+       } else if (ldb_comparision_objectSid_isString(v1)
+                  && !ldb_comparision_objectSid_isString(v2)) {
+               struct ldb_val v;
+               int ret;
+               if (ldif_read_objectSid(ldb, mem_ctx, v1, &v) != 0) {
+                       return -1;
+               }
+               ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+               talloc_free(v.data);
+               return ret;
+       } else if (!ldb_comparision_objectSid_isString(v1)
+                  && ldb_comparision_objectSid_isString(v2)) {
+               struct ldb_val v;
+               int ret;
+               if (ldif_read_objectSid(ldb, mem_ctx, v2, &v) != 0) {
+                       return -1;
+               }
+               ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+               talloc_free(v.data);
+               return ret;
+       }
+       return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+  canonicalise a objectSid
+*/
+static int ldb_canonicalise_objectSid(struct ldb_context *ldb, void *mem_ctx,
+                                     const struct ldb_val *in, struct ldb_val *out)
+{
+       if (ldb_comparision_objectSid_isString(in)) {
+               return ldif_read_objectSid(ldb, mem_ctx, in, out);
+       }
+       return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+/*
+  convert a ldif formatted objectGUID to a NDR formatted blob
+*/
+static int ldif_read_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+                               const struct ldb_val *in, struct ldb_val *out)
+{
+       struct GUID guid;
+       NTSTATUS status;
+
+       status = GUID_from_string((const char *)in->data, &guid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+
+       status = ndr_push_struct_blob(out, mem_ctx, &guid,
+                                     (ndr_push_flags_fn_t)ndr_push_GUID);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  convert a NDR formatted blob to a ldif formatted objectGUID
+*/
+static int ldif_write_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+                                const struct ldb_val *in, struct ldb_val *out)
+{
+       struct GUID guid;
+       NTSTATUS status;
+       status = ndr_pull_struct_blob(in, mem_ctx, &guid,
+                                     (ndr_pull_flags_fn_t)ndr_pull_GUID);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+       out->data = (uint8_t *)GUID_string(mem_ctx, &guid);
+       if (out->data == NULL) {
+               return -1;
+       }
+       out->length = strlen((const char *)out->data);
+       return 0;
+}
+
+static BOOL ldb_comparision_objectGUID_isString(const struct ldb_val *v)
+{
+       struct GUID guid;
+       NTSTATUS status;
+
+       /* see if the input if null-terninated */
+       if (v->data[v->length] != '\0') return False;
+
+       if (v->length < 33) return False;
+
+       status = GUID_from_string((const char *)v->data, &guid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       return True;
+}
+
+/*
+  compare two objectGUIDs
+*/
+static int ldb_comparison_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+                                    const struct ldb_val *v1, const struct ldb_val *v2)
+{
+       if (ldb_comparision_objectGUID_isString(v1) && ldb_comparision_objectGUID_isString(v2)) {
+               return strcmp((const char *)v1->data, (const char *)v2->data);
+       } else if (ldb_comparision_objectGUID_isString(v1)
+                  && !ldb_comparision_objectGUID_isString(v2)) {
+               struct ldb_val v;
+               int ret;
+               if (ldif_read_objectGUID(ldb, mem_ctx, v1, &v) != 0) {
+                       return -1;
+               }
+               ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+               talloc_free(v.data);
+               return ret;
+       } else if (!ldb_comparision_objectGUID_isString(v1)
+                  && ldb_comparision_objectGUID_isString(v2)) {
+               struct ldb_val v;
+               int ret;
+               if (ldif_read_objectGUID(ldb, mem_ctx, v2, &v) != 0) {
+                       return -1;
+               }
+               ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+               talloc_free(v.data);
+               return ret;
+       }
+       return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+  canonicalise a objectGUID
+*/
+static int ldb_canonicalise_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+                                      const struct ldb_val *in, struct ldb_val *out)
+{
+       if (ldb_comparision_objectGUID_isString(in)) {
+               return ldif_read_objectGUID(ldb, mem_ctx, in, out);
+       }
+       return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+
+/*
+  convert a ldif (SDDL) formatted ntSecurityDescriptor to a NDR formatted blob
+*/
+static int ldif_read_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+                                         const struct ldb_val *in, struct ldb_val *out)
+{
+       struct security_descriptor *sd;
+       NTSTATUS status;
+
+       sd = sddl_decode(mem_ctx, (const char *)in->data, NULL);
+       if (sd == NULL) {
+               return -1;
+       }
+       status = ndr_push_struct_blob(out, mem_ctx, sd, 
+                                     (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+       talloc_free(sd);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+       return 0;
+}
+
+/*
+  convert a NDR formatted blob to a ldif formatted ntSecurityDescriptor (SDDL format)
+*/
+static int ldif_write_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+                                          const struct ldb_val *in, struct ldb_val *out)
+{
+       struct security_descriptor *sd;
+       NTSTATUS status;
+
+       sd = talloc(mem_ctx, struct security_descriptor);
+       if (sd == NULL) {
+               return -1;
+       }
+       status = ndr_pull_struct_blob(in, sd, sd, 
+                                     (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(sd);
+               return -1;
+       }
+       out->data = (uint8_t *)sddl_encode(mem_ctx, sd, NULL);
+       talloc_free(sd);
+       if (out->data == NULL) {
+               return -1;
+       }
+       out->length = strlen((const char *)out->data);
+       return 0;
+}
+
+/* 
+   canonicolise an objectCategory.  We use the short form as the cannoical form:
+   cn=Person,cn=Schema,cn=Configuration,<basedn> becomes 'person'
+*/
+
+static int ldif_canonicalise_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+                                           const struct ldb_val *in, struct ldb_val *out)
+{
+       struct ldb_dn *dn1 = NULL;
+       char *oc1;
+
+       dn1 = ldb_dn_explode(mem_ctx, (char *)in->data);
+       if (dn1 == NULL) {
+               oc1 = talloc_strndup(mem_ctx, (char *)in->data, in->length);
+       } else if (dn1->comp_num >= 1 && strcasecmp(dn1->components[0].name, "cn") == 0) {
+               oc1 = talloc_strndup(mem_ctx, (char *)dn1->components[0].value.data, 
+                                    dn1->components[0].value.length);
+       } else {
+               return -1;
+       }
+
+       oc1 = ldb_casefold(ldb, mem_ctx, oc1);
+       out->data = (void *)oc1;
+       out->length = strlen(oc1);
+       return 0;
+}
+
+static int ldif_comparison_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+                                         const struct ldb_val *v1,
+                                         const struct ldb_val *v2)
+{
+       struct ldb_dn *dn1 = NULL, *dn2 = NULL;
+       const char *oc1, *oc2;
+
+       dn1 = ldb_dn_explode(mem_ctx, (char *)v1->data);
+       if (dn1 == NULL) {
+               oc1 = talloc_strndup(mem_ctx, (char *)v1->data, v1->length);
+       } else if (dn1->comp_num >= 1 && strcasecmp(dn1->components[0].name, "cn") == 0) {
+               oc1 = talloc_strndup(mem_ctx, (char *)dn1->components[0].value.data, 
+                                    dn1->components[0].value.length);
+       } else {
+               oc1 = NULL;
+       }
+
+       dn2 = ldb_dn_explode(mem_ctx, (char *)v2->data);
+       if (dn2 == NULL) {
+               oc2 = talloc_strndup(mem_ctx, (char *)v2->data, v2->length);
+       } else if (dn2->comp_num >= 2 && strcasecmp(dn2->components[0].name, "cn") == 0) {
+               oc2 = talloc_strndup(mem_ctx, (char *)dn2->components[0].value.data, 
+                                    dn2->components[0].value.length);
+       } else {
+               oc2 = NULL;
+       }
+
+       oc1 = ldb_casefold(ldb, mem_ctx, oc1);
+       oc2 = ldb_casefold(ldb, mem_ctx, oc2);
+       if (!oc1 && oc2) {
+               return -1;
+       } 
+       if (oc1 && !oc2) {
+               return 1;
+       }       
+       if (!oc1 && !oc2) {
+               return -1;
+       }
+
+       return strcmp(oc1, oc2);
+}
+
+static const struct ldb_attrib_handler samba_handlers[] = {
+       { 
+               .attr            = "objectSid",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectSid,
+               .ldif_write_fn   = ldif_write_objectSid,
+               .canonicalise_fn = ldb_canonicalise_objectSid,
+               .comparison_fn   = ldb_comparison_objectSid
+       },
+       { 
+               .attr            = "securityIdentifier",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectSid,
+               .ldif_write_fn   = ldif_write_objectSid,
+               .canonicalise_fn = ldb_canonicalise_objectSid,
+               .comparison_fn   = ldb_comparison_objectSid
+       },
+       { 
+               .attr            = "ntSecurityDescriptor",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_ntSecurityDescriptor,
+               .ldif_write_fn   = ldif_write_ntSecurityDescriptor,
+               .canonicalise_fn = ldb_handler_copy,
+               .comparison_fn   = ldb_comparison_binary
+       },
+       { 
+               .attr            = "objectGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "invocationId",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "schemaIDGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "attributeSecurityGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "parentGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "siteGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "pKTGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "fRSVersionGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "fRSReplicaSetGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "netbootGUID",
+               .flags           = 0,
+               .ldif_read_fn    = ldif_read_objectGUID,
+               .ldif_write_fn   = ldif_write_objectGUID,
+               .canonicalise_fn = ldb_canonicalise_objectGUID,
+               .comparison_fn   = ldb_comparison_objectGUID
+       },
+       { 
+               .attr            = "objectCategory",
+               .flags           = 0,
+               .ldif_read_fn    = ldb_handler_copy,
+               .ldif_write_fn   = ldb_handler_copy,
+               .canonicalise_fn = ldif_canonicalise_objectCategory,
+               .comparison_fn   = ldif_comparison_objectCategory,
+       }
+};
+
+/*
+  register the samba ldif handlers
+*/
+int ldb_register_samba_handlers(struct ldb_context *ldb)
+{
+       return ldb_set_attrib_handlers(ldb, samba_handlers, ARRAY_SIZE(samba_handlers));
+}
diff --git a/source3/lib/ldb/sqlite3.m4 b/source3/lib/ldb/sqlite3.m4
new file mode 100644 (file)
index 0000000..49e3807
--- /dev/null
@@ -0,0 +1,61 @@
+########################################################
+# Compile with SQLITE3 support?
+
+SQLITE3_LIBS=""
+with_sqlite3_support=no
+AC_MSG_CHECKING([for SQLITE3 support])
+
+AC_ARG_WITH(sqlite3,
+AS_HELP_STRING([--with-sqlite3],[SQLITE3 backend support (default=no)]),
+[ case "$withval" in
+    yes|no|auto)
+       with_sqlite3_support=$withval
+       ;;
+  esac ])
+
+AC_MSG_RESULT($with_sqlite3_support)
+
+if test x"$with_sqlite3_support" != x"no"; then
+  ##################################################################
+  # first test for sqlite3.h
+  AC_CHECK_HEADERS(sqlite3.h)
+  
+  if test x"$ac_cv_header_sqlite3_h" != x"yes"; then
+       if test x"$with_sqlite3_support" = x"yes"; then
+        AC_MSG_ERROR(sqlite3.h is needed for SQLITE3 support)
+       else
+        AC_MSG_WARN(sqlite3.h is needed for SQLITE3 support)
+       fi
+       
+       with_sqlite3_support=no
+  fi
+fi
+
+if test x"$with_sqlite3_support" != x"no"; then
+  ac_save_LIBS=$LIBS
+
+  ########################################################
+  # now see if we can find the sqlite3 libs in standard paths
+  AC_CHECK_LIB_EXT(sqlite3, SQLITE3_LIBS, sqlite3_open)
+
+  if test x"$ac_cv_lib_ext_sqlite3_sqlite3_open" = x"yes"; then
+    AC_DEFINE(HAVE_SQLITE3,1,[Whether sqlite3 is available])
+    AC_MSG_CHECKING(whether SQLITE3 support is used)
+    AC_MSG_RESULT(yes)
+    with_sqlite3_support=yes
+    SMB_ENABLE(SQLITE3,YES)
+  else
+    if test x"$with_sqlite3_support" = x"yes"; then
+       AC_MSG_ERROR(libsqlite3 is needed for SQLITE3 support)
+    else
+       AC_MSG_WARN(libsqlite3 is needed for SQLITE3 support)
+    fi
+
+    SQLITE3_LIBS=""
+    with_sqlite3_support=no
+  fi
+
+  LIBS=$ac_save_LIBS;
+fi
+
+SMB_EXT_LIB(SQLITE3,[${SQLITE3_LIBS}],[${SQLITE3_CFLAGS}],[${SQLITE3_CPPFLAGS}],[${SQLITE3_LDFLAGS}])
diff --git a/source3/lib/ldb/swig/Ldb.py b/source3/lib/ldb/swig/Ldb.py
new file mode 100644 (file)
index 0000000..c7e6191
--- /dev/null
@@ -0,0 +1,179 @@
+"""Provide a more Pythonic and object-oriented interface to ldb."""
+
+#
+# Swig interface to Samba
+#
+# Copyright (C) Tim Potter 2006
+#
+# 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.
+#
+
+#
+# Interface notes:
+#
+#   - should an empty dn be represented as None, or an empty string?
+#
+#   - should single-valued attributes be a string, or a list with one
+#     element?
+#
+
+from ldb import *
+
+# Global initialisation
+
+result = ldb_global_init()
+
+if result != 0:
+    raise LdbError, (result, 'ldb_global_init failed')
+
+# Ldb exceptions
+
+class LdbError(Exception):
+    """An exception raised when a ldb error occurs.
+    The exception data is a tuple consisting of the ldb number and a
+    string description of the error."""
+    pass
+
+# Ldb classes
+
+class LdbMessage:
+    """A class representing a ldb message as a Python dictionary."""
+    
+    def __init__(self):
+        self.mem_ctx = talloc_init(None)
+        self.msg = ldb_msg_new(self.mem_ctx)
+
+    def __del__(self):
+        if self.mem_ctx is not None:
+            talloc_free(self.mem_ctx)
+            self.mem_ctx = None
+            self.msg = None
+
+    # Make the dn attribute of the object dynamic
+
+    def __getattr__(self, attr):
+        if attr == 'dn':
+            return ldb_dn_linearize(None, self.msg.dn)
+        return self.__dict__[attr]
+
+    def __setattr__(self, attr, value):
+        if attr == 'dn':
+            self.msg.dn = ldb_dn_explode(self.msg, value)
+            if self.msg.dn == None:
+                err = LDB_ERR_INVALID_DN_SYNTAX
+                raise LdbError(err, ldb_strerror(err))
+            return
+        self.__dict__[attr] = value
+        
+    # Get and set individual elements
+
+    def __getitem__(self, key):
+
+        elt = ldb_msg_find_element(self.msg, key)
+
+        if elt is None:
+            raise KeyError, "No such attribute '%s'" % key
+
+        return [ldb_val_array_getitem(elt.values, i)
+                for i in range(elt.num_values)]
+
+    def __setitem__(self, key, value):
+        ldb_msg_remove_attr(self.msg, key)
+        if type(value) in (list, tuple):
+            [ldb_msg_add_value(self.msg, key, v) for v in value]
+        else:
+            ldb_msg_add_value(self.msg, key, value)
+
+    # Dictionary interface
+    # TODO: move to iterator based interface
+
+    def len(self):
+        return self.msg.num_elements
+
+    def keys(self):
+        return [ldb_message_element_array_getitem(self.msg.elements, i).name
+                for i in range(self.msg.num_elements)]
+
+    def values(self):
+        return [self[k] for k in self.keys()]
+
+    def items(self):
+        return [(k, self[k]) for k in self.keys()]
+
+    # Misc stuff
+
+    def sanity_check(self):
+        return ldb_msg_sanity_check(self.msg)
+
+class Ldb:
+    """A class representing a binding to a ldb file."""
+
+    def __init__(self, url, flags = 0):
+        """Initialise underlying ldb."""
+    
+        self.mem_ctx = talloc_init('mem_ctx for ldb 0x%x' % id(self))
+        self.ldb_ctx = ldb_init(self.mem_ctx)
+
+        result = ldb_connect(self.ldb_ctx, url, flags, None)
+
+        if result != LDB_SUCCESS:
+            raise LdbError, (result, ldb_strerror(result))
+        
+    def __del__(self):
+        """Called when the object is to be garbage collected."""
+        self.close()
+
+    def close(self):
+        """Close down a ldb."""
+        if self.mem_ctx is not None:
+            talloc_free(self.mem_ctx)
+            self.mem_ctx = None
+            self.ldb_ctx = None
+
+    def _ldb_call(self, fn, *args):
+        """Call a ldb function with args.  Raise a LdbError exception
+        if the function returns a non-zero return value."""
+        
+        result = fn(*args)
+
+        if result != LDB_SUCCESS:
+            raise LdbError, (result, ldb_strerror(result))
+
+    def search(self, expression):
+        """Search a ldb for a given expression."""
+
+        self._ldb_call(ldb_search, self.ldb_ctx, None, LDB_SCOPE_DEFAULT,
+                       expression, None);
+
+        return [LdbMessage(ldb_message_ptr_array_getitem(result.msgs, ndx))
+                for ndx in range(result.count)]
+
+    def delete(self, dn):
+        """Delete a dn."""
+
+        _dn = ldb_dn_explode(self.ldb_ctx, dn)
+
+        self._ldb_call(ldb_delete, self.ldb_ctx, _dn)
+
+    def rename(self, olddn, newdn):
+        """Rename a dn."""
+        
+        _olddn = ldb_dn_explode(self.ldb_ctx, olddn)
+        _newdn = ldb_dn_explode(self.ldb_ctx, newdn)
+        
+        self._ldb_call(ldb_rename, self.ldb_ctx, _olddn, _newdn)
+
+    def add(self, m):
+        self._ldb_call(ldb_add, self.ldb_ctx, m.msg)
diff --git a/source3/lib/ldb/swig/ldb.i b/source3/lib/ldb/swig/ldb.i
new file mode 100644 (file)
index 0000000..09d3461
--- /dev/null
@@ -0,0 +1,240 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Swig interface to ldb.
+
+   Copyright (C) 2005,2006 Tim Potter <tpot@samba.org>
+   Copyright (C) 2006 Simo Sorce <idra@samba.org>
+
+     ** 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
+*/
+
+%module ldb
+
+%{
+
+/* Some typedefs to help swig along */
+
+typedef unsigned char uint8_t;
+typedef unsigned long long uint64_t;
+typedef long long int64_t;
+
+/* Include headers */
+
+#include "lib/ldb/include/ldb.h"
+#include "lib/talloc/talloc.h"
+
+%}
+
+%include "carrays.i"
+%include "exception.i"
+
+/*
+ * Constants
+ */
+
+#define LDB_SUCCESS                            0
+#define LDB_ERR_OPERATIONS_ERROR               1
+#define LDB_ERR_PROTOCOL_ERROR                 2
+#define LDB_ERR_TIME_LIMIT_EXCEEDED            3
+#define LDB_ERR_SIZE_LIMIT_EXCEEDED            4
+#define LDB_ERR_COMPARE_FALSE                  5
+#define LDB_ERR_COMPARE_TRUE                   6
+#define LDB_ERR_AUTH_METHOD_NOT_SUPPORTED      7
+#define LDB_ERR_STRONG_AUTH_REQUIRED           8
+/* 9 RESERVED */
+#define LDB_ERR_REFERRAL                       10
+#define LDB_ERR_ADMIN_LIMIT_EXCEEDED           11
+#define LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION 12
+#define LDB_ERR_CONFIDENTIALITY_REQUIRED       13
+#define LDB_ERR_SASL_BIND_IN_PROGRESS          14
+#define LDB_ERR_NO_SUCH_ATTRIBUTE              16
+#define LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE       17
+#define LDB_ERR_INAPPROPRIATE_MATCHING         18
+#define LDB_ERR_CONSTRAINT_VIOLATION           19
+#define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS      20
+#define LDB_ERR_INVALID_ATTRIBUTE_SYNTAX       21
+/* 22-31 unused */
+#define LDB_ERR_NO_SUCH_OBJECT                 32
+#define LDB_ERR_ALIAS_PROBLEM                  33
+#define LDB_ERR_INVALID_DN_SYNTAX              34
+/* 35 RESERVED */
+#define LDB_ERR_ALIAS_DEREFERENCING_PROBLEM    36
+/* 37-47 unused */
+#define LDB_ERR_INAPPROPRIATE_AUTHENTICATION   48
+#define LDB_ERR_INVALID_CREDENTIALS            49
+#define LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS     50
+#define LDB_ERR_BUSY                           51
+#define LDB_ERR_UNAVAILABLE                    52
+#define LDB_ERR_UNWILLING_TO_PERFORM           53
+#define LDB_ERR_LOOP_DETECT                    54
+/* 55-63 unused */
+#define LDB_ERR_NAMING_VIOLATION               64
+#define LDB_ERR_OBJECT_CLASS_VIOLATION         65
+#define LDB_ERR_NOT_ALLOWED_ON_NON_LEAF                66
+#define LDB_ERR_NOT_ALLOWED_ON_RDN             67
+#define LDB_ERR_ENTRY_ALREADY_EXISTS           68
+#define LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED   69
+/* 70 RESERVED FOR CLDAP */
+#define LDB_ERR_AFFECTS_MULTIPLE_DSAS          71
+/* 72-79 unused */
+#define LDB_ERR_OTHER                          80
+
+enum ldb_scope {LDB_SCOPE_DEFAULT=-1, 
+               LDB_SCOPE_BASE=0, 
+               LDB_SCOPE_ONELEVEL=1,
+               LDB_SCOPE_SUBTREE=2};
+
+/* 
+ * Wrap struct ldb_context
+ */
+
+/* The ldb functions will crash if a NULL ldb context is passed so
+   catch this before it happens. */
+
+%typemap(check) struct ldb_context* {
+       if ($1 == NULL)
+               SWIG_exception(SWIG_ValueError, 
+                       "ldb context must be non-NULL");
+}
+
+/* 
+ * Wrap a small bit of talloc
+ */
+
+/* Use talloc_init() to create a parameter to pass to ldb_init().  Don't
+   forget to free it using talloc_free() afterwards. */
+
+TALLOC_CTX *talloc_init(char *name);
+int talloc_free(TALLOC_CTX *ptr);
+
+/*
+ * Wrap struct ldb_val
+ */
+
+%typemap(in) struct ldb_val *INPUT (struct ldb_val temp) {
+       $1 = &temp;
+       if (!PyString_Check($input)) {
+               PyErr_SetString(PyExc_TypeError, "string arg expected");
+               return NULL;
+       }
+       $1->length = PyString_Size($input);
+       $1->data = PyString_AsString($input);
+}
+
+%typemap(out) struct ldb_val {
+       $result = PyString_FromStringAndSize($1.data, $1.length);
+}
+
+/*
+ * Wrap struct ldb_result
+ */
+
+%typemap(in, numinputs=0) struct ldb_result **OUT (struct ldb_result *temp_ldb_result) {
+       $1 = &temp_ldb_result;
+}
+
+%typemap(argout) struct ldb_result ** {
+       resultobj = SWIG_NewPointerObj(*$1, SWIGTYPE_p_ldb_result, 0);
+}      
+
+%types(struct ldb_result *);
+
+/*
+ * Wrap struct ldb_message_element
+ */
+
+%array_functions(struct ldb_val, ldb_val_array);
+
+struct ldb_message_element {
+       unsigned int flags;
+       const char *name;
+       unsigned int num_values;
+       struct ldb_val *values;
+};
+
+/*
+ * Wrap struct ldb_message
+ */
+
+%array_functions(struct ldb_message_element, ldb_message_element_array);
+
+struct ldb_message {
+       struct ldb_dn *dn;
+       unsigned int num_elements;
+       struct ldb_message_element *elements;
+       void *private_data;
+};
+
+/*
+ * Wrap struct ldb_result
+ */
+
+%array_functions(struct ldb_message *, ldb_message_ptr_array);
+
+struct ldb_result {
+       unsigned int count;
+       struct ldb_message **msgs;
+       char **refs;
+       struct ldb_control **controls;
+};
+
+/*
+ * Wrap ldb functions 
+ */
+
+/* Initialisation */
+
+int ldb_global_init(void);
+struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx);
+
+/* Error handling */
+
+const char *ldb_errstring(struct ldb_context *ldb);
+const char *ldb_strerror(int ldb_err);
+
+/* Top-level ldb operations */
+
+int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[]);
+
+int ldb_search(struct ldb_context *ldb, const struct ldb_dn *base, enum ldb_scope scope, const char *expression, const char * const *attrs, struct ldb_result **OUT);
+
+int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn);
+
+int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct ldb_dn *newdn);
+
+int ldb_add(struct ldb_context *ldb, const struct ldb_message *message);
+
+/* Ldb message operations */
+
+struct ldb_message *ldb_msg_new(void *mem_ctx);
+
+struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, const char *attr_name);
+
+int ldb_msg_add_value(struct ldb_message *msg, const char *attr_name, const struct ldb_val *INPUT);
+
+void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr);
+
+int ldb_msg_sanity_check(struct ldb_message *msg);
+
+/* DN operations */
+
+struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn);
+
+char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *dn);
diff --git a/source3/lib/ldb/tests/init.ldif b/source3/lib/ldb/tests/init.ldif
new file mode 100644 (file)
index 0000000..2e0b83c
--- /dev/null
@@ -0,0 +1,40 @@
+dn: o=University of Michigan,c=TEST
+objectclass: organization
+objectclass: domainRelatedObject
+l: Ann Arbor, Michigan
+st: Michigan
+o: University of Michigan
+o: UMICH
+o: UM
+o: U-M
+o: U of M
+description: The University of Michigan at Ann Arbor
+seeAlso:
+postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
+ 09 $ US
+telephonenumber: +1 313 764-1817
+associateddomain: example.com
+
+dn: ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: Ldb Test
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=LdbTspace,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: LdbTspace
+description: test white space removal in comparisons
+uidNumber: 0
+gidNumber: 0
diff --git a/source3/lib/ldb/tests/init_slapd.sh b/source3/lib/ldb/tests/init_slapd.sh
new file mode 100755 (executable)
index 0000000..cf06acd
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh 
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR=`dirname $0`/..
+    export LDBDIR
+fi
+
+rm -rf tests/tmp/db
+mkdir -p tests/tmp/db
+
+if [ -f tests/tmp/slapd.pid ]; then
+    kill `cat tests/tmp/slapd.pid`
+    sleep 1
+fi
+if [ -f tests/tmp/slapd.pid ]; then
+    kill -9 `cat tests/tmp/slapd.pid`
+    rm -f tests/tmp/slapd.pid
+fi
+
+# we don't consider a slapadd failure as a test suite failure, as it
+# has nothing to do with ldb
+
+MODCONF=tests/tmp/modules.conf
+rm -f $MODCONF
+touch $MODCONF || exit 1
+
+slaptest -u -f $LDBDIR/tests/slapd.conf > /dev/null 2>&1 || {
+ echo "enabling sladp modules"
+cat > $MODCONF <<EOF
+modulepath     /usr/lib/ldap
+moduleload     back_bdb
+EOF
+}
+
+slaptest -u -f $LDBDIR/tests/slapd.conf || {
+    echo "slaptest failed - skipping ldap tests"
+    exit 0
+}
+
+slapadd -f $LDBDIR/tests/slapd.conf < $LDBDIR/tests/init.ldif || exit 0
+
diff --git a/source3/lib/ldb/tests/kill_slapd.sh b/source3/lib/ldb/tests/kill_slapd.sh
new file mode 100755 (executable)
index 0000000..91beb10
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh 
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR=`dirname $0`/..
+    export LDBDIR
+fi
+
+if [ -f tests/tmp/slapd.pid ]; then
+    echo "killing slapd process `cat tests/tmp/slapd.pid`"
+    kill -9 `cat tests/tmp/slapd.pid`
+    rm -f tests/tmp/slapd.pid
+fi
diff --git a/source3/lib/ldb/tests/ldapi_url.sh b/source3/lib/ldb/tests/ldapi_url.sh
new file mode 100755 (executable)
index 0000000..fef6c35
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# aargh, did LDAP ever have to expose this crap to users ...
+
+BASE=`pwd`
+
+TMPDIR=$BASE/tests/tmp
+
+LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
+
+echo "ldapi://$LDAPI_ESCAPE"
diff --git a/source3/lib/ldb/tests/photo.ldif b/source3/lib/ldb/tests/photo.ldif
new file mode 100644 (file)
index 0000000..28981b1
--- /dev/null
@@ -0,0 +1,5 @@
+dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+changetype: modify
+add: jpegPhoto
+jpegPhoto:< file://tests/tmp/samba4.png
+
diff --git a/source3/lib/ldb/tests/samba4.png b/source3/lib/ldb/tests/samba4.png
new file mode 100644 (file)
index 0000000..c809688
Binary files /dev/null and b/source3/lib/ldb/tests/samba4.png differ
diff --git a/source3/lib/ldb/tests/schema-tests/schema-add-test.ldif b/source3/lib/ldb/tests/schema-tests/schema-add-test.ldif
new file mode 100644 (file)
index 0000000..997b801
--- /dev/null
@@ -0,0 +1,66 @@
+dn: CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: container
+cn: Users
+description: Default container for upgraded user accounts
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+uSNChanged: 1
+showInAdvancedViewOnly: FALSE
+name: Users
+objectGUID: b847056a-9934-d87b-8a1a-99fabe0863c8
+systemFlags: 0x8c000000
+objectCategory: CN=Container,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+nTSecurityDescriptor: foo
+
+dn: CN=Administrator,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: Administrator
+description: Built-in account for administering the computer/domain
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+memberOf: CN=Group Policy Creator Owners,CN=Users,DC=schema,DC=test
+memberOf: CN=Domain Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Enterprise Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Schema Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Administrators,CN=Builtin,DC=schema,DC=test
+uSNChanged: 1
+name: Administrator
+objectGUID: 6c02f98c-46c6-aa38-5f13-a510cac04e6c
+userAccountControl: 0x10200
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+pwdLastSet: 0
+primaryGroupID: 513
+objectSid: S-1-5-21-43662522-77495566-38969261-500
+adminCount: 1
+accountExpires: -1
+logonCount: 0
+sAMAccountName: Administrator
+sAMAccountType: 0x30000000
+objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+unicodePwd: samba
+nTSecurityDescriptor: foo
+
+dn: CN=Test,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: test
+cn: Test
+description: This is a test
+objectCategory: CN=Test,CN=Schema,CN=Configuration,DC=schema,DC=test
+nTSecurityDescriptor: foo
+instanceType: 4
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif b/source3/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif
new file mode 100644 (file)
index 0000000..b976724
--- /dev/null
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+replace: description
+description: this test must not fail
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif b/source3/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif
new file mode 100644 (file)
index 0000000..fa193af
--- /dev/null
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: description
+# this test must not fail
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif b/source3/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif
new file mode 100644 (file)
index 0000000..8ab7798
--- /dev/null
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: description
+description: this test must not fail
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif b/source3/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif
new file mode 100644 (file)
index 0000000..cbf0e60
--- /dev/null
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: foo
+foo: this test must fail
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif b/source3/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif
new file mode 100644 (file)
index 0000000..bc64e9e
--- /dev/null
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: nTSecurityDescriptor
+# this test must fail
+
diff --git a/source3/lib/ldb/tests/schema-tests/schema.ldif b/source3/lib/ldb/tests/schema-tests/schema.ldif
new file mode 100644 (file)
index 0000000..5154081
--- /dev/null
@@ -0,0 +1,112 @@
+dn: @INDEXLIST
+@IDXATTR: name
+@IDXATTR: sAMAccountName
+@IDXATTR: objectSid
+@IDXATTR: objectClass
+@IDXATTR: member
+@IDXATTR: uidNumber
+@IDXATTR: gidNumber
+@IDXATTR: unixName
+@IDXATTR: privilege
+@IDXATTR: lDAPDisplayName
+
+dn: @ATTRIBUTES
+realm: CASE_INSENSITIVE
+userPrincipalName: CASE_INSENSITIVE
+servicePrincipalName: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+unicodePwd: HIDDEN
+ntPwdHash: HIDDEN
+ntPwdHistory: HIDDEN
+lmPwdHash: HIDDEN
+lmPwdHistory: HIDDEN
+createTimestamp: HIDDEN
+modifyTimestamp: HIDDEN
+
+dn: @SUBCLASSES
+top: domain
+top: person
+top: group
+domain: domainDNS
+domain: builtinDomain
+person: organizationalPerson
+organizationalPerson: user
+user: computer
+template: userTemplate
+template: groupTemplate
+
+dn: @MODULES
+@LIST: timestamps,schema
+
+dn: CN=Top,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: top
+cn: Top
+uSNCreated: 1
+uSNChanged: 1
+subClassOf: top
+systemMustContain: objectClass
+systemMayContain: structuralObjectClass
+systemMayContain: createTimeStamp
+systemMayContain: modifyTimeStamp
+systemMayContain: creatorsName
+systemMayContain: modifiersName
+systemMayContain: hasSubordinates
+systemMayContain: subschemaSubentry
+systemMayContain: collectiveSubentry
+systemMayContain: entryUUID
+systemMayContain: entryCSN
+systemMayContain: namingCSN
+systemMayContain: superiorUUID
+systemMayContain: contextCSN
+systemMayContain: whenCreated
+systemMayContain: whenChanged
+systemMayContain: uSNCreated
+systemMayContain: uSNChanged
+systemMayContain: distinguishedName
+systemMayContain: name
+systemMayContain: cn
+systemMayContain: userPassword
+systemMayContain: labeledURI
+
+dn: CN=Class-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: classSchema
+cn: Class-Schema
+uSNCreated: 2
+uSNChanged: 2
+lDAPDisplayName: classSchema
+subClassOf: top
+systemMustContain: cn
+systemMustContain: subClassOf
+systemMayContain: systemPossSuperiors
+systemMayContain: systemOnly
+systemMayContain: systemMustContain
+systemMayContain: systemMayContain
+systemMayContain: systemAuxiliaryClass
+systemMayContain: possSuperiors
+systemMayContain: mustContain
+systemMayContain: mayContain
+systemMayContain: lDAPDisplayName
+systemMayContain: auxiliaryClass
+
+dn: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+cn: Attribute-Schema
+uSNCreated: 3
+uSNChanged: 3
+lDAPDisplayName: attributeSchema
+subClassOf: top
+systemMustContain: oMSyntax
+systemMustContain: lDAPDisplayName
+systemMustContain: isSingleValued
+systemMustContain: cn
+systemMustContain: attributeSyntax
+systemMustContain: attributeID
+
diff --git a/source3/lib/ldb/tests/slapd.conf b/source3/lib/ldb/tests/slapd.conf
new file mode 100644 (file)
index 0000000..fa2789d
--- /dev/null
@@ -0,0 +1,26 @@
+loglevel 0
+
+include tests/schema/core.schema
+include tests/schema/cosine.schema
+include tests/schema/inetorgperson.schema
+include tests/schema/openldap.schema
+include tests/schema/nis.schema
+
+
+pidfile                tests/tmp/slapd.pid
+argsfile       tests/tmp/slapd.args
+
+access to * by * write
+
+allow update_anon bind_anon_dn
+
+include tests/tmp/modules.conf
+
+defaultsearchbase "o=University of Michigan,c=TEST"
+
+backend                bdb
+database        bdb
+suffix         "o=University of Michigan,c=TEST"
+directory      tests/tmp/db
+index           objectClass eq
+index           uid eq
diff --git a/source3/lib/ldb/tests/start_slapd.sh b/source3/lib/ldb/tests/start_slapd.sh
new file mode 100755 (executable)
index 0000000..22e8548
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR=`dirname $0`/..
+    export LDBDIR
+fi
+
+mkdir -p $LDBDIR/tests/tmp/db
+
+# running slapd with -d0 means it stays in the same process group, so it can be
+# killed by timelimit
+slapd -d0 -f $LDBDIR/tests/slapd.conf -h "`$LDBDIR/tests/ldapi_url.sh`" $* &
+
+sleep 2
diff --git a/source3/lib/ldb/tests/test-attribs.ldif b/source3/lib/ldb/tests/test-attribs.ldif
new file mode 100644 (file)
index 0000000..0bb3ebe
--- /dev/null
@@ -0,0 +1,15 @@
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+cn: CASE_INSENSITIVE
+ou: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+
+dn: @SUBCLASSES
+top: domain
+top: person
+domain: domainDNS
+person: organizationalPerson
+person: fooPerson
+organizationalPerson: user
+organizationalPerson: OpenLDAPperson
+user: computer
diff --git a/source3/lib/ldb/tests/test-config.ldif b/source3/lib/ldb/tests/test-config.ldif
new file mode 100644 (file)
index 0000000..7926a9e
--- /dev/null
@@ -0,0 +1,67 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=Config,cn=Samba
+objectclass: globalconfig
+LocalConfigCn: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;1: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;2: cn=%I,cn=Config,cn=Samba
+LocalConfigCn;3: cn=%M,cn=Config,cn=Samba
+
+#############
+dn: cn=Protocol,cn=Global,cn=Config,cn=Samba
+maxXmit: 7000
+
+################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+
+#################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+Include: cn=%U,cn=MyConfig,cn=Config,cn=Samba
+
+####   ((objectClass=fileshare)(cn=test))
+##############################
+# [test] share
+dn: cn=test,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: test
+Comment: a test share
+Path: /home/tridge/samba4/prefix/test
+ReadOnly: no
+
+#####################################
+# [msdn] a remote proxy share, stored
+# on \\msdn\test
+dn: cn=msdn,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: msdn
+NtvfsHandler: cifs
+ReadOnly: no
+_CifsServer: msdn
+_CifsUser: administrator
+_CifsPassword: penguin
+_CifsDomain: winxp
+_CifsShare: test
+
+
+##############################
+# [VisualC] share
+dn: cn=visualc,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: VisualC
+Comment: VisualC development
+Path: /home/tridge/VisualC
+ReadOnly: no
+NtvfsHandler: simple
+
diff --git a/source3/lib/ldb/tests/test-default-config.ldif b/source3/lib/ldb/tests/test-default-config.ldif
new file mode 100644 (file)
index 0000000..87b7bcd
--- /dev/null
@@ -0,0 +1,17 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=DefaultConfig,cn=Samba
+objectclass: globalconfig
+Workgroup: WORKGROUP
+UnixCharset: UTF8
+Security: user
+NetbiosName: blu
+GuestAccount: nobody
+
+##############################
+# [_default_] share
+dn: cn=_default_,cn=Shares,cn=DefaultConfig,cn=Samba
+objectclass: fileshare
+cn: _default_
+Path: /tmp
+ReadOnly: yes
diff --git a/source3/lib/ldb/tests/test-extended.sh b/source3/lib/ldb/tests/test-extended.sh
new file mode 100755 (executable)
index 0000000..a84e3b7
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+echo "Running extended search tests"
+
+mv $LDB_URL $LDB_URL.1
+
+cat <<EOF | bin/ldbadd || exit 1
+dn: cn=testrec1,cn=TEST
+i1: 1
+i2: 0
+i3: 1234
+i4: 0x7003004
+
+dn: cn=testrec2,cn=TEST
+i1: 0x800000
+
+dn: cn=testrec3,cn=TEST
+i1: 0x101010101
+i1: 7
+
+dn: cn=auser1,cn=TEST
+groupType: 2147483648
+samAccountType: 805306368
+
+dn: cn=auser2,cn=TEST
+groupType: 2147483648
+samAccountType: 805306369
+
+dn: cn=auser3,cn=TEST
+groupType: 2147483649
+samAccountType: 805306370
+
+dn: cn=auser4,cn=TEST
+groupType: 2147483649
+samAccountType: 805306369
+EOF
+
+checkcount() {
+    count=$1
+    expression="$2"
+    n=`bin/ldbsearch "$expression" | grep '^dn' | wc -l`
+    if [ $n != $count ]; then
+       echo "Got $n but expected $count for $expression"
+       bin/ldbsearch "$expression"
+       exit 1
+    fi
+    echo "OK: $count $expression"
+}
+
+checkcount 1 '(i3=1234)'
+checkcount 0 '(i3=12345)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.803:=1)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=3)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=7)'
+checkcount 0 '(i1:1.2.840.113556.1.4.803:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=8388608)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=1)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=3)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=7)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=8388608)'
+
+# this is one that w2k gives
+checkcount 3 '(|(|(&(!(groupType:1.2.840.113556.1.4.803:=1))(groupType:1.2.840.113556.1.4.803:=2147483648)(groupType:1.2.840.113556.1.4.804:=10))(samAccountType=805306368))(samAccountType=805306369))'
+
diff --git a/source3/lib/ldb/tests/test-generic.sh b/source3/lib/ldb/tests/test-generic.sh
new file mode 100755 (executable)
index 0000000..e4085c6
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+if [ -z "$LDB_SPECIALS" ]; then
+    LDB_SPECIALS=1
+    export LDB_SPECIALS
+fi
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding base elements"
+$VALGRIND ldbadd $LDBDIR/tests/test.ldif || exit 1
+
+echo "Modifying elements"
+$VALGRIND ldbmodify $LDBDIR/tests/test-modify.ldif || exit 1
+
+echo "Showing modified record"
+$VALGRIND ldbsearch '(uid=uham)'  || exit 1
+
+echo "Rename entry"
+OLDDN="cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+NEWDN="cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+$VALGRIND ldbrename "$OLDDN" "$NEWDN"  || exit 1
+
+echo "Showing renamed record"
+$VALGRIND ldbsearch '(uid=uham)' || exit 1
+
+echo "Starting ldbtest"
+$VALGRIND ldbtest --num-records 100 --num-searches 10  || exit 1
+
+if [ $LDB_SPECIALS = 1 ]; then
+ echo "Adding index"
+ $VALGRIND ldbadd $LDBDIR/tests/test-index.ldif  || exit 1
+fi
+
+echo "Adding attributes"
+$VALGRIND ldbadd $LDBDIR/tests/test-wrong_attributes.ldif  || exit 1
+
+echo "testing indexed search"
+$VALGRIND ldbsearch '(uid=uham)'  || exit 1
+$VALGRIND ldbsearch '(&(objectclass=person)(objectclass=person)(objectclass=top))' || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uham))'  || exit 1
+$VALGRIND ldbsearch '(|(uid=uham)(uid=uham))'  || exit 1
+$VALGRIND ldbsearch '(|(uid=uham)(uid=uham)(objectclass=OpenLDAPperson))'  || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uham)(!(objectclass=xxx)))'  || exit 1
+$VALGRIND ldbsearch '(&(objectclass=person)(uid=uham)(!(uid=uhamxx)))' uid \* \+ dn  || exit 1
+$VALGRIND ldbsearch '(&(uid=uham)(uid=uha*)(title=*))' uid || exit 1
+
+# note that the "((" is treated as an attribute not an expression
+# this matches the openldap ldapsearch behaviour of looking for a '='
+# to see if the first argument is an expression or not
+$VALGRIND ldbsearch '((' uid || exit 1
+$VALGRIND ldbsearch '(objectclass=)' uid || exit 1
+$VALGRIND ldbsearch -b 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' -s base "" sn || exit 1
+
+echo "Test wildcard match"
+$VALGRIND ldbadd $LDBDIR/tests/test-wildcard.ldif  || exit 1
+$VALGRIND ldbsearch '(cn=test*multi)'  || exit 1
+$VALGRIND ldbsearch '(cn=*test*multi*)'  || exit 1
+$VALGRIND ldbsearch '(cn=*test_multi)'  || exit 1
+$VALGRIND ldbsearch '(cn=test_multi*)'  || exit 1
+$VALGRIND ldbsearch '(cn=test*multi*test*multi)'  || exit 1
+$VALGRIND ldbsearch '(cn=test*multi*test*multi*multi_*)' || exit 1
+
+echo "Starting ldbtest indexed"
+$VALGRIND ldbtest --num-records 100 --num-searches 500  || exit 1
+
+echo "Testing one level search"
+count=`$VALGRIND ldbsearch -b 'ou=Groups,o=University of Michigan,c=TEST' -s one 'objectclass=*' none |grep '^dn' | wc -l`
+if [ $count != 3 ]; then
+    echo returned $count records - expected 3
+    exit 1
+fi
+
+echo "Testing binary file attribute value"
+mkdir -p tests/tmp
+cp $LDBDIR/tests/samba4.png tests/tmp/samba4.png
+$VALGRIND ldbmodify $LDBDIR/tests/photo.ldif || exit 1
+count=`$VALGRIND ldbsearch '(cn=Ursula Hampster)' jpegPhoto | grep '^dn' | wc -l`
+if [ $count != 1 ]; then
+    echo returned $count records - expected 1
+    exit 1
+fi
+rm -f tests/tmp/samba4.png
+
+echo "*TODO* Testing UTF8 upper lower case searches !!"
+
+echo "Testing compare"
+count=`$VALGRIND ldbsearch '(cn>=t)' cn | grep '^dn' | wc -l`
+if [ $count != 2 ]; then
+    echo returned $count records - expected 2
+    echo "this fails on opsnLdap ..."
+fi
+
+count=`$VALGRIND ldbsearch '(cn<=t)' cn | grep '^dn' | wc -l`
+if [ $count != 13 ]; then
+    echo returned $count records - expected 13
+    echo "this fails on opsnLdap ..."
+fi
+
+checkcount() {
+    count=$1
+    scope=$2
+    basedn=$3
+    expression="$4"
+    n=`bin/ldbsearch -s "$scope" -b "$basedn" "$expression" | grep '^dn' | wc -l`
+    if [ $n != $count ]; then
+       echo "Got $n but expected $count for $expression"
+       bin/ldbsearch "$expression"
+       exit 1
+    fi
+    echo "OK: $count $expression"
+}
+
+checkcount 0 'base' '' '(uid=uham)'
+checkcount 0 'one' '' '(uid=uham)'
+
+checkcount 1 'base' 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=People,o=University of Michigan,c=TEST' '(ou=ldb  test)'
diff --git a/source3/lib/ldb/tests/test-index.ldif b/source3/lib/ldb/tests/test-index.ldif
new file mode 100644 (file)
index 0000000..a793537
--- /dev/null
@@ -0,0 +1,11 @@
+dn: @INDEXLIST
+@IDXATTR: uid
+@IDXATTR: objectclass
+
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+
+dn: @SUBCLASSES
+top: person
+person: organizationalPerson
+organizationalPerson: OpenLDAPperson
diff --git a/source3/lib/ldb/tests/test-ldap.sh b/source3/lib/ldb/tests/test-ldap.sh
new file mode 100755 (executable)
index 0000000..b9d224e
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH
+export PATH
+SCHEMA_NEEDED="core nis cosine inetorgperson openldap"
+
+# setup needed schema files
+for f in $SCHEMA_NEEDED; do
+    if [ ! -r tests/schema/$f.schema ]; then
+       mkdir -p tests/schema
+       if [ -r /etc/ldap/schema/$f.schema ]; then
+           ln -s /etc/ldap/schema/$f.schema tests/schema/$f.schema
+           continue;
+       fi
+       if [ -r /etc/openldap/schema/$f.schema ]; then
+           ln -s /etc/openldap/schema/$f.schema tests/schema/$f.schema
+           continue;
+       fi
+
+       echo "SKIPPING TESTS: you need the following OpenLDAP schema files"
+       for f in $SCHEMA_NEEDED; do
+           echo "  $f.schema"
+       done
+       exit 0
+    fi
+done
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR=`dirname $0`/..
+    export LDBDIR
+fi
+
+LDB_URL=`$LDBDIR/tests/ldapi_url.sh`
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+if $LDBDIR/tests/init_slapd.sh && 
+   $LDBDIR/tests/start_slapd.sh &&
+   $LDBDIR/tests/test-generic.sh; then
+    echo "ldap tests passed";
+    ret=0
+else
+    echo "ldap tests failed";
+    ret=$?
+fi
+
+$LDBDIR/tests/kill_slapd.sh
+
+exit $ret
diff --git a/source3/lib/ldb/tests/test-modify.ldif b/source3/lib/ldb/tests/test-modify.ldif
new file mode 100644 (file)
index 0000000..e5b9ca4
--- /dev/null
@@ -0,0 +1,23 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: modify
+add: drink
+drink: mango lassi
+-
+add: drink
+drink: lemonade
+-
+delete: pager
+-
+replace: telephonenumber
+telephonenumber: +61 2 6260 6012
+telephonenumber: +61 412 666 929
+-
+delete: telephonenumber
+telephonenumber: +61 2 6260 6012
+-
+delete: telephonenumber
+telephonenumber: +61 412 666 929
+-
+add: telephonenumber
+telephonenumber: +61 412 666 929
diff --git a/source3/lib/ldb/tests/test-schema.sh b/source3/lib/ldb/tests/test-schema.sh
new file mode 100755 (executable)
index 0000000..2f10fb4
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+LDB_URL="tdb://schema.ldb"
+export LDB_URL
+
+rm -f schema.ldb
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding schema"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema.ldif || exit 1
+
+echo "Adding few test elements (no failure expected here)"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema-add-test.ldif || exit 1
+
+echo "Modifying elements (2 failures expected here)"
+
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-1.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-2.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-3.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-4.ldif
+if [ "$?" == "0" ]; then
+       echo "test failed!"
+       exit 1
+fi
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-5.ldif
+if [ "$?" == "0" ]; then
+       echo "test failed!"
+       exit 1
+fi
+
+echo "Showing modified record"
+$VALGRIND bin/ldbsearch '(cn=Test)'  || exit 1
+
diff --git a/source3/lib/ldb/tests/test-sqlite3.sh b/source3/lib/ldb/tests/test-sqlite3.sh
new file mode 100755 (executable)
index 0000000..a51ae63
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+
+LDB_URL="sqlite://sqltest.ldb"
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f sqltest.ldb
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR="."
+    export LDBDIR
+fi
+
+. $LDBDIR/tests/test-generic.sh
+
+#. $LDBDIR/tests/test-extended.sh
+
+#. $LDBDIR/tests/test-tdb-features.sh
+
diff --git a/source3/lib/ldb/tests/test-tdb-features.sh b/source3/lib/ldb/tests/test-tdb-features.sh
new file mode 100644 (file)
index 0000000..6f1afdc
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+echo "Running tdb feature tests"
+
+mv $LDB_URL $LDB_URL.2
+
+checkcount() {
+    count=$1
+    expression="$2"
+    n=`bin/ldbsearch "$expression" | grep '^dn' | wc -l`
+    if [ $n != $count ]; then
+       echo "Got $n but expected $count for $expression"
+       $VALGRIND bin/ldbsearch "$expression"
+       exit 1
+    fi
+    echo "OK: $count $expression"
+}
+
+echo "Testing case sensitive search"
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: cn=t1,cn=TEST
+objectClass: testclass
+test: foo
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 0 '(test=FO*)'
+
+echo "Making case insensitive"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: add
+add: test
+test: CASE_INSENSITIVE
+EOF
+
+echo $ldif | $VALGRIND bin/ldbmodify || exit 1
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=fo*)'
+
+echo "adding i"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: i
+i: 0x100
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 0 '(i=256)'
+
+echo "marking i as INTEGER"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: i
+i: INTEGER
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+
+echo "adding j"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: j
+j: 0x100
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 0 '(j=256)'
+
+echo "Adding wildcard attribute"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: *
+*: INTEGER
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 1 '(j=256)'
+
+echo "Testing class search"
+checkcount 0 '(objectClass=otherclass)'
+checkcount 1 '(objectClass=testclass)'
+
+echo "Adding subclass"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @SUBCLASSES
+changetype: add
+add: otherclass
+otherclass: testclass
+EOF
+checkcount 1 '(objectClass=otherclass)'
+checkcount 1 '(objectClass=testclass)'
+
+echo "Adding index"
+cat <<EOF | $VALGRIND bin/ldbadd || exit 1
+dn: @INDEXLIST
+@IDXATTR: i
+@IDXATTR: test
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+checkcount 0 '(i=-256)'
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=*f*o)'
+
+echo "making test case sensitive"
+cat <<EOF | $VALGRIND bin/ldbmodify || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+replace: test
+test: NONE
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 1 '(test=f*o*)'
+
diff --git a/source3/lib/ldb/tests/test-tdb.sh b/source3/lib/ldb/tests/test-tdb.sh
new file mode 100755 (executable)
index 0000000..e1052d1
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+       LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+       LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+    LDBDIR=`dirname $0`/..
+    export LDBDIR
+fi
+
+. $LDBDIR/tests/test-generic.sh
+
+. $LDBDIR/tests/test-extended.sh
+
+. $LDBDIR/tests/test-tdb-features.sh
diff --git a/source3/lib/ldb/tests/test-wildcard.ldif b/source3/lib/ldb/tests/test-wildcard.ldif
new file mode 100644 (file)
index 0000000..222512e
--- /dev/null
@@ -0,0 +1,5 @@
+dn: cn=test_multi_test_multi_test_multi,o=University of Michigan,c=TEST
+objectclass: person
+cn: test_multi_test_multi_test_multi
+sn: multi_test
+description: test multi wildcards matching
diff --git a/source3/lib/ldb/tests/test-wrong_attributes.ldif b/source3/lib/ldb/tests/test-wrong_attributes.ldif
new file mode 100644 (file)
index 0000000..27f45f0
--- /dev/null
@@ -0,0 +1,3 @@
+dn: @ATTRIBUTES
+uid: CASE_INTENSIVE
+
diff --git a/source3/lib/ldb/tests/test.ldif b/source3/lib/ldb/tests/test.ldif
new file mode 100644 (file)
index 0000000..ab8b814
--- /dev/null
@@ -0,0 +1,416 @@
+dn: ou=Groups,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+#LEAD COMMENT
+
+# another comment
+dn: CN=All Staff,ou=Groups,o=University of Michigan,c=TEST
+#EMBEDDED COMMENT
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=University 
+ of Michigan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Univ
+ ersity of Michigan,c=TEST
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Univers
+ ity of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=University of Michigan,c=TEST
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Universit
+ y of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid:: YmplCW5zZW4
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Ann 
+ Arbor, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Ann Arbor, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=University 
+ of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Ann Arbor, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Ann Arbor, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,o=University of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,o=University of Michigan,c=TEST
+uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=U
+ niversity of Michigan,c=TEST
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ o=University of Michigan,c=TEST
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Universi
+ ty of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 933 Brooks $ Ann Arbor, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Ann Arbor, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Michigan
+ ,c=TEST
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Ann Arbor, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=University of M
+ ichigan,c=TEST
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 912 East Bllvd $ Ann Arbor, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,o=University of Michigan,c=TEST
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michigan,c=
+ TEST
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+mail: uham@mail.alumni.example.com
+description: a long attribute name, longer than 128 bytes so that we
+ trigger sign extension problems in tdb_pack, no thats not long enough
+ yet, maybe this is. I'll just keep going till it triggers the error
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
+
+dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Ldb Test
+
diff --git a/source3/lib/ldb/tests/testdata.txt b/source3/lib/ldb/tests/testdata.txt
new file mode 100644 (file)
index 0000000..dadb9f0
--- /dev/null
@@ -0,0 +1,8 @@
+foo=bar5
+(&(|(a=b)(c=d))(e=f))
+(&(|(a=b)(c=d)(g=h))(e=f))
+name=firstname lastname
+(&(sid=S-1-2-3)(name = fred bloggs))
+(&(|(a=b)(c=d))(g=f))
+(&(sid=S-1-2-3)(!(name = fred bloggs)))
+(&(!(|(a=b)(c=d))(g=f)))
diff --git a/source3/lib/ldb/tests/testsearch.txt b/source3/lib/ldb/tests/testsearch.txt
new file mode 100644 (file)
index 0000000..c573863
--- /dev/null
@@ -0,0 +1,5 @@
+(blah=foo)
+(objectclass=person)
+(dn=*)
+(&(objectclass=person)(objectclass=person))
+(&(objectclass=person)(objectclass=personx))
diff --git a/source3/lib/ldb/tools/ad2oLschema.c b/source3/lib/ldb/tools/ad2oLschema.c
new file mode 100644 (file)
index 0000000..8b1203b
--- /dev/null
@@ -0,0 +1,630 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Bartlett 2006
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ad2oLschema
+ *
+ *  Description: utility to convert an AD schema into the format required by OpenLDAP
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "system/locale.h"
+#include "ldb/tools/cmdline.h"
+#include "ldb/tools/convert.h"
+
+struct schema_conv {
+       int count;
+       int skipped;
+       int failures;
+};
+
+enum convert_target {
+       TARGET_OPENLDAP,
+       TARGET_FEDORA_DS
+};
+       
+
+static void usage(void)
+{
+       printf("Usage: ad2oLschema <options>\n");
+       printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
+       printf("Options:\n");
+       printf("  -I inputfile     inputfile of mapped OIDs and skipped attributes/ObjectClasses");
+       printf("  -H url           LDB or LDAP server to read schmea from\n");
+       printf("  -O outputfile    outputfile otherwise STDOUT\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
+       exit(1);
+}
+
+static int fetch_attrs_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
+                             TALLOC_CTX *mem_ctx, 
+                             struct ldb_result **attrs_res)
+{
+       TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
+       int ret;
+       const char *attrs[] = {
+               "lDAPDisplayName",
+               "isSingleValued",
+               "attributeID",
+               "attributeSyntax",
+               "description",          
+               NULL
+       };
+
+       if (!local_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       /* Downlaod schema */
+       ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
+                        "objectClass=attributeSchema", 
+                        attrs, attrs_res);
+       if (ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       return ret;
+}
+
+static const char *oc_attrs[] = {
+       "lDAPDisplayName",
+       "mayContain",
+       "mustContain",
+       "systemMayContain",
+       "systemMustContain",
+       "objectClassCategory",
+       "governsID",
+       "description",
+       "subClassOf",
+       NULL
+};
+
+static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn, 
+                             TALLOC_CTX *mem_ctx, 
+                             struct ldb_result *search_from,
+                             struct ldb_result *res_list)
+{
+       int i;
+       int ret = 0;
+       for (i=0; i < search_from->count; i++) {
+               struct ldb_result *res;
+               const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i], 
+                                                              "lDAPDisplayname", NULL);
+               char *filter = talloc_asprintf(mem_ctx, "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))", 
+                                              name, name);
+
+               ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
+                                filter,
+                                oc_attrs, &res);
+               talloc_free(filter);
+               if (ret != LDB_SUCCESS) {
+                       printf("Search failed: %s\n", ldb_errstring(ldb));
+                       return ret;
+               }
+               
+               talloc_steal(mem_ctx, res);
+
+               res_list->msgs = talloc_realloc(res_list, res_list->msgs, 
+                                               struct ldb_message *, res_list->count + 2);
+               if (!res_list->msgs) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               res_list->msgs[res_list->count] = talloc_move(res_list, 
+                                                             &search_from->msgs[i]);
+               res_list->count++;
+               res_list->msgs[res_list->count] = NULL;
+
+               if (res->count > 0) {
+                       ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list); 
+               }
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn, 
+                                   TALLOC_CTX *mem_ctx, 
+                                   struct ldb_result **objectclasses_res)
+{
+       TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
+       struct ldb_result *top_res, *ret_res;
+       int ret;
+       if (!local_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       /* Downlaod 'top' */
+       ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
+                        "(&(objectClass=classSchema)(lDAPDisplayName=top))", 
+                        oc_attrs, &top_res);
+       if (ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       talloc_steal(local_ctx, top_res);
+
+       if (top_res->count != 1) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret_res = talloc_zero(local_ctx, struct ldb_result);
+       if (!ret_res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res); 
+
+       if (ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       *objectclasses_res = talloc_move(mem_ctx, &ret_res);
+       return ret;
+}
+
+static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) 
+{
+       const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
+       struct ldb_dn *schemadn;
+       struct ldb_dn *basedn = ldb_dn_explode(mem_ctx, "");
+       struct ldb_result *rootdse_res;
+       int ldb_ret;
+       if (!basedn) {
+               return NULL;
+       }
+       
+       /* Search for rootdse */
+       ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
+       if (ldb_ret != LDB_SUCCESS) {
+               printf("Search failed: %s\n", ldb_errstring(ldb));
+               return NULL;
+       }
+       
+       talloc_steal(mem_ctx, rootdse_res);
+
+       if (rootdse_res->count != 1) {
+               printf("Failed to find rootDSE");
+               return NULL;
+       }
+       
+       /* Locate schema */
+       schemadn = ldb_msg_find_attr_as_dn(mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
+       if (!schemadn) {
+               return NULL;
+       }
+
+       talloc_free(rootdse_res);
+       return schemadn;
+}
+
+#define IF_NULL_FAIL_RET(x) do {     \
+               if (!x) {               \
+                       ret.failures++; \
+                       return ret;     \
+               }                       \
+       } while (0) 
+
+
+static struct schema_conv process_convert(struct ldb_context *ldb, enum convert_target target, FILE *in, FILE *out) 
+{
+       /* Read list of attributes to skip, OIDs to map */
+       TALLOC_CTX *mem_ctx = talloc_new(ldb);
+       char *line;
+       const char **attrs_skip = NULL;
+       int num_skip = 0;
+       struct oid_map {
+               char *old_oid;
+               char *new_oid;
+       } *oid_map = NULL;
+       int num_maps = 0;
+       struct ldb_result *attrs_res, *objectclasses_res;
+       struct ldb_dn *schemadn;
+       struct schema_conv ret;
+
+       int ldb_ret, i;
+
+       ret.count = 0;
+       ret.skipped = 0;
+       ret.failures = 0;
+
+       while ((line = afdgets(fileno(in), mem_ctx, 0))) {
+               /* Blank Line */
+               if (line[0] == '\0') {
+                       continue;
+               }
+               /* Comment */
+               if (line[0] == '#') {
+                       continue;
+               }
+               if (isdigit(line[0])) {
+                       char *p = strchr(line, ':');
+                       IF_NULL_FAIL_RET(p);
+                       if (!p) {
+                               ret.failures = 1;
+                               return ret;
+                       }
+                       p[0] = '\0';
+                       p++;
+                       oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_maps + 2);
+                       trim_string(line, " ", " ");
+                       oid_map[num_maps].old_oid = talloc_move(oid_map, &line);
+                       trim_string(p, " ", " ");
+                       oid_map[num_maps].new_oid = p;
+                       num_maps++;
+                       oid_map[num_maps].old_oid = NULL;
+               } else {
+                       attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
+                       trim_string(line, " ", " ");
+                       attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
+                       num_skip++;
+                       attrs_skip[num_skip] = NULL;
+               }
+       }
+
+       schemadn = find_schema_dn(ldb, mem_ctx);
+       if (!schemadn) {
+               printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
+               ret.failures = 1;
+               return ret;
+       }
+       
+       ldb_ret = fetch_attrs_schema(ldb, schemadn, mem_ctx, &attrs_res);
+       if (ldb_ret != LDB_SUCCESS) {
+               printf("Failed to fetch attribute schema: %s\n", ldb_errstring(ldb));
+               ret.failures = 1;
+               return ret;
+       }
+       
+       switch (target) {
+       case TARGET_OPENLDAP:
+               break;
+       case TARGET_FEDORA_DS:
+               fprintf(out, "dn: cn=schema\n");
+               break;
+       }
+
+       for (i=0; i < attrs_res->count; i++) {
+               struct ldb_message *msg = attrs_res->msgs[i];
+
+               const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
+               const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
+               const char *oid = ldb_msg_find_attr_as_string(msg, "attributeID", NULL);
+               const char *syntax = ldb_msg_find_attr_as_string(msg, "attributeSyntax", NULL);
+               BOOL single_value = ldb_msg_find_attr_as_bool(msg, "isSingleValued", False);
+               const struct syntax_map *map = find_syntax_map_by_ad_oid(syntax);
+               char *schema_entry = NULL;
+               int j;
+
+               /* We have been asked to skip some attributes/objectClasses */
+               if (str_list_check_ci(attrs_skip, name)) {
+                       ret.skipped++;
+                       continue;
+               }
+
+               /* We might have been asked to remap this oid, due to a conflict */
+               for (j=0; oid && oid_map[j].old_oid; j++) {
+                       if (strcmp(oid, oid_map[j].old_oid) == 0) {
+                               oid =  oid_map[j].new_oid;
+                               break;
+                       }
+               }
+               
+               switch (target) {
+               case TARGET_OPENLDAP:
+                       schema_entry = talloc_asprintf(mem_ctx, 
+                                                      "attributetype (\n"
+                                                      "  %s\n", oid);
+                       break;
+               case TARGET_FEDORA_DS:
+                       schema_entry = talloc_asprintf(mem_ctx, 
+                                                      "attributeTypes: (\n"
+                                                      "  %s\n", oid);
+                       break;
+               }
+               IF_NULL_FAIL_RET(schema_entry);
+
+               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                     "  NAME '%s'\n", name);
+               IF_NULL_FAIL_RET(schema_entry);
+
+               if (description) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  DESC %s\n", description);
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+
+               if (map) {
+                       const char *syntax_oid;
+                       if (map->equality) {
+                               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                                     "  EQUALITY %s\n", map->equality);
+                               IF_NULL_FAIL_RET(schema_entry);
+                       }
+                       if (map->substring) {
+                               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                                     "  SUBSTR %s\n", map->substring);
+                               IF_NULL_FAIL_RET(schema_entry);
+                       }
+                       syntax_oid = map->Standard_OID;
+                       /* We might have been asked to remap this oid,
+                        * due to a conflict, or lack of
+                        * implementation */
+                       for (j=0; syntax_oid && oid_map[j].old_oid; j++) {
+                               if (strcmp(syntax_oid, oid_map[j].old_oid) == 0) {
+                                       syntax_oid =  oid_map[j].new_oid;
+                                       break;
+                               }
+                       }
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  SYNTAX %s\n", syntax_oid);
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+
+               if (single_value) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  SINGLE-VALUE\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+               
+               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                     "  )");
+
+               switch (target) {
+               case TARGET_OPENLDAP:
+                       fprintf(out, "%s\n\n", schema_entry);
+                       break;
+               case TARGET_FEDORA_DS:
+                       fprintf(out, "%s\n", schema_entry);
+                       break;
+               }
+               ret.count++;
+       }
+
+       ldb_ret = fetch_objectclass_schema(ldb, schemadn, mem_ctx, &objectclasses_res);
+       if (ldb_ret != LDB_SUCCESS) {
+               printf("Failed to fetch objectClass schema elements: %s\n", ldb_errstring(ldb));
+               ret.failures = 1;
+               return ret;
+       }
+       
+       for (i=0; i < objectclasses_res->count; i++) {
+               struct ldb_message *msg = objectclasses_res->msgs[i];
+               const char *name = ldb_msg_find_attr_as_string(msg, "lDAPDisplayName", NULL);
+               const char *description = ldb_msg_find_attr_as_string(msg, "description", NULL);
+               const char *oid = ldb_msg_find_attr_as_string(msg, "governsID", NULL);
+               const char *subClassOf = ldb_msg_find_attr_as_string(msg, "subClassOf", NULL);
+               int objectClassCategory = ldb_msg_find_attr_as_int(msg, "objectClassCategory", 0);
+               struct ldb_message_element *must = ldb_msg_find_element(msg, "mustContain");
+               struct ldb_message_element *sys_must = ldb_msg_find_element(msg, "systemMustContain");
+               struct ldb_message_element *may = ldb_msg_find_element(msg, "mayContain");
+               struct ldb_message_element *sys_may = ldb_msg_find_element(msg, "systemMayContain");
+               char *schema_entry = NULL;
+               int j;
+
+               /* We have been asked to skip some attributes/objectClasses */
+               if (str_list_check_ci(attrs_skip, name)) {
+                       ret.skipped++;
+                       continue;
+               }
+
+               /* We might have been asked to remap this oid, due to a conflict */
+               for (j=0; oid_map[j].old_oid; j++) {
+                       if (strcmp(oid, oid_map[j].old_oid) == 0) {
+                               oid =  oid_map[j].new_oid;
+                               break;
+                       }
+               }
+               
+               switch (target) {
+               case TARGET_OPENLDAP:
+                       schema_entry = talloc_asprintf(mem_ctx, 
+                                                      "objectclass (\n"
+                                                      "  %s\n", oid);
+                       break;
+               case TARGET_FEDORA_DS:
+                       schema_entry = talloc_asprintf(mem_ctx, 
+                                                      "objectClasses: (\n"
+                                                      "  %s\n", oid);
+                       break;
+               }
+               IF_NULL_FAIL_RET(schema_entry);
+               if (!schema_entry) {
+                       ret.failures++;
+                       break;
+               }
+
+               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                     "  NAME '%s'\n", name);
+               IF_NULL_FAIL_RET(schema_entry);
+
+               if (!schema_entry) return ret;
+
+               if (description) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  DESC %s\n", description);
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+
+               if (subClassOf) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  SUP %s\n", subClassOf);
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+               
+               switch (objectClassCategory) {
+               case 1:
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  STRUCTURAL\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+                       break;
+               case 2:
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  ABSTRACT\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+                       break;
+               case 3:
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  AUXILIARY\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+                       break;
+               }
+
+#define APPEND_ATTRS(attributes) \
+               do {                                            \
+                       int k;                                          \
+                       for (k=0; attributes && k < attributes->num_values; k++) { \
+                               schema_entry = talloc_asprintf_append(schema_entry, \
+                                                                     " %s", \
+                                                                     (const char *)attributes->values[k].data); \
+                               IF_NULL_FAIL_RET(schema_entry);         \
+                               if (k != (attributes->num_values - 1)) { \
+                                       schema_entry = talloc_asprintf_append(schema_entry, \
+                                                                             " $"); \
+                                       IF_NULL_FAIL_RET(schema_entry); \
+                                       if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
+                                               schema_entry = talloc_asprintf_append(schema_entry, \
+                                                                                     "\n  "); \
+                                               IF_NULL_FAIL_RET(schema_entry); \
+                                       }                               \
+                               }                                       \
+                       }                                               \
+               } while (0)
+
+               if (must || sys_must) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  MUST (");
+                       IF_NULL_FAIL_RET(schema_entry);
+
+                       APPEND_ATTRS(must);
+                       if (must && sys_must) {
+                               schema_entry = talloc_asprintf_append(schema_entry, \
+                                                                     " $"); \
+                       }
+                       APPEND_ATTRS(sys_must);
+                       
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             " )\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+
+               if (may || sys_may) {
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             "  MAY (");
+                       IF_NULL_FAIL_RET(schema_entry);
+
+                       APPEND_ATTRS(may);
+                       if (may && sys_may) {
+                               schema_entry = talloc_asprintf_append(schema_entry, \
+                                                                     " $"); \
+                       }
+                       APPEND_ATTRS(sys_may);
+                       
+                       schema_entry = talloc_asprintf_append(schema_entry, 
+                                                             " )\n");
+                       IF_NULL_FAIL_RET(schema_entry);
+               }
+
+               schema_entry = talloc_asprintf_append(schema_entry, 
+                                                     "  )");
+
+               switch (target) {
+               case TARGET_OPENLDAP:
+                       fprintf(out, "%s\n\n", schema_entry);
+                       break;
+               case TARGET_FEDORA_DS:
+                       fprintf(out, "%s\n", schema_entry);
+                       break;
+               }
+               ret.count++;
+       }
+
+       return ret;
+}
+
+ int main(int argc, const char **argv)
+{
+       TALLOC_CTX *ctx;
+       struct ldb_cmdline *options;
+       FILE *in = stdin;
+       FILE *out = stdout;
+       struct ldb_context *ldb;
+       struct schema_conv ret;
+       const char *target_str;
+       enum convert_target target;
+
+       ldb_global_init();
+
+       ctx = talloc_new(NULL);
+       ldb = ldb_init(ctx);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       if (options->input) {
+               in = fopen(options->input, "r");
+               if (!in) {
+                       perror(options->input);
+                       exit(1);
+               }
+       }
+       if (options->output) {
+               out = fopen(options->output, "w");
+               if (!out) {
+                       perror(options->output);
+                       exit(1);
+               }
+       }
+
+       target_str = lp_parm_string(-1, "convert", "target");
+
+       if (!target_str || strcasecmp(target_str, "openldap") == 0) {
+               target = TARGET_OPENLDAP;
+       } else if (strcasecmp(target_str, "fedora-ds") == 0) {
+               target = TARGET_FEDORA_DS;
+       } else {
+               printf("Unsupported target: %s\n", target_str);
+               exit(1);
+       }
+
+       ret = process_convert(ldb, target, in, out);
+
+       fclose(in);
+       fclose(out);
+
+       printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/cmdline.c b/source3/lib/ldb/tools/cmdline.c
new file mode 100644 (file)
index 0000000..09bb99c
--- /dev/null
@@ -0,0 +1,747 @@
+/* 
+   ldb database library - command line handling for ldb tools
+
+   Copyright (C) Andrew Tridgell  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+#if (_SAMBA_BUILD_ >= 4)
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/samba/ldif_handlers.h"
+#include "auth/auth.h"
+#include "db_wrap.h"
+#endif
+
+
+
+/*
+  process command line options
+*/
+struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const char **argv,
+                                       void (*usage)(void))
+{
+       static struct ldb_cmdline options; /* needs to be static for older compilers */
+       struct ldb_cmdline *ret=NULL;
+       poptContext pc;
+#if (_SAMBA_BUILD_ >= 4)
+       int r;
+#endif
+       int num_options = 0;
+       int opt;
+       int flags = 0;
+
+       struct poptOption popt_options[] = {
+               POPT_AUTOHELP
+               { "url",       'H', POPT_ARG_STRING, &options.url, 0, "database URL", "URL" },
+               { "basedn",    'b', POPT_ARG_STRING, &options.basedn, 0, "base DN", "DN" },
+               { "editor",    'e', POPT_ARG_STRING, &options.editor, 0, "external editor", "PROGRAM" },
+               { "scope",     's', POPT_ARG_STRING, NULL, 's', "search scope", "SCOPE" },
+               { "verbose",   'v', POPT_ARG_NONE, NULL, 'v', "increase verbosity", NULL },
+               { "interactive", 'i', POPT_ARG_NONE, &options.interactive, 0, "input from stdin", NULL },
+               { "recursive", 'r', POPT_ARG_NONE, &options.recursive, 0, "recursive delete", NULL },
+               { "num-searches", 0, POPT_ARG_INT, &options.num_searches, 0, "number of test searches", NULL },
+               { "num-records", 0, POPT_ARG_INT, &options.num_records, 0, "number of test records", NULL },
+               { "all", 'a',    POPT_ARG_NONE, &options.all_records, 0, "(|(objectClass=*)(distinguishedName=*))", NULL },
+               { "nosync", 0,   POPT_ARG_NONE, &options.nosync, 0, "non-synchronous transactions", NULL },
+               { "sorted", 'S', POPT_ARG_NONE, &options.sorted, 0, "sort attributes", NULL },
+               { "sasl-mechanism", 0, POPT_ARG_STRING, &options.sasl_mechanism, 0, "choose SASL mechanism", "MECHANISM" },
+               { "input", 'I', POPT_ARG_STRING, &options.input, 0, "Input File", "Input" },
+               { "output", 'O', POPT_ARG_STRING, &options.output, 0, "Output File", "Output" },
+               { NULL,    'o', POPT_ARG_STRING, NULL, 'o', "ldb_connect option", "OPTION" },
+               { "controls", 0, POPT_ARG_STRING, NULL, 'c', "controls", NULL },
+#if (_SAMBA_BUILD_ >= 4)
+               POPT_COMMON_SAMBA
+               POPT_COMMON_CREDENTIALS
+               POPT_COMMON_VERSION
+#endif
+               { NULL }
+       };
+
+       ldb_global_init();
+
+#if (_SAMBA_BUILD_ >= 4)
+       r = ldb_register_samba_handlers(ldb);
+       if (r != 0) {
+               goto failed;
+       }
+
+#endif
+
+       ret = talloc_zero(ldb, struct ldb_cmdline);
+       if (ret == NULL) {
+               ldb_oom(ldb);
+               goto failed;
+       }
+
+       options = *ret;
+       
+       /* pull in URL */
+       options.url = getenv("LDB_URL");
+
+       /* and editor (used by ldbedit) */
+       options.editor = getenv("VISUAL");
+       if (!options.editor) {
+               options.editor = getenv("EDITOR");
+       }
+       if (!options.editor) {
+               options.editor = "vi";
+       }
+
+       options.scope = LDB_SCOPE_DEFAULT;
+
+       pc = poptGetContext(argv[0], argc, argv, popt_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 's': {
+                       const char *arg = poptGetOptArg(pc);
+                       if (strcmp(arg, "base") == 0) {
+                               options.scope = LDB_SCOPE_BASE;
+                       } else if (strcmp(arg, "sub") == 0) {
+                               options.scope = LDB_SCOPE_SUBTREE;
+                       } else if (strcmp(arg, "one") == 0) {
+                               options.scope = LDB_SCOPE_ONELEVEL;
+                       } else {
+                               fprintf(stderr, "Invalid scope '%s'\n", arg);
+                               goto failed;
+                       }
+                       break;
+               }
+
+               case 'v':
+                       options.verbose++;
+                       break;
+
+               case 'o':
+                       options.options = talloc_realloc(ret, options.options, 
+                                                        const char *, num_options+3);
+                       if (options.options == NULL) {
+                               ldb_oom(ldb);
+                               goto failed;
+                       }
+                       options.options[num_options] = poptGetOptArg(pc);
+                       options.options[num_options+1] = NULL;
+                       num_options++;
+                       break;
+
+               case 'c': {
+                       const char *cs = poptGetOptArg(pc);
+                       const char *p, *q;
+                       int cc;
+
+                       for (p = cs, cc = 1; (q = strchr(p, ',')); cc++, p = q + 1) ;
+
+                       options.controls = talloc_array(ret, char *, cc + 1);
+                       if (options.controls == NULL) {
+                               ldb_oom(ldb);
+                               goto failed;
+                       }
+                       for (p = cs, cc = 0; p != NULL; cc++) {
+                               const char *t;
+
+                               t = strchr(p, ',');
+                               if (t == NULL) {
+                                       options.controls[cc] = talloc_strdup(options.controls, p);
+                                       p = NULL;
+                               } else {
+                                       options.controls[cc] = talloc_strndup(options.controls, p, t-p);
+                                       p = t + 1;
+                               }
+                       }
+                       options.controls[cc] = NULL;
+
+                       break;    
+               }
+               default:
+                       fprintf(stderr, "Invalid option %s: %s\n", 
+                               poptBadOption(pc, 0), poptStrerror(opt));
+                       if (usage) usage();
+                       goto failed;
+               }
+       }
+
+       /* setup the remaining options for the main program to use */
+       options.argv = poptGetArgs(pc);
+       if (options.argv) {
+               options.argv++;
+               while (options.argv[options.argc]) options.argc++;
+       }
+
+       *ret = options;
+
+       /* all utils need some option */
+       if (ret->url == NULL) {
+               fprintf(stderr, "You must supply a url with -H or with $LDB_URL\n");
+               if (usage) usage();
+               goto failed;
+       }
+
+       if (strcmp(ret->url, "NONE") == 0) {
+               return ret;
+       }
+
+       if (options.nosync) {
+               flags |= LDB_FLG_NOSYNC;
+       }
+
+#if (_SAMBA_BUILD_ >= 4)
+       /* Must be after we have processed command line options */
+       gensec_init(); 
+       
+       if (ldb_set_opaque(ldb, "sessionInfo", system_session(ldb))) {
+               goto failed;
+       }
+       if (ldb_set_opaque(ldb, "credentials", cmdline_credentials)) {
+               goto failed;
+       }
+       ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+#endif
+
+       /* now connect to the ldb */
+       if (ldb_connect(ldb, ret->url, flags, ret->options) != 0) {
+               fprintf(stderr, "Failed to connect to %s - %s\n", 
+                       ret->url, ldb_errstring(ldb));
+               goto failed;
+       }
+
+       return ret;
+
+failed:
+       talloc_free(ret);
+       exit(1);
+       return NULL;
+}
+
+struct ldb_control **parse_controls(void *mem_ctx, char **control_strings)
+{
+       int i;
+       struct ldb_control **ctrl;
+
+       if (control_strings == NULL || control_strings[0] == NULL)
+               return NULL;
+
+       for (i = 0; control_strings[i]; i++);
+
+       ctrl = talloc_array(mem_ctx, struct ldb_control *, i + 1);
+
+       for (i = 0; control_strings[i]; i++) {
+               if (strncmp(control_strings[i], "vlv:", 4) == 0) {
+                       struct ldb_vlv_req_control *control;
+                       const char *p;
+                       char attr[1024];
+                       char ctxid[1024];
+                       int crit, bc, ac, os, cc, ret;
+
+                       attr[0] = '\0';
+                       ctxid[0] = '\0';
+                       p = &(control_strings[i][4]);
+                       ret = sscanf(p, "%d:%d:%d:%d:%d:%1023[^$]", &crit, &bc, &ac, &os, &cc, ctxid);
+                       if (ret < 5) {
+                               ret = sscanf(p, "%d:%d:%d:%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid);
+                       }
+                              
+                       if ((ret < 4) || (crit < 0) || (crit > 1)) {
+                               fprintf(stderr, "invalid server_sort control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):bc(n):ac(n):<os(n):cc(n)|attr(s)>[:ctxid(o)]\n");
+                               fprintf(stderr, "   note: b = boolean, n = number, s = string, o = b64 binary blob\n");
+                               return NULL;
+                       }
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_VLV_REQ_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_vlv_req_control);
+                       control->beforeCount = bc;
+                       control->afterCount = ac;
+                       if (attr[0]) {
+                               control->type = 1;
+                               control->match.gtOrEq.value = talloc_strdup(control, attr);
+                               control->match.gtOrEq.value_len = strlen(attr);
+                       } else {
+                               control->type = 0;
+                               control->match.byOffset.offset = os;
+                               control->match.byOffset.contentCount = cc;
+                       }
+                       if (ctxid[0]) {
+                               control->ctxid_len = ldb_base64_decode(ctxid);
+                               control->contextId = (char *)talloc_memdup(control, ctxid, control->ctxid_len);
+                       } else {
+                               control->ctxid_len = 0;
+                               control->contextId = NULL;
+                       }
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "dirsync:", 8) == 0) {
+                       struct ldb_dirsync_control *control;
+                       const char *p;
+                       char cookie[1024];
+                       int crit, flags, max_attrs, ret;
+                      
+                       cookie[0] = '\0';
+                       p = &(control_strings[i][8]);
+                       ret = sscanf(p, "%d:%d:%d:%1023[^$]", &crit, &flags, &max_attrs, cookie);
+
+                       if ((ret < 3) || (crit < 0) || (crit > 1) || (flags < 0) || (max_attrs < 0)) {
+                               fprintf(stderr, "invalid dirsync control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):flags(n):max_attrs(n)[:cookie(o)]\n");
+                               fprintf(stderr, "   note: b = boolean, n = number, o = b64 binary blob\n");
+                               return NULL;
+                       }
+
+                       /* w2k3 seems to ignore the parameter,
+                        * but w2k sends a wrong cookie when this value is to small
+                        * this would cause looping forever, while getting
+                        * the same data and same cookie forever
+                        */
+                       if (max_attrs == 0) max_attrs = 0x0FFFFFFF;
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_DIRSYNC_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_dirsync_control);
+                       control->flags = flags;
+                       control->max_attributes = max_attrs;
+                       if (*cookie) {
+                               control->cookie_len = ldb_base64_decode(cookie);
+                               control->cookie = (char *)talloc_memdup(control, cookie, control->cookie_len);
+                       } else {
+                               control->cookie = NULL;
+                               control->cookie_len = 0;
+                       }
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "asq:", 4) == 0) {
+                       struct ldb_asq_control *control;
+                       const char *p;
+                       char attr[256];
+                       int crit, ret;
+
+                       attr[0] = '\0';
+                       p = &(control_strings[i][4]);
+                       ret = sscanf(p, "%d:%255[^$]", &crit, attr);
+                       if ((ret != 2) || (crit < 0) || (crit > 1) || (attr[0] == '\0')) {
+                               fprintf(stderr, "invalid asq control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):attr(s)\n");
+                               fprintf(stderr, "   note: b = boolean, s = string\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_ASQ_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_asq_control);
+                       control->request = 1;
+                       control->source_attribute = talloc_strdup(control, attr);
+                       control->src_attr_len = strlen(attr);
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "extended_dn:", 12) == 0) {
+                       struct ldb_extended_dn_control *control;
+                       const char *p;
+                       int crit, type, ret;
+
+                       p = &(control_strings[i][12]);
+                       ret = sscanf(p, "%d:%d", &crit, &type);
+                       if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) {
+                               fprintf(stderr, "invalid extended_dn control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):type(b)\n");
+                               fprintf(stderr, "   note: b = boolean\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_extended_dn_control);
+                       control->type = type;
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "sd_flags:", 9) == 0) {
+                       struct ldb_sd_flags_control *control;
+                       const char *p;
+                       int crit, ret;
+                       unsigned secinfo_flags;
+
+                       p = &(control_strings[i][9]);
+                       ret = sscanf(p, "%d:%u", &crit, &secinfo_flags);
+                       if ((ret != 2) || (crit < 0) || (crit > 1) || (secinfo_flags < 0) || (secinfo_flags > 0xF)) {
+                               fprintf(stderr, "invalid sd_flags control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):secinfo_flags(n)\n");
+                               fprintf(stderr, "   note: b = boolean, n = number\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_SD_FLAGS_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_sd_flags_control);
+                       control->secinfo_flags = secinfo_flags;
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "search_options:", 15) == 0) {
+                       struct ldb_search_options_control *control;
+                       const char *p;
+                       int crit, ret;
+                       unsigned search_options;
+
+                       p = &(control_strings[i][15]);
+                       ret = sscanf(p, "%d:%u", &crit, &search_options);
+                       if ((ret != 2) || (crit < 0) || (crit > 1) || (search_options < 0) || (search_options > 0xF)) {
+                               fprintf(stderr, "invalid search_options control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):search_options(n)\n");
+                               fprintf(stderr, "   note: b = boolean, n = number\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_SEARCH_OPTIONS_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_search_options_control);
+                       control->search_options = search_options;
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "domain_scope:", 13) == 0) {
+                       const char *p;
+                       int crit, ret;
+
+                       p = &(control_strings[i][13]);
+                       ret = sscanf(p, "%d", &crit);
+                       if ((ret != 1) || (crit < 0) || (crit > 1)) {
+                               fprintf(stderr, "invalid domain_scope control syntax\n");
+                               fprintf(stderr, " syntax: crit(b)\n");
+                               fprintf(stderr, "   note: b = boolean\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_DOMAIN_SCOPE_OID;
+                       ctrl[i]->critical = crit;
+                       ctrl[i]->data = NULL;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "paged_results:", 14) == 0) {
+                       struct ldb_paged_control *control;
+                       const char *p;
+                       int crit, size, ret;
+                      
+                       p = &(control_strings[i][14]);
+                       ret = sscanf(p, "%d:%d", &crit, &size);
+
+                       if ((ret != 2) || (crit < 0) || (crit > 1) || (size < 0)) {
+                               fprintf(stderr, "invalid paged_results control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):size(n)\n");
+                               fprintf(stderr, "   note: b = boolean, n = number\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc(ctrl[i], struct ldb_paged_control);
+                       control->size = size;
+                       control->cookie = NULL;
+                       control->cookie_len = 0;
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "server_sort:", 12) == 0) {
+                       struct ldb_server_sort_control **control;
+                       const char *p;
+                       char attr[256];
+                       char rule[128];
+                       int crit, rev, ret;
+
+                       attr[0] = '\0';
+                       rule[0] = '\0';
+                       p = &(control_strings[i][12]);
+                       ret = sscanf(p, "%d:%d:%255[^:]:%127[^:]", &crit, &rev, attr, rule);
+                       if ((ret < 3) || (crit < 0) || (crit > 1) || (rev < 0 ) || (rev > 1) ||attr[0] == '\0') {
+                               fprintf(stderr, "invalid server_sort control syntax\n");
+                               fprintf(stderr, " syntax: crit(b):rev(b):attr(s)[:rule(s)]\n");
+                               fprintf(stderr, "   note: b = boolean, s = string\n");
+                               return NULL;
+                       }
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_SERVER_SORT_OID;
+                       ctrl[i]->critical = crit;
+                       control = talloc_array(ctrl[i], struct ldb_server_sort_control *, 2);
+                       control[0] = talloc(control, struct ldb_server_sort_control);
+                       control[0]->attributeName = talloc_strdup(control, attr);
+                       if (rule[0])
+                               control[0]->orderingRule = talloc_strdup(control, rule);
+                       else
+                               control[0]->orderingRule = NULL;
+                       control[0]->reverse = rev;
+                       control[1] = NULL;
+                       ctrl[i]->data = control;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "notification:", 13) == 0) {
+                       const char *p;
+                       int crit, ret;
+
+                       p = &(control_strings[i][13]);
+                       ret = sscanf(p, "%d", &crit);
+                       if ((ret != 1) || (crit < 0) || (crit > 1)) {
+                               fprintf(stderr, "invalid notification control syntax\n");
+                               fprintf(stderr, " syntax: crit(b)\n");
+                               fprintf(stderr, "   note: b = boolean\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_NOTIFICATION_OID;
+                       ctrl[i]->critical = crit;
+                       ctrl[i]->data = NULL;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "show_deleted:", 13) == 0) {
+                       const char *p;
+                       int crit, ret;
+
+                       p = &(control_strings[i][13]);
+                       ret = sscanf(p, "%d", &crit);
+                       if ((ret != 1) || (crit < 0) || (crit > 1)) {
+                               fprintf(stderr, "invalid show_deleted control syntax\n");
+                               fprintf(stderr, " syntax: crit(b)\n");
+                               fprintf(stderr, "   note: b = boolean\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_SHOW_DELETED_OID;
+                       ctrl[i]->critical = crit;
+                       ctrl[i]->data = NULL;
+
+                       continue;
+               }
+
+               if (strncmp(control_strings[i], "permissive_modify:", 18) == 0) {
+                       const char *p;
+                       int crit, ret;
+
+                       p = &(control_strings[i][18]);
+                       ret = sscanf(p, "%d", &crit);
+                       if ((ret != 1) || (crit < 0) || (crit > 1)) {
+                               fprintf(stderr, "invalid permissive_modify control syntax\n");
+                               fprintf(stderr, " syntax: crit(b)\n");
+                               fprintf(stderr, "   note: b = boolean\n");
+                               return NULL;
+                       }
+
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       ctrl[i]->oid = LDB_CONTROL_PERMISSIVE_MODIFY_OID;
+                       ctrl[i]->critical = crit;
+                       ctrl[i]->data = NULL;
+
+                       continue;
+               }
+
+               /* no controls matched, throw an error */
+               fprintf(stderr, "Invalid control name: '%s'\n", control_strings[i]);
+               return NULL;
+       }
+
+       ctrl[i] = NULL;
+
+       return ctrl;
+}
+
+
+/* this function check controls reply and determines if more
+ * processing is needed setting up the request controls correctly
+ *
+ * returns:
+ *     -1 error
+ *     0 all ok
+ *     1 all ok, more processing required
+ */
+int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request)
+{
+       int i, j;
+               int ret = 0;
+
+       if (reply == NULL || request == NULL) return -1;
+       
+       for (i = 0; reply[i]; i++) {
+               if (strcmp(LDB_CONTROL_VLV_RESP_OID, reply[i]->oid) == 0) {
+                       struct ldb_vlv_resp_control *rep_control;
+
+                       rep_control = talloc_get_type(reply[i]->data, struct ldb_vlv_resp_control);
+                       
+                       /* check we have a matching control in the request */
+                       for (j = 0; request[j]; j++) {
+                               if (strcmp(LDB_CONTROL_VLV_REQ_OID, request[j]->oid) == 0)
+                                       break;
+                       }
+                       if (! request[j]) {
+                               fprintf(stderr, "Warning VLV reply received but no request have been made\n");
+                               continue;
+                       }
+
+                       /* check the result */
+                       if (rep_control->vlv_result != 0) {
+                               fprintf(stderr, "Warning: VLV not performed with error: %d\n", rep_control->vlv_result);
+                       } else {
+                               fprintf(stderr, "VLV Info: target position = %d, content count = %d\n", rep_control->targetPosition, rep_control->contentCount);
+                       }
+
+                       continue;
+               }
+
+               if (strcmp(LDB_CONTROL_ASQ_OID, reply[i]->oid) == 0) {
+                       struct ldb_asq_control *rep_control;
+
+                       rep_control = talloc_get_type(reply[i]->data, struct ldb_asq_control);
+
+                       /* check the result */
+                       if (rep_control->result != 0) {
+                               fprintf(stderr, "Warning: ASQ not performed with error: %d\n", rep_control->result);
+                       }
+
+                       continue;
+               }
+
+               if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, reply[i]->oid) == 0) {
+                       struct ldb_paged_control *rep_control, *req_control;
+
+                       rep_control = talloc_get_type(reply[i]->data, struct ldb_paged_control);
+                       if (rep_control->cookie_len == 0) /* we are done */
+                               break;
+
+                       /* more processing required */
+                       /* let's fill in the request control with the new cookie */
+
+                       for (j = 0; request[j]; j++) {
+                               if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, request[j]->oid) == 0)
+                                       break;
+                       }
+                       /* if there's a reply control we must find a request
+                        * control matching it */
+                       if (! request[j]) return -1;
+
+                       req_control = talloc_get_type(request[j]->data, struct ldb_paged_control);
+
+                       if (req_control->cookie)
+                               talloc_free(req_control->cookie);
+                       req_control->cookie = (char *)talloc_memdup(
+                               req_control, rep_control->cookie,
+                               rep_control->cookie_len);
+                       req_control->cookie_len = rep_control->cookie_len;
+
+                       ret = 1;
+
+                       continue;
+               }
+
+               if (strcmp(LDB_CONTROL_SORT_RESP_OID, reply[i]->oid) == 0) {
+                       struct ldb_sort_resp_control *rep_control;
+
+                       rep_control = talloc_get_type(reply[i]->data, struct ldb_sort_resp_control);
+
+                       /* check we have a matching control in the request */
+                       for (j = 0; request[j]; j++) {
+                               if (strcmp(LDB_CONTROL_SERVER_SORT_OID, request[j]->oid) == 0)
+                                       break;
+                       }
+                       if (! request[j]) {
+                               fprintf(stderr, "Warning Server Sort reply received but no request found\n");
+                               continue;
+                       }
+
+                       /* check the result */
+                       if (rep_control->result != 0) {
+                               fprintf(stderr, "Warning: Sorting not performed with error: %d\n", rep_control->result);
+                       }
+
+                       continue;
+               }
+
+               if (strcmp(LDB_CONTROL_DIRSYNC_OID, reply[i]->oid) == 0) {
+                       struct ldb_dirsync_control *rep_control, *req_control;
+                       char *cookie;
+
+                       rep_control = talloc_get_type(reply[i]->data, struct ldb_dirsync_control);
+                       if (rep_control->cookie_len == 0) /* we are done */
+                               break;
+
+                       /* more processing required */
+                       /* let's fill in the request control with the new cookie */
+
+                       for (j = 0; request[j]; j++) {
+                               if (strcmp(LDB_CONTROL_DIRSYNC_OID, request[j]->oid) == 0)
+                                       break;
+                       }
+                       /* if there's a reply control we must find a request
+                        * control matching it */
+                       if (! request[j]) return -1;
+
+                       req_control = talloc_get_type(request[j]->data, struct ldb_dirsync_control);
+
+                       if (req_control->cookie)
+                               talloc_free(req_control->cookie);
+                       req_control->cookie = (char *)talloc_memdup(
+                               req_control, rep_control->cookie,
+                               rep_control->cookie_len);
+                       req_control->cookie_len = rep_control->cookie_len;
+
+                       cookie = ldb_base64_encode(req_control, rep_control->cookie, rep_control->cookie_len);
+                       printf("# DIRSYNC cookie returned was:\n# %s\n", cookie);
+
+                       continue;
+               }
+
+               /* no controls matched, throw a warning */
+               fprintf(stderr, "Unknown reply control oid: %s\n", reply[i]->oid);
+       }
+
+       return ret;
+}
+
diff --git a/source3/lib/ldb/tools/cmdline.h b/source3/lib/ldb/tools/cmdline.h
new file mode 100644 (file)
index 0000000..0855f77
--- /dev/null
@@ -0,0 +1,54 @@
+/* 
+   ldb database library - command line handling for ldb tools
+
+   Copyright (C) Andrew Tridgell  2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <popt.h>
+
+struct ldb_cmdline {
+       const char *url;
+       enum ldb_scope scope;
+       const char *basedn;
+       int interactive;
+       int sorted;
+       const char *editor;
+       int verbose;
+       int recursive;
+       int all_records;
+       int nosync;
+       const char **options;
+       int argc;
+       const char **argv;
+       int num_records;
+       int num_searches;
+       const char *sasl_mechanism;
+       const char *input;
+       const char *output;
+       char **controls;
+};
+
+struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const char **argv,
+                                       void (*usage)(void));
+
+
+struct ldb_control **parse_controls(void *mem_ctx, char **control_strings);
+int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request);
diff --git a/source3/lib/ldb/tools/convert.c b/source3/lib/ldb/tools/convert.c
new file mode 100644 (file)
index 0000000..2320970
--- /dev/null
@@ -0,0 +1,166 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce 2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "convert.h"
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/* Shared map for converting syntax between formats */
+static const struct syntax_map syntax_map[] = {
+       { 
+               .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.12", 
+               .AD_OID = "2.5.5.1", 
+               .equality = "distinguishedNameMatch",
+               .comment = "Object(DS-DN) == a DN" 
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.38",
+               .AD_OID =  "2.5.5.2",
+               .equality = "objectIdentifierMatch",
+               .comment =  "OID String"
+       },
+       { 
+               .Standard_OID =  "1.2.840.113556.1.4.905", 
+               .AD_OID =  "2.5.5.4",
+               .equality = "caseIgnoreMatch",
+               .substring = "caseIgnoreSubstringsMatch",
+               .comment =   "Case Insensitive String" 
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.26",
+               .AD_OID =   "2.5.5.5",
+               .equality = "caseExactIA5Match",
+               .comment = "Printable String"
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.36",
+               .AD_OID =   "2.5.5.6", 
+               .equality = "numericStringMatch",
+               .substring = "numericStringSubstringsMatch",
+               .comment = "Numeric String" 
+       },
+       { 
+               .Standard_OID =  "1.2.840.113556.1.4.903", 
+               .AD_OID =  "2.5.5.7", 
+               .equality = "distinguishedNameMatch",
+               .comment = "OctetString: Binary+DN" 
+       },
+       { 
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.7",
+               .AD_OID =   "2.5.5.8", 
+               .equality = "booleanMatch",
+               .comment = "Boolean" 
+       },
+       { 
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.27",
+               .AD_OID =   "2.5.5.9", 
+               .equality = "integerMatch",
+               .comment = "Integer" 
+       },
+       { 
+               .Standard_OID = "1.3.6.1.4.1.1466.115.121.1.40",
+               .AD_OID       = "2.5.5.10",
+               .equality     = "octetStringMatch",
+               .comment      =  "Octet String"
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.24",
+               .AD_OID =   "2.5.5.11", 
+               .equality = "generalizedTimeMatch",
+               .comment = "Generalized Time"
+       },
+       { 
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.53",
+               .AD_OID =   "2.5.5.11", 
+               .equality = "generalizedTimeMatch",
+               .comment = "UTC Time" 
+       },
+       { 
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.15",
+               .AD_OID =   "2.5.5.12", 
+               .equality = "caseIgnoreMatch",
+               .substring = "caseIgnoreSubstringsMatch",
+               .comment = "Directory String"
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.43",
+               .AD_OID =   "2.5.5.13", 
+               .comment = "Presentation Address" 
+       },
+       {
+               .Standard_OID =   "Not Found Yet", 
+               .AD_OID =  "2.5.5.14", 
+               .equality = "distinguishedNameMatch",
+               .comment = "OctetString: String+DN" 
+       },
+       {
+               .Standard_OID =  "1.2.840.113556.1.4.907",
+               .AD_OID =   "2.5.5.15", 
+               .equality     = "octetStringMatch",
+               .comment = "NT Security Descriptor"
+       },
+       { 
+               .Standard_OID =  "1.2.840.113556.1.4.906", 
+               .AD_OID =  "2.5.5.16", 
+               .equality = "integerMatch",
+               .comment = "Large Integer" 
+       },
+       {
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.40",
+               .AD_OID =   "2.5.5.17",
+               .equality     = "octetStringMatch",
+               .comment =  "Octet String - Security Identifier (SID)" 
+       },
+       { 
+               .Standard_OID =  "1.3.6.1.4.1.1466.115.121.1.26", 
+               .AD_OID =  "2.5.5.5", 
+               .equality = "caseExactIA5Match",
+               .comment = "IA5 String" 
+       },
+       {       .Standard_OID = NULL
+       }
+};
+
+
+const struct syntax_map *find_syntax_map_by_ad_oid(const char *ad_oid) 
+{
+       int i;
+       for (i=0; syntax_map[i].Standard_OID; i++) {
+               if (strcasecmp(ad_oid, syntax_map[i].AD_OID) == 0) {
+                       return &syntax_map[i];
+               }
+       }
+       return NULL;
+}
+
+const struct syntax_map *find_syntax_map_by_standard_oid(const char *standard_oid) 
+{
+       int i;
+       for (i=0; syntax_map[i].Standard_OID; i++) {
+               if (strcasecmp(standard_oid, syntax_map[i].Standard_OID) == 0) {
+                       return &syntax_map[i];
+               }
+       }
+       return NULL;
+}
diff --git a/source3/lib/ldb/tools/convert.h b/source3/lib/ldb/tools/convert.h
new file mode 100644 (file)
index 0000000..de37934
--- /dev/null
@@ -0,0 +1,10 @@
+struct syntax_map {
+       const char *Standard_OID;
+       const char *AD_OID;
+       const char *equality;
+       const char *substring;
+       const char *comment;
+};
+
+const struct syntax_map *find_syntax_map_by_ad_oid(const char *ad_oid); 
+const struct syntax_map *find_syntax_map_by_standard_oid(const char *standard_oid);
diff --git a/source3/lib/ldb/tools/ldbadd.c b/source3/lib/ldb/tools/ldbadd.c
new file mode 100644 (file)
index 0000000..6a0f510
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+   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: ldbadd
+ *
+ *  Description: utility to add records - modelled on ldapadd
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static int failures;
+
+static void usage(void)
+{
+       printf("Usage: ldbadd <options> <ldif...>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Adds records to a ldb, reading ldif the specified list of files\n\n");
+       exit(1);
+}
+
+
+/*
+  add records from an opened file
+*/
+static int process_file(struct ldb_context *ldb, FILE *f)
+{
+       struct ldb_ldif *ldif;
+       int ret, count=0;
+
+       while ((ldif = ldb_ldif_read_file(ldb, f))) {
+               if (ldif->changetype != LDB_CHANGETYPE_ADD &&
+                   ldif->changetype != LDB_CHANGETYPE_NONE) {
+                       fprintf(stderr, "Only CHANGETYPE_ADD records allowed\n");
+                       break;
+               }
+
+               ldif->msg = ldb_msg_canonicalize(ldb, ldif->msg);
+
+               ret = ldb_add(ldb, ldif->msg);
+               if (ret != LDB_SUCCESS) {
+                       fprintf(stderr, "ERR: \"%s\" on DN %s\n", 
+                               ldb_errstring(ldb), ldb_dn_linearize(ldb, ldif->msg->dn));
+                       failures++;
+               } else {
+                       count++;
+               }
+               ldb_ldif_read_free(ldb, ldif);
+       }
+
+       return count;
+}
+
+
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       int i, count=0;
+       struct ldb_cmdline *options;
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       if (options->argc == 0) {
+               count += process_file(ldb, stdin);
+       } else {
+               for (i=0;i<options->argc;i++) {
+                       const char *fname = options->argv[i];
+                       FILE *f;
+                       f = fopen(fname, "r");
+                       if (!f) {
+                               perror(fname);
+                               exit(1);
+                       }
+                       count += process_file(ldb, f);
+                       fclose(f);
+               }
+       }
+
+       talloc_free(ldb);
+
+       printf("Added %d records with %d failures\n", count, failures);
+       
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/ldbdel.c b/source3/lib/ldb/tools/ldbdel.c
new file mode 100644 (file)
index 0000000..aee911e
--- /dev/null
@@ -0,0 +1,119 @@
+/* 
+   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: ldbdel
+ *
+ *  Description: utility to delete records - modelled on ldapdelete
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static int ldb_delete_recursive(struct ldb_context *ldb, const struct ldb_dn *dn)
+{
+       int ret, i, total=0;
+       const char *attrs[] = { NULL };
+       struct ldb_result *res;
+       
+       ret = ldb_search(ldb, dn, LDB_SCOPE_SUBTREE, "distinguishedName=*", attrs, &res);
+       if (ret != LDB_SUCCESS) return -1;
+
+       for (i = 0; i < res->count; i++) {
+               if (ldb_delete(ldb, res->msgs[i]->dn) == 0) {
+                       total++;
+               }
+       }
+
+       talloc_free(res);
+
+       if (total == 0) {
+               return -1;
+       }
+       printf("Deleted %d records\n", total);
+       return 0;
+}
+
+static void usage(void)
+{
+       printf("Usage: ldbdel <options> <DN...>\n");
+       printf("Options:\n");
+       printf("  -r               recursively delete the given subtree\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Deletes records from a ldb\n\n");
+       exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       int ret, i;
+       struct ldb_cmdline *options;
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       if (options->argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i=0;i<options->argc;i++) {
+               const struct ldb_dn *dn;
+
+               dn = ldb_dn_explode(ldb, options->argv[i]);
+               if (dn == NULL) {
+                       printf("Invalid DN format\n");
+                       exit(1);
+               }
+               if (options->recursive) {
+                       ret = ldb_delete_recursive(ldb, dn);
+               } else {
+                       ret = ldb_delete(ldb, dn);
+                       if (ret == 0) {
+                               printf("Deleted 1 record\n");
+                       }
+               }
+               if (ret != 0) {
+                       printf("delete of '%s' failed - %s\n",
+                               ldb_dn_linearize(ldb, dn),
+                               ldb_errstring(ldb));
+               }
+       }
+
+       talloc_free(ldb);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/ldbedit.c b/source3/lib/ldb/tools/ldbedit.c
new file mode 100644 (file)
index 0000000..17ade15
--- /dev/null
@@ -0,0 +1,333 @@
+/* 
+   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: ldbedit
+ *
+ *  Description: utility for ldb database editing
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static struct ldb_cmdline *options;
+
+/*
+  debug routine 
+*/
+static void ldif_write_msg(struct ldb_context *ldb, 
+                          FILE *f, 
+                          enum ldb_changetype changetype,
+                          struct ldb_message *msg)
+{
+       struct ldb_ldif ldif;
+       ldif.changetype = changetype;
+       ldif.msg = msg;
+       ldb_ldif_write_file(ldb, f, &ldif);
+}
+
+/*
+  modify a database record so msg1 becomes msg2
+  returns the number of modified elements
+*/
+static int modify_record(struct ldb_context *ldb, 
+                        struct ldb_message *msg1,
+                        struct ldb_message *msg2)
+{
+       struct ldb_message *mod;
+
+       mod = ldb_msg_diff(ldb, msg1, msg2);
+       if (mod == NULL) {
+               fprintf(stderr, "Failed to calculate message differences\n");
+               return -1;
+       }
+
+       if (mod->num_elements == 0) {
+               return 0;
+       }
+
+       if (options->verbose > 0) {
+               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
+       }
+
+       if (ldb_modify(ldb, mod) != 0) {
+               fprintf(stderr, "failed to modify %s - %s\n", 
+                       ldb_dn_linearize(ldb, msg1->dn), ldb_errstring(ldb));
+               return -1;
+       }
+
+       return mod->num_elements;
+}
+
+/*
+  find dn in msgs[]
+*/
+static struct ldb_message *msg_find(struct ldb_context *ldb,
+                                   struct ldb_message **msgs,
+                                   int count,
+                                   const struct ldb_dn *dn)
+{
+       int i;
+       for (i=0;i<count;i++) {
+               if (ldb_dn_compare(ldb, dn, msgs[i]->dn) == 0) {
+                       return msgs[i];
+               }
+       }
+       return NULL;
+}
+
+/*
+  merge the changes in msgs2 into the messages from msgs1
+*/
+static int merge_edits(struct ldb_context *ldb,
+                      struct ldb_message **msgs1, int count1,
+                      struct ldb_message **msgs2, int count2)
+{
+       int i;
+       struct ldb_message *msg;
+       int ret = 0;
+       int adds=0, modifies=0, deletes=0;
+
+       /* do the adds and modifies */
+       for (i=0;i<count2;i++) {
+               msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
+               if (!msg) {
+                       if (options->verbose > 0) {
+                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
+                       }
+                       if (ldb_add(ldb, msgs2[i]) != 0) {
+                               fprintf(stderr, "failed to add %s - %s\n",
+                                       ldb_dn_linearize(ldb, msgs2[i]->dn),
+                                       ldb_errstring(ldb));
+                               return -1;
+                       }
+                       adds++;
+               } else {
+                       if (modify_record(ldb, msg, msgs2[i]) > 0) {
+                               modifies++;
+                       }
+               }
+       }
+
+       /* do the deletes */
+       for (i=0;i<count1;i++) {
+               msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
+               if (!msg) {
+                       if (options->verbose > 0) {
+                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
+                       }
+                       if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
+                               fprintf(stderr, "failed to delete %s - %s\n",
+                                       ldb_dn_linearize(ldb, msgs1[i]->dn),
+                                       ldb_errstring(ldb));
+                               return -1;
+                       }
+                       deletes++;
+               }
+       }
+
+       printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
+
+       return ret;
+}
+
+/*
+  save a set of messages as ldif to a file
+*/
+static int save_ldif(struct ldb_context *ldb, 
+                    FILE *f, struct ldb_message **msgs, int count)
+{
+       int i;
+
+       fprintf(f, "# editing %d records\n", count);
+
+       for (i=0;i<count;i++) {
+               struct ldb_ldif ldif;
+               fprintf(f, "# record %d\n", i+1);
+
+               ldif.changetype = LDB_CHANGETYPE_NONE;
+               ldif.msg = msgs[i];
+
+               ldb_ldif_write_file(ldb, f, &ldif);
+       }
+
+       return 0;
+}
+
+
+/*
+  edit the ldb search results in msgs using the user selected editor
+*/
+static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
+                  const char *editor)
+{
+       int fd, ret;
+       FILE *f;
+       char file_template[] = "/tmp/ldbedit.XXXXXX";
+       char *cmd;
+       struct ldb_ldif *ldif;
+       struct ldb_message **msgs2 = NULL;
+       int count2 = 0;
+
+       /* write out the original set of messages to a temporary
+          file */
+       fd = mkstemp(file_template);
+
+       if (fd == -1) {
+               perror(file_template);
+               return -1;
+       }
+
+       f = fdopen(fd, "r+");
+
+       if (!f) {
+               perror("fopen");
+               close(fd);
+               unlink(file_template);
+               return -1;
+       }
+
+       if (save_ldif(ldb, f, msgs1, count1) != 0) {
+               return -1;
+       }
+
+       fclose(f);
+
+       cmd = talloc_asprintf(ldb, "%s %s", editor, file_template);
+
+       if (!cmd) {
+               unlink(file_template);
+               fprintf(stderr, "out of memory\n");
+               return -1;
+       }
+
+       /* run the editor */
+       ret = system(cmd);
+       talloc_free(cmd);
+
+       if (ret != 0) {
+               unlink(file_template);
+               fprintf(stderr, "edit with %s failed\n", editor);
+               return -1;
+       }
+
+       /* read the resulting ldif into msgs2 */
+       f = fopen(file_template, "r");
+       if (!f) {
+               perror(file_template);
+               return -1;
+       }
+
+       while ((ldif = ldb_ldif_read_file(ldb, f))) {
+               msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
+               if (!msgs2) {
+                       fprintf(stderr, "out of memory");
+                       return -1;
+               }
+               msgs2[count2++] = ldif->msg;
+       }
+
+       fclose(f);
+       unlink(file_template);
+
+       return merge_edits(ldb, msgs1, count1, msgs2, count2);
+}
+
+static void usage(void)
+{
+       printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -s base|sub|one  choose search scope\n");
+       printf("  -b basedn        choose baseDN\n");
+       printf("  -a               edit all records (expression 'objectclass=*')\n");
+       printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
+       printf("  -v               verbose mode\n");
+       exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       struct ldb_result *result = NULL;
+       struct ldb_dn *basedn = NULL;
+       int ret;
+       const char *expression = "(|(objectClass=*)(distinguishedName=*))";
+       const char * const * attrs = NULL;
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       /* the check for '=' is for compatibility with ldapsearch */
+       if (options->argc > 0 && 
+           strchr(options->argv[0], '=')) {
+               expression = options->argv[0];
+               options->argv++;
+               options->argc--;
+       }
+
+       if (options->argc > 0) {
+               attrs = (const char * const *)(options->argv);
+       }
+
+       if (options->basedn != NULL) {
+               basedn = ldb_dn_explode(ldb, options->basedn);
+               if (basedn == NULL) {
+                       printf("Invalid Base DN format\n");
+                       exit(1);
+               }
+       }
+
+       ret = ldb_search(ldb, basedn, options->scope, expression, attrs, &result);
+       if (ret != LDB_SUCCESS) {
+               printf("search failed - %s\n", ldb_errstring(ldb));
+               exit(1);
+       }
+
+       if (result->count == 0) {
+               printf("no matching records - cannot edit\n");
+               return 0;
+       }
+
+       do_edit(ldb, result->msgs, result->count, options->editor);
+
+       if (result) {
+               ret = talloc_free(result);
+               if (ret == -1) {
+                       fprintf(stderr, "talloc_free failed\n");
+                       exit(1);
+               }
+       }
+
+       talloc_free(ldb);
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/ldbmodify.c b/source3/lib/ldb/tools/ldbmodify.c
new file mode 100644 (file)
index 0000000..24f9386
--- /dev/null
@@ -0,0 +1,124 @@
+/* 
+   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: ldbmodify
+ *
+ *  Description: utility to modify records - modelled on ldapmodify
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static int failures;
+
+static void usage(void)
+{
+       printf("Usage: ldbmodify <options> <ldif...>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Modifies a ldb based upon ldif change records\n\n");
+       exit(1);
+}
+
+/*
+  process modifies for one file
+*/
+static int process_file(struct ldb_context *ldb, FILE *f)
+{
+       struct ldb_ldif *ldif;
+       int ret = -1, count = 0;
+       
+       while ((ldif = ldb_ldif_read_file(ldb, f))) {
+               switch (ldif->changetype) {
+               case LDB_CHANGETYPE_NONE:
+               case LDB_CHANGETYPE_ADD:
+                       ret = ldb_add(ldb, ldif->msg);
+                       break;
+               case LDB_CHANGETYPE_DELETE:
+                       ret = ldb_delete(ldb, ldif->msg->dn);
+                       break;
+               case LDB_CHANGETYPE_MODIFY:
+                       ret = ldb_modify(ldb, ldif->msg);
+                       break;
+               }
+               if (ret != 0) {
+                       fprintf(stderr, "ERR: \"%s\" on DN %s\n", 
+                               ldb_errstring(ldb), ldb_dn_linearize(ldb, ldif->msg->dn));
+                       failures++;
+               } else {
+                       count++;
+               }
+               ldb_ldif_read_free(ldb, ldif);
+       }
+
+       return count;
+}
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       int count=0;
+       int i;
+       struct ldb_cmdline *options;
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       if (options->argc == 0) {
+               count += process_file(ldb, stdin);
+       } else {
+               for (i=0;i<options->argc;i++) {
+                       const char *fname = options->argv[i];
+                       FILE *f;
+                       f = fopen(fname, "r");
+                       if (!f) {
+                               perror(fname);
+                               exit(1);
+                       }
+                       count += process_file(ldb, f);
+               }
+       }
+
+       talloc_free(ldb);
+
+       printf("Modified %d records with %d failures\n", count, failures);
+
+       if (failures != 0) {
+               return -1;
+       }
+       
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/ldbrename.c b/source3/lib/ldb/tools/ldbrename.c
new file mode 100644 (file)
index 0000000..9c08707
--- /dev/null
@@ -0,0 +1,85 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+   Copyright (C) Stefan Metzmacher  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: ldbrename
+ *
+ *  Description: utility to rename records - modelled on ldapmodrdn
+ *
+ *  Author: Andrew Tridgell
+ *  Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static void usage(void)
+{
+       printf("Usage: ldbrename [<options>] <olddn> <newdn>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Renames records in a ldb\n\n");
+       exit(1);
+}
+
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       int ret;
+       struct ldb_cmdline *options;
+       const struct ldb_dn *dn1, *dn2;
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       if (options->argc < 2) {
+               usage();
+       }
+
+       dn1 = ldb_dn_explode(ldb, options->argv[0]);
+       dn2 = ldb_dn_explode(ldb, options->argv[1]);
+
+       ret = ldb_rename(ldb, dn1, dn2);
+       if (ret == 0) {
+               printf("Renamed 1 record\n");
+       } else  {
+               printf("rename of '%s' to '%s' failed - %s\n", 
+                       options->argv[0], options->argv[1], ldb_errstring(ldb));
+       }
+
+       talloc_free(ldb);
+       
+       return ret;
+}
diff --git a/source3/lib/ldb/tools/ldbsearch.c b/source3/lib/ldb/tools/ldbsearch.c
new file mode 100644 (file)
index 0000000..23d8115
--- /dev/null
@@ -0,0 +1,321 @@
+/* 
+   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: ldbsearch
+ *
+ *  Description: utility for ldb search - modelled on ldapsearch
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static void usage(void)
+{
+       printf("Usage: ldbsearch <options> <expression> <attrs...>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  -s base|sub|one  choose search scope\n");
+       printf("  -b basedn        choose baseDN\n");
+       printf("  -i               read search expressions from stdin\n");
+        printf("  -S               sort returned attributes\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       exit(1);
+}
+
+static int do_compare_msg(struct ldb_message **el1,
+                         struct ldb_message **el2,
+                         void *opaque)
+{
+       struct ldb_context *ldb = talloc_get_type(opaque, struct ldb_context);
+       return ldb_dn_compare(ldb, (*el1)->dn, (*el2)->dn);
+}
+
+struct search_context {
+       struct ldb_control **req_ctrls;
+
+       int sort;
+       int num_stored;
+       struct ldb_message **store;
+       char **refs_store;
+
+       int entries;
+       int refs;
+
+       int pending;
+       int status;
+};
+
+static int store_message(struct ldb_message *msg, struct search_context *sctx) {
+
+       sctx->store = talloc_realloc(sctx, sctx->store, struct ldb_message *, sctx->num_stored + 2);
+       if (!sctx->store) {
+               fprintf(stderr, "talloc_realloc failed while storing messages\n");
+               return -1;
+       }
+
+       sctx->store[sctx->num_stored] = talloc_move(sctx->store, &msg);
+       sctx->num_stored++;
+       sctx->store[sctx->num_stored] = NULL;
+
+       return 0;
+}
+
+static int store_referral(char *referral, struct search_context *sctx) {
+
+       sctx->refs_store = talloc_realloc(sctx, sctx->refs_store, char *, sctx->refs + 2);
+       if (!sctx->refs_store) {
+               fprintf(stderr, "talloc_realloc failed while storing referrals\n");
+               return -1;
+       }
+
+       sctx->refs_store[sctx->refs] = talloc_move(sctx->refs_store, &referral);
+       sctx->refs++;
+       sctx->refs_store[sctx->refs] = NULL;
+
+       return 0;
+}
+
+static int display_message(struct ldb_context *ldb, struct ldb_message *msg, struct search_context *sctx) {
+       struct ldb_ldif ldif;
+
+       sctx->entries++;
+       printf("# record %d\n", sctx->entries);
+
+       ldif.changetype = LDB_CHANGETYPE_NONE;
+       ldif.msg = msg;
+
+               if (sctx->sort) {
+       /*
+        * Ensure attributes are always returned in the same
+        * order.  For testing, this makes comparison of old
+        * vs. new much easier.
+        */
+               ldb_msg_sort_elements(ldif.msg);
+               }
+
+       ldb_ldif_write_file(ldb, stdout, &ldif);
+
+       return 0;
+}
+
+static int display_referral(char *referral, struct search_context *sctx)
+{
+
+       sctx->refs++;
+       printf("# Referral\nref: %s\n\n", referral);
+
+       return 0;
+}
+
+static int search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+       struct search_context *sctx = talloc_get_type(context, struct search_context);
+       int ret;
+       
+       switch (ares->type) {
+
+       case LDB_REPLY_ENTRY:
+               if (sctx->sort) {
+                       ret = store_message(ares->message, sctx);
+               } else {
+                       ret = display_message(ldb, ares->message, sctx);
+               }
+               break;
+
+       case LDB_REPLY_REFERRAL:
+               if (sctx->sort) {
+                       ret = store_referral(ares->referral, sctx);
+               } else {
+                       ret = display_referral(ares->referral, sctx);
+               }
+               break;
+
+       case LDB_REPLY_DONE:
+               if (ares->controls) {
+                       if (handle_controls_reply(ares->controls, sctx->req_ctrls) == 1)
+                               sctx->pending = 1;
+               }
+               ret = 0;
+               break;
+               
+       default:
+               fprintf(stderr, "unknown Reply Type\n");
+               return LDB_ERR_OTHER;
+       }
+
+       if (talloc_free(ares) == -1) {
+               fprintf(stderr, "talloc_free failed\n");
+               sctx->pending = 0;
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ret) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return LDB_SUCCESS;
+}
+
+static int do_search(struct ldb_context *ldb,
+                    const struct ldb_dn *basedn,
+                    struct ldb_cmdline *options,
+                    const char *expression,
+                    const char * const *attrs)
+{
+       struct ldb_request *req;
+       struct search_context *sctx;
+       int ret;
+
+       req = talloc(ldb, struct ldb_request);
+       if (!req) return -1;
+       
+       sctx = talloc(req, struct search_context);
+       if (!sctx) return -1;
+
+       sctx->sort = options->sorted;
+       sctx->num_stored = 0;
+       sctx->store = NULL;
+       sctx->req_ctrls = parse_controls(ldb, options->controls);
+       if (options->controls != NULL &&  sctx->req_ctrls== NULL) return -1;
+       sctx->entries = 0;
+       sctx->refs = 0;
+
+       if (basedn == NULL) {
+               basedn = ldb_get_default_basedn(ldb);
+       }
+
+       req->operation = LDB_SEARCH;
+       req->op.search.base = basedn;
+       req->op.search.scope = options->scope;
+       req->op.search.tree = ldb_parse_tree(ldb, expression);
+       if (req->op.search.tree == NULL) return -1;
+       req->op.search.attrs = attrs;
+       req->controls = sctx->req_ctrls;
+       req->context = sctx;
+       req->callback = &search_callback;
+       ldb_set_timeout(ldb, req, 0); /* TODO: make this settable by command line */
+
+again:
+       sctx->pending = 0;
+
+       ret = ldb_request(ldb, req);
+       if (ret != LDB_SUCCESS) {
+               printf("search failed - %s\n", ldb_errstring(ldb));
+               return -1;
+       }
+
+       ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+               if (ret != LDB_SUCCESS) {
+               printf("search error - %s\n", ldb_errstring(ldb));
+               return -1;
+       }
+
+       if (sctx->pending)
+               goto again;
+
+       if (sctx->sort && sctx->num_stored != 0) {
+               int i;
+
+               ldb_qsort(sctx->store, ret, sizeof(struct ldb_message *),
+                         ldb, (ldb_qsort_cmp_fn_t)do_compare_msg);
+
+               if (ret != 0) {
+                       fprintf(stderr, "An error occurred while sorting messages\n");
+                       exit(1);
+               }
+
+               for (i = 0; i < sctx->num_stored; i++) {
+                       display_message(ldb, sctx->store[i], sctx);
+               }
+
+               for (i = 0; i < sctx->refs; i++) {
+                       display_referral(sctx->refs_store[i], sctx);
+               }
+       }
+
+       printf("# returned %d records\n# %d entries\n# %d referrals\n",
+               sctx->entries + sctx->refs, sctx->entries, sctx->refs);
+
+       talloc_free(req);
+
+       return 0;
+}
+
+int main(int argc, const char **argv)
+{
+       struct ldb_context *ldb;
+       struct ldb_dn *basedn = NULL;
+       const char * const * attrs = NULL;
+       struct ldb_cmdline *options;
+       int ret = -1;
+       const char *expression = "(|(objectClass=*)(distinguishedName=*))";
+
+       ldb_global_init();
+
+       ldb = ldb_init(NULL);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       /* the check for '=' is for compatibility with ldapsearch */
+       if (!options->interactive &&
+           options->argc > 0 && 
+           strchr(options->argv[0], '=')) {
+               expression = options->argv[0];
+               options->argv++;
+               options->argc--;
+       }
+
+       if (options->argc > 0) {
+               attrs = (const char * const *)(options->argv);
+       }
+
+       if (options->basedn != NULL) {
+               basedn = ldb_dn_explode(ldb, options->basedn);
+               if (basedn == NULL) {
+                       fprintf(stderr, "Invalid Base DN format\n");
+                       exit(1);
+               }
+       }
+
+       if (options->interactive) {
+               char line[1024];
+               while (fgets(line, sizeof(line), stdin)) {
+                       if (do_search(ldb, basedn, options, line, attrs) == -1) {
+                               ret = -1;
+                       }
+               }
+       } else {
+               ret = do_search(ldb, basedn, options, expression, attrs);
+       }
+
+       talloc_free(ldb);
+       return ret;
+}
diff --git a/source3/lib/ldb/tools/ldbtest.c b/source3/lib/ldb/tools/ldbtest.c
new file mode 100644 (file)
index 0000000..6cc8dfe
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+   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: ldbtest
+ *
+ *  Description: utility to test ldb
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+
+static struct timeval tp1,tp2;
+static struct ldb_cmdline *options;
+
+static void _start_timer(void)
+{
+       gettimeofday(&tp1,NULL);
+}
+
+static double _end_timer(void)
+{
+       gettimeofday(&tp2,NULL);
+       return((tp2.tv_sec - tp1.tv_sec) + 
+              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void add_records(struct ldb_context *ldb,
+                       const struct ldb_dn *basedn,
+                       int count)
+{
+       struct ldb_message msg;
+       int i;
+
+#if 0
+        if (ldb_lock(ldb, "transaction") != 0) {
+                printf("transaction lock failed\n");
+                exit(1);
+        }
+#endif
+       for (i=0;i<count;i++) {
+               struct ldb_message_element el[6];
+               struct ldb_val vals[6][1];
+               char *name;
+               TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+
+               name = talloc_asprintf(tmp_ctx, "Test%d", i);
+
+               msg.dn = ldb_dn_build_child(tmp_ctx, "cn", name, basedn);
+               msg.num_elements = 6;
+               msg.elements = el;
+
+               el[0].flags = 0;
+               el[0].name = talloc_strdup(tmp_ctx, "cn");
+               el[0].num_values = 1;
+               el[0].values = vals[0];
+               vals[0][0].data = (uint8_t *)name;
+               vals[0][0].length = strlen(name);
+
+               el[1].flags = 0;
+               el[1].name = "title";
+               el[1].num_values = 1;
+               el[1].values = vals[1];
+               vals[1][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "The title of %s", name);
+               vals[1][0].length = strlen((char *)vals[1][0].data);
+
+               el[2].flags = 0;
+               el[2].name = talloc_strdup(tmp_ctx, "uid");
+               el[2].num_values = 1;
+               el[2].values = vals[2];
+               vals[2][0].data = (uint8_t *)ldb_casefold(ldb, tmp_ctx, name);
+               vals[2][0].length = strlen((char *)vals[2][0].data);
+
+               el[3].flags = 0;
+               el[3].name = talloc_strdup(tmp_ctx, "mail");
+               el[3].num_values = 1;
+               el[3].values = vals[3];
+               vals[3][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@example.com", name);
+               vals[3][0].length = strlen((char *)vals[3][0].data);
+
+               el[4].flags = 0;
+               el[4].name = talloc_strdup(tmp_ctx, "objectClass");
+               el[4].num_values = 1;
+               el[4].values = vals[4];
+               vals[4][0].data = (uint8_t *)talloc_strdup(tmp_ctx, "OpenLDAPperson");
+               vals[4][0].length = strlen((char *)vals[4][0].data);
+
+               el[5].flags = 0;
+               el[5].name = talloc_strdup(tmp_ctx, "sn");
+               el[5].num_values = 1;
+               el[5].values = vals[5];
+               vals[5][0].data = (uint8_t *)name;
+               vals[5][0].length = strlen((char *)vals[5][0].data);
+
+               ldb_delete(ldb, msg.dn);
+
+               if (ldb_add(ldb, &msg) != 0) {
+                       printf("Add of %s failed - %s\n", name, ldb_errstring(ldb));
+                       exit(1);
+               }
+
+               printf("adding uid %s\r", name);
+               fflush(stdout);
+
+               talloc_free(tmp_ctx);
+       }
+#if 0
+        if (ldb_unlock(ldb, "transaction") != 0) {
+                printf("transaction unlock failed\n");
+                exit(1);
+        }
+#endif
+       printf("\n");
+}
+
+static void modify_records(struct ldb_context *ldb,
+                          const struct ldb_dn *basedn,
+                          int count)
+{
+       struct ldb_message msg;
+       int i;
+
+       for (i=0;i<count;i++) {
+               struct ldb_message_element el[3];
+               struct ldb_val vals[3];
+               char *name;
+               TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+               
+               name = talloc_asprintf(tmp_ctx, "Test%d", i);
+               msg.dn = ldb_dn_build_child(tmp_ctx, "cn", name, basedn);
+
+               msg.num_elements = 3;
+               msg.elements = el;
+
+               el[0].flags = LDB_FLAG_MOD_DELETE;
+               el[0].name = talloc_strdup(tmp_ctx, "mail");
+               el[0].num_values = 0;
+
+               el[1].flags = LDB_FLAG_MOD_ADD;
+               el[1].name = talloc_strdup(tmp_ctx, "mail");
+               el[1].num_values = 1;
+               el[1].values = &vals[1];
+               vals[1].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other.example.com", name);
+               vals[1].length = strlen((char *)vals[1].data);
+
+               el[2].flags = LDB_FLAG_MOD_REPLACE;
+               el[2].name = talloc_strdup(tmp_ctx, "mail");
+               el[2].num_values = 1;
+               el[2].values = &vals[2];
+               vals[2].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other2.example.com", name);
+               vals[2].length = strlen((char *)vals[2].data);
+
+               if (ldb_modify(ldb, &msg) != 0) {
+                       printf("Modify of %s failed - %s\n", name, ldb_errstring(ldb));
+                       exit(1);
+               }
+
+               printf("Modifying uid %s\r", name);
+               fflush(stdout);
+
+               talloc_free(tmp_ctx);
+       }
+
+       printf("\n");
+}
+
+
+static void delete_records(struct ldb_context *ldb,
+                          const struct ldb_dn *basedn,
+                          int count)
+{
+       int i;
+
+       for (i=0;i<count;i++) {
+               struct ldb_dn *dn;
+               char *name = talloc_asprintf(ldb, "Test%d", i);
+               dn = ldb_dn_build_child(name, "cn", name, basedn);
+
+               printf("Deleting uid Test%d\r", i);
+               fflush(stdout);
+
+               if (ldb_delete(ldb, dn) != 0) {
+                       printf("Delete of %s failed - %s\n", ldb_dn_linearize(ldb, dn), ldb_errstring(ldb));
+                       exit(1);
+               }
+               talloc_free(name);
+       }
+
+       printf("\n");
+}
+
+static void search_uid(struct ldb_context *ldb, struct ldb_dn *basedn, int nrecords, int nsearches)
+{
+       int i;
+
+       for (i=0;i<nsearches;i++) {
+               int uid = (i * 700 + 17) % (nrecords * 2);
+               char *expr;
+               struct ldb_result *res = NULL;
+               int ret;
+
+               expr = talloc_asprintf(ldb, "(uid=TEST%d)", uid);
+               ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE, expr, NULL, &res);
+
+               if (ret != LDB_SUCCESS || (uid < nrecords && res->count != 1)) {
+                       printf("Failed to find %s - %s\n", expr, ldb_errstring(ldb));
+                       exit(1);
+               }
+
+               if (uid >= nrecords && res->count > 0) {
+                       printf("Found %s !? - %d\n", expr, ret);
+                       exit(1);
+               }
+
+               printf("testing uid %d/%d - %d  \r", i, uid, res->count);
+               fflush(stdout);
+
+               talloc_free(res);
+               talloc_free(expr);
+       }
+
+       printf("\n");
+}
+
+static void start_test(struct ldb_context *ldb, int nrecords, int nsearches)
+{
+       struct ldb_dn *basedn;
+
+       basedn = ldb_dn_explode(ldb, options->basedn);
+
+       printf("Adding %d records\n", nrecords);
+       add_records(ldb, basedn, nrecords);
+
+       printf("Starting search on uid\n");
+       _start_timer();
+       search_uid(ldb, basedn, nrecords, nsearches);
+       printf("uid search took %.2f seconds\n", _end_timer());
+
+       printf("Modifying records\n");
+       modify_records(ldb, basedn, nrecords);
+
+       printf("Deleting records\n");
+       delete_records(ldb, basedn, nrecords);
+}
+
+
+/*
+      2) Store an @indexlist record
+
+      3) Store a record that contains fields that should be index according
+to @index
+
+      4) disconnection from database
+
+      5) connect to same database
+
+      6) search for record added in step 3 using a search key that should
+be indexed
+*/
+static void start_test_index(struct ldb_context **ldb)
+{
+       struct ldb_message *msg;
+       struct ldb_result *res = NULL;
+       struct ldb_dn *indexlist;
+       struct ldb_dn *basedn;
+       int ret;
+       int flags = 0;
+       const char *specials;
+
+       specials = getenv("LDB_SPECIALS");
+       if (specials && atoi(specials) == 0) {
+               printf("LDB_SPECIALS disabled - skipping index test\n");
+               return;
+       }
+
+       if (options->nosync) {
+               flags |= LDB_FLG_NOSYNC;
+       }
+
+       printf("Starting index test\n");
+
+       indexlist = ldb_dn_explode(NULL, "@INDEXLIST");
+
+       ldb_delete(*ldb, indexlist);
+
+       msg = ldb_msg_new(NULL);
+
+       msg->dn = indexlist;
+       ldb_msg_add_string(msg, "@IDXATTR", strdup("uid"));
+
+       if (ldb_add(*ldb, msg) != 0) {
+               printf("Add of %s failed - %s\n", ldb_dn_linearize(*ldb, msg->dn), ldb_errstring(*ldb));
+               exit(1);
+       }
+
+       basedn = ldb_dn_explode(NULL, options->basedn);
+
+       memset(msg, 0, sizeof(*msg));
+       msg->dn = ldb_dn_build_child(msg, "cn", "test", basedn);
+       ldb_msg_add_string(msg, "cn", strdup("test"));
+       ldb_msg_add_string(msg, "sn", strdup("test"));
+       ldb_msg_add_string(msg, "uid", strdup("test"));
+       ldb_msg_add_string(msg, "objectClass", strdup("OpenLDAPperson"));
+
+       if (ldb_add(*ldb, msg) != 0) {
+               printf("Add of %s failed - %s\n", ldb_dn_linearize(*ldb, msg->dn), ldb_errstring(*ldb));
+               exit(1);
+       }
+
+       if (talloc_free(*ldb) != 0) {
+               printf("failed to free/close ldb database");
+               exit(1);
+       }
+
+       (*ldb) = ldb_init(options);
+       
+       ret = ldb_connect(*ldb, options->url, flags, NULL);
+       if (ret != 0) {
+               printf("failed to connect to %s\n", options->url);
+               exit(1);
+       }
+
+       ret = ldb_search(*ldb, basedn, LDB_SCOPE_SUBTREE, "uid=test", NULL, &res);
+       if (ret != LDB_SUCCESS) { 
+               printf("Search with (uid=test) filter failed!\n");
+               exit(1);
+       }
+       if(res->count != 1) {
+               printf("Should have found 1 record - found %d\n", res->count);
+               exit(1);
+       }
+
+       if (ldb_delete(*ldb, msg->dn) != 0 ||
+           ldb_delete(*ldb, indexlist) != 0) {
+               printf("cleanup failed - %s\n", ldb_errstring(*ldb));
+               exit(1);
+       }
+
+       printf("Finished index test\n");
+}
+
+
+static void usage(void)
+{
+       printf("Usage: ldbtest <options>\n");
+       printf("Options:\n");
+       printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
+       printf("  --num-records  nrecords      database size to use\n");
+       printf("  --num-searches nsearches     number of searches to do\n");
+       printf("\n");
+       printf("tests ldb API\n\n");
+       exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       struct ldb_context *ldb;
+
+       ldb_global_init();
+
+       ldb = ldb_init(mem_ctx);
+
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+       talloc_steal(mem_ctx, options);
+
+       if (options->basedn == NULL) {
+               options->basedn = "ou=Ldb Test,ou=People,o=University of Michigan,c=TEST";
+       }
+
+       srandom(1);
+
+       printf("Testing with num-records=%d and num-searches=%d\n", 
+              options->num_records, options->num_searches);
+
+       start_test(ldb, options->num_records, options->num_searches);
+
+       start_test_index(&ldb);
+
+       talloc_free(mem_ctx);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/tools/oLschema2ldif.c b/source3/lib/ldb/tools/oLschema2ldif.c
new file mode 100644 (file)
index 0000000..a9e157e
--- /dev/null
@@ -0,0 +1,608 @@
+/* 
+   ldb database library
+
+   Copyright (C) Simo Sorce 2005
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: oLschema2ldif
+ *
+ *  Description: utility to convert an OpenLDAP schema into AD LDIF
+ *
+ *  Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+#include "ldb/tools/cmdline.h"
+#include "ldb/tools/convert.h"
+
+#define SCHEMA_UNKNOWN 0
+#define SCHEMA_NAME 1
+#define SCHEMA_SUP 2
+#define SCHEMA_STRUCTURAL 3
+#define SCHEMA_ABSTRACT 4
+#define SCHEMA_AUXILIARY 5
+#define SCHEMA_MUST 6
+#define SCHEMA_MAY 7
+#define SCHEMA_SINGLE_VALUE 8
+#define SCHEMA_EQUALITY 9
+#define SCHEMA_ORDERING 10
+#define SCHEMA_SUBSTR 11
+#define SCHEMA_SYNTAX 12
+#define SCHEMA_DESC 13
+
+struct schema_conv {
+       int count;
+       int failures;
+};
+
+struct schema_token {
+       int type;
+       char *value;
+};
+
+struct ldb_context *ldb_ctx;
+struct ldb_dn *basedn;
+
+static int check_braces(const char *string)
+{
+       int b;
+       char *c;
+
+       b = 0;
+       if ((c = strchr(string, '(')) == NULL) {
+               return -1;
+       }
+       b++;
+       c++;
+       while (b) {
+               c = strpbrk(c, "()");
+               if (c == NULL) return 1;
+               if (*c == '(') b++;
+               if (*c == ')') b--;
+               c++;
+       }
+       return 0;
+}
+
+static char *skip_spaces(char *string) {
+       return (string + strspn(string, " \t\n"));
+}
+
+static int add_multi_string(struct ldb_message *msg, const char *attr, char *values)
+{
+       char *c;
+       char *s;
+       int n;
+
+       c = skip_spaces(values);
+       while (*c) {
+               n = strcspn(c, " \t$");
+               s = talloc_strndup(msg, c, n);
+               if (ldb_msg_add_string(msg, attr, s) != 0) {
+                       return -1;
+               }
+               c += n;
+               c += strspn(c, " \t$");
+       }
+
+       return 0;
+}
+
+#define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0)
+#define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0)
+
+static char *get_def_value(TALLOC_CTX *ctx, char **string)
+{
+       char *c = *string;
+       char *value;
+       int n;
+
+       if (*c == '\'') {
+               c++;
+               n = strcspn(c, "\'");
+               value = talloc_strndup(ctx, c, n);
+               c += n;
+               c++; /* skip closing \' */
+       } else {
+               n = strcspn(c, " \t\n");
+               value = talloc_strndup(ctx, c, n);
+               c += n;
+       }
+       *string = c;
+
+       return value;
+}
+
+static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string)
+{
+       char *c = skip_spaces(*string);
+       char *type;
+       struct schema_token *token;
+       int n;
+
+       token = talloc(ctx, struct schema_token);
+
+       n = strcspn(c, " \t\n");
+       type = talloc_strndup(token, c, n);
+       c += n;
+       c = skip_spaces(c);
+
+       if (strcasecmp("NAME", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_NAME;
+               /* we do not support aliases so we get only the first name given and skip others */
+               if (*c == '(') {
+                       char *s = strchr(c, ')');
+                       if (s == NULL) return NULL;
+                       s = skip_spaces(s);
+                       *string = s;
+
+                       c++;
+                       c = skip_spaces(c);
+               }
+
+               token->value = get_def_value(ctx, &c);
+
+               if (*string < c) { /* single name */
+                       c = skip_spaces(c);
+                       *string = c;
+               }
+               return token;
+       }
+       if (strcasecmp("SUP", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_SUP;
+
+               if (*c == '(') {
+                       c++;
+                       n = strcspn(c, ")");
+                       token->value = talloc_strndup(ctx, c, n);
+                       c += n;
+                       c++;
+               } else {
+                       token->value = get_def_value(ctx, &c);
+               }
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("STRUCTURAL", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_STRUCTURAL;
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("ABSTRACT", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_ABSTRACT;
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("AUXILIARY", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_AUXILIARY;
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("MUST", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_MUST;
+
+               if (*c == '(') {
+                       c++;
+                       n = strcspn(c, ")");
+                       token->value = talloc_strndup(ctx, c, n);
+                       c += n;
+                       c++;
+               } else {
+                       token->value = get_def_value(ctx, &c);
+               }
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("MAY", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_MAY;
+
+               if (*c == '(') {
+                       c++;
+                       n = strcspn(c, ")");
+                       token->value = talloc_strndup(ctx, c, n);
+                       c += n;
+                       c++;
+               } else {
+                       token->value = get_def_value(ctx, &c);
+               }
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("SINGLE-VALUE", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_SINGLE_VALUE;
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("EQUALITY", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_EQUALITY;
+
+               token->value = get_def_value(ctx, &c);
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("ORDERING", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_ORDERING;
+
+               token->value = get_def_value(ctx, &c);
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("SUBSTR", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_SUBSTR;
+
+               token->value = get_def_value(ctx, &c);
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("SYNTAX", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_SYNTAX;
+
+               token->value = get_def_value(ctx, &c);
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       if (strcasecmp("DESC", type) == 0) {
+               talloc_free(type);
+               token->type = SCHEMA_DESC;
+
+               token->value = get_def_value(ctx, &c);
+
+               c = skip_spaces(c);
+               *string = c;
+               return token;
+       }
+
+       token->type = SCHEMA_UNKNOWN;
+       token->value = type;
+       if (*c == ')') {
+               *string = c;
+               return token;
+       }
+       if (*c == '\'') {
+               c = strchr(++c, '\'');
+               c++;
+       } else {
+               c += strcspn(c, " \t\n");
+       }
+       c = skip_spaces(c);
+       *string = c;
+
+       return token;
+}
+
+static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry)
+{
+       TALLOC_CTX *ctx;
+       struct ldb_message *msg;
+       struct schema_token *token;
+        char *c, *s;
+        int n;
+
+       ctx = talloc_new(mem_ctx);
+       msg = ldb_msg_new(ctx);
+
+       ldb_msg_add_string(msg, "objectClass", "top");
+
+       c = talloc_strdup(ctx, entry);
+       if (!c) return NULL;
+
+       c = skip_spaces(c);
+
+       switch (*c) {
+       case 'a':
+               if (strncmp(c, "attributetype", 13) == 0) {
+                       c += 13;
+                       MSG_ADD_STRING("objectClass", "attributeSchema");
+                       break;
+               }
+               goto failed;
+       case 'o':
+               if (strncmp(c, "objectclass", 11) == 0) {
+                       c += 11;
+                       MSG_ADD_STRING("objectClass", "classSchema");
+                       break;
+               }
+               goto failed;
+       default:
+               goto failed;
+       }
+
+       c = strchr(c, '(');
+       if (c == NULL) goto failed;
+       c++;
+
+       c = skip_spaces(c);
+
+       /* get attributeID */
+       n = strcspn(c, " \t");
+       s = talloc_strndup(msg, c, n);
+       MSG_ADD_STRING("attributeID", s);
+       c += n;
+       c = skip_spaces(c);     
+
+       while (*c != ')') {
+               token = get_next_schema_token(msg, &c);
+               if (!token) goto failed;
+
+               switch (token->type) {
+               case SCHEMA_NAME:
+                       MSG_ADD_STRING("cn", token->value);
+                       MSG_ADD_STRING("name", token->value);
+                       MSG_ADD_STRING("lDAPDisplayName", token->value);
+                       msg->dn = ldb_dn_string_compose(msg, basedn,
+                                                       "CN=%s,CN=Schema,CN=Configuration",
+                                                       token->value);
+                       break;
+
+               case SCHEMA_SUP:
+                       MSG_ADD_M_STRING("subClassOf", token->value);
+                       break;
+
+               case SCHEMA_STRUCTURAL:
+                       MSG_ADD_STRING("objectClassCategory", "1");
+                       break;
+
+               case SCHEMA_ABSTRACT:
+                       MSG_ADD_STRING("objectClassCategory", "2");
+                       break;
+
+               case SCHEMA_AUXILIARY:
+                       MSG_ADD_STRING("objectClassCategory", "3");
+                       break;
+
+               case SCHEMA_MUST:
+                       MSG_ADD_M_STRING("mustContain", token->value);
+                       break;
+
+               case SCHEMA_MAY:
+                       MSG_ADD_M_STRING("mayContain", token->value);
+                       break;
+
+               case SCHEMA_SINGLE_VALUE:
+                       MSG_ADD_STRING("isSingleValued", "TRUE");
+                       break;
+
+               case SCHEMA_EQUALITY:
+                       /* TODO */
+                       break;
+
+               case SCHEMA_ORDERING:
+                       /* TODO */
+                       break;
+
+               case SCHEMA_SUBSTR:
+                       /* TODO */
+                       break;
+
+               case SCHEMA_SYNTAX:
+               {
+                       const struct syntax_map *map = 
+                               find_syntax_map_by_standard_oid(token->value);
+                       if (!map) {
+                               break;
+                       }
+                       MSG_ADD_STRING("attributeSyntax", map->AD_OID);
+                       break;
+               }
+               case SCHEMA_DESC:
+                       MSG_ADD_STRING("description", token->value);
+                       break;
+
+               default:
+                       fprintf(stderr, "Unknown Definition: %s\n", token->value);
+               }
+       }
+
+       talloc_steal(mem_ctx, msg);
+       talloc_free(ctx);
+       return msg;
+
+failed:
+       talloc_free(ctx);
+       return NULL;
+}
+
+static struct schema_conv process_file(FILE *in, FILE *out)
+{
+       TALLOC_CTX *ctx;
+       struct schema_conv ret;
+       char *entry;
+       int c, t, line;
+       struct ldb_ldif ldif;
+
+       ldif.changetype = LDB_CHANGETYPE_NONE;
+
+       ctx = talloc_new(NULL);
+
+       ret.count = 0;
+       ret.failures = 0;
+       line = 0;
+
+       while ((c = fgetc(in)) != EOF) {
+               line++;
+               /* fprintf(stderr, "Parsing line %d\n", line); */
+               if (c == '#') {
+                       do {
+                               c = fgetc(in);
+                       } while (c != EOF && c != '\n');
+                       continue;
+               }
+               if (c == '\n') {
+                       continue;
+               }
+
+               t = 0;
+               entry = talloc_array(ctx, char, 1024);
+               if (entry == NULL) exit(-1);
+
+               do { 
+                       if (c == '\n') {
+                               entry[t] = '\0';        
+                               if (check_braces(entry) == 0) {
+                                       ret.count++;
+                                       ldif.msg = process_entry(ctx, entry);
+                                       if (ldif.msg == NULL) {
+                                               ret.failures++;
+                                               fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
+                                               break;
+                                       }
+                                       ldb_ldif_write_file(ldb_ctx, out, &ldif);
+                                       break;
+                               }
+                               line++;
+                       } else {
+                               entry[t] = c;
+                               t++;
+                       }
+                       if ((t % 1023) == 0) {
+                               entry = talloc_realloc(ctx, entry, char, t + 1024);
+                               if (entry == NULL) exit(-1);
+                       }
+               } while ((c = fgetc(in)) != EOF); 
+
+               if (c != '\n') {
+                       entry[t] = '\0';
+                       if (check_braces(entry) == 0) {
+                               ret.count++;
+                               ldif.msg = process_entry(ctx, entry);
+                               if (ldif.msg == NULL) {
+                                       ret.failures++;
+                                       fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
+                                       break;
+                               }
+                               ldb_ldif_write_file(ldb_ctx, out, &ldif);
+                       } else {
+                               fprintf(stderr, "malformed entry on line %d\n", line);
+                               ret.failures++;
+                       }
+               }
+       
+               if (c == EOF) break;
+       }
+
+       return ret;
+}
+
+static void usage(void)
+{
+       printf("Usage: oLschema2ldif -H NONE <options>\n");
+       printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
+       printf("Options:\n");
+       printf("  -I inputfile     inputfile of OpenLDAP style schema otherwise STDIN\n");
+       printf("  -O outputfile    outputfile otherwise STDOUT\n");
+       printf("  -o options       pass options like modules to activate\n");
+       printf("              e.g: -o modules:timestamps\n");
+       printf("\n");
+       printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
+       exit(1);
+}
+
+ int main(int argc, const char **argv)
+{
+       TALLOC_CTX *ctx;
+       struct schema_conv ret;
+       struct ldb_cmdline *options;
+       FILE *in = stdin;
+       FILE *out = stdout;
+       ldb_global_init();
+
+       ctx = talloc_new(NULL);
+       ldb_ctx = ldb_init(ctx);
+
+       setenv("LDB_URL", "NONE", 1);
+       options = ldb_cmdline_process(ldb_ctx, argc, argv, usage);
+
+       if (options->basedn == NULL) {
+               perror("Base DN not specified");
+               exit(1);
+       } else {
+               basedn = ldb_dn_explode(ctx, options->basedn);
+               if (basedn == NULL) {
+                       perror("Malformed Base DN");
+                       exit(1);
+               }
+       }
+
+       if (options->input) {
+               in = fopen(options->input, "r");
+               if (!in) {
+                       perror(options->input);
+                       exit(1);
+               }
+       }
+       if (options->output) {
+               out = fopen(options->output, "w");
+               if (!out) {
+                       perror(options->output);
+                       exit(1);
+               }
+       }
+
+       ret = process_file(in, out);
+
+       fclose(in);
+       fclose(out);
+
+       printf("Converted %d records with %d failures\n", ret.count, ret.failures);
+
+       return 0;
+}
diff --git a/source3/lib/ldb/web/index.html b/source3/lib/ldb/web/index.html
new file mode 100644 (file)
index 0000000..2715a0d
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<TITLE>ldb</TITLE>
+</HEAD>
+<BODY BGCOLOR="#ffffff" TEXT="#000000" VLINK="#292555" LINK="#292555" ALINK="#cc0033">
+
+<h1>ldb</h1>
+
+ldb is a LDAP-like embedded database. It is not at all LDAP standards
+compliant, so if you want a standards compliant database then please
+see the excellent <a href="http://www.openldap.org/">OpenLDAP</a>
+project.<p>
+
+What ldb does is provide a fast database with an LDAP-like API
+designed to be used within an application. In some ways it can be seen
+as a intermediate solution between key-value pair databases and a real
+LDAP database.<p>
+
+ldb is the database engine used in Samba4.
+
+<h2>Features</h2>
+
+The main features that separate ldb from other solutions are:
+
+<ul>
+<li>Safe multi-reader, multi-writer, using byte range locking
+<li>LDAP-like API
+<li>fast operation
+<li>choice of local tdb or remote LDAP backends
+<li>integration with <a href="http://talloc.samba.org">talloc</a>
+<li>schema-less operation, for trivial setup
+<li>modules for extensions (such as schema support)
+<li>easy setup of indexes and attribute properties
+<li>ldbedit tool for database editing (reminiscent of 'vipw')
+<li>ldif for import/export
+</ul>
+
+<h2>Documentation</h2>
+
+Currently ldb is completely lacking in programmer or user
+documentation. This is your opportunity to make a contribution! Start
+with the public functions declared in <a
+href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/include/ldb.h">ldb.h</a>
+and the example code in the <a
+href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/tools/">tools
+directory</a>. Documentation in the same docbook format used by Samba
+would be preferred.
+
+<h2>Discussion and bug reports</h2>
+
+ldb does not currently have its own mailing list or bug tracking
+system. For now, please use the <a
+href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba
+bugzilla</a> bug tracking system.
+
+<h2>Download</h2>
+
+You can download the latest release either via rsync or anonymous
+svn. To fetch via svn use the following commands:
+
+<pre>
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
+  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/replace libreplace
+</pre>
+
+To fetch via rsync use these commands:
+
+<pre>
+  rsync -Pavz samba.org::ftp/unpacked/ldb .
+  rsync -Pavz samba.org::ftp/unpacked/tdb .
+  rsync -Pavz samba.org::ftp/unpacked/talloc .
+  rsync -Pavz samba.org::ftp/unpacked/libreplace .
+</pre>
+
+and build in ldb. It will find the other libraries in the directory
+above automatically.
+
+<hr>
+<tiny>
+<a href="http://samba.org/~tridge/">Andrew Tridgell</a><br>
+ldb AT tridgell.net
+</tiny>
+
+</BODY>
+</HTML>