diff options
author | Andrew Tridgell <tridge@samba.org> | 2006-09-24 02:49:04 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:14:36 -0500 |
commit | 3e2c696e45b24b0192ab7b1ddaf1dd4d79571609 (patch) | |
tree | 9c4423bdb006f7c183fea0c87331d23e7043d0b1 | |
parent | 0c3194816b513e3743ddcacd1eca2b683ca39b88 (diff) | |
download | samba-3e2c696e45b24b0192ab7b1ddaf1dd4d79571609.tar.gz samba-3e2c696e45b24b0192ab7b1ddaf1dd4d79571609.tar.bz2 samba-3e2c696e45b24b0192ab7b1ddaf1dd4d79571609.zip |
r18866: Jeremy and Volker have given the go-ahead on the group mapping ldb
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, 37877 insertions, 0 deletions
diff --git a/source3/lib/ldb/Doxyfile b/source3/lib/ldb/Doxyfile new file mode 100644 index 0000000000..07b12b516a --- /dev/null +++ b/source3/lib/ldb/Doxyfile @@ -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 index 0000000000..106e493530 --- /dev/null +++ b/source3/lib/ldb/Makefile.in @@ -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 index 0000000000..2abd9378f4 --- /dev/null +++ b/source3/lib/ldb/README_gcov.txt @@ -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 index 0000000000..5605e476ba --- /dev/null +++ b/source3/lib/ldb/aclocal.m4 @@ -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 index 0000000000..500cab87d5 --- /dev/null +++ b/source3/lib/ldb/autogen.sh @@ -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 index 0000000000..8e437964f4 --- /dev/null +++ b/source3/lib/ldb/common/attrib_handlers.c @@ -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 index 0000000000..760a311383 --- /dev/null +++ b/source3/lib/ldb/common/ldb.c @@ -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 index 0000000000..c8a7909b4c --- /dev/null +++ b/source3/lib/ldb/common/ldb_attributes.c @@ -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 index 0000000000..d2729c82ab --- /dev/null +++ b/source3/lib/ldb/common/ldb_controls.c @@ -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 index 0000000000..2548a5495a --- /dev/null +++ b/source3/lib/ldb/common/ldb_debug.c @@ -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 index 0000000000..a0f48723ab --- /dev/null +++ b/source3/lib/ldb/common/ldb_dn.c @@ -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 index 0000000000..c5084aaa6c --- /dev/null +++ b/source3/lib/ldb/common/ldb_ldif.c @@ -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 index 0000000000..64e52d285d --- /dev/null +++ b/source3/lib/ldb/common/ldb_match.c @@ -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 index 0000000000..06a8a4bcf6 --- /dev/null +++ b/source3/lib/ldb/common/ldb_modules.c @@ -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 index 0000000000..52c6b82484 --- /dev/null +++ b/source3/lib/ldb/common/ldb_msg.c @@ -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 index 0000000000..d9044a8b0c --- /dev/null +++ b/source3/lib/ldb/common/ldb_parse.c @@ -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 index 0000000000..d2d12b8f51 --- /dev/null +++ b/source3/lib/ldb/common/ldb_utf8.c @@ -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 index 0000000000..ef7be2c14e --- /dev/null +++ b/source3/lib/ldb/common/qsort.c @@ -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 index 0000000000..ad5281e66e --- /dev/null +++ b/source3/lib/ldb/config.guess @@ -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 index 0000000000..6a23005b8f --- /dev/null +++ b/source3/lib/ldb/config.mk @@ -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 index 0000000000..1c366dfde9 --- /dev/null +++ b/source3/lib/ldb/config.sub @@ -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 index 0000000000..70a4100451 --- /dev/null +++ b/source3/lib/ldb/configure.ac @@ -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 index 0000000000..449dcb2681 --- /dev/null +++ b/source3/lib/ldb/docs/builddocs.sh @@ -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 index 0000000000..0bb278b5b4 --- /dev/null +++ b/source3/lib/ldb/docs/design.txt @@ -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 index 0000000000..6cc7b74ad5 --- /dev/null +++ b/source3/lib/ldb/docs/installdocs.sh @@ -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 index 0000000000..ef4b4f0a40 --- /dev/null +++ b/source3/lib/ldb/examples.dox @@ -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 index 0000000000..207c6c3d42 --- /dev/null +++ b/source3/lib/ldb/examples/ldbreader.c @@ -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 index 0000000000..3b8591e73f --- /dev/null +++ b/source3/lib/ldb/examples/ldifreader.c @@ -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 index 0000000000..3779a4cc61 --- /dev/null +++ b/source3/lib/ldb/include/dlinklist.h @@ -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 index 0000000000..ce0d40e101 --- /dev/null +++ b/source3/lib/ldb/include/includes.h @@ -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 index 0000000000..cf4a1f282b --- /dev/null +++ b/source3/lib/ldb/include/ldb.h @@ -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 index 0000000000..3b04c7c17f --- /dev/null +++ b/source3/lib/ldb/include/ldb_errors.h @@ -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 index 0000000000..96b71ff3b4 --- /dev/null +++ b/source3/lib/ldb/include/ldb_private.h @@ -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 index 0000000000..58719246f0 --- /dev/null +++ b/source3/lib/ldb/install-sh @@ -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 index 0000000000..911c1364c8 --- /dev/null +++ b/source3/lib/ldb/ldap.m4 @@ -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 index 0000000000..8b0536e21e --- /dev/null +++ b/source3/lib/ldb/ldb.pc.in @@ -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 index 0000000000..5b69ac06c9 --- /dev/null +++ b/source3/lib/ldb/ldb_ildap/ldb_ildap.c @@ -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 index 0000000000..9de67e5ad7 --- /dev/null +++ b/source3/lib/ldb/ldb_ldap/ldb_ldap.c @@ -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 index 0000000000..6cda0a7759 --- /dev/null +++ b/source3/lib/ldb/ldb_sqlite3/README @@ -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 index 0000000000..4286979123 --- /dev/null +++ b/source3/lib/ldb/ldb_sqlite3/base160.c @@ -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 index 0000000000..91256222b1 --- /dev/null +++ b/source3/lib/ldb/ldb_sqlite3/ldb_sqlite3.c @@ -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 index 0000000000..08dc50de08 --- /dev/null +++ b/source3/lib/ldb/ldb_sqlite3/schema @@ -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 index 0000000000..433a064816 --- /dev/null +++ b/source3/lib/ldb/ldb_sqlite3/trees.ps @@ -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 index 0000000000..d6d66dd37f --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_cache.c @@ -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 = ⪙ + 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(<db->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 index 0000000000..59c1645ba8 --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_index.c @@ -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 index 0000000000..c6edf663ae --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_pack.c @@ -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 index 0000000000..7cdb2b672f --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_search.c @@ -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 index 0000000000..8f676654a6 --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_tdb.c @@ -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 = <db_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 index 0000000000..670d3b6801 --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_tdb.h @@ -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 index 0000000000..b28bf77450 --- /dev/null +++ b/source3/lib/ldb/ldb_tdb/ldb_tdb_wrap.c @@ -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 index 0000000000..845563b4a1 --- /dev/null +++ b/source3/lib/ldb/libldb.m4 @@ -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 index 0000000000..bbd8d9c502 --- /dev/null +++ b/source3/lib/ldb/mainpage.dox @@ -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 index 0000000000..6ae8996477 --- /dev/null +++ b/source3/lib/ldb/man/ad2oLschema.1.xml @@ -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 index 0000000000..6a3a789034 --- /dev/null +++ b/source3/lib/ldb/man/ldb.3.xml @@ -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 <ldb.h></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 index 0000000000..7ad0f835d0 --- /dev/null +++ b/source3/lib/ldb/man/ldbadd.1.xml @@ -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 <ldb-url></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 index 0000000000..7dfc7366f6 --- /dev/null +++ b/source3/lib/ldb/man/ldbdel.1.xml @@ -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 <ldb-url></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 index 0000000000..15c69b1b25 --- /dev/null +++ b/source3/lib/ldb/man/ldbedit.1.xml @@ -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 <ldb-url></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 index 0000000000..bc19647785 --- /dev/null +++ b/source3/lib/ldb/man/ldbmodify.1.xml @@ -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 <ldb-url></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 index 0000000000..391ec84ccc --- /dev/null +++ b/source3/lib/ldb/man/ldbrename.1.xml @@ -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 <ldb-url></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 index 0000000000..ed3749b920 --- /dev/null +++ b/source3/lib/ldb/man/ldbsearch.1.xml @@ -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 <ldb-url></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 index 0000000000..b1e681be4e --- /dev/null +++ b/source3/lib/ldb/man/oLschema2ldif.1.xml @@ -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 index 0000000000..229a6eacd9 --- /dev/null +++ b/source3/lib/ldb/modules/asq.c @@ -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 index 0000000000..0c58687ddb --- /dev/null +++ b/source3/lib/ldb/modules/ldb_map.c @@ -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 index 0000000000..c5c455bcb2 --- /dev/null +++ b/source3/lib/ldb/modules/ldb_map.h @@ -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 index 0000000000..404b2ce528 --- /dev/null +++ b/source3/lib/ldb/modules/ldb_map_inbound.c @@ -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 index 0000000000..e1b207f6eb --- /dev/null +++ b/source3/lib/ldb/modules/ldb_map_outbound.c @@ -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 index 0000000000..7fb2745179 --- /dev/null +++ b/source3/lib/ldb/modules/ldb_map_private.h @@ -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 index 0000000000..493ecdaad4 --- /dev/null +++ b/source3/lib/ldb/modules/objectclass.c @@ -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 index 0000000000..c327a96f90 --- /dev/null +++ b/source3/lib/ldb/modules/operational.c @@ -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 index 0000000000..3ab575ef6b --- /dev/null +++ b/source3/lib/ldb/modules/paged_results.c @@ -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 index 0000000000..c7d163307d --- /dev/null +++ b/source3/lib/ldb/modules/paged_searches.c @@ -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 index 0000000000..fce1d34ac0 --- /dev/null +++ b/source3/lib/ldb/modules/rdn_name.c @@ -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 index 0000000000..556a35060d --- /dev/null +++ b/source3/lib/ldb/modules/schema.c @@ -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 index 0000000000..2adef580b1 --- /dev/null +++ b/source3/lib/ldb/modules/skel.c @@ -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 index 0000000000..3a0598c528 --- /dev/null +++ b/source3/lib/ldb/modules/sort.c @@ -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 index 0000000000..3fa47159ca --- /dev/null +++ b/source3/lib/ldb/samba/README @@ -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 index 0000000000..b490c8f005 --- /dev/null +++ b/source3/lib/ldb/samba/ldif_handlers.c @@ -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 index 0000000000..49e3807730 --- /dev/null +++ b/source3/lib/ldb/sqlite3.m4 @@ -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 index 0000000000..c7e6191c8a --- /dev/null +++ b/source3/lib/ldb/swig/Ldb.py @@ -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 index 0000000000..09d3461c2a --- /dev/null +++ b/source3/lib/ldb/swig/ldb.i @@ -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 index 0000000000..2e0b83c769 --- /dev/null +++ b/source3/lib/ldb/tests/init.ldif @@ -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 index 0000000000..cf06acd08b --- /dev/null +++ b/source3/lib/ldb/tests/init_slapd.sh @@ -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 index 0000000000..91beb10814 --- /dev/null +++ b/source3/lib/ldb/tests/kill_slapd.sh @@ -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 index 0000000000..fef6c35f2b --- /dev/null +++ b/source3/lib/ldb/tests/ldapi_url.sh @@ -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 index 0000000000..28981b1f24 --- /dev/null +++ b/source3/lib/ldb/tests/photo.ldif @@ -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 Binary files differnew file mode 100644 index 0000000000..c8096889a6 --- /dev/null +++ b/source3/lib/ldb/tests/samba4.png 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 index 0000000000..997b801d84 --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-add-test.ldif @@ -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 index 0000000000..b976724485 --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif @@ -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 index 0000000000..fa193af683 --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif @@ -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 index 0000000000..8ab7798f9c --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif @@ -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 index 0000000000..cbf0e60bbe --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif @@ -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 index 0000000000..bc64e9edb6 --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif @@ -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 index 0000000000..515408144a --- /dev/null +++ b/source3/lib/ldb/tests/schema-tests/schema.ldif @@ -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 index 0000000000..fa2789d8c1 --- /dev/null +++ b/source3/lib/ldb/tests/slapd.conf @@ -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 index 0000000000..22e8548791 --- /dev/null +++ b/source3/lib/ldb/tests/start_slapd.sh @@ -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 index 0000000000..0bb3ebead6 --- /dev/null +++ b/source3/lib/ldb/tests/test-attribs.ldif @@ -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 index 0000000000..7926a9e3c5 --- /dev/null +++ b/source3/lib/ldb/tests/test-config.ldif @@ -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 index 0000000000..87b7bcd3cc --- /dev/null +++ b/source3/lib/ldb/tests/test-default-config.ldif @@ -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 index 0000000000..a84e3b78a3 --- /dev/null +++ b/source3/lib/ldb/tests/test-extended.sh @@ -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 index 0000000000..e4085c6d65 --- /dev/null +++ b/source3/lib/ldb/tests/test-generic.sh @@ -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 index 0000000000..a793537187 --- /dev/null +++ b/source3/lib/ldb/tests/test-index.ldif @@ -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 index 0000000000..b9d224e0af --- /dev/null +++ b/source3/lib/ldb/tests/test-ldap.sh @@ -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 index 0000000000..e5b9ca4086 --- /dev/null +++ b/source3/lib/ldb/tests/test-modify.ldif @@ -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 index 0000000000..2f10fb45e2 --- /dev/null +++ b/source3/lib/ldb/tests/test-schema.sh @@ -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 index 0000000000..a51ae637dc --- /dev/null +++ b/source3/lib/ldb/tests/test-sqlite3.sh @@ -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 index 0000000000..6f1afdcf33 --- /dev/null +++ b/source3/lib/ldb/tests/test-tdb-features.sh @@ -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 index 0000000000..e1052d1651 --- /dev/null +++ b/source3/lib/ldb/tests/test-tdb.sh @@ -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 index 0000000000..222512eeab --- /dev/null +++ b/source3/lib/ldb/tests/test-wildcard.ldif @@ -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 index 0000000000..27f45f0e56 --- /dev/null +++ b/source3/lib/ldb/tests/test-wrong_attributes.ldif @@ -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 index 0000000000..ab8b81437a --- /dev/null +++ b/source3/lib/ldb/tests/test.ldif @@ -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 index 0000000000..dadb9f0f98 --- /dev/null +++ b/source3/lib/ldb/tests/testdata.txt @@ -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 index 0000000000..c5738639b7 --- /dev/null +++ b/source3/lib/ldb/tests/testsearch.txt @@ -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 index 0000000000..8b1203bff0 --- /dev/null +++ b/source3/lib/ldb/tools/ad2oLschema.c @@ -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 index 0000000000..09bb99c096 --- /dev/null +++ b/source3/lib/ldb/tools/cmdline.c @@ -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 index 0000000000..0855f77575 --- /dev/null +++ b/source3/lib/ldb/tools/cmdline.h @@ -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 index 0000000000..2320970030 --- /dev/null +++ b/source3/lib/ldb/tools/convert.c @@ -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 index 0000000000..de379343a6 --- /dev/null +++ b/source3/lib/ldb/tools/convert.h @@ -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 index 0000000000..6a0f510244 --- /dev/null +++ b/source3/lib/ldb/tools/ldbadd.c @@ -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 index 0000000000..aee911efaf --- /dev/null +++ b/source3/lib/ldb/tools/ldbdel.c @@ -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 index 0000000000..17ade152b7 --- /dev/null +++ b/source3/lib/ldb/tools/ldbedit.c @@ -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 index 0000000000..24f9386266 --- /dev/null +++ b/source3/lib/ldb/tools/ldbmodify.c @@ -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 index 0000000000..9c0870721d --- /dev/null +++ b/source3/lib/ldb/tools/ldbrename.c @@ -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 index 0000000000..23d8115c20 --- /dev/null +++ b/source3/lib/ldb/tools/ldbsearch.c @@ -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 index 0000000000..6cc8dfe19b --- /dev/null +++ b/source3/lib/ldb/tools/ldbtest.c @@ -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 index 0000000000..a9e157e323 --- /dev/null +++ b/source3/lib/ldb/tools/oLschema2ldif.c @@ -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 index 0000000000..2715a0d8bd --- /dev/null +++ b/source3/lib/ldb/web/index.html @@ -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> |