diff options
author | Simo Sorce <idra@samba.org> | 2008-09-05 10:13:10 -0400 |
---|---|---|
committer | Simo Sorce <idra@samba.org> | 2008-09-05 10:13:10 -0400 |
commit | 23b61265b85f321ae2ceddc6c75771f8881566d8 (patch) | |
tree | bb43d44fb09247c8e12d58371e56af911a7c0db9 /ldb | |
download | sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.gz sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.bz2 sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.zip |
Initial Import
Diffstat (limited to 'ldb')
142 files changed, 46866 insertions, 0 deletions
diff --git a/ldb/Doxyfile b/ldb/Doxyfile new file mode 100644 index 00000000..07b12b51 --- /dev/null +++ b/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/ldb/Makefile.in b/ldb/Makefile.in new file mode 100644 index 00000000..c94c820a --- /dev/null +++ b/ldb/Makefile.in @@ -0,0 +1,183 @@ +#!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@:@libreplacedir@ +srcdir = @srcdir@ +builddir = @builddir@ +buildinstdir = @buildinstdir@ +INSTALLCMD = @INSTALL@ +SLAPD = @SLAPD@ +SWIG = swig +EXTRA_OBJ=@EXTRA_OBJ@ +TESTS=test-tdb.sh @TESTS@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PYTHON = @PYTHON@ +PYTHON_CONFIG = @PYTHON_CONFIG@ +ldbdir = $(srcdir) + +TALLOC_LIBS = @TALLOC_LIBS@ +TALLOC_CFLAGS = @TALLOC_CFLAGS@ +TALLOC_OBJ = @TALLOC_OBJ@ + +TDB_LIBS = @TDB_LIBS@ +TDB_CFLAGS = @TDB_CFLAGS@ +TDB_OBJ = @TDB_OBJ@ + +EVENTS_LIBS = @EVENTS_LIBS@ +EVENTS_CFLAGS = @EVENTS_CFLAGS@ +EVENTS_OBJ = @EVENTS_OBJ@ + +POPT_LIBS = @POPT_LIBS@ +POPT_CFLAGS = @POPT_CFLAGS@ +POPT_OBJ = @POPT_OBJ@ + +LDAP_LIBS = @LDAP_LIBS@ + +LIBDL = @LIBDL@ + +SHLIBEXT = @SHLIBEXT@ + +LD_EXPORT_DYNAMIC = @LD_EXPORT_DYNAMIC@ +SHLD = @SHLD@ +SHLD_FLAGS = @SHLD_FLAGS@ + +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +PICFLAG = @PICFLAG@ +CFLAGS=-g -I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \ + $(POPT_CFLAGS) $(TALLOC_CFLAGS) $(TDB_CFLAGS) $(EVENTS_CFLAGS) \ + -DLIBDIR=\"$(libdir)\" -DSHLIBEXT=\"$(SHLIBEXT)\" -DUSE_MMAP=1 @CFLAGS@ + +MDLD = @MDLD@ +MDLD_FLAGS = @MDLD_FLAGS@ + +OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) $(TDB_OBJ) $(EVENTS_OBJ) $(TALLOC_OBJ) $(POPT_OBJ) $(LDB_MAP_OBJ) @LIBREPLACEOBJ@ $(EXTRA_OBJ) + +headers = $(srcdir)/include/ldb.h $(srcdir)/include/ldb_errors.h $(srcdir)/include/ldb_handlers.h + +BINS = bin/ldbadd bin/ldbsearch bin/ldbdel bin/ldbmodify bin/ldbedit bin/ldbrename bin/ldbtest + +EXAMPLES = examples/ldbreader examples/ldifreader + +DIRS = lib bin common ldb_tdb ldb_ldap ldb_sqlite3 modules tools examples + +default: all + +include $(ldbdir)/rules.mk + +nss: nssdir all $(NSS_LIB) + +nssdir: + @mkdir -p $(NSSDIR) + +SONAME = libldb.$(SHLIBEXT).0 +SOLIB = libldb.$(SHLIBEXT).$(PACKAGE_VERSION) +LIBSOLIB = lib/$(SOLIB) +STATICLIB = lib/libldb.a + +lib/$(SONAME): $(LIBSOLIB) + ln -fs libldb.$(SHLIBEXT).$(PACKAGE_VERSION) $@ + +lib/libldb.$(SHLIBEXT): $(LIBSOLIB) + ln -fs libldb.$(SHLIBEXT).$(PACKAGE_VERSION) $@ + +lib/libnss_ldb.$(SHLIBEXT).2: $(NSS_OBJ) $(LIBSOLIB) + $(SHLD) $(SHLD_FLAGS) -o $@ $(NSS_OBJ) $(LDFLAGS) $(LIBSOLIB) @SONAMEFLAG@libnss_ldb.$(SHLIBEXT).2 + +$(LIBSOLIB): $(OBJS) + $(SHLD) $(SHLD_FLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) $(TALLOC_LIBS) $(TDB_LIBS) $(EVENTS_LIBS) $(LIBDL) $(LDAP_LIBS) @SONAMEFLAG@$(SONAME) + ln -sf libldb.$(SHLIBEXT).$(PACKAGE_VERSION) lib/libldb.$(SHLIBEXT) + +all: showflags dirs $(OBJS) $(STATICLIB) $(LIBSOLIB) $(BINS) $(EXAMPLES) manpages \ + @PYTHON_BUILD_TARGET@ + +build-install: all + ${INSTALLCMD} -d $(buildinstdir)/lib + ${INSTALLCMD} -m 755 $(STATICLIB) $(buildinstdir)/lib + ${INSTALLCMD} -m 755 $(LIBSOLIB) $(buildinstdir)/lib + ln -sf $(SOLIB) $(buildinstdir)/lib/$(SONAME) + ln -sf $(SOLIB) $(buildinstdir)/lib/libldb.so + ${INSTALLCMD} -d $(buildinstdir)/include + ${INSTALLCMD} -m 644 $(srcdir)/include/ldb.h $(buildinstdir)/include + ${INSTALLCMD} -m 644 $(srcdir)/include/ldb_errors.h $(buildinstdir)/include + ${INSTALLCMD} -m 644 $(srcdir)/include/ldb_handlers.h $(buildinstdir)/include + +dirs: + @mkdir -p $(DIRS) + +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) $(STATICLIB) $(NSS_LIB) $(LIBSOLIB) + rm -f $(POPT_OBJ) + rm -f man/*.1 man/*.3 man/*.html + rm -f $(EXAMPLES) + rm -rf apidocs/ + rm -rf tests/schema/ + +distclean:: clean + 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 include/config.h.in + +check:: test @PYTHON_CHECK_TARGET@ + +check-soloading: sample.$(SHLIBEXT) + LDB_MODULES_PATH=$(builddir) $(srcdir)/tests/test-soloading.sh + +test:: all check-soloading + 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 installdirs installheaders installlibs installbin installdocs \ + @PYTHON_INSTALL_TARGET@ + +installdirs:: + mkdir -p $(DESTDIR)$(includedir) $(DESTDIR)$(libdir) $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/pkgconfig + +installheaders:: installdirs + cp $(headers) $(DESTDIR)$(includedir) + +installlibs:: installdirs + cp $(STATICLIB) $(LIBSOLIB) $(DESTDIR)$(libdir) + cp ldb.pc $(DESTDIR)$(libdir)/pkgconfig + +installbin:: installdirs + cp $(BINS) $(DESTDIR)$(bindir) + +installdocs:: installdirs + $(srcdir)/docs/installdocs.sh $(DESTDIR)$(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 + +include $(ldbdir)/ldb.mk diff --git a/ldb/README_gcov.txt b/ldb/README_gcov.txt new file mode 100644 index 00000000..2abd9378 --- /dev/null +++ b/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/ldb/aclocal.m4 b/ldb/aclocal.m4 new file mode 100644 index 00000000..5605e476 --- /dev/null +++ b/ldb/aclocal.m4 @@ -0,0 +1 @@ +m4_include(libreplace.m4) diff --git a/ldb/autogen.sh b/ldb/autogen.sh new file mode 100755 index 00000000..1681b504 --- /dev/null +++ b/ldb/autogen.sh @@ -0,0 +1,18 @@ +#!/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 ./external" + +autoheader $IPATHS || exit 1 +autoconf $IPATHS || exit 1 + +rm -rf autom4te.cache + +swig -O -Wall -python -keyword ldb.i # Ignore errors, for now + +echo "Now run ./configure and then make." +exit 0 + diff --git a/ldb/build_macros.m4 b/ldb/build_macros.m4 new file mode 100644 index 00000000..bb05a582 --- /dev/null +++ b/ldb/build_macros.m4 @@ -0,0 +1,14 @@ +AC_DEFUN(BUILD_WITH_BUILD_INST_DIR, + [ AC_ARG_WITH([build-install-dir], + [AC_HELP_STRING([--with-build-install-dir=DIR], + [temporary build directory where libraries are installed [$srcdir/buildinst]])]) + + buildinstdir="$srcdir/buildinst" + if test x"$with_build_install_dir" != x; then + buildinstdir=$with_build_install_dir + CFLAGS="$CFLAGS -I$with_build_install_dir/include" + CPPFLAGS="$CPPFLAGS -I$with_build_install_dir/include" + LIBS="$LIBS -L$with_build_install_dir/lib" + fi + AC_SUBST(buildinstdir) + ]) diff --git a/ldb/common/attrib_handlers.c b/ldb/common/attrib_handlers.c new file mode 100644 index 00000000..fb57e2da --- /dev/null +++ b/ldb/common/attrib_handlers.c @@ -0,0 +1,378 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ +/* + attribute handlers for well known attribute types, selected by syntax OID + see rfc2252 +*/ + +#include "ldb_includes.h" +#include "system/locale.h" +#include "ldb_handlers.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 +*/ +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), in->length); + 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 +*/ +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 +*/ +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 +*/ +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; + size_t n1 = v1->length, n2 = v2->length; + const char *u1, *u2; + char *b1, *b2; + int ret; + while (*s1 == ' ' && n1) { s1++; n1--; }; + while (*s2 == ' ' && n2) { s2++; n2--; }; + /* TODO: make utf8 safe, possibly with helper function from application */ + while (*s1 && *s2 && n1 && n2) { + /* 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] && n1) { s1++; n1--; } + while (s2[0] == s2[1] && n2) { s2++; n2--; } + } + s1++; s2++; + n1--; n2--; + } + 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++; n1--; } + while (*s2 == ' ') { s2++; n2--; } + } + if (n1 != n2) { + return n1 - n2; + } + return (int)(toupper(*s1)) - (int)(toupper(*s2)); + +utf8str: + /* no need to recheck from the start, just from the first utf8 char found */ + b1 = ldb_casefold(ldb, mem_ctx, s1, n1); + b2 = ldb_casefold(ldb, mem_ctx, s2, n2); + + if (b1 && b2) { + /* Both strings converted correctly */ + + u1 = b1; + u2 = b2; + } else { + /* One of the strings was not UTF8, so we have no options but to do a binary compare */ + + u1 = s1; + u2 = 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 +*/ +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_new(ldb, mem_ctx, (char *)in->data); + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_INVALID_DN_SYNTAX; + } + + out->data = (uint8_t *)ldb_dn_alloc_casefold(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 +*/ +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_new(ldb, mem_ctx, (char *)v1->data); + if ( ! ldb_dn_validate(dn1)) return -1; + + dn2 = ldb_dn_new(ldb, mem_ctx, (char *)v2->data); + if ( ! ldb_dn_validate(dn2)) { + talloc_free(dn1); + return -1; + } + + ret = ldb_dn_compare(dn1, dn2); + + talloc_free(dn1); + talloc_free(dn2); + return ret; +} + +/* + compare two utc time values. 1 second resolution +*/ +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 +*/ +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_schema_syntax ldb_standard_syntaxes[] = { + { + .name = LDB_SYNTAX_INTEGER, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_Integer, + .comparison_fn = ldb_comparison_Integer + }, + { + .name = LDB_SYNTAX_OCTET_STRING, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_copy, + .comparison_fn = ldb_comparison_binary + }, + { + .name = LDB_SYNTAX_DIRECTORY_STRING, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_fold, + .comparison_fn = ldb_comparison_fold + }, + { + .name = LDB_SYNTAX_DN, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_dn, + .comparison_fn = ldb_comparison_dn + }, + { + .name = LDB_SYNTAX_OBJECTCLASS, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_fold, + .comparison_fn = ldb_comparison_fold + }, + { + .name = LDB_SYNTAX_UTC_TIME, + .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_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb, + const char *syntax) +{ + int i; + unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]); + /* TODO: should be replaced with a binary search */ + for (i=0;i<num_handlers;i++) { + if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) { + return &ldb_standard_syntaxes[i]; + } + } + return NULL; +} diff --git a/ldb/common/ldb.c b/ldb/common/ldb.c new file mode 100644 index 00000000..ce4796de --- /dev/null +++ b/ldb/common/ldb.c @@ -0,0 +1,1261 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Simo Sorce 2005-2008 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb core API + * + * Description: core API routines interfacing to ldb backends + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" + +/* + initialise a ldb context + The mem_ctx is required + The event_ctx is required +*/ +struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx, struct event_context *ev_ctx) +{ + struct ldb_context *ldb; + int ret; + + ldb = talloc_zero(mem_ctx, struct ldb_context); + /* FIXME: Hack a new event context so that CMD line utilities work + * until we have them all converted */ + if (ev_ctx == NULL) { + ev_ctx = event_context_init(talloc_autofree_context()); + } + + ret = ldb_setup_wellknown_attributes(ldb); + if (ret != 0) { + talloc_free(ldb); + return NULL; + } + + ldb_set_utf8_default(ldb); + ldb_set_create_perms(ldb, 0666); + ldb_set_modules_dir(ldb, LDB_MODULESDIR); + ldb_set_event_context(ldb, ev_ctx); + + /* TODO: get timeout from options if available there */ + ldb->default_timeout = 300; /* set default to 5 minutes */ + + return ldb; +} + +/* + 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. +*/ +void ldb_set_default_dns(struct ldb_context *ldb) +{ + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_result *res; + struct ldb_dn *tmp_dn=NULL; + static const char *attrs[] = { + "rootDomainNamingContext", + "configurationNamingContext", + "schemaNamingContext", + "defaultNamingContext", + NULL + }; + + tmp_ctx = talloc_new(ldb); + ret = ldb_search(ldb, ldb_dn_new(tmp_ctx, ldb, NULL), LDB_SCOPE_BASE, + "(objectClass=*)", attrs, &res); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return; + } + + if (res->count != 1) { + talloc_free(res); + return; + } + + if (!ldb_get_opaque(ldb, "rootDomainNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "rootDomainNamingContext"); + ldb_set_opaque(ldb, "rootDomainNamingContext", tmp_dn); + } + + if (!ldb_get_opaque(ldb, "configurationNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "configurationNamingContext"); + ldb_set_opaque(ldb, "configurationNamingContext", tmp_dn); + } + + if (!ldb_get_opaque(ldb, "schemaNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "schemaNamingContext"); + ldb_set_opaque(ldb, "schemaNamingContext", tmp_dn); + } + + if (!ldb_get_opaque(ldb, "defaultNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "defaultNamingContext"); + ldb_set_opaque(ldb, "defaultNamingContext", tmp_dn); + } + + talloc_free(res); + talloc_free(tmp_ctx); +} + +struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb) +{ + void *opaque = ldb_get_opaque(ldb, "rootDomainNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); +} + +struct ldb_dn *ldb_get_config_basedn(struct ldb_context *ldb) +{ + void *opaque = ldb_get_opaque(ldb, "configurationNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); +} + +struct ldb_dn *ldb_get_schema_basedn(struct ldb_context *ldb) +{ + void *opaque = ldb_get_opaque(ldb, "schemaNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); +} + +struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb) +{ + void *opaque = ldb_get_opaque(ldb, "defaultNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); +} + +/* + 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; + const char *url2; + /* We seem to need to do this here, or else some utilities don't + * get ldb backends */ + + ldb->flags = flags; + + url2 = talloc_strdup(ldb, url); + if (!url2) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + ret = ldb_set_opaque(ldb, "ldb_url", talloc_strdup(ldb, url2)); + if (ret != LDB_SUCCESS) { + return ret; + } + + 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: %s\n", + url, ldb_errstring(ldb)); + 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_dns(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; + char *old_string = NULL; + + if (ldb->err_string) { + old_string = ldb->err_string; + } + + va_start(ap, format); + ldb->err_string = talloc_vasprintf(ldb, format, ap); + va_end(ap); + talloc_free(old_string); +} + +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) +{ + int ret; + if (!handle) { + return LDB_SUCCESS; + } + + ret = handle->module->ops->wait(handle, type); + if (!ldb_errstring(handle->module->ldb)) { + /* Set a default error string, to place the blame somewhere */ + ldb_asprintf_errstring(handle->module->ldb, + "error waiting on module %s: %s (%d)", + handle->module->ops->name, + ldb_strerror(ret), ret); + } + return ret; +} + +/* 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; +} + + +/* + set the permissions for new files to be passed to open() in + backends that use local files + */ +void ldb_set_create_perms(struct ldb_context *ldb, unsigned int perms) +{ + ldb->create_perms = perms; +} + +void ldb_set_event_context(struct ldb_context *ldb, struct event_context *ev) +{ + ldb->ev_ctx = ev; +} + +struct event_context * ldb_get_event_context(struct ldb_context *ldb) +{ + return ldb->ev_ctx; +} + +/* + 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_EXTENDED: + FIRST_OP(ldb, extended); + ret = module->ops->extended(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 + +*/ +int ldb_search_default_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 = talloc_get_type(context, struct ldb_result); + + if (!res || !ares) { + ldb_set_errstring(ldb, "NULL res or ares in callback"); + goto error; + } + + switch (ares->type) { + case 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++; + break; + case 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; + break; + case LDB_REPLY_EXTENDED: + case LDB_REPLY_DONE: + /* TODO: we should really support controls on entries + * and referrals too! */ + res->controls = talloc_move(res, &ares->controls); + break; + } + talloc_free(ares); + return LDB_SUCCESS; + +error: + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; +} + +int ldb_build_search_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + void *mem_ctx, + 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, ldb, NULL); + } 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, + const 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, + const 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; +} + +int ldb_extended_default_callback(struct ldb_context *ldb, + void *context, + struct ldb_reply *ares) +{ + struct ldb_result *res; + + if (!context) { + ldb_set_errstring(ldb, "NULL Context in callback"); + return LDB_ERR_OPERATIONS_ERROR; + } + + res = talloc_get_type(context, struct ldb_result); + if (!res || !ares) { + ldb_set_errstring(ldb, "NULL res or ares in callback"); + goto error; + } + + switch (ares->type) { + case LDB_REPLY_ENTRY: + case LDB_REPLY_REFERRAL: + case LDB_REPLY_DONE: + ldb_set_errstring(ldb, "invalid ares type in callback"); + goto error; + case LDB_REPLY_EXTENDED: + /* TODO: we should really support controls on entries and + * referrals too! */ + res->extended = talloc_move(res, &ares->response); + res->controls = talloc_move(res, &ares->controls); + break; + } + talloc_free(ares); + return LDB_SUCCESS; + +error: + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; +} + +int ldb_build_extended_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + void *mem_ctx, + const char *oid, + void *data, + 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_EXTENDED; + req->op.extended.oid = oid; + req->op.extended.data = data; + req->controls = controls; + req->context = context; + req->callback = callback; + + *ret_req = req; + + return LDB_SUCCESS; +} + +int ldb_extended(struct ldb_context *ldb, + const char *oid, + void *data, + struct ldb_result **_res) +{ + struct ldb_request *req; + int ret; + struct ldb_result *res; + + *_res = NULL; + + res = talloc_zero(ldb, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_build_extended_req(&req, ldb, ldb, + oid, data, NULL, + res, ldb_extended_default_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 = res; + return ret; +} + +/* + 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, + 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; + struct ldb_result *res; + + *_res = NULL; + + 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_default_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 = res; + return ret; +} + +/* + a useful search function where you can easily define the expression and that + takes a memory context where results are allocated +*/ + +int ldb_search_exp_fmt(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + struct ldb_result **result, struct ldb_dn *base, + enum ldb_scope scope, const char * const *attrs, + const char *exp_fmt, ...) +{ + struct ldb_result *res; + char *expression; + va_list ap; + int ret; + + res = NULL; + *result = NULL; + + va_start(ap, exp_fmt); + expression = talloc_vasprintf(mem_ctx, exp_fmt, ap); + va_end(ap); + + if ( ! expression) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_search(ldb, base, scope, expression, attrs, &res); + + if (ret == LDB_SUCCESS) { + talloc_steal(mem_ctx, res); + *result = res; + } + + talloc_free(expression); + + 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, 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, + struct ldb_dn *olddn, 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/ldb/common/ldb_attributes.c b/ldb/common/ldb_attributes.c new file mode 100644 index 00000000..747f2417 --- /dev/null +++ b/ldb/common/ldb_attributes.c @@ -0,0 +1,227 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ +/* + 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 "ldb_includes.h" + +/* + add a attribute to the ldb_schema + + if flags contains LDB_ATTR_FLAG_ALLOCATED + the attribute name string will be copied using + talloc_strdup(), otherwise it needs to be a static const + string at least with a lifetime longer than the ldb struct! + + the ldb_schema_syntax structure should be a pointer + to a static const struct or at least it needs to be + a struct with a longer lifetime than the ldb context! + +*/ +int ldb_schema_attribute_add_with_syntax(struct ldb_context *ldb, + const char *attribute, + unsigned flags, + const struct ldb_schema_syntax *syntax) +{ + int i, n; + struct ldb_schema_attribute *a; + + if (!syntax) { + return LDB_ERR_OPERATIONS_ERROR; + } + + n = ldb->schema.num_attributes + 1; + + a = talloc_realloc(ldb, ldb->schema.attributes, + struct ldb_schema_attribute, n); + if (a == NULL) { + ldb_oom(ldb); + return -1; + } + ldb->schema.attributes = a; + + for (i = 0; i < ldb->schema.num_attributes; i++) { + int cmp = ldb_attr_cmp(attribute, a[i].name); + if (cmp == 0) { + /* silently ignore attempts to overwrite fixed attributes */ + if (a[i].flags & LDB_ATTR_FLAG_FIXED) { + return 0; + } + if (a[i].flags & LDB_ATTR_FLAG_ALLOCATED) { + talloc_free(discard_const_p(char, a[i].name)); + } + /* To cancel out increment below */ + ldb->schema.num_attributes--; + break; + } else if (cmp < 0) { + memmove(a+i+1, a+i, sizeof(*a) * (ldb->schema.num_attributes-i)); + break; + } + } + ldb->schema.num_attributes++; + + a[i].name = attribute; + a[i].flags = flags; + a[i].syntax = syntax; + + if (a[i].flags & LDB_ATTR_FLAG_ALLOCATED) { + a[i].name = talloc_strdup(a, a[i].name); + if (a[i].name == NULL) { + ldb_oom(ldb); + return -1; + } + } + + return 0; +} + +static const struct ldb_schema_syntax ldb_syntax_default = { + .name = LDB_SYNTAX_OCTET_STRING, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_handler_copy, + .comparison_fn = ldb_comparison_binary +}; + +static const struct ldb_schema_attribute ldb_attribute_default = { + .name = NULL, + .flags = 0, + .syntax = &ldb_syntax_default +}; + +/* + return the attribute handlers for a given attribute +*/ +const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb, + const char *name) +{ + int i, e, b = 0, r; + const struct ldb_schema_attribute *def = &ldb_attribute_default; + + /* as handlers are sorted, '*' must be the first if present */ + if (strcmp(ldb->schema.attributes[0].name, "*") == 0) { + def = &ldb->schema.attributes[0]; + b = 1; + } + + /* do a binary search on the array */ + e = ldb->schema.num_attributes - 1; + + while (b <= e) { + + i = (b + e) / 2; + + r = ldb_attr_cmp(name, ldb->schema.attributes[i].name); + if (r == 0) { + return &ldb->schema.attributes[i]; + } + if (r < 0) { + e = i - 1; + } else { + b = i + 1; + } + + } + + return def; +} + + +/* + add to the list of ldif handlers for this ldb context +*/ +void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name) +{ + const struct ldb_schema_attribute *a; + int i; + + a = ldb_schema_attribute_by_name(ldb, name); + if (a == NULL || a->name == NULL) { + return; + } + + /* FIXED attributes are never removed */ + if (a->flags & LDB_ATTR_FLAG_FIXED) { + return; + } + + if (a->flags & LDB_ATTR_FLAG_ALLOCATED) { + talloc_free(discard_const_p(char, a->name)); + } + + i = a - ldb->schema.attributes; + if (i < ldb->schema.num_attributes - 1) { + memmove(&ldb->schema.attributes[i], + a+1, sizeof(*a) * (ldb->schema.num_attributes-(i+1))); + } + + ldb->schema.num_attributes--; +} + +/* + setup a attribute handler using a standard syntax +*/ +int ldb_schema_attribute_add(struct ldb_context *ldb, + const char *attribute, + unsigned flags, + const char *syntax) +{ + const struct ldb_schema_syntax *s = ldb_standard_syntax_by_name(ldb, syntax); + return ldb_schema_attribute_add_with_syntax(ldb, attribute, flags, s); +} + +/* + 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 }, + { "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; + int ret; + + for (i=0;i<ARRAY_SIZE(wellknown);i++) { + ret = ldb_schema_attribute_add(ldb, wellknown[i].attr, 0, + wellknown[i].syntax); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return LDB_SUCCESS; +} + diff --git a/ldb/common/ldb_controls.c b/ldb/common/ldb_controls.c new file mode 100644 index 00000000..e3f85514 --- /dev/null +++ b/ldb/common/ldb_controls.c @@ -0,0 +1,577 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb_controls.c + * + * Component: ldb controls utility functions + * + * Description: helper functions for control modules + * + * Author: Simo Sorce + */ + +#include "ldb_includes.h" + +/* check if a control with the specified "oid" exist and return it */ +/* returns NULL if not found */ +struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char *oid) +{ + int i; + + /* check if there's a paged request control */ + if (req->controls != NULL) { + for (i = 0; req->controls[i]; i++) { + if (strcmp(oid, req->controls[i]->oid) == 0) { + break; + } + } + + return req->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; +} + +int ldb_request_add_control(struct ldb_request *req, const char *oid, bool critical, void *data) +{ + unsigned n; + struct ldb_control **ctrls; + struct ldb_control *ctrl; + + for (n=0; req->controls && req->controls[n];) { n++; } + + ctrls = talloc_realloc(req, req->controls, + struct ldb_control *, + n + 2); + if (!ctrls) return LDB_ERR_OPERATIONS_ERROR; + req->controls = ctrls; + ctrls[n] = NULL; + ctrls[n+1] = NULL; + + ctrl = talloc(ctrls, struct ldb_control); + if (!ctrl) return LDB_ERR_OPERATIONS_ERROR; + + ctrl->oid = talloc_strdup(ctrl, oid); + if (!ctrl->oid) return LDB_ERR_OPERATIONS_ERROR; + ctrl->critical = critical; + ctrl->data = data; + + ctrls[n] = ctrl; + return LDB_SUCCESS; +} + +/* Parse controls from the format used on the command line and in ejs */ + +struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *mem_ctx, const char **control_strings) +{ + int i; + struct ldb_control **ctrl; + + char *error_string = NULL; + + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):<os(n):cc(n)|attr(s)>[:ctxid(o)]\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, s = string, o = b64 binary blob"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + if (!(ctrl[i] = talloc(ctrl, struct ldb_control))) { + ldb_oom(ldb); + return NULL; + } + ctrl[i]->oid = LDB_CONTROL_VLV_REQ_OID; + ctrl[i]->critical = crit; + if (!(control = talloc(ctrl[i], + struct ldb_vlv_req_control))) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid dirsync control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):flags(n):max_attrs(n)[:cookie(o)]\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, o = b64 binary blob"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + 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')) { + error_string = talloc_asprintf(mem_ctx, "invalid asq control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):attr(s)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + ret = sscanf(p, "%d", &crit); + if ((ret != 1) || (crit < 0) || (crit > 1)) { + error_string = talloc_asprintf(mem_ctx, "invalid extended_dn control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)[:type(i)]\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean\n"); + error_string = talloc_asprintf_append(error_string, " i = integer\n"); + error_string = talloc_asprintf_append(error_string, " valid values are: 0 - hexadecimal representation\n"); + error_string = talloc_asprintf_append(error_string, " 1 - normal string representation"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + control = NULL; + } else { + control = talloc(ctrl, struct ldb_extended_dn_control); + control->type = type; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID; + ctrl[i]->critical = crit; + ctrl[i]->data = talloc_steal(ctrl[i], 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid sd_flags control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):secinfo_flags(n)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid search_options control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):search_options(n)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid domain_scope control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid paged_results control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):size(n)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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') { + error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):rev(b):attr(s)[:rule(s)]\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid notification control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid show_deleted control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + 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)) { + error_string = talloc_asprintf(mem_ctx, "invalid permissive_modify control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + ctrl[i]->oid = LDB_CONTROL_PERMISSIVE_MODIFY_OID; + ctrl[i]->critical = crit; + ctrl[i]->data = NULL; + + continue; + } + + /* no controls matched, throw an error */ + ldb_asprintf_errstring(ldb, "Invalid control name: '%s'", control_strings[i]); + return NULL; + } + + ctrl[i] = NULL; + + return ctrl; +} + + diff --git a/ldb/common/ldb_debug.c b/ldb/common/ldb_debug.c new file mode 100644 index 00000000..0f78e37c --- /dev/null +++ b/ldb/common/ldb_debug.c @@ -0,0 +1,103 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb debug + * + * Description: functions for printing debug messages + * + * Author: Andrew Tridgell + */ + +#include "ldb_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/ldb/common/ldb_dn.c b/ldb/common/ldb_dn.c new file mode 100644 index 00000000..c0d36cfb --- /dev/null +++ b/ldb/common/ldb_dn.c @@ -0,0 +1,1463 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb dn creation and manipulation utility functions + * + * Description: - explode a dn into it's own basic elements + * and put them in a structure (only if necessary) + * - manipulate ldb_dn structures + * + * Author: Simo Sorce + */ + +#include "ldb_includes.h" +#include <ctype.h> + +#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed + +#define LDB_FREE(x) do { talloc_free(x); x = NULL; } while(0) + +/** + internal ldb exploded dn structures +*/ +struct ldb_dn_component { + + char *name; + struct ldb_val value; + + char *cf_name; + struct ldb_val cf_value; +}; + +struct ldb_dn { + + struct ldb_context *ldb; + + /* Special DNs are always linearized */ + bool special; + bool invalid; + + bool valid_case; + + char *linearized; + char *casefold; + + unsigned int comp_num; + struct ldb_dn_component *components; + +}; + +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn) +{ + struct ldb_dn *dn; + + if (! ldb) return NULL; + + dn = talloc_zero(mem_ctx, struct ldb_dn); + LDB_DN_NULL_FAILED(dn); + + dn->ldb = ldb; + + if (strdn->data && strdn->length) { + if (strdn->data[0] == '@') { + dn->special = true; + } + if (strdn->length >= 6 && strncasecmp((const char *)strdn->data, "<GUID=", 6) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a GUID string to ldb_dn structure */ + } else if (strdn->length >= 8 && strncasecmp((const char *)strdn->data, "<SID=", 8) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a SID string to ldb_dn structure */ + } else if (strdn->length >= 8 && strncasecmp((const char *)strdn->data, "<WKGUID=", 8) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a WKGUID string to ldb_dn structure */ + } + dn->linearized = talloc_strndup(dn, (const char *)strdn->data, strdn->length); + } else { + dn->linearized = talloc_strdup(dn, ""); + } + LDB_DN_NULL_FAILED(dn->linearized); + + return dn; + +failed: + talloc_free(dn); + return NULL; +} + +/* strdn may be NULL */ +struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn) +{ + struct ldb_val blob; + blob.data = strdn; + blob.length = strdn ? strlen(strdn) : 0; + return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob); +} + +struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...) +{ + struct ldb_dn *dn; + char *strdn; + va_list ap; + + if ( (! mem_ctx) || (! ldb)) return NULL; + + dn = talloc_zero(mem_ctx, struct ldb_dn); + LDB_DN_NULL_FAILED(dn); + + dn->ldb = ldb; + + va_start(ap, new_fmt); + strdn = talloc_vasprintf(dn, new_fmt, ap); + va_end(ap); + LDB_DN_NULL_FAILED(strdn); + + if (strdn[0] == '@') { + dn->special = true; + } + if (strncasecmp(strdn, "<GUID=", 6) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a GUID string to ldb_dn structure */ + } else if (strncasecmp(strdn, "<SID=", 8) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a SID string to ldb_dn structure */ + } else if (strncasecmp(strdn, "<WKGUID=", 8) == 0) { + /* this is special DN returned when the + * exploded_dn control is used */ + dn->special = true; + /* FIXME: add a WKGUID string to ldb_dn structure */ + } + dn->linearized = strdn; + + return dn; + +failed: + talloc_free(dn); + return NULL; +} + +static int ldb_dn_escape_internal(char *dst, const char *src, int len) +{ + const char *p, *s; + char *d; + int l; + + p = s = src; + d = 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 += 3; + p++; /* skip the zero */ + } + s = p; /* move forward */ + } + + /* copy the last part (with zero) and return */ + l = len - (s - src); + memcpy(d, s, l + 1); + + /* return the length of the resulting string */ + return (l + (d - dst)); +} + +char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) +{ + char *dst; + + if (!value.length) + return NULL; + + /* allocate destination string, it will be at most 3 times the source */ + dst = talloc_array(mem_ctx, char, value.length * 3 + 1); + if ( ! dst) { + talloc_free(dst); + return NULL; + } + + ldb_dn_escape_internal(dst, (const char *)value.data, value.length); + + dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1); + + return dst; +} + +/* + explode a DN string into a ldb_dn structure + based on RFC4514 except that we don't support multiple valued RDNs +*/ +static bool ldb_dn_explode(struct ldb_dn *dn) +{ + char *p, *data, *d, *dt, *t; + bool trim = false; + bool in_attr = false; + bool in_value = false; + bool in_quote = false; + bool is_oid = false; + bool escape = false; + unsigned x; + int l; + + if ( ! dn || dn->invalid) return false; + + if (dn->components) { + return true; + } + + if ( ! dn->linearized) { + return false; + } + + /* Empty DNs */ + if (dn->linearized[0] == '\0') { + return true; + } + + /* Special DNs case */ + if (dn->special) { + return true; + } + + /* make sure we free this if alloced previously before replacing */ + talloc_free(dn->components); + + /* in the common case we have 3 or more components */ + /* make sure all components are zeroed, other functions depend on this */ + dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3); + if ( ! dn->components) { + return false; + } + dn->comp_num = 0; + + /* Components data space is allocated here once */ + data = talloc_array(dn->components, char, strlen(dn->linearized) + 1); + if (!data) { + return false; + } + + p = dn->linearized; + in_attr = true; + trim = true; + t = NULL; + d = dt = data; + + while (*p) { + + if (in_attr) { + if (trim) { + if (*p == ' ') { + p++; + continue; + } + + /* first char */ + trim = false; + + if (!isascii(*p)) { + /* attr names must be ascii only */ + dn->invalid = true; + goto failed; + } + + if (isdigit(*p)) { + is_oid = true; + } else + if ( ! isalpha(*p)) { + /* not a digit nor an alpha, invalid attribute name */ + dn->invalid = true; + goto failed; + } + + *d++ = *p++; + continue; + } + + if (*p == ' ') { + p++; + /* valid only if we are at the end */ + trim = true; + continue; + } + + if (trim && (*p != '=')) { + /* spaces/tabs are not allowed in attribute names */ + dn->invalid = true; + goto failed; + } + + if (*p == '=') { + /* attribute terminated */ + in_attr = false; + in_value = true; + trim = true; + l = 0; + + *d++ = '\0'; + dn->components[dn->comp_num].name = talloc_strdup(dn->components, dt); + if ( ! dn->components[dn->comp_num].name) { + /* ouch */ + goto failed; + } + + dt = d; + + p++; + continue; + } + + if (!isascii(*p)) { + /* attr names must be ascii only */ + dn->invalid = true; + goto failed; + } + + if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) { + /* not a digit nor a dot, invalid attribute oid */ + dn->invalid = true; + goto failed; + } else + if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) { + /* not ALPHA, DIGIT or HYPHEN */ + dn->invalid = true; + goto failed; + } + + *d++ = *p++; + continue; + } + + if (in_value) { + if (in_quote) { + if (*p == '\"') { + if (p[-1] != '\\') { + p++; + in_quote = false; + continue; + } + } + *d++ = *p++; + l++; + continue; + } + + if (trim) { + if (*p == ' ') { + p++; + continue; + } + + /* first char */ + trim = false; + + if (*p == '\"') { + in_quote = true; + p++; + continue; + } + } + + switch (*p) { + + /* TODO: support ber encoded values + case '#': + */ + + case ',': + if (escape) { + *d++ = *p++; + l++; + escape = false; + continue; + } + /* ok found value terminator */ + + if ( t ) { + /* trim back */ + d -= (p - t); + l -= (p - t); + } + + in_attr = true; + in_value = false; + trim = true; + + p++; + *d++ = '\0'; + dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt); + dn->components[dn->comp_num].value.length = l; + if ( ! dn->components[dn->comp_num].value.data) { + /* ouch ! */ + goto failed; + } + + dt = d; + + dn->comp_num++; + if (dn->comp_num > 2) { + dn->components = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + dn->comp_num + 1); + if ( ! dn->components) { + /* ouch ! */ + goto failed; + } + /* make sure all components are zeroed, other functions depend on this */ + memset(&dn->components[dn->comp_num], '\0', sizeof(struct ldb_dn_component)); + } + + continue; + + case '=': + case '\n': + case '+': + case '<': + case '>': + case '#': + case ';': + case '\"': + /* a string with not escaped specials is invalid (tested) */ + if ( ! escape) { + dn->invalid = true; + goto failed; + } + escape = false; + + *d++ = *p++; + l++; + + if ( t ) t = NULL; + break; + + case '\\': + if ( ! escape) { + escape = true; + p++; + continue; + } + escape = false; + + *d++ = *p++; + l++; + + if ( t ) t = NULL; + break; + + default: + if (escape) { + if (sscanf(p, "%02x", &x) != 1) { + /* invalid escaping sequence */ + dn->invalid = true; + goto failed; + } + escape = false; + + p += 2; + *d++ = (unsigned char)x; + l++; + + if ( t ) t = NULL; + break; + } + + if (*p == ' ') { + if ( ! t) t = p; + } else { + if ( t ) t = NULL; + } + + *d++ = *p++; + l++; + + break; + } + + } + } + + if (in_attr || in_quote) { + /* invalid dn */ + dn->invalid = true; + goto failed; + } + + /* save last element */ + if ( t ) { + /* trim back */ + d -= (p - t); + l -= (p - t); + } + + *d++ = '\0'; + dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt); + dn->components[dn->comp_num].value.length = l; + + if ( ! dn->components[dn->comp_num].value.data) { + /* ouch */ + goto failed; + } + + dn->comp_num++; + + talloc_free(data); + return true; + +failed: + dn->comp_num = 0; + talloc_free(dn->components); + return false; +} + +bool ldb_dn_validate(struct ldb_dn *dn) +{ + return ldb_dn_explode(dn); +} + +const char *ldb_dn_get_linearized(struct ldb_dn *dn) +{ + int i, len; + char *d, *n; + + if ( ! dn || ( dn->invalid)) return NULL; + + if (dn->linearized) return dn->linearized; + + if ( ! dn->components) { + dn->invalid = true; + return NULL; + } + + if (dn->comp_num == 0) { + dn->linearized = talloc_strdup(dn, ""); + if ( ! dn->linearized) return NULL; + return dn->linearized; + } + + /* calculate maximum possible length of DN */ + for (len = 0, i = 0; i < dn->comp_num; i++) { + len += strlen(dn->components[i].name); /* name len */ + len += (dn->components[i].value.length * 3); /* max escaped data len */ + len += 2; /* '=' and ',' */ + } + dn->linearized = talloc_array(dn, char, len); + if ( ! dn->linearized) return NULL; + + d = dn->linearized; + + for (i = 0; i < dn->comp_num; i++) { + + /* copy the name */ + n = dn->components[i].name; + while (*n) *d++ = *n++; + + *d++ = '='; + + /* and the value */ + d += ldb_dn_escape_internal( d, + (char *)dn->components[i].value.data, + dn->components[i].value.length); + *d++ = ','; + } + + *(--d) = '\0'; + + /* don't waste more memory than necessary */ + dn->linearized = talloc_realloc(dn, dn->linearized, char, (d - dn->linearized + 1)); + + return dn->linearized; +} + +char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn) +{ + return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn)); +} + +/* + casefold a dn. We need to casefold the attribute names, and canonicalize + attribute values of case insensitive attributes. +*/ + +static bool ldb_dn_casefold_internal(struct ldb_dn *dn) +{ + int i, ret; + + if ( ! dn || dn->invalid) return false; + + if (dn->valid_case) return true; + + if (( ! dn->components) && ( ! ldb_dn_explode(dn))) { + return false; + } + + for (i = 0; i < dn->comp_num; i++) { + const struct ldb_schema_attribute *a; + + dn->components[i].cf_name = ldb_attr_casefold(dn->components, dn->components[i].name); + if (!dn->components[i].cf_name) { + goto failed; + } + + a = ldb_schema_attribute_by_name(dn->ldb, dn->components[i].cf_name); + ret = a->syntax->canonicalise_fn(dn->ldb, dn->components, + &(dn->components[i].value), + &(dn->components[i].cf_value)); + if (ret != 0) { + goto failed; + } + } + + dn->valid_case = true; + + return true; + +failed: + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + return false; +} + +const char *ldb_dn_get_casefold(struct ldb_dn *dn) +{ + int i, len; + char *d, *n; + + if (dn->casefold) return dn->casefold; + + if (dn->special) { + dn->casefold = talloc_strdup(dn, dn->linearized); + if (!dn->casefold) return NULL; + dn->valid_case = true; + return dn->casefold; + } + + if ( ! ldb_dn_casefold_internal(dn)) { + return NULL; + } + + if (dn->comp_num == 0) { + if (dn->linearized && dn->linearized[0] == '\0') { + /* hmm a NULL dn, should we faild casefolding ? */ + dn->casefold = talloc_strdup(dn, ""); + return dn->casefold; + } + /* A DN must be NULL, special, or have components */ + dn->invalid = true; + return NULL; + } + + /* calculate maximum possible length of DN */ + for (len = 0, i = 0; i < dn->comp_num; i++) { + len += strlen(dn->components[i].cf_name); /* name len */ + len += (dn->components[i].cf_value.length * 3); /* max escaped data len */ + len += 2; /* '=' and ',' */ + } + dn->casefold = talloc_array(dn, char, len); + if ( ! dn->casefold) return NULL; + + d = dn->casefold; + + for (i = 0; i < dn->comp_num; i++) { + + /* copy the name */ + n = dn->components[i].cf_name; + while (*n) *d++ = *n++; + + *d++ = '='; + + /* and the value */ + d += ldb_dn_escape_internal( d, + (char *)dn->components[i].cf_value.data, + dn->components[i].cf_value.length); + *d++ = ','; + } + *(--d) = '\0'; + + /* don't waste more memory than necessary */ + dn->casefold = talloc_realloc(dn, dn->casefold, char, strlen(dn->casefold) + 1); + + return dn->casefold; +} + +char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn) +{ + return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn)); +} + +/* 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_dn *base, struct ldb_dn *dn) +{ + int ret; + int n_base, n_dn; + + if ( ! base || base->invalid) return 1; + if ( ! dn || dn->invalid) return -1; + + if (( ! base->valid_case) || ( ! dn->valid_case)) { + if (base->linearized && dn->linearized) { + /* try with a normal compare first, if we are lucky + * we will avoid exploding and casfolding */ + int dif; + dif = strlen(dn->linearized) - strlen(base->linearized); + if (dif < 0) return dif; + if (strcmp(base->linearized, &dn->linearized[dif]) == 0) return 0; + } + + if ( ! ldb_dn_casefold_internal(base)) { + return 1; + } + + if ( ! ldb_dn_casefold_internal(dn)) { + return -1; + } + + } + + /* if base has more components, + * they don't have the same base */ + if (base->comp_num > dn->comp_num) { + return (dn->comp_num - base->comp_num); + } + + if (dn->comp_num == 0) { + if (dn->special && base->special) { + return strcmp(base->linearized, dn->linearized); + } else if (dn->special) { + return -1; + } else if (base->special) { + return 1; + } else { + return 0; + } + } + + n_base = base->comp_num - 1; + n_dn = dn->comp_num - 1; + + while (n_base >= 0) { + /* compare attr names */ + ret = strcmp(base->components[n_base].cf_name, dn->components[n_dn].cf_name); + if (ret != 0) return ret; + + /* compare attr.cf_value. */ + if (base->components[n_base].cf_value.length != dn->components[n_dn].cf_value.length) { + return base->components[n_base].cf_value.length - dn->components[n_dn].cf_value.length; + } + ret = strcmp((char *)base->components[n_base].cf_value.data, (char *)dn->components[n_dn].cf_value.data); + if (ret != 0) return ret; + + n_base--; + n_dn--; + } + + return 0; +} + +/* compare DNs using casefolding compare functions. + + If they match, then return 0 + */ + +int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) +{ + int i, ret; + + if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) return -1; + + if (( ! dn0->valid_case) || ( ! dn1->valid_case)) { + if (dn0->linearized && dn1->linearized) { + /* try with a normal compare first, if we are lucky + * we will avoid exploding and casfolding */ + if (strcmp(dn0->linearized, dn1->linearized) == 0) return 0; + } + + if ( ! ldb_dn_casefold_internal(dn0)) { + return 1; + } + + if ( ! ldb_dn_casefold_internal(dn1)) { + return -1; + } + + } + + if (dn0->comp_num != dn1->comp_num) { + return (dn1->comp_num - dn0->comp_num); + } + + if (dn0->comp_num == 0) { + if (dn0->special && dn1->special) { + return strcmp(dn0->linearized, dn1->linearized); + } else if (dn0->special) { + return 1; + } else if (dn1->special) { + return -1; + } else { + return 0; + } + } + + for (i = 0; i < dn0->comp_num; i++) { + /* compare attr names */ + ret = strcmp(dn0->components[i].cf_name, dn1->components[i].cf_name); + if (ret != 0) return ret; + + /* compare attr.cf_value. */ + if (dn0->components[i].cf_value.length != dn1->components[i].cf_value.length) { + return dn0->components[i].cf_value.length - dn1->components[i].cf_value.length; + } + ret = strcmp((char *)dn0->components[i].cf_value.data, (char *)dn1->components[i].cf_value.data); + if (ret != 0) return ret; + } + + return 0; +} + +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) { + LDB_FREE(dst.value.data); + return dst; + } + + if (src->cf_value.data) { + dst.cf_value = ldb_val_dup(mem_ctx, &(src->cf_value)); + if (dst.cf_value.data == NULL) { + LDB_FREE(dst.value.data); + LDB_FREE(dst.name); + return dst; + } + + dst.cf_name = talloc_strdup(mem_ctx, src->cf_name); + if (dst.cf_name == NULL) { + LDB_FREE(dst.cf_name); + LDB_FREE(dst.value.data); + LDB_FREE(dst.name); + return dst; + } + } else { + dst.cf_value.data = NULL; + dst.cf_name = NULL; + } + + return dst; +} + +struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + if (!dn || dn->invalid) { + return NULL; + } + + new_dn = talloc_zero(mem_ctx, struct ldb_dn); + if ( !new_dn) { + return NULL; + } + + *new_dn = *dn; + + if (dn->components) { + int i; + + new_dn->components = talloc_zero_array(new_dn, struct ldb_dn_component, dn->comp_num); + if ( ! new_dn->components) { + talloc_free(new_dn); + return NULL; + } + + for (i = 0; i < dn->comp_num; i++) { + new_dn->components[i] = ldb_dn_copy_component(new_dn->components, &dn->components[i]); + if ( ! new_dn->components[i].value.data) { + talloc_free(new_dn); + return NULL; + } + } + } + + if (dn->casefold) { + new_dn->casefold = talloc_strdup(new_dn, dn->casefold); + if ( ! new_dn->casefold) { + talloc_free(new_dn); + return NULL; + } + } + + if (dn->linearized) { + new_dn->linearized = talloc_strdup(new_dn, dn->linearized); + if ( ! new_dn->linearized) { + talloc_free(new_dn); + return NULL; + } + } + + return new_dn; +} + +/* modify the given dn by adding a base. + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base) +{ + const char *s; + char *t; + + if ( !base || base->invalid || !dn || dn->invalid) { + return false; + } + + if (dn->components) { + int i; + + if ( ! ldb_dn_validate(base)) { + return false; + } + + s = NULL; + if (dn->valid_case) { + if ( ! (s = ldb_dn_get_casefold(base))) { + return false; + } + } + + dn->components = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + dn->comp_num + base->comp_num); + if ( ! dn->components) { + dn->invalid = true; + return false; + } + + for (i = 0; i < base->comp_num; dn->comp_num++, i++) { + dn->components[dn->comp_num] = ldb_dn_copy_component(dn->components, &base->components[i]); + if (dn->components[dn->comp_num].value.data == NULL) { + dn->invalid = true; + return false; + } + } + + if (dn->casefold && s) { + if (*dn->casefold) { + t = talloc_asprintf(dn, "%s,%s", dn->casefold, s); + } else { + t = talloc_strdup(dn, s); + } + LDB_FREE(dn->casefold); + dn->casefold = t; + } + } + + if (dn->linearized) { + + s = ldb_dn_get_linearized(base); + if ( ! s) { + return false; + } + + if (*dn->linearized) { + t = talloc_asprintf(dn, "%s,%s", dn->linearized, s); + } else { + t = talloc_strdup(dn, s); + } + if ( ! t) { + dn->invalid = true; + return false; + } + LDB_FREE(dn->linearized); + dn->linearized = t; + } + + return true; +} + +/* modify the given dn by adding a base. + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...) +{ + struct ldb_dn *base; + char *base_str; + va_list ap; + bool ret; + + if ( !dn || dn->invalid) { + return false; + } + + va_start(ap, base_fmt); + base_str = talloc_vasprintf(dn, base_fmt, ap); + va_end(ap); + + if (base_str == NULL) { + return false; + } + + base = ldb_dn_new(base_str, dn->ldb, base_str); + + ret = ldb_dn_add_base(dn, base); + + talloc_free(base_str); + + return ret; +} + +/* modify the given dn by adding children elements. + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child) +{ + const char *s; + char *t; + + if ( !child || child->invalid || !dn || dn->invalid) { + return false; + } + + if (dn->components) { + int n, i, j; + + if ( ! ldb_dn_validate(child)) { + return false; + } + + s = NULL; + if (dn->valid_case) { + if ( ! (s = ldb_dn_get_casefold(child))) { + return false; + } + } + + n = dn->comp_num + child->comp_num; + + dn->components = talloc_realloc(dn, + dn->components, + struct ldb_dn_component, + n); + if ( ! dn->components) { + dn->invalid = true; + return false; + } + + for (i = dn->comp_num - 1, j = n - 1; i >= 0; i--, j--) { + dn->components[j] = dn->components[i]; + } + + for (i = 0; i < child->comp_num; i++) { + dn->components[i] = ldb_dn_copy_component(dn->components, &child->components[i]); + if (dn->components[i].value.data == NULL) { + dn->invalid = true; + return false; + } + } + + dn->comp_num = n; + + if (dn->casefold && s) { + t = talloc_asprintf(dn, "%s,%s", s, dn->casefold); + LDB_FREE(dn->casefold); + dn->casefold = t; + } + } + + if (dn->linearized) { + + s = ldb_dn_get_linearized(child); + if ( ! s) { + return false; + } + + t = talloc_asprintf(dn, "%s,%s", s, dn->linearized); + if ( ! t) { + dn->invalid = true; + return false; + } + LDB_FREE(dn->linearized); + dn->linearized = t; + } + + return true; +} + +/* modify the given dn by adding children elements. + * + * return true if successful and false if not + * if false is returned the dn may be marked invalid + */ +bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...) +{ + struct ldb_dn *child; + char *child_str; + va_list ap; + bool ret; + + if ( !dn || dn->invalid) { + return false; + } + + va_start(ap, child_fmt); + child_str = talloc_vasprintf(dn, child_fmt, ap); + va_end(ap); + + if (child_str == NULL) { + return false; + } + + child = ldb_dn_new(child_str, dn->ldb, child_str); + + ret = ldb_dn_add_child(dn, child); + + talloc_free(child_str); + + return ret; +} + +bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num) +{ + int i; + + if ( ! ldb_dn_validate(dn)) { + return false; + } + + if (dn->comp_num < num) { + return false; + } + + /* free components */ + for (i = num; i > 0; i--) { + LDB_FREE(dn->components[dn->comp_num - i].name); + LDB_FREE(dn->components[dn->comp_num - i].value.data); + LDB_FREE(dn->components[dn->comp_num - i].cf_name); + LDB_FREE(dn->components[dn->comp_num - i].cf_value.data); + } + + dn->comp_num -= num; + + if (dn->valid_case) { + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + dn->valid_case = false; + } + + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + + return true; +} + +bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num) +{ + int i, j; + + if ( ! ldb_dn_validate(dn)) { + return false; + } + + if (dn->comp_num < num) { + return false; + } + + for (i = 0, j = num; j < dn->comp_num; i++, j++) { + if (i < num) { + LDB_FREE(dn->components[i].name); + LDB_FREE(dn->components[i].value.data); + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + dn->components[i] = dn->components[j]; + } + + dn->comp_num -= num; + + if (dn->valid_case) { + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + dn->valid_case = false; + } + + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + + return true; +} + +struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + new_dn = ldb_dn_copy(mem_ctx, dn); + if ( !new_dn ) { + return NULL; + } + + if ( ! ldb_dn_remove_child_components(new_dn, 1)) { + talloc_free(new_dn); + return NULL; + } + + return new_dn; +} + +/* 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, struct ldb_dn *dn, int ex_format) { + int i; + TALLOC_CTX *tmpctx; + char *cracked = NULL; + const char *format = (ex_format ? "\n" : "/" ); + + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + + tmpctx = talloc_new(mem_ctx); + + /* 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(tmpctx, "%s.%s", + ldb_dn_escape_value(tmpctx, dn->components[i].value), + cracked); + } else { + cracked = ldb_dn_escape_value(tmpctx, dn->components[i].value); + } + if (!cracked) { + goto done; + } + } + + /* Only domain components? Finish here */ + if (i < 0) { + cracked = talloc_strdup_append_buffer(cracked, format); + talloc_steal(mem_ctx, cracked); + goto done; + } + + /* Now walk backwards appending remaining components */ + for (; i > 0; i--) { + cracked = talloc_asprintf_append_buffer(cracked, "/%s", + ldb_dn_escape_value(tmpctx, dn->components[i].value)); + if (!cracked) { + goto done; + } + } + + /* Last one, possibly a newline for the 'ex' format */ + cracked = talloc_asprintf_append_buffer(cracked, "%s%s", format, + ldb_dn_escape_value(tmpctx, dn->components[i].value)); + + talloc_steal(mem_ctx, cracked); +done: + talloc_free(tmpctx); + return cracked; +} + +/* Wrapper functions for the above, for the two different string formats */ +char *ldb_dn_canonical_string(void *mem_ctx, struct ldb_dn *dn) { + return ldb_dn_canonical(mem_ctx, dn, 0); + +} + +char *ldb_dn_canonical_ex_string(void *mem_ctx, struct ldb_dn *dn) { + return ldb_dn_canonical(mem_ctx, dn, 1); +} + +int ldb_dn_get_comp_num(struct ldb_dn *dn) +{ + if ( ! ldb_dn_validate(dn)) { + return -1; + } + return dn->comp_num; +} + +const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num) +{ + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + if (num >= dn->comp_num) return NULL; + return dn->components[num].name; +} + +const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, unsigned int num) +{ + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + if (num >= dn->comp_num) return NULL; + return &dn->components[num].value; +} + +const char *ldb_dn_get_rdn_name(struct ldb_dn *dn) +{ + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + if (dn->comp_num == 0) return NULL; + return dn->components[0].name; +} + +const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn) +{ + if ( ! ldb_dn_validate(dn)) { + return NULL; + } + if (dn->comp_num == 0) return NULL; + return &dn->components[0].value; +} + +int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const struct ldb_val val) +{ + char *n; + struct ldb_val v; + + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_OTHER; + } + + if (num >= dn->comp_num) { + return LDB_ERR_OTHER; + } + + n = talloc_strdup(dn, name); + if ( ! n) { + return LDB_ERR_OTHER; + } + + v.length = val.length; + v.data = (uint8_t *)talloc_memdup(dn, val.data, v.length+1); + if ( ! v.data) { + talloc_free(n); + return LDB_ERR_OTHER; + } + + talloc_free(dn->components[num].name); + talloc_free(dn->components[num].value.data); + dn->components[num].name = n; + dn->components[num].value = v; + + if (dn->valid_case) { + int i; + for (i = 0; i < dn->comp_num; i++) { + LDB_FREE(dn->components[i].cf_name); + LDB_FREE(dn->components[i].cf_value.data); + } + dn->valid_case = false; + } + LDB_FREE(dn->casefold); + LDB_FREE(dn->linearized); + + return LDB_SUCCESS; +} + +bool ldb_dn_is_valid(struct ldb_dn *dn) +{ + if ( ! dn) return false; + return ! dn->invalid; +} + +bool ldb_dn_is_special(struct ldb_dn *dn) +{ + if ( ! dn || dn->invalid) return false; + return dn->special; +} + +bool ldb_dn_check_special(struct ldb_dn *dn, const char *check) +{ + if ( ! dn || dn->invalid) return false; + return ! strcmp(dn->linearized, check); +} + +bool ldb_dn_is_null(struct ldb_dn *dn) +{ + if ( ! dn || dn->invalid) return false; + if (dn->linearized && (dn->linearized[0] == '\0')) return true; + return false; +} diff --git a/ldb/common/ldb_ldif.c b/ldb/common/ldb_ldif.c new file mode 100644 index 00000000..fb93e17c --- /dev/null +++ b/ldb/common/ldb_ldif.c @@ -0,0 +1,757 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldif routines + * + * Description: ldif pack/unpack routines + * + * Author: Andrew Tridgell + */ + +/* + see RFC2849 for the LDIF format definition +*/ + +#include "ldb_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 = (uint8_t *)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; +} + +#undef CHECK_RET + +/* + 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} +}; + +/* this macro is used to handle the return checking on fprintf_fn() */ +#define CHECK_RET do { if (ret < 0) { talloc_free(mem_ctx); return ret; } total += ret; } while (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) +{ + TALLOC_CTX *mem_ctx; + unsigned int i, j; + int total=0, ret; + const struct ldb_message *msg; + + mem_ctx = talloc_named_const(NULL, 0, "ldb_ldif_write"); + + msg = ldif->msg; + + ret = fprintf_fn(private_data, "dn: %s\n", ldb_dn_get_linearized(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); + talloc_free(mem_ctx); + 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_schema_attribute *a; + + a = ldb_schema_attribute_by_name(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 = a->syntax->ldif_write_fn(ldb, mem_ctx, &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; + + chunk = next_chunk(ldb, fgetc_fn, private_data); + if (!chunk) { + goto failed; + } + talloc_steal(ldif, 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_new(msg, ldb, (char *)value.data); + + if ( ! ldb_dn_validate(msg->dn)) { + 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_schema_attribute *a; + 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, NULL) != 0) { + goto failed; + } + continue; + } + + el = &msg->elements[msg->num_elements-1]; + + a = ldb_schema_attribute_by_name(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 = a->syntax->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 = a->syntax->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 = + (struct ldif_read_file_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 = + (struct ldif_read_string_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 = + (struct ldif_write_file_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/ldb/common/ldb_match.c b/ldb/common/ldb_match.c new file mode 100644 index 00000000..64d0e547 --- /dev/null +++ b/ldb/common/ldb_match.c @@ -0,0 +1,428 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb expression matching + * + * Description: ldb expression matching + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" + +/* + check if the scope matches in a search result +*/ +static int ldb_match_scope(struct ldb_context *ldb, + struct ldb_dn *base, + 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(base, dn) == 0) { + ret = 1; + } + break; + + case LDB_SCOPE_ONELEVEL: + if (ldb_dn_get_comp_num(dn) == (ldb_dn_get_comp_num(base) + 1)) { + if (ldb_dn_compare_base(base, dn) == 0) { + ret = 1; + } + } + break; + + case LDB_SCOPE_SUBTREE: + default: + if (ldb_dn_compare_base(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_schema_attribute *a; + 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; + } + + a = ldb_schema_attribute_by_name(ldb, el->name); + + for (i = 0; i < el->num_values; i++) { + ret = a->syntax->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_schema_attribute *a; + struct ldb_dn *valuedn; + int ret; + + if (ldb_attr_dn(tree->u.equality.attr) == 0) { + valuedn = ldb_dn_new(ldb, ldb, (char *)tree->u.equality.value.data); + if (valuedn == NULL) { + return 0; + } + + ret = ldb_dn_compare(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; + } + + a = ldb_schema_attribute_by_name(ldb, el->name); + + for (i=0;i<el->num_values;i++) { + if (a->syntax->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_schema_attribute *a; + struct ldb_val val; + struct ldb_val cnk; + struct ldb_val *chunk; + char *p, *g; + uint8_t *save_p = NULL; + int c = 0; + + a = ldb_schema_attribute_by_name(ldb, tree->u.substring.attr); + + if(a->syntax->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(a->syntax->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(a->syntax->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, + 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/ldb/common/ldb_modules.c b/ldb/common/ldb_modules.c new file mode 100644 index 00000000..4d69dc66 --- /dev/null +++ b/ldb/common/ldb_modules.c @@ -0,0 +1,625 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb modules core + * + * Description: core modules routines + * + * Author: Simo Sorce + */ + +#include "ldb_includes.h" + +#if (_SAMBA_BUILD_ >= 4) +#include "includes.h" +#endif + +#define LDB_MODULE_PREFIX "modules:" +#define LDB_MODULE_PREFIX_LEN 8 + +void ldb_set_modules_dir(struct ldb_context *ldb, const char *path) +{ + talloc_free(ldb->modules_dir); + ldb->modules_dir = talloc_strdup(ldb, path); +} + +static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string) +{ + int i, len; + char *trimmed; + + trimmed = talloc_strdup(mem_ctx, string); + if (!trimmed) { + 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 = ldb_modules_strdup_no_spaces(mem_ctx, string); + if ( ! modstr) { + ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n"); + 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 backends_list_entry { + struct ldb_backend_ops *ops; + struct backends_list_entry *prev, *next; +} *ldb_backends = NULL; + +static struct ops_list_entry { + const struct ldb_module_ops *ops; + struct ops_list_entry *next; +} *registered_modules = NULL; + +static const struct ldb_builtins { + const struct ldb_backend_ops *backend_ops; + const struct ldb_module_ops *module_ops; +} builtins[]; + +static ldb_connect_fn ldb_find_backend(const char *url) +{ + struct backends_list_entry *backend; + int i; + + for (i = 0; builtins[i].backend_ops || builtins[i].module_ops; i++) { + if (builtins[i].backend_ops == NULL) continue; + + if (strncmp(builtins[i].backend_ops->name, url, + strlen(builtins[i].backend_ops->name)) == 0) { + return builtins[i].backend_ops->connect_fn; + } + } + + for (backend = ldb_backends; backend; backend = backend->next) { + if (strncmp(backend->ops->name, url, + strlen(backend->ops->name)) == 0) { + return backend->ops->connect_fn; + } + } + + return NULL; +} + +/* + register a new ldb backend +*/ +int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn) +{ + struct ldb_backend_ops *backend; + struct backends_list_entry *entry; + + backend = talloc(talloc_autofree_context(), struct ldb_backend_ops); + if (!backend) return LDB_ERR_OPERATIONS_ERROR; + + entry = talloc(talloc_autofree_context(), struct backends_list_entry); + if (!entry) { + talloc_free(backend); + return LDB_ERR_OPERATIONS_ERROR; + } + + 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; + entry->ops = backend; + DLIST_ADD(ldb_backends, entry); + + 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) { + struct ldb_backend_ops *ops; + char *symbol_name = talloc_asprintf(ldb, "ldb_%s_backend_ops", backend); + if (symbol_name == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + ops = ldb_dso_load_symbol(ldb, backend, symbol_name); + if (ops != NULL) { + fn = ops->connect_fn; + } + talloc_free(symbol_name); + } + + 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; +} + +static const struct ldb_module_ops *ldb_find_module_ops(const char *name) +{ + struct ops_list_entry *e; + int i; + + for (i = 0; builtins[i].backend_ops || builtins[i].module_ops; i++) { + if (builtins[i].module_ops == NULL) continue; + + if (strcmp(builtins[i].module_ops->name, name) == 0) + return builtins[i].module_ops; + } + + for (e = registered_modules; e; e = e->next) { + if (strcmp(e->ops->name, name) == 0) + return e->ops; + } + + return NULL; +} + + +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; +} + +void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name, + const char *symbol) +{ + char *path; + void *handle; + void *sym; + + if (ldb->modules_dir == NULL) + return NULL; + + path = talloc_asprintf(ldb, "%s/%s.%s", ldb->modules_dir, name, + SHLIBEXT); + + ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path); + + handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror()); + return NULL; + } + + sym = (int (*)(void))dlsym(handle, symbol); + + if (sym == NULL) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `%s' found in %s: %s\n", symbol, path, dlerror()); + return NULL; + } + + talloc_free(path); + + return sym; +} + +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) { + char *symbol_name = talloc_asprintf(ldb, "ldb_%s_module_ops", + module_list[i]); + if (symbol_name == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + ops = ldb_dso_load_symbol(ldb, module_list[i], symbol_name); + talloc_free(symbol_name); + } + + 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; + } + talloc_set_name(current, "ldb_module: %s", module_list[i]); + + 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; + + /* init is different in that it is not an error if modules + * do not require initialization */ + + if (module) { + int ret = module->ops->init_context(module); + if (ret != LDB_SUCCESS) { + ldb_debug(ldb, LDB_DEBUG_FATAL, "module %s initialization failed\n", module->ops->name); + return ret; + } + } + + 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_new(mem_ctx, ldb, "@MODULES"); + if (mods_dn == NULL) { + talloc_free(mem_ctx); + return -1; + } + + ret = ldb_search_exp_fmt(ldb, mods_dn, &res, mods_dn, LDB_SCOPE_BASE, attrs, "@LIST=*"); + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db"); + } 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 ret; + } else { + const char *module_list; + if (res->count == 0) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db"); + } else 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; + } else { + module_list = ldb_msg_find_attr_as_string(res->msgs[0], "@LIST", NULL); + if (!module_list) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db"); + } + modules = ldb_modules_list_from_string(ldb, mem_ctx, + module_list); + } + } + + talloc_free(mods_dn); + } + + if (modules != NULL) { + ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules); + if (ret != LDB_SUCCESS) { + talloc_free(mem_ctx); + return ret; + } + } else { + ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database"); + } + + ret = ldb_init_module_chain(ldb, ldb->modules); + talloc_free(mem_ctx); + return ret; +} + +/* + 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) +{ + int ret; + switch (request->operation) { + case LDB_SEARCH: + FIND_OP(module, search); + ret = module->ops->search(module, request); + break; + case LDB_ADD: + FIND_OP(module, add); + ret = module->ops->add(module, request); + break; + case LDB_MODIFY: + FIND_OP(module, modify); + ret = module->ops->modify(module, request); + break; + case LDB_DELETE: + FIND_OP(module, del); + ret = module->ops->del(module, request); + break; + case LDB_RENAME: + FIND_OP(module, rename); + ret = module->ops->rename(module, request); + break; + case LDB_EXTENDED: + FIND_OP(module, extended); + ret = module->ops->extended(module, request); + break; + case LDB_SEQUENCE_NUMBER: + FIND_OP(module, sequence_number); + ret = module->ops->sequence_number(module, request); + break; + default: + FIND_OP(module, request); + ret = module->ops->request(module, request); + break; + } + if (ret == LDB_SUCCESS) { + return ret; + } + if (!ldb_errstring(module->ldb)) { + /* Set a default error string, to place the blame somewhere */ + ldb_asprintf_errstring(module->ldb, "error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret); + } + return ret; +} + +int ldb_next_init(struct ldb_module *module) +{ + module = module->next; + + return ldb_init_module_chain(module->ldb, 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); +} + +#ifndef STATIC_LIBLDB_MODULES + +#ifdef HAVE_LDB_LDAP +#define LDAP_BACKEND LDB_BACKEND(ldap), LDB_BACKEND(ldapi), LDB_BACKEND(ldaps), +#else +#define LDAP_BACKEND +#endif + +#ifdef HAVE_LDB_SQLITE3 +#define SQLITE3_BACKEND LDB_BACKEND(sqlite3), +#else +#define SQLITE3_BACKEND +#endif + +#define STATIC_LIBLDB_MODULES \ + LDB_BACKEND(tdb), \ + LDAP_BACKEND \ + SQLITE3_BACKEND \ + LDB_MODULE(operational), \ + LDB_MODULE(rdn_name), \ + LDB_MODULE(paged_results), \ + LDB_MODULE(server_sort), \ + LDB_MODULE(asq), \ + NULL +#endif + +/* + * this is a bit hacked, as STATIC_LIBLDB_MODULES contains ',' + * between the elements and we want to autogenerate the + * extern struct declarations, so we do some hacks and let the + * ',' appear in an unused function prototype. + */ +#undef NULL +#define NULL LDB_MODULE(NULL), + +#define LDB_BACKEND(name) \ + int); \ + extern const struct ldb_backend_ops ldb_ ## name ## _backend_ops;\ + extern void ldb_noop ## name (int +#define LDB_MODULE(name) \ + int); \ + extern const struct ldb_module_ops ldb_ ## name ## _module_ops;\ + extern void ldb_noop ## name (int + +extern void ldb_start_noop(int, +STATIC_LIBLDB_MODULES +int); + +#undef NULL +#define NULL { \ + .backend_ops = (void *)0, \ + .module_ops = (void *)0 \ +} + +#undef LDB_BACKEND +#define LDB_BACKEND(name) { \ + .backend_ops = &ldb_ ## name ## _backend_ops, \ + .module_ops = (void *)0 \ +} +#undef LDB_MODULE +#define LDB_MODULE(name) { \ + .backend_ops = (void *)0, \ + .module_ops = &ldb_ ## name ## _module_ops \ +} + +static const struct ldb_builtins builtins[] = { + STATIC_LIBLDB_MODULES +}; diff --git a/ldb/common/ldb_msg.c b/ldb/common/ldb_msg.c new file mode 100644 index 00000000..2f5fe1d1 --- /dev/null +++ b/ldb/common/ldb_msg.c @@ -0,0 +1,899 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb message component utility functions + * + * Description: functions for manipulating ldb_message structures + * + * Author: Andrew Tridgell + */ + +#include "ldb_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 **return_el) +{ + struct ldb_message_element *els; + + 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++; + + if (return_el) { + *return_el = &els[msg->num_elements-1]; + } + + 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) +{ + /* We have to copy this, just in case *el is a pointer into + * what ldb_msg_add_empty() is about to realloc() */ + struct ldb_message_element el_copy = *el; + if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->elements[msg->num_elements-1] = el_copy; + 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 **return_el) +{ + struct ldb_message_element *el; + struct ldb_val *vals; + int ret; + + el = ldb_msg_find_element(msg, attr_name); + if (!el) { + ret = ldb_msg_add_empty(msg, attr_name, 0, &el); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + 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++; + + if (return_el) { + *return_el = el; + } + + 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; + struct ldb_message_element *el; + + ret = ldb_msg_add_value(msg, attr_name, val, &el); + if (ret == LDB_SUCCESS) { + 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); + + if (val.length == 0) { + /* allow empty strings as non-existant attributes */ + return LDB_SUCCESS; + } + + return ldb_msg_add_value(msg, attr_name, &val, NULL); +} + +/* + 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 (v->length == 5 && strncasecmp((const char *)v->data, "FALSE", 5) == 0) { + return 0; + } + if (v->length == 4 && strncasecmp((const char *)v->data, "TRUE", 4) == 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(struct ldb_context *ldb, + void *mem_ctx, + const struct ldb_message *msg, + const char *attr_name) +{ + struct ldb_dn *res_dn; + const struct ldb_val *v; + + v = ldb_msg_find_ldb_val(msg, attr_name); + if (!v || !v->data) { + return NULL; + } + res_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, v); + if ( ! ldb_dn_validate(res_dn)) { + talloc_free(res_dn); + return NULL; + } + return res_dn; +} + +/* + 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->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, NULL) != 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; + } + + /* 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_get_linearized(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). The new attribute is added to the list. +*/ +const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr) +{ + const char **ret; + int i; + bool found = false; + for (i=0;attrs[i];i++) { + if (ldb_attr_cmp(attrs[i], new_attr) == 0) { + found = true; + } + } + if (found) { + return ldb_attr_list_copy(mem_ctx, attrs); + } + 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 && 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 element in a search result +*/ +void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *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--; +} + + +/* + 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) { + ldb_msg_remove_element(msg, el); + } +} + +/* + return a LDAP formatted GeneralizedTime string +*/ +char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t) +{ + struct tm *tm = gmtime(&t); + char *ts; + int r; + + if (!tm) { + return NULL; + } + + /* we now excatly how long this string will be */ + ts = talloc_array(mem_ctx, char, 18); + + /* formatted like: 20040408072012.0Z */ + r = snprintf(ts, 18, + "%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); + + if (r != 17) { + talloc_free(ts); + return NULL; + } + + return ts; +} + +/* + convert a LDAP GeneralizedTime 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); +} + +/* + return a LDAP formatted UTCTime string +*/ +char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t) +{ + struct tm *tm = gmtime(&t); + char *ts; + int r; + + if (!tm) { + return NULL; + } + + /* we now excatly how long this string will be */ + ts = talloc_array(mem_ctx, char, 14); + + /* formatted like: 20040408072012.0Z => 040408072012Z */ + r = snprintf(ts, 14, + "%02u%02u%02u%02u%02u%02uZ", + (tm->tm_year+1900)%100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + + if (r != 13) { + talloc_free(ts); + return NULL; + } + + return ts; +} + +/* + convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert +*/ +time_t ldb_string_utc_to_time(const char *s) +{ + struct tm tm; + + if (s == NULL) return 0; + + memset(&tm, 0, sizeof(tm)); + if (sscanf(s, "%02u%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; + } + if (tm.tm_year < 50) { + tm.tm_year += 100; + } + 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_p(uint8_t, value); + val.length = strlen(value); + + if (ldb_msg_find_val(el, &val)) + return 1; + + return 0; +} diff --git a/ldb/common/ldb_parse.c b/ldb/common/ldb_parse.c new file mode 100644 index 00000000..8c970a44 --- /dev/null +++ b/ldb/common/ldb_parse.c @@ -0,0 +1,819 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_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 = (uint8_t *)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_p(uint8_t, 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; + + if (*p == '@') { /* for internal attributes the first char can be @ */ + 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 = (char *)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 = (char *)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/ldb/common/ldb_utf8.c b/ldb/common/ldb_utf8.c new file mode 100644 index 00000000..69ee2b69 --- /dev/null +++ b/ldb/common/ldb_utf8.c @@ -0,0 +1,136 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb utf8 handling + * + * Description: case folding and case comparison for UTF8 strings + * + * Author: Andrew Tridgell + */ + +#include "ldb_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 *, size_t)) +{ + 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, size_t n) +{ + int i; + char *ret = talloc_strndup(mem_ctx, s, n); + 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, size_t n) +{ + return ldb->utf8_fns.casefold(ldb->utf8_fns.context, mem_ctx, s, n); +} + +/* + 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; +} + +char *ldb_attr_casefold(void *mem_ctx, const char *s) +{ + int i; + char *ret = talloc_strdup(mem_ctx, s); + if (!ret) { + 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/ldb/common/qsort.c b/ldb/common/qsort.c new file mode 100644 index 00000000..0fa76d3b --- /dev/null +++ b/ldb/common/qsort.c @@ -0,0 +1,251 @@ +/* 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, see <http://www.gnu.org/licenses/>. */ + +/* 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 "ldb_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/ldb/config.guess b/ldb/config.guess new file mode 100755 index 00000000..354dbe17 --- /dev/null +++ b/ldb/config.guess @@ -0,0 +1,1464 @@ +#! /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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# 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/ldb/config.mk b/ldb/config.mk new file mode 100644 index 00000000..fe3b71d1 --- /dev/null +++ b/ldb/config.mk @@ -0,0 +1,151 @@ +################################################ +# Start MODULE ldb_asq +[MODULE::ldb_asq] +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +CFLAGS = -I$(ldbsrcdir)/include +INIT_FUNCTION = LDB_MODULE(asq) +SUBSYSTEM = LIBLDB + +ldb_asq_OBJ_FILES = $(ldbsrcdir)/modules/asq.o +# End MODULE ldb_asq +################################################ + +################################################ +# Start MODULE ldb_server_sort +[MODULE::ldb_server_sort] +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +CFLAGS = -I$(ldbsrcdir)/include +INIT_FUNCTION = LDB_MODULE(server_sort) +SUBSYSTEM = LIBLDB + +# End MODULE ldb_sort +################################################ +ldb_server_sort_OBJ_FILES = $(ldbsrcdir)/modules/sort.o + +################################################ +# Start MODULE ldb_paged_results +[MODULE::ldb_paged_results] +INIT_FUNCTION = LDB_MODULE(paged_results) +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +SUBSYSTEM = LIBLDB +# End MODULE ldb_paged_results +################################################ + +ldb_paged_results_OBJ_FILES = $(ldbsrcdir)/modules/paged_results.o + +################################################ +# Start MODULE ldb_paged_results +[MODULE::ldb_paged_searches] +INIT_FUNCTION = LDB_MODULE(paged_searches) +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +SUBSYSTEM = LIBLDB +# End MODULE ldb_paged_results +################################################ + +ldb_paged_searches_OBJ_FILES = $(ldbsrcdir)/modules/paged_searches.o + +################################################ +# Start MODULE ldb_operational +[MODULE::ldb_operational] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +INIT_FUNCTION = LDB_MODULE(operational) +# End MODULE ldb_operational +################################################ + +ldb_operational_OBJ_FILES = $(ldbsrcdir)/modules/operational.o + +################################################ +# Start MODULE ldb_rdn_name +[MODULE::ldb_rdn_name] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +INIT_FUNCTION = LDB_MODULE(rdn_name) +# End MODULE ldb_rdn_name +################################################ + +ldb_rdn_name_OBJ_FILES = $(ldbsrcdir)/modules/rdn_name.o + +ldb_map_OBJ_FILES = $(addprefix $(ldbsrcdir)/ldb_map/, ldb_map_inbound.o ldb_map_outbound.o ldb_map.o) + +$(ldb_map_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/ldb_map + +################################################ +# Start MODULE ldb_skel +[MODULE::ldb_skel] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS +INIT_FUNCTION = LDB_MODULE(skel) +# End MODULE ldb_skel +################################################ + +ldb_skel_OBJ_FILES = $(ldbsrcdir)/modules/skel.o + +################################################ +# Start MODULE ldb_sqlite3 +[MODULE::ldb_sqlite3] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC SQLITE3 LIBEVENTS +INIT_FUNCTION = LDB_BACKEND(sqlite3) +# End MODULE ldb_sqlite3 +################################################ + +ldb_sqlite3_OBJ_FILES = $(ldbsrcdir)/ldb_sqlite3/ldb_sqlite3.o + +################################################ +# Start MODULE ldb_tdb +[MODULE::ldb_tdb] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include -I$(ldbsrcdir)/ldb_tdb +PRIVATE_DEPENDENCIES = \ + LIBTDB LIBTALLOC LIBEVENTS +INIT_FUNCTION = LDB_BACKEND(tdb) +# End MODULE ldb_tdb +################################################ + +ldb_tdb_OBJ_FILES = $(addprefix $(ldbsrcdir)/ldb_tdb/, ldb_tdb.o ldb_search.o ldb_pack.o ldb_index.o ldb_cache.o ldb_tdb_wrap.o) + + +################################################ +# Start SUBSYSTEM ldb +[LIBRARY::LIBLDB] +CFLAGS = -I$(ldbsrcdir)/include +PUBLIC_DEPENDENCIES = \ + LIBTALLOC LIBEVENTS +PRIVATE_DEPENDENCIES = \ + SOCKET_WRAPPER + +PC_FILES += $(ldbsrcdir)/ldb.pc +# +# End SUBSYSTEM ldb +################################################ + +LIBLDB_VERSION = 0.0.1 +LIBLDB_SOVERSION = 0 + +LIBLDB_OBJ_FILES = $(addprefix $(ldbsrcdir)/common/, ldb.o ldb_ldif.o ldb_parse.o ldb_msg.o ldb_utf8.o ldb_debug.o ldb_modules.o ldb_match.o ldb_attributes.o attrib_handlers.o ldb_dn.o ldb_controls.o qsort.o) $(ldb_map_OBJ_FILES) + +$(LIBLDB_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/include + +PUBLIC_HEADERS += $(ldbsrcdir)/include/ldb.h $(ldbsrcdir)/include/ldb_errors.h + +MANPAGES += $(ldbsrcdir)/man/ldb.3 + +################################################ +# Start BINARY ldbtest +[BINARY::ldbtest] +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbtest +################################################ + +ldbtest_OBJ_FILES = $(ldbsrcdir)/tools/ldbtest.o + +mkinclude tools/config.mk +mkinclude ldb_ildap/config.mk diff --git a/ldb/config.sub b/ldb/config.sub new file mode 100755 index 00000000..23cd6fd7 --- /dev/null +++ b/ldb/config.sub @@ -0,0 +1,1577 @@ +#! /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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# 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/ldb/configure.ac b/ldb/configure.ac new file mode 100644 index 00000000..1abb08fa --- /dev/null +++ b/ldb/configure.ac @@ -0,0 +1,102 @@ +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(ldb, 0.9.2) +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_DEFINE_UNQUOTED(LDB_MODULESDIR, LIBDIR "/ldb" , [Modules directory] ) +AC_SUBST(LDB_MODULESDIR) + +TESTS="" +EXTRA_OBJ="" + +m4_include(build_macros.m4) +BUILD_WITH_BUILD_INST_DIR + +m4_include(pkg.m4) +m4_include(libpopt.m4) +m4_include(libtalloc.m4) +m4_include(libtdb.m4) +m4_include(libevents.m4) + +m4_include(ldap.m4) +if test x"$with_ldap_support" = x"yes"; then + CFLAGS="$CFLAGS -DHAVE_LDB_LDAP=1" + EXTRA_OBJ="$EXTRA_OBJ ldb_ldap/ldb_ldap.o" + LDAP_LIBS="-llber -lldap" + AC_SUBST(LDAP_LIBS) + 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_LDB_SQLITE3=1" + EXTRA_OBJ="$EXTRA_OBJ ldb_sqlite3/ldb_sqlite3.o" + TESTS="$TESTS test-sqlite3.sh" +fi + +AC_SUBST(TESTS) +AC_SUBST(EXTRA_OBJ) + +AC_LD_EXPORT_DYNAMIC +AC_LD_PICFLAG +AC_LD_SHLIBEXT +AC_LD_SONAMEFLAG +AC_LIBREPLACE_SHLD +AC_LIBREPLACE_SHLD_FLAGS +AC_LIBREPLACE_MDLD +AC_LIBREPLACE_MDLD_FLAGS +AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR + +AC_PATH_PROGS([PYTHON_CONFIG], [python2.6-config python2.5-config python2.4-config python-config]) +AC_PATH_PROGS([PYTHON], [python2.6 python2.5 python2.4 python]) + +PYTHON_BUILD_TARGET="build-python" +PYTHON_INSTALL_TARGET="install-python" +PYTHON_CHECK_TARGET="check-python" +AC_SUBST(PYTHON_BUILD_TARGET) +AC_SUBST(PYTHON_INSTALL_TARGET) +AC_SUBST(PYTHON_CHECK_TARGET) + +if test -z "$PYTHON_CONFIG"; then + PYTHON_BUILD_TARGET="" + PYTHON_CHECK_TARGET="" + PYTHON_INSTALL_TARGET="" +fi + +m4_include(libldb.m4) +AC_OUTPUT(Makefile ldb.pc) diff --git a/ldb/docs/builddocs.sh b/ldb/docs/builddocs.sh new file mode 100755 index 00000000..449dcb26 --- /dev/null +++ b/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/ldb/docs/design.txt b/ldb/docs/design.txt new file mode 100644 index 00000000..0bb278b5 --- /dev/null +++ b/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/ldb/docs/installdocs.sh b/ldb/docs/installdocs.sh new file mode 100755 index 00000000..6cc7b74a --- /dev/null +++ b/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/ldb/examples.dox b/ldb/examples.dox new file mode 100644 index 00000000..ef4b4f0a --- /dev/null +++ b/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/ldb/examples/ldbreader.c b/ldb/examples/ldbreader.c new file mode 100644 index 00000000..6e581145 --- /dev/null +++ b/ldb/examples/ldbreader.c @@ -0,0 +1,123 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/** \example ldbreader.c + +The code below shows a simple LDB application. + +It lists / dumps the records in a LDB database to standard output. + +*/ + +#include "ldb_includes.h" +#include "ldb.h" +#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, 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/ldb/examples/ldifreader.c b/ldb/examples/ldifreader.c new file mode 100644 index 00000000..12e7a1a6 --- /dev/null +++ b/ldb/examples/ldifreader.c @@ -0,0 +1,127 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/** \example ldifreader.c + +The code below shows a simple LDB application. + +It lists / dumps the entries in an LDIF file to standard output. + +*/ + +#include "ldb_includes.h" +#include "ldb.h" +#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, 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/ldb/external/libevents.m4 b/ldb/external/libevents.m4 new file mode 100644 index 00000000..4fd2e71e --- /dev/null +++ b/ldb/external/libevents.m4 @@ -0,0 +1,7 @@ +AC_SUBST(EVENTS_OBJ) +AC_SUBST(EVENTS_CFLAGS) +AC_SUBST(EVENTS_LIBS) + +AC_CHECK_HEADER(events.h, + [AC_CHECK_LIB(events, event_context_init, [EVENTS_LIBS="-levents"]) ], + [PKG_CHECK_MODULES(EVENTS, events)]) diff --git a/ldb/external/libpopt.m4 b/ldb/external/libpopt.m4 new file mode 100644 index 00000000..c5d12550 --- /dev/null +++ b/ldb/external/libpopt.m4 @@ -0,0 +1,7 @@ +POPT_OBJ="" +AC_SUBST(POPT_OBJ) +AC_SUBST(POPT_LIBS) +AC_SUBST(POPT_CFLAGS) + +AC_CHECK_HEADERS(popt.h) +AC_CHECK_LIB(popt, poptGetContext, [ POPT_LIBS="-lpopt" ]) diff --git a/ldb/external/libtalloc.m4 b/ldb/external/libtalloc.m4 new file mode 100644 index 00000000..a4c5b8a9 --- /dev/null +++ b/ldb/external/libtalloc.m4 @@ -0,0 +1,7 @@ +AC_SUBST(TALLOC_OBJ) +AC_SUBST(TALLOC_CFLAGS) +AC_SUBST(TALLOC_LIBS) + +AC_CHECK_HEADER(talloc.h, + [AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"]) ], + [PKG_CHECK_MODULES(TALLOC, talloc)]) diff --git a/ldb/external/libtdb.m4 b/ldb/external/libtdb.m4 new file mode 100644 index 00000000..8c2cab70 --- /dev/null +++ b/ldb/external/libtdb.m4 @@ -0,0 +1,7 @@ +AC_SUBST(TDB_OBJ) +AC_SUBST(TDB_CFLAGS) +AC_SUBST(TDB_LIBS) + +AC_CHECK_HEADER(tdb.h, + [AC_CHECK_LIB(tdb, tdb_open, [TDB_LIBS="-ltdb"]) ], + [PKG_CHECK_MODULES(TDB, tdb >= 1.1.0)]) diff --git a/ldb/external/pkg.m4 b/ldb/external/pkg.m4 new file mode 100644 index 00000000..a8b3d06c --- /dev/null +++ b/ldb/external/pkg.m4 @@ -0,0 +1,156 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant <scott@netsplit.com>. +# +# 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# 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. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/ldb/include/dlinklist.h b/ldb/include/dlinklist.h new file mode 100644 index 00000000..d3252751 --- /dev/null +++ b/ldb/include/dlinklist.h @@ -0,0 +1,110 @@ +/* + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* 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/ldb/include/ldb.h b/ldb/include/ldb.h new file mode 100644 index 00000000..937029f5 --- /dev/null +++ b/ldb/include/ldb.h @@ -0,0 +1,1698 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 */ + +/* opaque ldb_dn structures, see ldb_dn.c for internals */ +struct ldb_dn_component; +struct ldb_dn; + +/** + 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; +}; + +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; + +/* 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, TALLOC_CTX *mem_ctx, const char *s, size_t n); +}; + +/** + 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 + +/** + Flag to tell backends not to use mmap +*/ +#define LDB_FLG_NOMMAP 8 + +/* + 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(TALLOC_CTX *mem_ctx, const char *s); +char *ldb_filter_from_tree(TALLOC_CTX *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 mem_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(TALLOC_CTX *mem_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(TALLOC_CTX *mem_ctx, const char *string); + +/* + functions for controlling attribute handling +*/ +typedef int (*ldb_attr_handler_t)(struct ldb_context *, TALLOC_CTX *mem_ctx, const struct ldb_val *, struct ldb_val *); +typedef int (*ldb_attr_comparison_t)(struct ldb_context *, TALLOC_CTX *mem_ctx, const struct ldb_val *, const struct ldb_val *); + +/* + attribute handler structure + + attr -> The attribute name + flags -> LDB_ATTR_FLAG_* + ldif_read_fn -> convert from ldif to binary format + ldif_write_fn -> convert from binary to ldif format + canonicalise_fn -> canonicalise a value, for use by indexing and dn construction + comparison_fn -> compare two values +*/ + +struct ldb_schema_syntax { + const char *name; + ldb_attr_handler_t ldif_read_fn; + ldb_attr_handler_t ldif_write_fn; + ldb_attr_handler_t canonicalise_fn; + ldb_attr_comparison_t comparison_fn; +}; + +struct ldb_schema_attribute { + const char *name; + unsigned flags; + const struct ldb_schema_syntax *syntax; +}; + +const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb, + const char *name); + +/** + The attribute is not returned by default +*/ +#define LDB_ATTR_FLAG_HIDDEN (1<<0) + +/* the attribute handler name should be freed when released */ +#define LDB_ATTR_FLAG_ALLOCATED (1<<1) + +/** + The attribute is supplied by the application and should not be removed +*/ +#define LDB_ATTR_FLAG_FIXED (1<<2) + +/** + 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" + +/** +*/ +#define LDB_EXTENDED_DYNAMIC_OID "1.3.6.1.4.1.1466.101.119.1" + +/** +*/ +#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; +}; + +/* + * 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) + */ + +#define LDB_SEARCH_OPTION_DOMAIN_SCOPE 0x00000001 +#define LDB_SEARCH_OPTION_PHANTOM_ROOT 0x00000002 + +struct ldb_search_options_control { + 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_SEQUENCE_NUMBER, + LDB_REQ_REGISTER_CONTROL, + LDB_REQ_REGISTER_PARTITION +}; + +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_extended { + const char *oid; + void *data; /* NULL or a valid talloc pointer! talloc_get_type() will be used on it */ +}; + +struct ldb_result { + unsigned int count; + struct ldb_message **msgs; + char **refs; + struct ldb_extended *extended; + struct ldb_control **controls; +}; + +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 { + struct ldb_dn *base; + enum ldb_scope scope; + 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 { + struct ldb_dn *dn; +}; + +struct ldb_rename { + struct ldb_dn *olddn; + struct ldb_dn *newdn; +}; + +struct ldb_register_control { + const char *oid; +}; + +struct ldb_register_partition { + struct ldb_dn *dn; +}; + +enum ldb_sequence_type { + LDB_SEQ_HIGHEST_SEQ, + LDB_SEQ_HIGHEST_TIMESTAMP, + LDB_SEQ_NEXT +}; + +struct ldb_sequence_number { + enum ldb_sequence_type 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_extended extended; + struct ldb_sequence_number seq_num; + struct ldb_register_control reg_control; + struct ldb_register_partition reg_partition; + } 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); +void ldb_set_create_perms(struct ldb_context *ldb, unsigned int perms); +void ldb_set_modules_dir(struct ldb_context *ldb, const char *path); +struct event_context; +void ldb_set_event_context(struct ldb_context *ldb, struct event_context *ev); +struct event_context * ldb_get_event_context(struct ldb_context *ldb); + +/** + 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(TALLOC_CTX *mem_ctx, struct event_context *ev_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 rootDomainNamingContext of the rootDSE + This value have been set in an opaque pointer at connection time +*/ +struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb); + +/* + return an automatic basedn from the configurationNamingContext of the rootDSE + This value have been set in an opaque pointer at connection time +*/ +struct ldb_dn *ldb_get_config_basedn(struct ldb_context *ldb); + +/* + return an automatic basedn from the schemaNamingContext of the rootDSE + This value have been set in an opaque pointer at connection time +*/ +struct ldb_dn *ldb_get_schema_basedn(struct ldb_context *ldb); + +/* + return an automatic baseDN from the defaultNamingContext of the rootDSE + This value have been set in an opaque pointer at connection time +*/ +struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb); + +/** + The default async search callback function + + \param ldb the context associated with the database (from ldb_init()) + \param context the callback context (struct ldb_result *) + \param ares a single reply from the async core + + \return result code (LDB_SUCCESS on success, or a failure code) + + \note this function expects the context to always be an struct ldb_result pointer + AND a talloc context, this function will steal on the context each message + from the ares reply passed on by the async core so that in the end all the + messages will be in the context (ldb_result) memory tree. + Freeing the passed context (ldb_result tree) will free all the resources + (the request need to be freed separately and the result doe not depend on the + request that can be freed as sson as the search request is finished) +*/ + +int ldb_search_default_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares); + +/** + Helper function to build a search request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param base the Base Distinguished Name for the query (use ldb_dn_new() for an empty one) + \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 controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ + +int ldb_build_search_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + 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); + +/** + Helper function to build an add request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param message contains the entry to be added + \param controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ + +int ldb_build_add_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + const struct ldb_message *message, + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback); + +/** + Helper function to build a modify request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param message contains the entry to be modified + \param controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ + +int ldb_build_mod_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + const struct ldb_message *message, + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback); + +/** + Helper function to build a delete request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param dn the DN to be deleted + \param controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ + +int ldb_build_del_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback); + +/** + Helper function to build a rename request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param olddn the old DN + \param newdn the new DN + \param controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ + +int ldb_build_rename_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *olddn, + struct ldb_dn *newdn, + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback); + +/** + Add a ldb_control to a ldb_request + + \param req the request struct where to add the control + \param oid the object identifier of the control as string + \param critical whether the control should be critical or not + \param data a talloc pointer to the control specific data + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ +int ldb_request_add_control(struct ldb_request *req, const char *oid, bool critical, void *data); + +/** + check if a control with the specified "oid" exist and return it + \param req the request struct where to add the control + \param oid the object identifier of the control as string + + \return the control, NULL if not found +*/ +struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char *oid); + +/** + 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 (use ldb_dn_new() for an empty one) + \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, + struct ldb_dn *base, + enum ldb_scope scope, + const char *expression, + const char * const *attrs, struct ldb_result **res); + +/* + * a useful search function where you can easily define the expression and + * that takes a memory context where results are allocated +*/ + +int ldb_search_exp_fmt(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + struct ldb_result **result, struct ldb_dn *base, + enum ldb_scope scope, const char * const *attrs, + const char *exp_fmt, ...) PRINTF_ATTRIBUTE(7,8); + +/** + 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, struct ldb_dn *olddn, 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, struct ldb_dn *dn); + +/** + The default async extended operation callback function + + \param ldb the context associated with the database (from ldb_init()) + \param context the callback context (struct ldb_result *) + \param ares a single reply from the async core + + \return result code (LDB_SUCCESS on success, or a failure code) + + \note this function expects the context to always be an struct ldb_result pointer + AND a talloc context, this function will steal on the context each message + from the ares reply passed on by the async core so that in the end all the + messages will be in the context (ldb_result) memory tree. + Freeing the passed context (ldb_result tree) will free all the resources + (the request need to be freed separately and the result doe not depend on the + request that can be freed as sson as the search request is finished) +*/ +int ldb_extended_default_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares); + +/** + Helper function to build a extended request + + \param ret_req the request structure is returned here (talloced on mem_ctx) + \param ldb the context associated with the database (from ldb_init()) + \param mem_ctx a talloc memory context (used as parent of ret_req) + \param oid the OID of the extended operation. + \param data a void pointer a the extended operation specific parameters, + it needs to be NULL or a valid talloc pointer! talloc_get_type() will be used on it + \param controls an array of controls + \param context the callback function context + \param callback the callback function to handle the async replies + + \return result code (LDB_SUCCESS on success, or a failure code) +*/ +int ldb_build_extended_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + const char *oid, + void *data,/* NULL or a valid talloc pointer! talloc_get_type() will be used on it */ + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback); + +/** + call an extended operation + + This function deletes a record from the database. + + \param ldb the context associated with the database (from ldb_init()) + \param oid the OID of the extended operation. + \param data a void pointer a the extended operation specific parameters, + it needs to be NULL or a valid talloc pointer! talloc_get_type() will be used on it + \param res the result of the extended operation + + \return result code (LDB_SUCCESS if the extended operation returned fine, + otherwise a failure code) +*/ +int ldb_extended(struct ldb_context *ldb, + const char *oid, + void *data,/* NULL or a valid talloc pointer! talloc_get_type() will be used on it */ + struct ldb_result **res); + +/** + 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, TALLOC_CTX *mem_ctx, const char *s, size_t n); + +/** + Check the attribute name is valid according to rfc2251 + \param s the 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(TALLOC_CTX *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); + +/* The following definitions come from lib/ldb/common/ldb_dn.c */ + +struct ldb_dn *ldb_dn_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *dn); +struct ldb_dn *ldb_dn_new_fmt(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...) PRINTF_ATTRIBUTE(3,4); +struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn); +bool ldb_dn_validate(struct ldb_dn *dn); + +char *ldb_dn_escape_value(TALLOC_CTX *mem_ctx, struct ldb_val value); +const char *ldb_dn_get_linearized(struct ldb_dn *dn); +const char *ldb_dn_get_casefold(struct ldb_dn *dn); +char *ldb_dn_alloc_linearized(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); +char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); + +int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn); +int ldb_dn_compare(struct ldb_dn *edn0, struct ldb_dn *edn1); + +bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base); +bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...) PRINTF_ATTRIBUTE(2,3); +bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child); +bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...) PRINTF_ATTRIBUTE(2,3); +bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num); +bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num); + +struct ldb_dn *ldb_dn_copy(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); +struct ldb_dn *ldb_dn_get_parent(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); +char *ldb_dn_canonical_string(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); +char *ldb_dn_canonical_ex_string(TALLOC_CTX *mem_ctx, struct ldb_dn *dn); +int ldb_dn_get_comp_num(struct ldb_dn *dn); +const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num); +const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, unsigned int num); +const char *ldb_dn_get_rdn_name(struct ldb_dn *dn); +const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn); +int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const struct ldb_val val); + +bool ldb_dn_is_valid(struct ldb_dn *dn); +bool ldb_dn_is_special(struct ldb_dn *dn); +bool ldb_dn_check_special(struct ldb_dn *dn, const char *check); +bool ldb_dn_is_null(struct ldb_dn *dn); + + +/** + Compare two attributes + + This function compares to attribute names. Note that this is a + case-insensitive comparison. + + \param a the first attribute name to compare + \param b 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 + + attribute names are restricted by rfc2251 so using + strcasecmp and toupper here is ok. + return 0 for match +*/ +#define ldb_attr_cmp(a, b) strcasecmp(a, b) +char *ldb_attr_casefold(TALLOC_CTX *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(TALLOC_CTX *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, + struct ldb_message_element **return_el); + +/** + 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, + struct ldb_message_element **return_el); +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(struct ldb_context *ldb, + TALLOC_CTX *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(TALLOC_CTX *mem_ctx, + const struct ldb_message *msg); +struct ldb_message *ldb_msg_copy(TALLOC_CTX *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 ldb context in which to perform the checks + \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(TALLOC_CTX *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 *, size_t n)); + +/** + 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 char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs); +const char **ldb_attr_list_copy_add(TALLOC_CTX *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 + GeneralizedTime 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(TALLOC_CTX *mem_ctx, time_t t); + +/** + Convert a string to a time structure + + This function converts an LDAP formatted GeneralizedTime 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); + +/** + Convert a time structure to a string + + This function converts a time_t structure to an LDAP formatted + UTCTime 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_utc(TALLOC_CTX *mem_ctx, time_t t); + +/** + Convert a string to a time structure + + This function converts an LDAP formatted UTCTime 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_utc_to_time(const char *s); + + +void ldb_qsort (void *const pbase, size_t total_elems, size_t size, void *opaque, ldb_qsort_cmp_fn_t cmp); + + +/** + Convert an array of string represention of a control into an array of ldb_control structures + + \param ldb LDB context + \param mem_ctx TALLOC context to return result on, and to allocate error_string on + \param control_strings Array of string-formatted controls + + \return array of ldb_control elements +*/ +struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char **control_strings); + +#endif diff --git a/ldb/include/ldb_errors.h b/ldb/include/ldb_errors.h new file mode 100644 index 00000000..9362233f --- /dev/null +++ b/ldb/include/ldb_errors.h @@ -0,0 +1,310 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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/ldb/include/ldb_handlers.h b/ldb/include/ldb_handlers.h new file mode 100644 index 00000000..e1c14e67 --- /dev/null +++ b/ldb/include/ldb_handlers.h @@ -0,0 +1,67 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb header + * + * Description: defines attribute handlers + * + * Author: Simo Sorce + */ + + +int ldb_handler_copy( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out); + +int ldb_handler_fold( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out); + +int ldb_canonicalise_Integer( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out); + +int ldb_comparison_Integer( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_comparison_binary( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_comparison_fold( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_canonicalise_dn( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out); + +int ldb_comparison_dn( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_comparison_objectclass( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_comparison_utctime( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2); + +int ldb_canonicalise_utctime( struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out); + diff --git a/ldb/include/ldb_includes.h b/ldb/include/ldb_includes.h new file mode 100644 index 00000000..29c7b2dc --- /dev/null +++ b/ldb/include/ldb_includes.h @@ -0,0 +1,29 @@ +#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 + +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "talloc.h" +#include "events.h" +#include "ldb.h" +#include "ldb_errors.h" +#include "ldb_private.h" +#include "dlinklist.h" + +#endif /*_LDB_PRIVATE_INCLUDES_H_*/ diff --git a/ldb/include/ldb_private.h b/ldb/include/ldb_private.h new file mode 100644 index 00000000..e1026ab7 --- /dev/null +++ b/ldb/include/ldb_private.h @@ -0,0 +1,251 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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; + +struct ldb_backend_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); + + +struct ldb_backend_ops { + const char *name; + ldb_connect_fn connect_fn; +}; + +const char *ldb_default_modules_dir(void); + +/* + schema related information needed for matching rules +*/ +struct ldb_schema { + /* attribute handling table */ + unsigned num_attributes; + struct ldb_schema_attribute *attributes; +}; + +/* + 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; + + unsigned int create_perms; + + char *modules_dir; + + struct event_context *ev_ctx; +}; + +#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); +void ldb_set_default_dns(struct ldb_context *ldb); + +/* 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); +void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name, + const char *symbol); + +/* 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); + +extern const struct ldb_module_ops ldb_objectclass_module_ops; +extern const struct ldb_module_ops ldb_operational_module_ops; +extern const struct ldb_module_ops ldb_paged_results_module_ops; +extern const struct ldb_module_ops ldb_rdn_name_module_ops; +extern const struct ldb_module_ops ldb_schema_module_ops; +extern const struct ldb_module_ops ldb_asq_module_ops; +extern const struct ldb_module_ops ldb_server_sort_module_ops; +extern const struct ldb_module_ops ldb_ldap_module_ops; +extern const struct ldb_module_ops ldb_ildap_module_ops; +extern const struct ldb_module_ops ldb_paged_searches_module_ops; +extern const struct ldb_module_ops ldb_tdb_module_ops; +extern const struct ldb_module_ops ldb_skel_module_ops; +extern const struct ldb_module_ops ldb_subtree_rename_module_ops; +extern const struct ldb_module_ops ldb_subtree_delete_module_ops; +extern const struct ldb_module_ops ldb_sqlite3_module_ops; +extern const struct ldb_module_ops ldb_wins_ldb_module_ops; +extern const struct ldb_module_ops ldb_ranged_results_module_ops; + +extern const struct ldb_backend_ops ldb_tdb_backend_ops; +extern const struct ldb_backend_ops ldb_sqlite3_backend_ops; +extern const struct ldb_backend_ops ldb_ldap_backend_ops; +extern const struct ldb_backend_ops ldb_ldapi_backend_ops; +extern const struct ldb_backend_ops ldb_ldaps_backend_ops; + +int ldb_match_msg(struct ldb_context *ldb, + const struct ldb_message *msg, + const struct ldb_parse_tree *tree, + struct ldb_dn *base, + enum ldb_scope scope); + +const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb, + const char *syntax); + +/* The following definitions come from lib/ldb/common/ldb_attributes.c */ + +int ldb_schema_attribute_add_with_syntax(struct ldb_context *ldb, + const char *name, + unsigned flags, + const struct ldb_schema_syntax *syntax); +int ldb_schema_attribute_add(struct ldb_context *ldb, + const char *name, + unsigned flags, + const char *syntax); +void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name); +int ldb_setup_wellknown_attributes(struct ldb_context *ldb); + +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, size_t n); + +void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el); + +/** + 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/ldb/install-sh b/ldb/install-sh new file mode 100755 index 00000000..58719246 --- /dev/null +++ b/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/ldb/ldap.m4 b/ldb/ldap.m4 new file mode 100644 index 00000000..417083ed --- /dev/null +++ b/ldb/ldap.m4 @@ -0,0 +1,90 @@ +######################################################## +# 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]) + AC_DEFINE(HAVE_LDB_LDAP,1,[Whether ldb_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/ldb/ldb.i b/ldb/ldb.i new file mode 100644 index 00000000..4b61ddba --- /dev/null +++ b/ldb/ldb.i @@ -0,0 +1,931 @@ +/* + 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> + Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +%define DOCSTRING +"An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server." +%enddef + +%module(docstring=DOCSTRING) ldb + +%{ + +#include <stdint.h> +#include <stdbool.h> +#include "talloc.h" +#include "events.h" +#include "ldb.h" +#include "ldb_errors.h" +#include "ldb_private.h" + +typedef struct ldb_message ldb_msg; +typedef struct ldb_context ldb; +typedef struct ldb_dn ldb_dn; +typedef struct ldb_ldif ldb_ldif; +typedef struct ldb_message_element ldb_message_element; +typedef int ldb_error; +typedef int ldb_int_error; + +%} + +%import "carrays.i" +%import "typemaps.i" +%include "exception.i" +%import "stdint.i" + +/* Don't expose talloc contexts in Python code. Python does reference + counting for us, so just create a new top-level talloc context. + */ +%typemap(in, numinputs=0, noblock=1) TALLOC_CTX * { + $1 = NULL; +} + + + +%constant int SCOPE_DEFAULT = LDB_SCOPE_DEFAULT; +%constant int SCOPE_BASE = LDB_SCOPE_BASE; +%constant int SCOPE_ONELEVEL = LDB_SCOPE_ONELEVEL; +%constant int SCOPE_SUBTREE = LDB_SCOPE_SUBTREE; + +%constant int CHANGETYPE_NONE = LDB_CHANGETYPE_NONE; +%constant int CHANGETYPE_ADD = LDB_CHANGETYPE_ADD; +%constant int CHANGETYPE_DELETE = LDB_CHANGETYPE_DELETE; +%constant int CHANGETYPE_MODIFY = LDB_CHANGETYPE_MODIFY; + +/* + * Wrap struct ldb_context + */ + +/* The ldb functions will crash if a NULL ldb context is passed so + catch this before it happens. */ + +%typemap(check,noblock=1) struct ldb_context* { + if ($1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); +} + +%typemap(check,noblock=1) ldb_msg * { + if ($1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); +} + +/* + * Wrap struct ldb_val + */ + +%typemap(in,noblock=1) 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); +} + +%inline %{ +PyObject *ldb_val_to_py_object(struct ldb_context *ldb_ctx, + struct ldb_message_element *el, + struct ldb_val *val) +{ + const struct ldb_schema_attribute *a; + struct ldb_val new_val; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + PyObject *ret; + + new_val = *val; + + if (ldb_ctx != NULL) { + a = ldb_schema_attribute_by_name(ldb_ctx, el->name); + + if (a != NULL) { + if (a->syntax->ldif_write_fn(ldb_ctx, mem_ctx, val, &new_val) != 0) { + talloc_free(mem_ctx); + return NULL; + } + } + } + + ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length); + + talloc_free(mem_ctx); + + return ret; +} + +%} + +%typemap(out,noblock=1) struct ldb_val * { + $result = PyString_FromStringAndSize((const char *)$1->data, $1->length) +} + +%typemap(out,noblock=1) struct ldb_val { + $result = PyString_FromStringAndSize((const char *)$1.data, $1.length) +} + +/* + * Wrap struct ldb_result + */ + +%typemap(in,noblock=1,numinputs=0) struct ldb_result **OUT (struct ldb_result *temp_ldb_result) { + $1 = &temp_ldb_result; +} + +#ifdef SWIGPYTHON +%typemap(argout,noblock=1) struct ldb_result ** (int i) { + $result = PyList_New((*$1)->count); + for (i = 0; i < (*$1)->count; i++) { + PyList_SetItem($result, i, + SWIG_NewPointerObj((*$1)->msgs[i], SWIGTYPE_p_ldb_message, 0) + ); + } +} + +%typemap(in,noblock=1,numinputs=1) const char * const *NULL_STR_LIST { + if ($input == Py_None) { + $1 = NULL; + } else if (PySequence_Check($input)) { + int i; + $1 = talloc_array(NULL, char *, PySequence_Size($input)+1); + for(i = 0; i < PySequence_Size($input); i++) + $1[i] = PyString_AsString(PySequence_GetItem($input, i)); + $1[i] = NULL; + } else { + SWIG_exception(SWIG_TypeError, "expected sequence"); + } +} + +%typemap(freearg,noblock=1) const char * const *NULL_STR_LIST { + talloc_free($1); +} + +%apply const char * const *NULL_STR_LIST { const char * const *attrs } +%apply const char * const *NULL_STR_LIST { const char * const *control_strings } + +#endif + +%types(struct ldb_result *); + +/* + * Wrap struct ldb_dn + */ + +%rename(__str__) ldb_dn::get_linearized; +%rename(__cmp__) ldb_dn::compare; +%rename(__len__) ldb_dn::get_comp_num; +%rename(Dn) ldb_dn; +%feature("docstring") ldb_dn "A LDB distinguished name."; +typedef struct ldb_dn { + %extend { + %feature("docstring") ldb_dn "S.__init__(ldb, string)\n" \ + "Create a new DN."; + ldb_dn(ldb *ldb_ctx, const char *str) + { + ldb_dn *ret = ldb_dn_new(ldb_ctx, ldb_ctx, str); + /* ldb_dn_new() doesn't accept NULL as memory context, so + we do it this way... */ + talloc_steal(NULL, ret); + + if (ret == NULL) + SWIG_exception(SWIG_ValueError, + "unable to parse dn string"); +fail: + return ret; + } + ~ldb_dn() { talloc_free($self); } + %feature("docstring") validate "S.validate() -> bool\n" \ + "Validate DN is correct."; + bool validate(); + const char *get_casefold(); + const char *get_linearized(); + %feature("docstring") parent "S.parent() -> dn\n" \ + "Get the parent for this DN."; + ldb_dn *parent() { return ldb_dn_get_parent(NULL, $self); } + int compare(ldb_dn *other); + bool is_valid(); + %feature("docstring") is_special "S.is_special() -> bool\n" \ + "Check whether this is a special LDB DN."; + bool is_special(); + %feature("docstring") is_null "S.is_null() -> bool\n" \ + "Check whether this is a null DN."; + bool is_null(); + bool check_special(const char *name); + int get_comp_num(); + %feature("docstring") add_child "S.add_child(dn) -> None\n" \ + "Add a child DN to this DN."; + bool add_child(ldb_dn *child); + %feature("docstring") add_base "S.add_base(dn) -> None\n" \ + "Add a base DN to this DN."; + bool add_base(ldb_dn *base); + %feature("docstring") canonical_str "S.canonical_str() -> string\n" \ + "Canonical version of this DN (like a posix path)."; + const char *canonical_str() { + return ldb_dn_canonical_string($self, $self); + } + %feature("docstring") canonical_ex_str "S.canonical_ex_str() -> string\n" \ + "Canonical version of this DN (like a posix path, with terminating newline)."; + const char *canonical_ex_str() { + return ldb_dn_canonical_ex_string($self, $self); + } +#ifdef SWIGPYTHON + char *__repr__(void) + { + char *dn = ldb_dn_get_linearized($self), *ret; + asprintf(&ret, "Dn('%s')", dn); + talloc_free(dn); + return ret; + } + + ldb_dn *__add__(ldb_dn *other) + { + ldb_dn *ret = ldb_dn_copy(NULL, $self); + ldb_dn_add_child(ret, other); + return ret; + } + + /* FIXME: implement __getslice__ */ +#endif + %pythoncode { + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.__cmp__(other) == 0 + if isinstance(other, str): + return str(self) == other + return False + } + } +} ldb_dn; + +#ifdef SWIGPYTHON +%{ +struct ldb_context *ldb_context_from_py_object(PyObject *py_obj) +{ + struct ldb_context *ldb_ctx; + if (SWIG_ConvertPtr(py_obj, (void *)&ldb_ctx, SWIGTYPE_p_ldb_context, 0 | 0 ) < 0) + return NULL; + return ldb_ctx; +} + +int ldb_dn_from_pyobject(TALLOC_CTX *mem_ctx, PyObject *object, + struct ldb_context *ldb_ctx, ldb_dn **dn) +{ + int ret; + struct ldb_dn *odn; + if (ldb_ctx != NULL && PyString_Check(object)) { + odn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object)); + if (!odn) { + return SWIG_ERROR; + } + *dn = odn; + return 0; + } + ret = SWIG_ConvertPtr(object, (void **)&odn, SWIGTYPE_p_ldb_dn, + SWIG_POINTER_EXCEPTION); + *dn = ldb_dn_copy(mem_ctx, odn); + if (odn && !*dn) { + return SWIG_ERROR; + } + return ret; +} + +ldb_message_element *ldb_msg_element_from_pyobject(TALLOC_CTX *mem_ctx, + PyObject *set_obj, int flags, + const char *attr_name) +{ + struct ldb_message_element *me = talloc(mem_ctx, struct ldb_message_element); + me->name = attr_name; + me->flags = flags; + if (PyString_Check(set_obj)) { + me->num_values = 1; + me->values = talloc_array(me, struct ldb_val, me->num_values); + me->values[0].length = PyString_Size(set_obj); + me->values[0].data = (uint8_t *)talloc_strdup(me->values, + PyString_AsString(set_obj)); + } else if (PySequence_Check(set_obj)) { + int i; + me->num_values = PySequence_Size(set_obj); + me->values = talloc_array(me, struct ldb_val, me->num_values); + for (i = 0; i < me->num_values; i++) { + PyObject *obj = PySequence_GetItem(set_obj, i); + me->values[i].length = PyString_Size(obj); + me->values[i].data = (uint8_t *)PyString_AsString(obj); + } + } else { + talloc_free(me); + me = NULL; + } + + return me; +} + +PyObject *ldb_msg_element_to_set(struct ldb_context *ldb_ctx, + ldb_message_element *me) +{ + int i; + PyObject *result; + + /* Python << 2.5 doesn't have PySet_New and PySet_Add. */ + result = PyList_New(me->num_values); + + for (i = 0; i < me->num_values; i++) { + PyList_SetItem(result, i, + ldb_val_to_py_object(ldb_ctx, me, &me->values[i])); + } + + return result; +} + +%} +#endif + +/* ldb_message_element */ +%rename(MessageElement) ldb_message_element; +%feature("docstring") ldb_message_element "Message element."; +typedef struct ldb_message_element { + %extend { +#ifdef SWIGPYTHON + int __cmp__(ldb_message_element *other) + { + return ldb_msg_element_compare($self, other); + } + + PyObject *__iter__(void) + { + return PyObject_GetIter(ldb_msg_element_to_set(NULL, $self)); + } + + PyObject *__set__(void) + { + return ldb_msg_element_to_set(NULL, $self); + } + + ldb_message_element(PyObject *set_obj, int flags=0, const char *name = NULL) + { + return ldb_msg_element_from_pyobject(NULL, set_obj, flags, name); + } + + int __len__() + { + return $self->num_values; + } +#endif + + PyObject *get(int i) + { + if (i < 0 || i >= $self->num_values) + return Py_None; + + return ldb_val_to_py_object(NULL, $self, &$self->values[i]); + } + + ~ldb_message_element() { talloc_free($self); } + } + %pythoncode { + def __getitem__(self, i): + ret = self.get(i) + if ret is None: + raise KeyError("no such value") + return ret + + def __repr__(self): + return "MessageElement([%s])" % (",".join(repr(x) for x in self.__set__())) + + def __eq__(self, other): + if (len(self) == 1 and self.get(0) == other): + return True + if isinstance(other, self.__class__): + return self.__cmp__(other) == 0 + o = iter(other) + for i in range(len(self)): + if self.get(i) != o.next(): + return False + return True + } +} ldb_message_element; + +/* ldb_message */ + +%feature("docstring") ldb_message "Message."; +%rename(Message) ldb_message; +#ifdef SWIGPYTHON +%rename(__delitem__) ldb_message::remove_attr; +%typemap(out) ldb_message_element * { + if ($1 == NULL) + PyErr_SetString(PyExc_KeyError, "no such element"); + else + $result = SWIG_NewPointerObj($1, SWIGTYPE_p_ldb_message_element, 0); +} + +%inline { + PyObject *ldb_msg_list_elements(ldb_msg *msg) + { + int i, j = 0; + PyObject *obj = PyList_New(msg->num_elements+(msg->dn != NULL?1:0)); + if (msg->dn != NULL) { + PyList_SetItem(obj, j, PyString_FromString("dn")); + j++; + } + for (i = 0; i < msg->num_elements; i++) { + PyList_SetItem(obj, j, PyString_FromString(msg->elements[i].name)); + j++; + } + return obj; + } +} + +#endif + +typedef struct ldb_message { + ldb_dn *dn; + + %extend { + ldb_msg(ldb_dn *dn = NULL) { + ldb_msg *ret = ldb_msg_new(NULL); + ret->dn = talloc_reference(ret, dn); + return ret; + } + ~ldb_msg() { talloc_free($self); } + ldb_message_element *find_element(const char *name); + +#ifdef SWIGPYTHON + void __setitem__(const char *attr_name, ldb_message_element *val) + { + struct ldb_message_element *el; + + ldb_msg_remove_attr($self, attr_name); + + el = talloc($self, struct ldb_message_element); + el->name = talloc_strdup(el, attr_name); + el->num_values = val->num_values; + el->values = talloc_reference(el, val->values); + + ldb_msg_add($self, el, val->flags); + } + + void __setitem__(const char *attr_name, PyObject *val) + { + struct ldb_message_element *el = ldb_msg_element_from_pyobject(NULL, + val, 0, attr_name); + talloc_steal($self, el); + ldb_msg_remove_attr($self, attr_name); + ldb_msg_add($self, el, el->flags); + } + + unsigned int __len__() { return $self->num_elements; } + + PyObject *keys(void) + { + return ldb_msg_list_elements($self); + } + + PyObject *__iter__(void) + { + return PyObject_GetIter(ldb_msg_list_elements($self)); + } +#endif + void remove_attr(const char *name); +%pythoncode { + def get(self, key, default=None): + if key == "dn": + return self.dn + return self.find_element(key) + + def __getitem__(self, key): + ret = self.get(key, None) + if ret is None: + raise KeyError("No such element") + return ret + + def iteritems(self): + for k in self.keys(): + yield k, self[k] + + def items(self): + return list(self.iteritems()) + + def __repr__(self): + return "Message(%s)" % repr(dict(self.iteritems())) +} + } +} ldb_msg; + +/* FIXME: Convert ldb_result to 3-tuple: + (msgs, refs, controls) + */ + +typedef struct ldb_ldif ldb_ldif; + +#ifdef SWIGPYTHON +%{ +static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3, 0); + +static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) +{ + char *text; + PyObject *fn = context; + + vasprintf(&text, fmt, ap); + PyObject_CallFunction(fn, (char *)"(i,s)", level, text); + free(text); +} +%} + +%typemap(in,numinputs=1,noblock=1) (void (*debug)(void *context, enum ldb_debug_level level, const char *fmt, va_list ap), void *context) { + $1 = py_ldb_debug; + /* FIXME: Should be decreased somewhere as well. Perhaps register a + destructor and tie it to the ldb context ? */ + Py_INCREF($input); + $2 = $input; +} +#endif + +%inline { + static PyObject *ldb_ldif_to_pyobject(ldb_ldif *ldif) + { + if (ldif == NULL) { + return Py_None; + } else { + return Py_BuildValue((char *)"(iO)", ldif->changetype, + SWIG_NewPointerObj(ldif->msg, SWIGTYPE_p_ldb_message, 0)); + } + } +} + +/* + * Wrap ldb errors + */ + +%{ +PyObject *PyExc_LdbError; +%} + +%pythoncode %{ + LdbError = _ldb.LdbError +%} + +%init %{ + PyExc_LdbError = PyErr_NewException((char *)"_ldb.LdbError", NULL, NULL); + PyDict_SetItemString(d, "LdbError", PyExc_LdbError); +%} + +%ignore _LDB_ERRORS_H_; +%ignore LDB_SUCCESS; +%include "include/ldb_errors.h" + +/* + * Wrap ldb functions + */ + + +%typemap(out,noblock=1) ldb_error { + if ($1 != LDB_SUCCESS) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", $1, ldb_errstring(arg1))); + SWIG_fail; + } + $result = Py_None; +}; + +%typemap(out,noblock=1) ldb_int_error { + if ($1 != LDB_SUCCESS) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", $1, ldb_strerror($1))); + SWIG_fail; + } + $result = Py_None; +}; + +%typemap(out,noblock=1) struct ldb_control ** { + if ($1 == NULL) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(s)", ldb_errstring(arg1))); + SWIG_fail; + } + $result = SWIG_NewPointerObj($1, $1_descriptor, 0); +} + +%rename(Ldb) ldb_context; +%feature("docstring") ldb_context "Connection to a LDB database."; + +%typemap(in,noblock=1) struct ldb_dn * { + if (ldb_dn_from_pyobject(NULL, $input, arg1, &$1) != 0) { + SWIG_fail; + } +}; + +%typemap(freearg,noblock=1) struct ldb_dn * { + talloc_free($1); +}; + +%typemap(in,numinputs=1) ldb_msg *add_msg { + Py_ssize_t dict_pos, msg_pos; + ldb_message_element *msgel; + PyObject *key, *value; + + if (PyDict_Check($input)) { + PyObject *dn_value = PyDict_GetItemString($input, "dn"); + $1 = ldb_msg_new(NULL); + $1->elements = talloc_zero_array($1, struct ldb_message_element, PyDict_Size($input)); + msg_pos = dict_pos = 0; + if (dn_value) { + /* using argp1 (magic SWIG value) here is a hack */ + if (ldb_dn_from_pyobject($1, dn_value, argp1, &$1->dn) != 0) { + SWIG_exception(SWIG_TypeError, "unable to import dn object"); + } + if ($1->dn == NULL) { + SWIG_exception(SWIG_TypeError, "dn set but not found"); + } + } + + while (PyDict_Next($input, &dict_pos, &key, &value)) { + char *key_str = PyString_AsString(key); + if (strcmp(key_str, "dn") != 0) { + msgel = ldb_msg_element_from_pyobject($1->elements, value, 0, key_str); + if (msgel == NULL) { + SWIG_exception(SWIG_TypeError, "unable to import element"); + } + memcpy(&$1->elements[msg_pos], msgel, sizeof(*msgel)); + msg_pos++; + } + } + + if ($1->dn == NULL) { + SWIG_exception(SWIG_TypeError, "no dn set"); + } + + $1->num_elements = msg_pos; + } else { + if (SWIG_ConvertPtr($input, (void **)&$1, SWIGTYPE_p_ldb_message, 0) != 0) { + SWIG_exception(SWIG_TypeError, "unable to convert ldb message"); + } + } +} + +/* Top-level ldb operations */ +typedef struct ldb_context { + %extend { + ldb(void) { + return ldb_init(NULL, event_context_init(NULL)); + } + + %feature("docstring") connect "S.connect(url,flags=0,options=None) -> None\n" \ + "Connect to a LDB URL."; + ldb_error connect(const char *url, unsigned int flags = 0, + const char *options[] = NULL); + + ~ldb() { talloc_free($self); } + ldb_error search_ex(TALLOC_CTX *mem_ctx, + ldb_dn *base = NULL, + enum ldb_scope scope = LDB_SCOPE_DEFAULT, + const char *expression = NULL, + const char *const *attrs = NULL, + struct ldb_control **controls = NULL, + struct ldb_result **OUT) { + int ret; + struct ldb_result *res; + struct ldb_request *req; + res = talloc_zero(mem_ctx, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_build_search_req(&req, $self, mem_ctx, + base?base:ldb_get_default_basedn($self), + scope, + expression, + attrs, + controls, + res, + ldb_search_default_callback); + + if (ret != LDB_SUCCESS) { + talloc_free(res); + return ret; + } + + ldb_set_timeout($self, req, 0); /* use default timeout */ + + ret = ldb_request($self, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(req); + + *OUT = res; + return ret; + } + + %feature("docstring") delete "S.delete(dn) -> None\n" \ + "Remove an entry."; + ldb_error delete(ldb_dn *dn); + %feature("docstring") rename "S.rename(old_dn, new_dn) -> None\n" \ + "Rename an entry."; + ldb_error rename(ldb_dn *olddn, ldb_dn *newdn); + struct ldb_control **parse_control_strings(TALLOC_CTX *mem_ctx, + const char * const*control_strings); + %feature("docstring") add "S.add(message) -> None\n" \ + "Add an entry."; + ldb_error add(ldb_msg *add_msg); + %feature("docstring") modify "S.modify(message) -> None\n" \ + "Modify an entry."; + ldb_error modify(ldb_msg *message); + ldb_dn *get_config_basedn(); + ldb_dn *get_root_basedn(); + ldb_dn *get_schema_basedn(); + ldb_dn *get_default_basedn(); + PyObject *schema_format_value(const char *element_name, PyObject *val) + { + const struct ldb_schema_attribute *a; + struct ldb_val old_val; + struct ldb_val new_val; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + PyObject *ret; + + old_val.data = PyString_AsString(val); + old_val.length = PyString_Size(val); + + a = ldb_schema_attribute_by_name($self, element_name); + + if (a == NULL) { + return Py_None; + } + + if (a->syntax->ldif_write_fn($self, mem_ctx, &old_val, &new_val) != 0) { + talloc_free(mem_ctx); + return Py_None; + } + + ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length); + + talloc_free(mem_ctx); + + return ret; + } + + const char *errstring(); + %feature("docstring") set_create_perms "S.set_create_perms(mode) -> None\n" \ + "Set mode to use when creating new LDB files."; + void set_create_perms(unsigned int perms); + %feature("docstring") set_modules_dir "S.set_modules_dir(path) -> None\n" \ + "Set path LDB should search for modules"; + void set_modules_dir(const char *path); + %feature("docstring") set_debug "S.set_debug(callback) -> None\n" \ + "Set callback for LDB debug messages.\n" \ + "The callback should accept a debug level and debug text."; + ldb_error set_debug(void (*debug)(void *context, enum ldb_debug_level level, + const char *fmt, va_list ap), + void *context); + %feature("docstring") set_opaque "S.set_opaque(name, value) -> None\n" \ + "Set an opaque value on this LDB connection. \n" + ":note: Passing incorrect values may cause crashes."; + ldb_error set_opaque(const char *name, void *value); + %feature("docstring") get_opaque "S.get_opaque(name) -> value\n" \ + "Get an opaque value set on this LDB connection. \n" + ":note: The returned value may not be useful in Python."; + void *get_opaque(const char *name); + %feature("docstring") transaction_start "S.transaction_start() -> None\n" \ + "Start a new transaction."; + ldb_error transaction_start(); + %feature("docstring") transaction_commit "S.transaction_commit() -> None\n" \ + "Commit currently active transaction."; + ldb_error transaction_commit(); + %feature("docstring") transaction_cancel "S.transaction_cancel() -> None\n" \ + "Cancel currently active transaction."; + ldb_error transaction_cancel(); + void schema_attribute_remove(const char *name); + ldb_error schema_attribute_add(const char *attribute, unsigned flags, const char *syntax); + ldb_error setup_wellknown_attributes(void); + +#ifdef SWIGPYTHON + %typemap(in,numinputs=0,noblock=1) struct ldb_result **result_as_bool (struct ldb_result *tmp) { $1 = &tmp; } + %typemap(argout,noblock=1) struct ldb_result **result_as_bool { $result = ((*$1)->count > 0)?Py_True:Py_False; } + %typemap(freearg,noblock=1) struct ldb_result **result_as_bool { talloc_free(*$1); } + ldb_error __contains__(ldb_dn *dn, struct ldb_result **result_as_bool) + { + return ldb_search($self, dn, LDB_SCOPE_BASE, NULL, NULL, + result_as_bool); + } + + %feature("docstring") parse_ldif "S.parse_ldif(ldif) -> iter(messages)\n" \ + "Parse a string formatted using LDIF."; + + PyObject *parse_ldif(const char *s) + { + PyObject *list = PyList_New(0); + struct ldb_ldif *ldif; + while ((ldif = ldb_ldif_read_string($self, &s)) != NULL) { + PyList_Append(list, ldb_ldif_to_pyobject(ldif)); + } + return PyObject_GetIter(list); + } + + char *__repr__(void) + { + char *ret; + asprintf(&ret, "<ldb connection at 0x%x>", ret); + return ret; + } +#endif + } + %pythoncode { + def __init__(self, url=None, flags=0, options=None): + """Create a new LDB object. + + Will also connect to the specified URL if one was given. + """ + _ldb.Ldb_swiginit(self,_ldb.new_Ldb()) + if url is not None: + self.connect(url, flags, options) + + def search(self, base=None, scope=SCOPE_DEFAULT, expression=None, + attrs=None, controls=None): + """Search in a database. + + :param base: Optional base DN to search + :param scope: Search scope (SCOPE_BASE, SCOPE_ONELEVEL or SCOPE_SUBTREE) + :param expression: Optional search expression + :param attrs: Attributes to return (defaults to all) + :param controls: Optional list of controls + :return: Iterator over Message objects + """ + if not (attrs is None or isinstance(attrs, list)): + raise TypeError("attributes not a list") + parsed_controls = None + if controls is not None: + parsed_controls = self.parse_control_strings(controls) + return self.search_ex(base, scope, expression, attrs, + parsed_controls) + } + +} ldb; + +%typemap(in,noblock=1) struct ldb_dn *; +%typemap(freearg,noblock=1) struct ldb_dn *; + +%nodefault ldb_message; +%nodefault ldb_context; +%nodefault Dn; + +%rename(valid_attr_name) ldb_valid_attr_name; +%feature("docstring") ldb_valid_attr_name "S.valid_attr_name(name) -> bool\n" + "Check whether the supplied name is a valid attribute name."; +int ldb_valid_attr_name(const char *s); + +typedef unsigned long time_t; + +%feature("docstring") timestring "S.timestring(int) -> string\n" + "Generate a LDAP time string from a UNIX timestamp"; + +%inline %{ +static char *timestring(time_t t) +{ + char *tresult = ldb_timestring(NULL, t); + char *result = strdup(tresult); + talloc_free(tresult); + return result; +} +%} + +%rename(string_to_time) ldb_string_to_time; +%feature("docstring") ldb_string_to_time "S.string_to_time(string) -> int\n" + "Parse a LDAP time string into a UNIX timestamp."; +time_t ldb_string_to_time(const char *s); + +%typemap(in,noblock=1) const struct ldb_module_ops * { + $1 = talloc_zero(talloc_autofree_context(), struct ldb_module_ops); + + $1->name = (char *)PyObject_GetAttrString($input, (char *)"name"); +} + +%feature("docstring") ldb_register_module "S.register_module(module) -> None\n" + "Register a LDB module."; +%rename(register_module) ldb_register_module; +ldb_int_error ldb_register_module(const struct ldb_module_ops *); + +%pythoncode { +__docformat__ = "restructuredText" +} diff --git a/ldb/ldb.mk b/ldb/ldb.mk new file mode 100644 index 00000000..a7b1cb0c --- /dev/null +++ b/ldb/ldb.mk @@ -0,0 +1,86 @@ +LDB_LIB = -Llib -lldb + +LIB_FLAGS=$(LDFLAGS) $(LIBS) $(LDB_LIB) $(POPT_LIBS) $(TALLOC_LIBS) \ + $(TDB_LIBS) $(EVENTS_LIBS) $(LDAP_LIBS) $(LIBDL) + +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 + +LDB_MAP_DIR=ldb_map +LDB_MAP_OBJ=$(LDB_MAP_DIR)/ldb_map.o $(LDB_MAP_DIR)/ldb_map_inbound.o \ + $(LDB_MAP_DIR)/ldb_map_outbound.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)/rdn_name.o \ + $(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o + +NSSDIR=nssldb +NSS_OBJ= $(NSSDIR)/ldb-nss.o $(NSSDIR)/ldb-pwd.o $(NSSDIR)/ldb-grp.o +NSS_LIB = lib/libnss_ldb.$(SHLIBEXT).2 + +lib/libldb.a: $(OBJS) + ar -rv $@ $(OBJS) + @-ranlib $@ + +sample.$(SHLIBEXT): tests/sample_module.o + $(MDLD) $(MDLD_FLAGS) -o $@ tests/sample_module.o + +bin/ldbadd: tools/ldbadd.o tools/cmdline.o + $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o + $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbdel: tools/ldbdel.o tools/cmdline.o + $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o + $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbedit: tools/ldbedit.o tools/cmdline.o + $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbrename: tools/ldbrename.o tools/cmdline.o + $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +bin/ldbtest: tools/ldbtest.o tools/cmdline.o + $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) + +examples/ldbreader: examples/ldbreader.o + $(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS) + +examples/ldifreader: examples/ldifreader.o + $(CC) -o examples/ldifreader examples/ldifreader.o $(LIB_FLAGS) + +# Python bindings +build-python:: _ldb.$(SHLIBEXT) + +ldb_wrap.o: $(ldbdir)/ldb_wrap.c + $(CC) $(PICFLAG) -c $(ldbdir)/ldb_wrap.c $(CFLAGS) `$(PYTHON_CONFIG) --cflags` + +_ldb.$(SHLIBEXT): ldb_wrap.o + $(SHLD) $(SHLD_FLAGS) -o _ldb.$(SHLIBEXT) ldb_wrap.o $(LIB_FLAGS) `$(PYTHON_CONFIG) --ldflags` + +install-python:: build-python + mkdir -p $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(0, prefix='$(prefix)')"` \ + $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"` + cp $(ldbdir)/ldb.py $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(0, prefix='$(prefix)')"` + cp _ldb.$(SHLIBEXT) $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"` + +install-swig:: + cp ldb.i `$(SWIG) -swiglib` + +check-python:: build-python + LD_LIBRARY_PATH=lib PYTHONPATH=.:$(ldbdir) $(PYTHON) $(ldbdir)/tests/python/api.py + +clean:: + rm -f _ldb.$(SHLIBEXT) diff --git a/ldb/ldb.pc b/ldb/ldb.pc new file mode 100644 index 00000000..cf986a0f --- /dev/null +++ b/ldb/ldb.pc @@ -0,0 +1,16 @@ +prefix=/usr/local/samba +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +modulesdir=${prefix}/modules + +Name: ldb +Description: An LDAP-like embedded database +Version: 4 +Requires.private: tdb +Requires: talloc +Libs: -L${libdir} -lldb +Libs.private: @LDAP_LIBS@ +Cflags: -I${includedir} +Modulesdir: ${modulesdir} +URL: http://ldb.samba.org/ diff --git a/ldb/ldb.pc.in b/ldb/ldb.pc.in new file mode 100644 index 00000000..b7e4c858 --- /dev/null +++ b/ldb/ldb.pc.in @@ -0,0 +1,16 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +modulesdir=@modulesdir@ + +Name: ldb +Description: An LDAP-like embedded database +Version: @PACKAGE_VERSION@ +Requires.private: tdb +Requires: talloc +Libs: -L${libdir} -lldb +Libs.private: @LDAP_LIBS@ +Cflags: -I${includedir} +Modulesdir: ${modulesdir} +URL: http://ldb.samba.org/ diff --git a/ldb/ldb.py b/ldb/ldb.py new file mode 100644 index 00000000..ae2c1873 --- /dev/null +++ b/ldb/ldb.py @@ -0,0 +1,477 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 1.3.35 +# +# Don't modify this file, modify the SWIG interface instead. + +""" +An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server. +""" + +import _ldb +import new +new_instancemethod = new.instancemethod +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "thisown"): return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'PySwigObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static) or hasattr(self,name): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + if (name == "thisown"): return self.this.own() + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError,name + +def _swig_repr(self): + try: strthis = "proxy of " + self.this.__repr__() + except: strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +import types +try: + _object = types.ObjectType + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 +del types + + +def _swig_setattr_nondynamic_method(set): + def set_attr(self,name,value): + if (name == "thisown"): return self.this.own(value) + if hasattr(self,name) or (name == "this"): + set(self,name,value) + else: + raise AttributeError("You cannot add attributes to %s" % self) + return set_attr + + +SCOPE_DEFAULT = _ldb.SCOPE_DEFAULT +SCOPE_BASE = _ldb.SCOPE_BASE +SCOPE_ONELEVEL = _ldb.SCOPE_ONELEVEL +SCOPE_SUBTREE = _ldb.SCOPE_SUBTREE +CHANGETYPE_NONE = _ldb.CHANGETYPE_NONE +CHANGETYPE_ADD = _ldb.CHANGETYPE_ADD +CHANGETYPE_DELETE = _ldb.CHANGETYPE_DELETE +CHANGETYPE_MODIFY = _ldb.CHANGETYPE_MODIFY +ldb_val_to_py_object = _ldb.ldb_val_to_py_object +class Dn(object): + """A LDB distinguished name.""" + thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag') + def __init__(self, *args, **kwargs): + """ + S.__init__(ldb, string) + Create a new DN. + """ + _ldb.Dn_swiginit(self,_ldb.new_Dn(*args, **kwargs)) + __swig_destroy__ = _ldb.delete_Dn + def validate(*args, **kwargs): + """ + S.validate() -> bool + Validate DN is correct. + """ + return _ldb.Dn_validate(*args, **kwargs) + + def parent(*args, **kwargs): + """ + S.parent() -> dn + Get the parent for this DN. + """ + return _ldb.Dn_parent(*args, **kwargs) + + def is_special(*args, **kwargs): + """ + S.is_special() -> bool + Check whether this is a special LDB DN. + """ + return _ldb.Dn_is_special(*args, **kwargs) + + def is_null(*args, **kwargs): + """ + S.is_null() -> bool + Check whether this is a null DN. + """ + return _ldb.Dn_is_null(*args, **kwargs) + + def add_child(*args, **kwargs): + """ + S.add_child(dn) -> None + Add a child DN to this DN. + """ + return _ldb.Dn_add_child(*args, **kwargs) + + def add_base(*args, **kwargs): + """ + S.add_base(dn) -> None + Add a base DN to this DN. + """ + return _ldb.Dn_add_base(*args, **kwargs) + + def canonical_str(*args, **kwargs): + """ + S.canonical_str() -> string + Canonical version of this DN (like a posix path). + """ + return _ldb.Dn_canonical_str(*args, **kwargs) + + def canonical_ex_str(*args, **kwargs): + """ + S.canonical_ex_str() -> string + Canonical version of this DN (like a posix path, with terminating newline). + """ + return _ldb.Dn_canonical_ex_str(*args, **kwargs) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.__cmp__(other) == 0 + if isinstance(other, str): + return str(self) == other + return False + +Dn.validate = new_instancemethod(_ldb.Dn_validate,None,Dn) +Dn.get_casefold = new_instancemethod(_ldb.Dn_get_casefold,None,Dn) +Dn.__str__ = new_instancemethod(_ldb.Dn___str__,None,Dn) +Dn.parent = new_instancemethod(_ldb.Dn_parent,None,Dn) +Dn.__cmp__ = new_instancemethod(_ldb.Dn___cmp__,None,Dn) +Dn.is_valid = new_instancemethod(_ldb.Dn_is_valid,None,Dn) +Dn.is_special = new_instancemethod(_ldb.Dn_is_special,None,Dn) +Dn.is_null = new_instancemethod(_ldb.Dn_is_null,None,Dn) +Dn.check_special = new_instancemethod(_ldb.Dn_check_special,None,Dn) +Dn.__len__ = new_instancemethod(_ldb.Dn___len__,None,Dn) +Dn.add_child = new_instancemethod(_ldb.Dn_add_child,None,Dn) +Dn.add_base = new_instancemethod(_ldb.Dn_add_base,None,Dn) +Dn.canonical_str = new_instancemethod(_ldb.Dn_canonical_str,None,Dn) +Dn.canonical_ex_str = new_instancemethod(_ldb.Dn_canonical_ex_str,None,Dn) +Dn.__repr__ = new_instancemethod(_ldb.Dn___repr__,None,Dn) +Dn.__add__ = new_instancemethod(_ldb.Dn___add__,None,Dn) +Dn_swigregister = _ldb.Dn_swigregister +Dn_swigregister(Dn) + +class MessageElement(object): + """Message element.""" + thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag') + __repr__ = _swig_repr + def __init__(self, *args, **kwargs): + """Message element.""" + _ldb.MessageElement_swiginit(self,_ldb.new_MessageElement(*args, **kwargs)) + __swig_destroy__ = _ldb.delete_MessageElement + def __getitem__(self, i): + ret = self.get(i) + if ret is None: + raise KeyError("no such value") + return ret + + def __repr__(self): + return "MessageElement([%s])" % (",".join(repr(x) for x in self.__set__())) + + def __eq__(self, other): + if (len(self) == 1 and self.get(0) == other): + return True + if isinstance(other, self.__class__): + return self.__cmp__(other) == 0 + o = iter(other) + for i in range(len(self)): + if self.get(i) != o.next(): + return False + return True + +MessageElement.__cmp__ = new_instancemethod(_ldb.MessageElement___cmp__,None,MessageElement) +MessageElement.__iter__ = new_instancemethod(_ldb.MessageElement___iter__,None,MessageElement) +MessageElement.__set__ = new_instancemethod(_ldb.MessageElement___set__,None,MessageElement) +MessageElement.__len__ = new_instancemethod(_ldb.MessageElement___len__,None,MessageElement) +MessageElement.get = new_instancemethod(_ldb.MessageElement_get,None,MessageElement) +MessageElement_swigregister = _ldb.MessageElement_swigregister +MessageElement_swigregister(MessageElement) + +ldb_msg_list_elements = _ldb.ldb_msg_list_elements +class Message(object): + """Message.""" + thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag') + __repr__ = _swig_repr + dn = _swig_property(_ldb.Message_dn_get, _ldb.Message_dn_set) + def __init__(self, *args, **kwargs): + _ldb.Message_swiginit(self,_ldb.new_Message(*args, **kwargs)) + __swig_destroy__ = _ldb.delete_Message + def get(self, key, default=None): + if key == "dn": + return self.dn + return self.find_element(key) + + def __getitem__(self, key): + ret = self.get(key, None) + if ret is None: + raise KeyError("No such element") + return ret + + def iteritems(self): + for k in self.keys(): + yield k, self[k] + + def items(self): + return list(self.iteritems()) + + def __repr__(self): + return "Message(%s)" % repr(dict(self.iteritems())) + +Message.find_element = new_instancemethod(_ldb.Message_find_element,None,Message) +Message.__setitem__ = new_instancemethod(_ldb.Message___setitem__,None,Message) +Message.__len__ = new_instancemethod(_ldb.Message___len__,None,Message) +Message.keys = new_instancemethod(_ldb.Message_keys,None,Message) +Message.__iter__ = new_instancemethod(_ldb.Message___iter__,None,Message) +Message.__delitem__ = new_instancemethod(_ldb.Message___delitem__,None,Message) +Message_swigregister = _ldb.Message_swigregister +Message_swigregister(Message) + +ldb_ldif_to_pyobject = _ldb.ldb_ldif_to_pyobject +LdbError = _ldb.LdbError + +LDB_ERR_OPERATIONS_ERROR = _ldb.LDB_ERR_OPERATIONS_ERROR +LDB_ERR_PROTOCOL_ERROR = _ldb.LDB_ERR_PROTOCOL_ERROR +LDB_ERR_TIME_LIMIT_EXCEEDED = _ldb.LDB_ERR_TIME_LIMIT_EXCEEDED +LDB_ERR_SIZE_LIMIT_EXCEEDED = _ldb.LDB_ERR_SIZE_LIMIT_EXCEEDED +LDB_ERR_COMPARE_FALSE = _ldb.LDB_ERR_COMPARE_FALSE +LDB_ERR_COMPARE_TRUE = _ldb.LDB_ERR_COMPARE_TRUE +LDB_ERR_AUTH_METHOD_NOT_SUPPORTED = _ldb.LDB_ERR_AUTH_METHOD_NOT_SUPPORTED +LDB_ERR_STRONG_AUTH_REQUIRED = _ldb.LDB_ERR_STRONG_AUTH_REQUIRED +LDB_ERR_REFERRAL = _ldb.LDB_ERR_REFERRAL +LDB_ERR_ADMIN_LIMIT_EXCEEDED = _ldb.LDB_ERR_ADMIN_LIMIT_EXCEEDED +LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION = _ldb.LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION +LDB_ERR_CONFIDENTIALITY_REQUIRED = _ldb.LDB_ERR_CONFIDENTIALITY_REQUIRED +LDB_ERR_SASL_BIND_IN_PROGRESS = _ldb.LDB_ERR_SASL_BIND_IN_PROGRESS +LDB_ERR_NO_SUCH_ATTRIBUTE = _ldb.LDB_ERR_NO_SUCH_ATTRIBUTE +LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE = _ldb.LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE +LDB_ERR_INAPPROPRIATE_MATCHING = _ldb.LDB_ERR_INAPPROPRIATE_MATCHING +LDB_ERR_CONSTRAINT_VIOLATION = _ldb.LDB_ERR_CONSTRAINT_VIOLATION +LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS = _ldb.LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS +LDB_ERR_INVALID_ATTRIBUTE_SYNTAX = _ldb.LDB_ERR_INVALID_ATTRIBUTE_SYNTAX +LDB_ERR_NO_SUCH_OBJECT = _ldb.LDB_ERR_NO_SUCH_OBJECT +LDB_ERR_ALIAS_PROBLEM = _ldb.LDB_ERR_ALIAS_PROBLEM +LDB_ERR_INVALID_DN_SYNTAX = _ldb.LDB_ERR_INVALID_DN_SYNTAX +LDB_ERR_ALIAS_DEREFERENCING_PROBLEM = _ldb.LDB_ERR_ALIAS_DEREFERENCING_PROBLEM +LDB_ERR_INAPPROPRIATE_AUTHENTICATION = _ldb.LDB_ERR_INAPPROPRIATE_AUTHENTICATION +LDB_ERR_INVALID_CREDENTIALS = _ldb.LDB_ERR_INVALID_CREDENTIALS +LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS = _ldb.LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS +LDB_ERR_BUSY = _ldb.LDB_ERR_BUSY +LDB_ERR_UNAVAILABLE = _ldb.LDB_ERR_UNAVAILABLE +LDB_ERR_UNWILLING_TO_PERFORM = _ldb.LDB_ERR_UNWILLING_TO_PERFORM +LDB_ERR_LOOP_DETECT = _ldb.LDB_ERR_LOOP_DETECT +LDB_ERR_NAMING_VIOLATION = _ldb.LDB_ERR_NAMING_VIOLATION +LDB_ERR_OBJECT_CLASS_VIOLATION = _ldb.LDB_ERR_OBJECT_CLASS_VIOLATION +LDB_ERR_NOT_ALLOWED_ON_NON_LEAF = _ldb.LDB_ERR_NOT_ALLOWED_ON_NON_LEAF +LDB_ERR_NOT_ALLOWED_ON_RDN = _ldb.LDB_ERR_NOT_ALLOWED_ON_RDN +LDB_ERR_ENTRY_ALREADY_EXISTS = _ldb.LDB_ERR_ENTRY_ALREADY_EXISTS +LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED = _ldb.LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED +LDB_ERR_AFFECTS_MULTIPLE_DSAS = _ldb.LDB_ERR_AFFECTS_MULTIPLE_DSAS +LDB_ERR_OTHER = _ldb.LDB_ERR_OTHER +class Ldb(object): + """Connection to a LDB database.""" + thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag') + def __init__(self, *args, **kwargs): + _ldb.Ldb_swiginit(self,_ldb.new_Ldb(*args, **kwargs)) + def connect(*args, **kwargs): + """ + S.connect(url,flags=0,options=None) -> None + Connect to a LDB URL. + """ + return _ldb.Ldb_connect(*args, **kwargs) + + __swig_destroy__ = _ldb.delete_Ldb + def delete(*args, **kwargs): + """ + S.delete(dn) -> None + Remove an entry. + """ + return _ldb.Ldb_delete(*args, **kwargs) + + def rename(*args, **kwargs): + """ + S.rename(old_dn, new_dn) -> None + Rename an entry. + """ + return _ldb.Ldb_rename(*args, **kwargs) + + def add(*args, **kwargs): + """ + S.add(message) -> None + Add an entry. + """ + return _ldb.Ldb_add(*args, **kwargs) + + def modify(*args, **kwargs): + """ + S.modify(message) -> None + Modify an entry. + """ + return _ldb.Ldb_modify(*args, **kwargs) + + def set_create_perms(*args, **kwargs): + """ + S.set_create_perms(mode) -> None + Set mode to use when creating new LDB files. + """ + return _ldb.Ldb_set_create_perms(*args, **kwargs) + + def set_modules_dir(*args, **kwargs): + """ + S.set_modules_dir(path) -> None + Set path LDB should search for modules + """ + return _ldb.Ldb_set_modules_dir(*args, **kwargs) + + def set_debug(*args, **kwargs): + """ + S.set_debug(callback) -> None + Set callback for LDB debug messages. + The callback should accept a debug level and debug text. + """ + return _ldb.Ldb_set_debug(*args, **kwargs) + + def set_opaque(*args, **kwargs): + """ + S.set_opaque(name, value) -> None + Set an opaque value on this LDB connection. + :note: Passing incorrect values may cause crashes. + """ + return _ldb.Ldb_set_opaque(*args, **kwargs) + + def get_opaque(*args, **kwargs): + """ + S.get_opaque(name) -> value + Get an opaque value set on this LDB connection. + :note: The returned value may not be useful in Python. + """ + return _ldb.Ldb_get_opaque(*args, **kwargs) + + def transaction_start(*args, **kwargs): + """ + S.transaction_start() -> None + Start a new transaction. + """ + return _ldb.Ldb_transaction_start(*args, **kwargs) + + def transaction_commit(*args, **kwargs): + """ + S.transaction_commit() -> None + Commit currently active transaction. + """ + return _ldb.Ldb_transaction_commit(*args, **kwargs) + + def transaction_cancel(*args, **kwargs): + """ + S.transaction_cancel() -> None + Cancel currently active transaction. + """ + return _ldb.Ldb_transaction_cancel(*args, **kwargs) + + def parse_ldif(*args, **kwargs): + """ + S.parse_ldif(ldif) -> iter(messages) + Parse a string formatted using LDIF. + """ + return _ldb.Ldb_parse_ldif(*args, **kwargs) + + def __init__(self, url=None, flags=0, options=None): + """Create a new LDB object. + + Will also connect to the specified URL if one was given. + """ + _ldb.Ldb_swiginit(self,_ldb.new_Ldb()) + if url is not None: + self.connect(url, flags, options) + + def search(self, base=None, scope=SCOPE_DEFAULT, expression=None, + attrs=None, controls=None): + """Search in a database. + + :param base: Optional base DN to search + :param scope: Search scope (SCOPE_BASE, SCOPE_ONELEVEL or SCOPE_SUBTREE) + :param expression: Optional search expression + :param attrs: Attributes to return (defaults to all) + :param controls: Optional list of controls + :return: Iterator over Message objects + """ + if not (attrs is None or isinstance(attrs, list)): + raise TypeError("attributes not a list") + parsed_controls = None + if controls is not None: + parsed_controls = self.parse_control_strings(controls) + return self.search_ex(base, scope, expression, attrs, + parsed_controls) + +Ldb.connect = new_instancemethod(_ldb.Ldb_connect,None,Ldb) +Ldb.search_ex = new_instancemethod(_ldb.Ldb_search_ex,None,Ldb) +Ldb.delete = new_instancemethod(_ldb.Ldb_delete,None,Ldb) +Ldb.rename = new_instancemethod(_ldb.Ldb_rename,None,Ldb) +Ldb.parse_control_strings = new_instancemethod(_ldb.Ldb_parse_control_strings,None,Ldb) +Ldb.add = new_instancemethod(_ldb.Ldb_add,None,Ldb) +Ldb.modify = new_instancemethod(_ldb.Ldb_modify,None,Ldb) +Ldb.get_config_basedn = new_instancemethod(_ldb.Ldb_get_config_basedn,None,Ldb) +Ldb.get_root_basedn = new_instancemethod(_ldb.Ldb_get_root_basedn,None,Ldb) +Ldb.get_schema_basedn = new_instancemethod(_ldb.Ldb_get_schema_basedn,None,Ldb) +Ldb.get_default_basedn = new_instancemethod(_ldb.Ldb_get_default_basedn,None,Ldb) +Ldb.schema_format_value = new_instancemethod(_ldb.Ldb_schema_format_value,None,Ldb) +Ldb.errstring = new_instancemethod(_ldb.Ldb_errstring,None,Ldb) +Ldb.set_create_perms = new_instancemethod(_ldb.Ldb_set_create_perms,None,Ldb) +Ldb.set_modules_dir = new_instancemethod(_ldb.Ldb_set_modules_dir,None,Ldb) +Ldb.set_debug = new_instancemethod(_ldb.Ldb_set_debug,None,Ldb) +Ldb.set_opaque = new_instancemethod(_ldb.Ldb_set_opaque,None,Ldb) +Ldb.get_opaque = new_instancemethod(_ldb.Ldb_get_opaque,None,Ldb) +Ldb.transaction_start = new_instancemethod(_ldb.Ldb_transaction_start,None,Ldb) +Ldb.transaction_commit = new_instancemethod(_ldb.Ldb_transaction_commit,None,Ldb) +Ldb.transaction_cancel = new_instancemethod(_ldb.Ldb_transaction_cancel,None,Ldb) +Ldb.schema_attribute_remove = new_instancemethod(_ldb.Ldb_schema_attribute_remove,None,Ldb) +Ldb.schema_attribute_add = new_instancemethod(_ldb.Ldb_schema_attribute_add,None,Ldb) +Ldb.setup_wellknown_attributes = new_instancemethod(_ldb.Ldb_setup_wellknown_attributes,None,Ldb) +Ldb.__contains__ = new_instancemethod(_ldb.Ldb___contains__,None,Ldb) +Ldb.parse_ldif = new_instancemethod(_ldb.Ldb_parse_ldif,None,Ldb) +Ldb.__repr__ = new_instancemethod(_ldb.Ldb___repr__,None,Ldb) +Ldb_swigregister = _ldb.Ldb_swigregister +Ldb_swigregister(Ldb) + + +def valid_attr_name(*args, **kwargs): + """ + S.valid_attr_name(name) -> bool + Check whether the supplied name is a valid attribute name. + """ + return _ldb.valid_attr_name(*args, **kwargs) + +def timestring(*args, **kwargs): + """ + S.timestring(int) -> string + Generate a LDAP time string from a UNIX timestamp + """ + return _ldb.timestring(*args, **kwargs) + +def string_to_time(*args, **kwargs): + """ + S.string_to_time(string) -> int + Parse a LDAP time string into a UNIX timestamp. + """ + return _ldb.string_to_time(*args, **kwargs) + +def register_module(*args, **kwargs): + """ + S.register_module(module) -> None + Register a LDB module. + """ + return _ldb.register_module(*args, **kwargs) +__docformat__ = "restructuredText" + + + diff --git a/ldb/ldb_ildap/config.mk b/ldb/ldb_ildap/config.mk new file mode 100644 index 00000000..6a1ef816 --- /dev/null +++ b/ldb/ldb_ildap/config.mk @@ -0,0 +1,13 @@ +################################################ +# Start MODULE ldb_ildap +[MODULE::ldb_ildap] +SUBSYSTEM = LIBLDB +CFLAGS = -I$(ldbsrcdir)/include +PRIVATE_DEPENDENCIES = LIBTALLOC LIBCLI_LDAP CREDENTIALS +INIT_FUNCTION = LDB_BACKEND(ldapi),LDB_BACKEND(ldaps),LDB_BACKEND(ldap) +ALIASES = ldapi ldaps ldap +# End MODULE ldb_ildap +################################################ + +ldb_ildap_OBJ_FILES = $(ldbsrcdir)/ldb_ildap/ldb_ildap.o + diff --git a/ldb/ldb_ildap/ldb_ildap.c b/ldb/ldb_ildap/ldb_ildap.c new file mode 100644 index 00000000..5ad671ea --- /dev/null +++ b/ldb/ldb_ildap/ldb_ildap.c @@ -0,0 +1,843 @@ +/* + ldb database library - ildap backend + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Simo Sorce 2008 + + ** 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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_includes.h" + +#include "lib/events/events.h" +#include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_client.h" +#include "auth/auth.h" +#include "auth/credentials/credentials.h" +#include "param/param.h" + +struct ildb_private { + struct ldap_connection *ldap; + struct ldb_module *module; +}; + +struct ildb_context { + struct ildb_private *ildb; + struct ldb_handle *handle; + 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) +{ + TALLOC_CTX *mem_ctx = talloc_new(ildb); + if (NT_STATUS_IS_OK(status)) { + return LDB_SUCCESS; + } + if (!mem_ctx) { + ldb_oom(ildb->module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + ldb_set_errstring(ildb->module->ldb, + ldap_errstr(ildb->ldap, mem_ctx, status)); + talloc_free(mem_ctx); + 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 ildb_context *ac; + struct ldb_handle *handle; + + ac = talloc_get_type(private_data, struct ildb_context); + handle = ac->handle; + + ac = talloc_get_type(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 ildb_context *ac; + struct ldb_handle *handle; + struct ildb_private *ildb; + NTSTATUS status; + int i; + + ac = talloc_get_type(req->async.private_data, struct ildb_context); + ildb = ac->ildb; + handle = ac->handle; + 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->ildb->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->ildb->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->ildb->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->ildb->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->ildb->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_new(ares->message, ac->ildb->module->ldb, search->dn); + if ( ! ldb_dn_validate(ares->message->dn)) { + 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->ildb->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 ildb_context *init_ildb_handle(struct ildb_private *ildb, + struct ldb_request *req) +{ + struct ildb_context *ildb_ac; + struct ldb_handle *h; + + h = talloc_zero(req, struct ldb_handle); + if (h == NULL) { + ldb_set_errstring(ildb->module->ldb, "Out of Memory"); + return NULL; + } + + h->module = ildb->module; + + ildb_ac = talloc(h, struct ildb_context); + if (ildb_ac == NULL) { + ldb_set_errstring(ildb->module->ldb, "Out of Memory"); + talloc_free(h); + return NULL; + } + + h->private_data = ildb_ac; + + h->state = LDB_ASYNC_INIT; + h->status = LDB_SUCCESS; + + ildb_ac->ildb = ildb; + ildb_ac->handle = h; + ildb_ac->context = req->context; + ildb_ac->callback = req->callback; + + req->handle = h; + return ildb_ac; +} + +static int ildb_request_send(struct ildb_private *ildb, struct ldap_message *msg, struct ldb_request *r) +{ + struct ildb_context *ildb_ac = init_ildb_handle(ildb, r); + struct ldap_request *req; + + if (!ildb_ac) { + return LDB_ERR_OPERATIONS_ERROR; + } + + req = ldap_request_send(ildb->ldap, msg); + if (req == NULL) { + ldb_set_errstring(ildb->module->ldb, "async send request failed"); + return LDB_ERR_OPERATIONS_ERROR; + } + ildb_ac->req = talloc_steal(ildb_ac, req); + + if (!req->conn) { + ldb_set_errstring(ildb->module->ldb, "connection to remote LDAP server dropped?"); + return LDB_ERR_OPERATIONS_ERROR; + } + + talloc_free(req->time_event); + req->time_event = NULL; + if (r->timeout) { + req->time_event = event_add_timed(req->conn->event.event_ctx, ildb_ac, + timeval_current_ofs(r->timeout, 0), + ildb_request_timeout, ildb_ac); + } + + req->async.fn = ildb_callback; + req->async.private_data = ildb_ac; + + return LDB_SUCCESS; +} + +static int ildb_request_noop(struct ildb_private *ildb, struct ldb_request *req) +{ + struct ildb_context *ildb_ac = init_ildb_handle(ildb, req); + int ret = LDB_SUCCESS; + + if (!ildb_ac) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ildb_ac->callback) { + ret = ildb_ac->callback(ildb->module->ldb, ildb_ac->context, NULL); + } + ildb_ac->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(req); + 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_alloc_linearized(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(ildb, msg, req); +} + +/* + 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(ildb, req); + } + + msg = new_ldap_message(req); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->type = LDAP_TAG_AddRequest; + + msg->r.AddRequest.dn = ldb_dn_alloc_linearized(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(ildb, msg, req); +} + +/* + 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(ildb, req); + } + + msg = new_ldap_message(req); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->type = LDAP_TAG_ModifyRequest; + + msg->r.ModifyRequest.dn = ldb_dn_alloc_linearized(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(ildb, msg, req); +} + +/* + 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(ildb, req); + } + + msg = new_ldap_message(req); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->type = LDAP_TAG_DelRequest; + + msg->r.DelRequest.dn = ldb_dn_alloc_linearized(msg, req->op.del.dn); + if (msg->r.DelRequest.dn == NULL) { + talloc_free(msg); + return LDB_ERR_INVALID_DN_SYNTAX; + } + + return ildb_request_send(ildb, msg, req); +} + +/* + 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(ildb, req); + } + + msg = new_ldap_message(req); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->type = LDAP_TAG_ModifyDNRequest; + msg->r.ModifyDNRequest.dn = ldb_dn_alloc_linearized(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", + ldb_dn_get_rdn_name(req->op.rename.newdn), + ldb_dn_escape_value(msg, *ldb_dn_get_rdn_val(req->op.rename.newdn))); + if (msg->r.ModifyDNRequest.newrdn == NULL) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->r.ModifyDNRequest.newsuperior = + ldb_dn_alloc_linearized(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(ildb, msg, req); +} + +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 ldb_module *module; + struct ildb_private *ildb; + NTSTATUS status; + struct cli_credentials *creds; + struct event_context *event_ctx; + + module = talloc(ldb, struct ldb_module); + if (!module) { + ldb_oom(ldb); + return -1; + } + talloc_set_name_const(module, "ldb_ildap backend"); + module->ldb = ldb; + module->prev = module->next = NULL; + module->private_data = NULL; + module->ops = &ildb_ops; + + ildb = talloc(module, struct ildb_private); + if (!ildb) { + ldb_oom(ldb); + goto failed; + } + module->private_data = ildb; + ildb->module = module; + + event_ctx = ldb_get_event_context(ldb); + + ildb->ldap = ldap4_new_connection(ildb, ldb_get_opaque(ldb, "loadparm"), + event_ctx); + 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, module, status)); + goto failed; + } + + /* 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, module, status)); + goto failed; + } + } else { + status = ldap_bind_sasl(ildb->ldap, creds, ldb_get_opaque(ldb, "loadparm")); + if (!NT_STATUS_IS_OK(status)) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n", + ldap_errstr(ildb->ldap, module, status)); + goto failed; + } + } + } + + *_module = module; + return 0; + +failed: + talloc_free(module); + return -1; +} + +_PUBLIC_ const struct ldb_backend_ops ldb_ldap_backend_ops = { + .name = "ldap", + .connect_fn = ildb_connect +}; + +_PUBLIC_ const struct ldb_backend_ops ldb_ldapi_backend_ops = { + .name = "ldapi", + .connect_fn = ildb_connect +}; + +_PUBLIC_ const struct ldb_backend_ops ldb_ldaps_backend_ops = { + .name = "ldaps", + .connect_fn = ildb_connect +}; + diff --git a/ldb/ldb_ldap/ldb_ldap.c b/ldb/ldb_ldap/ldb_ldap.c new file mode 100644 index 00000000..a4534c54 --- /dev/null +++ b/ldb/ldb_ldap/ldb_ldap.c @@ -0,0 +1,842 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_includes.h" + +#define LDAP_DEPRECATED 1 +#include <ldap.h> + +struct lldb_private { + LDAP *ldap; + struct ldb_module *module; +}; + +struct lldb_context { + struct lldb_private *lldb; + struct ldb_handle *handle; + 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 lldb_context *init_lldb_handle(struct lldb_private *lldb, struct ldb_request *req) +{ + struct lldb_context *ac; + struct ldb_handle *h; + + h = talloc_zero(req, struct ldb_handle); + if (h == NULL) { + ldb_set_errstring(lldb->module->ldb, "Out of Memory"); + return NULL; + } + + h->module = lldb->module; + + ac = talloc(h, struct lldb_context); + if (ac == NULL) { + ldb_set_errstring(lldb->module->ldb, "Out of Memory"); + talloc_free(h); + return NULL; + } + + h->private_data = ac; + + h->state = LDB_ASYNC_INIT; + h->status = LDB_SUCCESS; + + ac->lldb = lldb; + ac->handle = h; + ac->context = req->context; + ac->callback = req->callback; + ac->timeout = req->timeout; + ac->starttime = req->starttime; + ac->msgid = 0; + + req->handle = h; + return ac; +} +/* + 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++) { + /* we have to ensure this is null terminated so that + ldb_msg_find_attr_as_string() can work */ + el->values[i].data = talloc_size(el->values, bval[i]->bv_len+1); + if (!el->values[i].data) { + errno = ENOMEM; + return -1; + } + memcpy(el->values[i].data, bval[i]->bv_val, bval[i]->bv_len); + el->values[i].data[bval[i]->bv_len] = 0; + 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"); + } + + lldb_ac = init_lldb_handle(lldb, req); + if (lldb_ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + search_base = ldb_dn_alloc_linearized(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; + } + + lldb_ac = init_lldb_handle(lldb, req); + if (lldb_ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + mods = lldb_msg_to_mods(lldb_ac, req->op.add.message, 0); + if (mods == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + dn = ldb_dn_alloc_linearized(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; + } + + lldb_ac = init_lldb_handle(lldb, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + mods = lldb_msg_to_mods(lldb_ac, req->op.mod.message, 1); + if (mods == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + dn = ldb_dn_alloc_linearized(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; + } + + lldb_ac = init_lldb_handle(lldb, req); + if (lldb_ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + dnstr = ldb_dn_alloc_linearized(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; + } + + lldb_ac = init_lldb_handle(lldb, req); + if (lldb_ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + old_dn = ldb_dn_alloc_linearized(lldb_ac, req->op.rename.olddn); + if (old_dn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + newrdn = talloc_asprintf(lldb_ac, "%s=%s", + ldb_dn_get_rdn_name(req->op.rename.newdn), + ldb_dn_escape_value(lldb, *(ldb_dn_get_rdn_val(req->op.rename.newdn)))); + if (!newrdn) { + return LDB_ERR_OPERATIONS_ERROR; + } + + parentdn = ldb_dn_alloc_linearized(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 lldb_context *ac, LDAPMessage *result) +{ + struct ldb_handle *handle = ac->handle; + struct lldb_private *lldb = ac->lldb; + 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); + + handle->status = 0; + + 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_new(ares->message, ac->lldb->module->ldb, dn); + if ( ! ldb_dn_validate(ares->message->dn)) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto error; + } + ldap_memfree(dn); + + ares->message->num_elements = 0; + ares->message->elements = 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->lldb->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->lldb->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->lldb->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->lldb->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->lldb->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 && *errmsgp) { + ldb_set_errstring(ac->lldb->module->ldb, errmsgp); + } else if (handle->status) { + ldb_set_errstring(ac->lldb->module->ldb, ldap_err2string(handle->status)); + } + if (errmsgp) { + ldap_memfree(errmsgp); + } + if (referralsp) ldap_value_free(referralsp); + if (serverctrlsp) ldap_controls_free(serverctrlsp); + + ldap_msgfree(result); + return lldb_ldap_to_ldb(handle->status); + +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 = ac->lldb; + 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(ac, 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(ac, 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 ldb_module *module; + struct lldb_private *lldb; + int version = 3; + int ret; + + module = talloc(ldb, struct ldb_module); + if (module == NULL) { + ldb_oom(ldb); + talloc_free(lldb); + return -1; + } + talloc_set_name_const(module, "ldb_ldap backend"); + module->ldb = ldb; + module->prev = module->next = NULL; + module->private_data = NULL; + module->ops = &lldb_ops; + + lldb = talloc(module, struct lldb_private); + if (!lldb) { + ldb_oom(ldb); + goto failed; + } + module->private_data = lldb; + lldb->module = module; + 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 = module; + return 0; + +failed: + talloc_free(module); + return -1; +} + +const struct ldb_backend_ops ldb_ldap_backend_ops = { + .name = "ldap", + .connect_fn = lldb_connect +}; + +const struct ldb_backend_ops ldb_ldapi_backend_ops = { + .name = "ldapi", + .connect_fn = lldb_connect +}; + +const struct ldb_backend_ops ldb_ldaps_backend_ops = { + .name = "ldaps", + .connect_fn = lldb_connect +}; diff --git a/ldb/ldb_map/ldb_map.c b/ldb/ldb_map/ldb_map.c new file mode 100644 index 00000000..b3f639dc --- /dev/null +++ b/ldb/ldb_map/ldb_map.c @@ -0,0 +1,1380 @@ +/* + ldb database mapping module + + Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Martin Kuehl <mkhl@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>. + +*/ + +/* + * 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 "ldb_includes.h" + +#include "ldb_map.h" +#include "ldb_map_private.h" + +#ifndef _PUBLIC_ +#define _PUBLIC_ +#endif + +/* 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; +} + +/* 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, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + new_dn = ldb_dn_copy(mem_ctx, dn); + if ( ! ldb_dn_validate(new_dn)) { + talloc_free(new_dn); + return NULL; + } + + /* may be we don't need to rebase at all */ + if ( ! data->remote_base_dn || ! data->local_base_dn) { + return new_dn; + } + + if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->remote_base_dn))) { + talloc_free(new_dn); + return NULL; + } + + if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) { + talloc_free(new_dn); + return NULL; + } + + return new_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, struct ldb_dn *dn) +{ + struct ldb_dn *new_dn; + + new_dn = ldb_dn_copy(mem_ctx, dn); + if ( ! ldb_dn_validate(new_dn)) { + talloc_free(new_dn); + return NULL; + } + + /* may be we don't need to rebase at all */ + if ( ! data->remote_base_dn || ! data->local_base_dn) { + return new_dn; + } + + if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) { + talloc_free(new_dn); + return NULL; + } + + if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) { + talloc_free(new_dn); + return NULL; + } + + return new_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, const 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, const 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, 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(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, struct ldb_dn *dn) +{ + const struct ldb_map_context *data = map_get_context(module); + struct ldb_dn *newdn; + const struct ldb_map_attribute *map; + enum ldb_map_attr_type map_type; + const char *name; + struct ldb_val value; + int i, ret; + + 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 < ldb_dn_get_comp_num(newdn); i++) { + map = map_attr_find_local(data, ldb_dn_get_component_name(dn, i)); + + /* 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", ldb_dn_get_component_name(dn, i)); + 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", ldb_dn_get_component_name(dn, i)); + goto failed; + } + /* fall through */ + case MAP_KEEP: + case MAP_RENAME: + name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i)); + if (name == NULL) goto failed; + + value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i)); + if (value.data == NULL) goto failed; + + ret = ldb_dn_set_component(newdn, i, name, value); + if (ret != LDB_SUCCESS) { + goto failed; + } + + 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, struct ldb_dn *dn) +{ + const struct ldb_map_context *data = map_get_context(module); + struct ldb_dn *newdn; + const struct ldb_map_attribute *map; + enum ldb_map_attr_type map_type; + const char *name; + struct ldb_val value; + int i, ret; + + 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 < ldb_dn_get_comp_num(newdn); i++) { + map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i)); + + /* 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", ldb_dn_get_component_name(dn, i)); + 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", ldb_dn_get_component_name(dn, i)); + goto failed; + } + /* fall through */ + case MAP_KEEP: + case MAP_RENAME: + name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i)); + if (name == NULL) goto failed; + + value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i)); + if (value.data == NULL) goto failed; + + ret = ldb_dn_set_component(newdn, i, name, value); + if (ret != LDB_SUCCESS) { + goto failed; + } + + 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, 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_new(mem_ctx, module->ldb, (char *)val->data); + if (! ldb_dn_validate(dn)) { + newval.length = 0; + newval.data = NULL; + talloc_free(dn); + return newval; + } + newdn = ldb_dn_map_local(module, mem_ctx, dn); + talloc_free(dn); + + newval.length = 0; + newval.data = (uint8_t *)ldb_dn_alloc_linearized(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_new(mem_ctx, module->ldb, (char *)val->data); + if (! ldb_dn_validate(dn)) { + newval.length = 0; + newval.data = NULL; + talloc_free(dn); + return newval; + } + newdn = ldb_dn_map_remote(module, mem_ctx, dn); + talloc_free(dn); + + newval.length = 0; + newval.data = (uint8_t *)ldb_dn_alloc_linearized(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) +{ + const struct ldb_map_context *data = map_get_context(module); + 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, "objectClass"); + 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, data->add_objectclass) == 0) { + found_extensibleObject = true; + } + } + + if (!found_extensibleObject) { + val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass); + val.length = strlen((char *)val.data); + + /* Append additional objectClass data->add_objectclass */ + 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 *local_attr, const struct ldb_message *remote) +{ + const struct ldb_map_context *data = map_get_context(module); + struct ldb_message_element *el, *oc; + struct ldb_val val; + int i; + + /* Find old remote objectClass */ + oc = ldb_msg_find_element(remote, "objectClass"); + 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, local_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, data->add_objectclass); + val.length = strlen((char *)val.data); + + /* Remove last value if it was the string in data->add_objectclass (eg samba4top, 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; +} + +static const struct ldb_map_attribute objectclass_convert_map = { + .local_name = "objectClass", + .type = MAP_CONVERT, + .u = { + .convert = { + .remote_name = "objectClass", + .convert_local = map_objectclass_convert_local, + .convert_remote = map_objectclass_convert_remote, + }, + }, +}; + + +/* 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) +{ + + return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_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, 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, 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, struct ldb_dn *olddn, 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 = ldb_dn_copy(msg, olddn); + dn = ldb_dn_alloc_linearized(msg, newdn); + if ( ! dn || ! ldb_dn_validate(msg->dn)) { + goto failed; + } + if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE, NULL) != 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. */ +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 + * ===================== */ + + +/* 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 = NULL, + } +}; + +static const struct ldb_map_attribute objectclass_attribute_map = { + .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, + }, + }, +}; + + +/* 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_new_fmt(data, module->ldb, "%s=%s", MAP_DN_NAME, name); + if ( ! ldb_dn_validate(dn)) { + 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); + talloc_free(res); + 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); + talloc_free(res); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + msg = res->msgs[0]; + data->local_base_dn = ldb_msg_find_attr_as_dn(module->ldb, data, msg, MAP_DN_FROM); + data->remote_base_dn = ldb_msg_find_attr_as_dn(module->ldb, 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+2); + 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++; + } + + if (data->add_objectclass) { + /* ObjectClass one is very last, if required */ + data->attribute_maps[last] = objectclass_attribute_map; + last++; + } else if (ocls) { + data->attribute_maps[last] = objectclass_convert_map; + 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; +} + +/* Initialize global private data. */ +_PUBLIC_ 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 *add_objectclass, + 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; + } + + data->context->add_objectclass = add_objectclass; + + /* 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; +} diff --git a/ldb/ldb_map/ldb_map.h b/ldb/ldb_map/ldb_map.h new file mode 100644 index 00000000..e40bb9cd --- /dev/null +++ b/ldb/ldb_map/ldb_map.h @@ -0,0 +1,173 @@ +/* + ldb database mapping module + + Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Martin Kuehl <mkhl@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>. + +*/ + +#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; + + /* ObjectClass (if any) to be added to remote attributes on add */ + const char *add_objectclass; + + /* struct ldb_context *mapped_ldb; */ + struct ldb_dn *local_base_dn; + 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 *add_objectclass, + const char *name); + +int map_add(struct ldb_module *module, struct ldb_request *req); +int map_search(struct ldb_module *module, struct ldb_request *req); +int map_rename(struct ldb_module *module, struct ldb_request *req); +int map_delete(struct ldb_module *module, struct ldb_request *req); +int map_modify(struct ldb_module *module, struct ldb_request *req); +int map_wait(struct ldb_handle *handle, enum ldb_wait_type type); + +#define LDB_MAP_OPS \ + .add = map_add, \ + .modify = map_modify, \ + .del = map_delete, \ + .rename = map_rename, \ + .search = map_search, \ + .wait = map_wait, + +#endif /* __LDB_MAP_H__ */ diff --git a/ldb/ldb_map/ldb_map_inbound.c b/ldb/ldb_map/ldb_map_inbound.c new file mode 100644 index 00000000..7fc3ac4e --- /dev/null +++ b/ldb/ldb_map/ldb_map_inbound.c @@ -0,0 +1,723 @@ +/* + ldb database mapping module + + Copyright (C) Jelmer Vernooij 2005 + Copyright (C) Martin Kuehl <mkhl@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ldb_includes.h" + +#include "ldb_map.h" +#include "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_alloc_linearized(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 */ + if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD, NULL) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + dn = ldb_dn_alloc_linearized(msg, ac->remote_req->op.mod.message->dn); + 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/ldb/ldb_map/ldb_map_outbound.c b/ldb/ldb_map/ldb_map_outbound.c new file mode 100644 index 00000000..fbc097f3 --- /dev/null +++ b/ldb/ldb_map/ldb_map_outbound.c @@ -0,0 +1,1289 @@ +/* + 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 + + ** 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 3 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, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ldb_includes.h" + +#include "ldb_map.h" +#include "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 *mem_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs) +{ + *local_attrs = map_attrs_select_local(module, mem_ctx, attrs); + *remote_attrs = map_attrs_collect_remote(module, mem_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, &old) != 0) { + return -1; + } + talloc_free(discard_const_p(char, old->name)); + } + + /* copy new element */ + *old = *el; + + /* and make sure we reference the contents */ + if (!talloc_reference(msg->elements, el->name)) { + return -1; + } + if (!talloc_reference(msg->elements, el->values)) { + return -1; + } + + 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 char *attr_name, + 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->values = talloc_array(el, struct ldb_val, old->num_values); + if (el->values == NULL) { + talloc_free(el); + map_oom(module); + return NULL; + } + + el->name = talloc_strdup(el, attr_name); + if (el->name == NULL) { + talloc_free(el); + map_oom(module); + return NULL; + } + + for (i = 0; i < old->num_values; i++) { + el->values[i] = ldb_val_map_remote(module, el->values, map, &old->values[i]); + /* Conversions might fail, in which case bail */ + if (!el->values[i].data) { + talloc_free(el); + return NULL; + } + el->num_values++; + } + + 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_map_context *data = map_get_context(module); + const struct ldb_map_attribute *map; + struct ldb_message_element *old, *el=NULL; + const char *remote_name = NULL; + + /* We handle wildcards in ldb_msg_el_merge_wildcard */ + if (ldb_attr_cmp(attr_name, "*") == 0) { + return LDB_SUCCESS; + } + + map = map_attr_find_local(data, attr_name); + + /* Unknown attribute in remote message: + * skip, attribute was probably auto-generated */ + if (map == NULL) { + return LDB_SUCCESS; + } + + switch (map->type) { + case MAP_IGNORE: + break; + case MAP_CONVERT: + remote_name = map->u.convert.remote_name; + break; + case MAP_KEEP: + remote_name = attr_name; + break; + case MAP_RENAME: + remote_name = map->u.rename.remote_name; + break; + case MAP_GENERATE: + break; + } + + switch (map->type) { + case MAP_IGNORE: + return LDB_SUCCESS; + + 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", + attr_name); + return LDB_SUCCESS; + } + /* fall through */ + case MAP_KEEP: + case MAP_RENAME: + old = ldb_msg_find_element(remote, remote_name); + if (old) { + el = ldb_msg_el_map_remote(module, local, map, attr_name, old); + } else { + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + 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", + attr_name); + return LDB_SUCCESS; + } + + el = map->u.generate.generate_local(module, local, attr_name, remote); + if (!el) { + /* Generation failure is probably due to lack of source attributes */ + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + break; + } + + if (el == NULL) { + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + return ldb_msg_replace(local, el); +} + +/* Handle wildcard parts of merging a remote message element into a local message. */ +static int ldb_msg_el_merge_wildcard(struct ldb_module *module, struct ldb_message *local, + struct ldb_message *remote) +{ + const struct ldb_map_context *data = map_get_context(module); + const struct ldb_map_attribute *map = map_attr_find_local(data, "*"); + struct ldb_message_element *el=NULL; + int i, ret; + + /* Perhaps we have a mapping for "*" */ + if (map && map->type == MAP_KEEP) { + /* We copy everything over, and hope that anything with a + more specific rule is overwritten */ + for (i = 0; i < remote->num_elements; i++) { + el = ldb_msg_el_map_remote(module, local, map, remote->elements[i].name, + &remote->elements[i]); + if (el == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_msg_replace(local, el); + if (ret) { + return ret; + } + } + } + + /* Now walk the list of possible mappings, and apply each */ + for (i = 0; data->attribute_maps[i].local_name; i++) { + ret = ldb_msg_el_merge(module, local, remote, + data->attribute_maps[i].local_name); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { + continue; + } else if (ret) { + return ret; + } else { + continue; + } + } + + return LDB_SUCCESS; +} + +/* 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 LDB_SUCCESS; +} + +/* Merge a local and a remote message into a single local one. */ +static int ldb_msg_merge_remote(struct map_context *ac, struct ldb_message *local, + struct ldb_message *remote) +{ + int i, ret; + const char * const *attrs = ac->all_attrs; + if (!attrs) { + ret = ldb_msg_el_merge_wildcard(ac->module, local, remote); + if (ret) { + return ret; + } + } + + for (i = 0; attrs && attrs[i]; i++) { + if (ldb_attr_cmp(attrs[i], "*") == 0) { + ret = ldb_msg_el_merge_wildcard(ac->module, local, remote); + if (ret) { + return ret; + } + break; + } + } + + /* Try to map each attribute back; + * Add to local message is possible, + * Overwrite old local attribute if necessary */ + for (i = 0; attrs && attrs[i]; i++) { + ret = ldb_msg_el_merge(ac->module, local, remote, + attrs[i]); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { + } else if (ret) { + return ret; + } + } + + return LDB_SUCCESS; +} + +/* Mapping search results + * ====================== */ + +/* Map a search result back into the local partition. */ +static int map_reply_remote(struct map_context *ac, 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(ac->module); + return -1; + } + + /* Merge remote message into new message */ + ret = ldb_msg_merge_remote(ac, msg, ares->message); + if (ret) { + talloc_free(msg); + return ret; + } + + /* Create corresponding local DN */ + dn = ldb_dn_map_rebase_remote(ac->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, struct map_context *ac, + const char * const *search_attrs, + const struct ldb_parse_tree *tree) +{ + void *tmp_ctx; + const char **tree_attrs; + const char **remote_attrs; + const char **local_attrs; + int ret; + + /* There is no tree, just partition the searched attributes */ + if (tree == NULL) { + ret = map_attrs_partition(module, ac, + &local_attrs, &remote_attrs, search_attrs); + if (ret == 0) { + ac->local_attrs = local_attrs; + ac->remote_attrs = remote_attrs; + ac->all_attrs = search_attrs; + } + return ret; + } + + /* Create context for temporary memory */ + tmp_ctx = talloc_new(ac); + 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, ac, &local_attrs, + &remote_attrs, tree_attrs); + + if (ret == 0) { + ac->local_attrs = local_attrs; + ac->remote_attrs = remote_attrs; + talloc_steal(ac, tree_attrs); + ac->all_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_get_linearized(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; ) { + struct ldb_message_element *el = &ares->message->elements[i]; + if (!ldb_attr_in_list(req->op.search.attrs, el->name)) { + ldb_msg_remove_element(ares->message, el); + } else { + i++; + } + } + } + + 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, 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; + int ret; + + const char *wildcard[] = { "*", NULL }; + const char * const *attrs; + + if (!module->private_data) /* if we're not yet initialized, go to the next module */ + return ldb_next_request(module, req); + + /* 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, + attrs, req->op.search.tree); + if (ret) { + goto failed; + } + + ac->search_reqs[0]->op.search.attrs = ac->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/ldb/ldb_map/ldb_map_private.h b/ldb/ldb_map/ldb_map_private.h new file mode 100644 index 00000000..58a9f270 --- /dev/null +++ b/ldb/ldb_map/ldb_map_private.h @@ -0,0 +1,110 @@ + +/* 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; + + struct ldb_dn *local_dn; + const struct ldb_parse_tree *local_tree; + const char * const *local_attrs; + const char * const *remote_attrs; + const char * const *all_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, 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, const struct ldb_val *val); +struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val); + +struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn); +struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn); +struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn); + +struct ldb_request *map_search_base_req(struct map_context *ac, 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, struct ldb_dn *dn); +struct ldb_request *map_build_fixup_req(struct map_context *ac, struct ldb_dn *olddn, 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_modify_do_remote(struct ldb_handle *handle); +int map_modify_do_local(struct ldb_handle *handle); + +int map_delete_do_remote(struct ldb_handle *handle); +int map_delete_do_local(struct ldb_handle *handle); + +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); diff --git a/ldb/ldb_sqlite3/README b/ldb/ldb_sqlite3/README new file mode 100644 index 00000000..6cda0a77 --- /dev/null +++ b/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/ldb/ldb_sqlite3/base160.c b/ldb/ldb_sqlite3/base160.c new file mode 100644 index 00000000..423e2b68 --- /dev/null +++ b/ldb/ldb_sqlite3/base160.c @@ -0,0 +1,154 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + + +/* + * 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/ldb/ldb_sqlite3/ldb_sqlite3.c b/ldb/ldb_sqlite3/ldb_sqlite3.c new file mode 100644 index 00000000..a0e63c8d --- /dev/null +++ b/ldb/ldb_sqlite3/ldb_sqlite3.c @@ -0,0 +1,1876 @@ +/* + ldb database library + + Copyright (C) Derrell Lipman 2005 + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb sqlite3 backend + * + * Description: core files for SQLITE3 backend + * + * Author: Derrell Lipman (based on Andrew Tridgell's LDAP backend) + */ + +#include "ldb_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, + struct ldb_request *req) +{ + 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 = req->context; + ac->callback = req->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_schema_attribute *a; + 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; + a = ldb_schema_attribute_by_name(module->ldb, attr); + + /* Get a canonicalised copy of the data */ + a->syntax->canonicalise_fn(module->ldb, mem_ctx, &(t->u.equality.value), &value); + if (value.data == NULL) { + return NULL; + } + + if (strcasecmp(t->u.equality.attr, "dn") == 0) { + /* DN query is a special ldb case */ + const char *cdn = ldb_dn_get_casefold( + ldb_dn_new(mem_ctx, 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; + a = ldb_schema_attribute_by_name(module->ldb, attr); + + subval.data = (void *)wild_card_string; + subval.length = strlen(wild_card_string) + 1; + + /* Get a canonicalised copy of the data */ + a->syntax->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; + a = ldb_schema_attribute_by_name(module->ldb, attr); + + /* Get a canonicalised copy of the data */ + a->syntax->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; + a = ldb_schema_attribute_by_name(module->ldb, attr); + + /* Get a canonicalised copy of the data */ + a->syntax->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; + a = ldb_schema_attribute_by_name(module->ldb, attr); + + /* Get a canonicalised copy of the data */ + a->syntax->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_schema_attribute *a; + struct ldb_val valX; + struct ldb_val valY; + int ret; + + switch (func[0]) { + /* greater */ + case '>': /* >= */ + a = ldb_schema_attribute_by_name(ldb, attr); + valX.data = (void *)cmp; + valX.length = strlen(cmp); + valY.data = (void *)val; + valY.length = strlen(val); + ret = a->syntax->comparison_fn(ldb, ldb, &valY, &valX); + if (ret >= 0) + sqlite3_result_int(ctx, 1); + else + sqlite3_result_int(ctx, 0); + return; + + /* lesser */ + case '<': /* <= */ + a = ldb_schema_attribute_by_name(ldb, attr); + valX.data = (void *)cmp; + valX.length = strlen(cmp); + valY.data = (void *)val; + valY.length = strlen(val); + ret = a->syntax->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_new(msg, ac->module->ldb, 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, 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_alloc_casefold(local_ctx, 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 + */ + +/* search for matching records, by tree */ +int lsql_search(struct ldb_module *module, struct ldb_request *req) +{ + 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; + + req->handle = init_handle(lsqlite3, module, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + lsql_ac = talloc_get_type(req->handle->private_data, struct lsql_context); + + if ((( ! ldb_dn_is_valid(req->op.search.base)) || ldb_dn_is_null(req->op.search.base)) && + (req->op.search.scope == LDB_SCOPE_BASE || req->op.search.scope == LDB_SCOPE_ONELEVEL)) + return LDB_ERR_OPERATIONS_ERROR; + + if (req->op.search.base) { + norm_basedn = ldb_dn_alloc_casefold(lsql_ac, req->op.search.base); + if (norm_basedn == NULL) { + ret = LDB_ERR_INVALID_DN_SYNTAX; + goto failed; + } + } else norm_basedn = talloc_strdup(lsql_ac, ""); + + /* Convert filter into a series of SQL conditions (constraints) */ + sqlfilter = parsetree_to_sql(module, lsql_ac, req->op.search.tree); + + switch(req->op.search.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 = req->op.search.attrs; + lsql_ac->ares = NULL; + + req->handle->state = LDB_ASYNC_PENDING; + + ret = sqlite3_exec(lsqlite3->sqlite, query, lsqlite3_search_callback, req->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; + + req->handle->status = lsql_ac->callback(module->ldb, lsql_ac->context, lsql_ac->ares); + if (req->handle->status != LDB_SUCCESS) + goto failed; + } + + req->handle->state = LDB_ASYNC_DONE; + + return LDB_SUCCESS; + +failed: + return LDB_ERR_OPERATIONS_ERROR; +} + +/* add a record */ +static int lsql_add(struct ldb_module *module, struct ldb_request *req) +{ + struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private); + struct lsql_context *lsql_ac; + struct ldb_message *msg = req->op.add.message; + long long eid; + char *dn, *ndn; + char *errmsg; + char *query; + int i; + int ret = LDB_SUCCESS; + + req->handle = init_handle(lsqlite3, module, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + lsql_ac = talloc_get_type(req->handle->private_data, struct lsql_context); + req->handle->state = LDB_ASYNC_DONE; + req->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_new(local_ctx, module->ldb, "@INDEXLIST"); + if (ldb_dn_compare(module->ldb, msg->dn, c) == 0) { +#warning "should we handle indexes somehow ?" + ret = LDB_ERR_UNWILLING_TO_PERFORM; + goto done; + } +*/ + /* Others return an error */ + ret = LDB_ERR_UNWILLING_TO_PERFORM; + goto done; + } + + /* create linearized and normalized dns */ + dn = ldb_dn_alloc_linearized(lsql_ac, msg->dn); + ndn = ldb_dn_alloc_casefold(lsql_ac, msg->dn); + if (dn == NULL || ndn == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + 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 done; + } + + 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 done; + } + + eid = lsqlite3_get_eid_ndn(lsqlite3->sqlite, lsql_ac, ndn); + if (eid == -1) { + ret = LDB_ERR_OTHER; + goto done; + } + + for (i = 0; i < msg->num_elements; i++) { + const struct ldb_message_element *el = &msg->elements[i]; + const struct ldb_schema_attribute *a; + 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 done; + } + + a = ldb_schema_attribute_by_name(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 */ + a->syntax->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value); + if (value.data == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + 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 done; + } + + 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 done; + } + } + } + + if (lsql_ac->callback) { + req->handle->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL); + } + +done: + req->handle->state = LDB_ASYNC_DONE; + return ret; +} + +/* modify a record */ +static int lsql_modify(struct ldb_module *module, struct ldb_request *req) +{ + struct lsqlite3_private *lsqlite3 = talloc_get_type(module->private_data, struct lsqlite3_private); + struct lsql_context *lsql_ac; + struct ldb_message *msg = req->op.mod.message; + long long eid; + char *errmsg; + int i; + int ret = LDB_SUCCESS; + + req->handle = init_handle(lsqlite3, module, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + lsql_ac = talloc_get_type(req->handle->private_data, struct lsql_context); + req->handle->state = LDB_ASYNC_DONE; + req->handle->status = LDB_SUCCESS; + + /* See if this is an ltdb special */ + if (ldb_dn_is_special(msg->dn)) { + /* Others return an error */ + ret = LDB_ERR_UNWILLING_TO_PERFORM; + goto done; + } + + eid = lsqlite3_get_eid(module, msg->dn); + if (eid == -1) { + ret = LDB_ERR_OTHER; + goto done; + } + + for (i = 0; i < msg->num_elements; i++) { + const struct ldb_message_element *el = &msg->elements[i]; + const struct ldb_schema_attribute *a; + 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 done; + } + + a = ldb_schema_attribute_by_name(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 done; + } + + 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 done; + } + + /* 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 */ + a->syntax->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value); + if (value.data == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + 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 done; + } + + 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 done; + } + } + + 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 done; + } + + 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 done; + } + } + + /* 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 */ + a->syntax->canonicalise_fn(module->ldb, lsql_ac, &(el->values[j]), &value); + if (value.data == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + 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 done; + } + + 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 done; + } + } + + break; + } + } + + if (lsql_ac->callback) { + req->handle->status = lsql_ac->callback(module->ldb, lsql_ac->context, NULL); + } + +done: + req->handle->state = LDB_ASYNC_DONE; + return ret; +} + +/* delete a record */ +static int lsql_delete(struct ldb_module *module, struct ldb_request *req) +{ + 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_SUCCESS; + + + req->handle = init_handle(lsqlite3, module, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + lsql_ac = talloc_get_type(req->handle->private_data, struct lsql_context); + req->handle->state = LDB_ASYNC_DONE; + req->handle->status = LDB_SUCCESS; + + eid = lsqlite3_get_eid(module, req->op.del.dn); + if (eid == -1) { + goto done; + } + + 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 done; + } + + ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg); + if (ret != SQLITE_OK) { + if (errmsg) { + ldb_set_errstring(module->ldb, errmsg); + free(errmsg); + } + req->handle->status = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + if (lsql_ac->callback) { + ret = lsql_ac->callback(module->ldb, lsql_ac->context, NULL); + } + +done: + req->handle->state = LDB_ASYNC_DONE; + return ret; +} + +/* rename a record */ +static int lsql_rename(struct ldb_module *module, struct ldb_request *req) +{ + 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_SUCCESS; + + req->handle = init_handle(lsqlite3, module, req); + if (req->handle == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + lsql_ac = talloc_get_type(req->handle->private_data, struct lsql_context); + req->handle->state = LDB_ASYNC_DONE; + req->handle->status = LDB_SUCCESS; + + /* create linearized and normalized dns */ + old_cdn = ldb_dn_alloc_casefold(lsql_ac, req->op.rename.olddn); + new_cdn = ldb_dn_alloc_casefold(lsql_ac, req->op.rename.newdn); + new_dn = ldb_dn_alloc_linearized(lsql_ac, req->op.rename.newdn); + if (old_cdn == NULL || new_cdn == NULL || new_dn == NULL) { + goto done; + } + + /* 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 done; + } + + /* 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 done; + } + + if (lsql_ac->callback) { + ret = lsql_ac->callback(module->ldb, lsql_ac->context, NULL); + } + +done: + req->handle->state = LDB_ASYNC_DONE; + 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 int destructor(struct lsqlite3_private *lsqlite3) +{ + if (lsqlite3->sqlite) { + sqlite3_close(lsqlite3->sqlite); + } + return 0; +} + +static int lsql_request(struct ldb_module *module, struct ldb_request *req) +{ + return LDB_ERR_OPERATIONS_ERROR; +} + +static int lsql_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + return handle->status; +} + +/* + * Table of operations for the sqlite3 backend + */ +static const struct ldb_module_ops lsqlite3_ops = { + .name = "sqlite", + .search = lsql_search, + .add = lsql_add, + .modify = lsql_modify, + .del = lsql_delete, + .rename = lsql_rename, + .request = lsql_request, + .start_transaction = lsql_start_trans, + .end_transaction = lsql_end_trans, + .del_transaction = lsql_del_trans, + .wait = lsql_wait, +}; + +/* + * 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, "sqlite3://", 10) != 0) { + return SQLITE_MISUSE; + } + + /* Update pointer to just after the protocol indicator */ + url += 10; + + /* 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; +} + +/* + * 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; + } + talloc_set_name_const(*module, "ldb_sqlite3 backend"); + (*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; +} + +const struct ldb_backend_ops ldb_sqlite3_backend_ops = { + .name = "sqlite3", + .connect_fn = lsqlite3_connect +}; diff --git a/ldb/ldb_sqlite3/schema b/ldb/ldb_sqlite3/schema new file mode 100644 index 00000000..ab7c5cc4 --- /dev/null +++ b/ldb/ldb_sqlite3/schema @@ -0,0 +1,328 @@ + -- ------------------------------------------------------ + + 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' + diff --git a/ldb/ldb_sqlite3/trees.ps b/ldb/ldb_sqlite3/trees.ps new file mode 100644 index 00000000..433a0648 --- /dev/null +++ b/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/ldb/ldb_tdb/ldb_cache.c b/ldb/ldb_tdb/ldb_cache.c new file mode 100644 index 00000000..2576e2c7 --- /dev/null +++ b/ldb/ldb_tdb/ldb_cache.c @@ -0,0 +1,458 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb tdb cache functions + * + * Description: cache special records in a ldb/tdb + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" + +#include "ldb_tdb.h" + +#define LTDB_FLAG_CASE_INSENSITIVE (1<<0) +#define LTDB_FLAG_INTEGER (1<<1) +#define LTDB_FLAG_HIDDEN (1<<2) + +/* 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 = (struct ltdb_private *)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++) { + ldb_schema_attribute_remove(module->ldb, msg->elements[i].name); + } + + 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 = (struct ltdb_private *)module->private_data; + struct ldb_message *msg = ltdb->cache->attributes; + struct ldb_dn *dn; + int i, r; + + dn = ldb_dn_new(module, module->ldb, LTDB_ATTRIBUTES); + if (dn == NULL) goto failed; + + r = ltdb_search_dn1(module, dn, msg); + talloc_free(dn); + if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { + goto failed; + } + if (r == LDB_ERR_NO_SUCH_OBJECT) { + return 0; + } + /* 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_schema_syntax *s; + + 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; + } + + s = ldb_standard_syntax_by_name(module->ldb, syntax); + if (s == NULL) { + ldb_debug(module->ldb, LDB_DEBUG_ERROR, + "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n", + syntax, msg->elements[i].name); + goto failed; + } + + flags |= LDB_ATTR_FLAG_ALLOCATED; + if (ldb_schema_attribute_add_with_syntax(module->ldb, msg->elements[i].name, flags, s) != 0) { + goto failed; + } + } + + return 0; +failed: + return -1; +} + + +/* + initialise the baseinfo record +*/ +static int ltdb_baseinfo_init(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)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_new(msg, module->ldb, 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 LDB_ERR_OPERATIONS_ERROR; +} + +/* + free any cache records + */ +static void ltdb_cache_free(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)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_cache_free(module); + return ltdb_cache_load(module); +} + +/* + load the cache records +*/ +int ltdb_cache_load(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + struct ldb_dn *baseinfo_dn = NULL, *options_dn = NULL; + struct ldb_dn *indexlist_dn = NULL; + uint64_t seq; + struct ldb_message *baseinfo = NULL, *options = NULL; + int r; + + /* a very fast check to avoid extra database reads */ + if (ltdb->cache != NULL && + tdb_get_seqnum(ltdb->tdb) == ltdb->tdb_seqnum) { + return 0; + } + + 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->attributes = talloc_zero(ltdb->cache, struct ldb_message); + if (ltdb->cache->indexlist == NULL || + ltdb->cache->attributes == NULL) { + goto failed; + } + } + + baseinfo = talloc(ltdb->cache, struct ldb_message); + if (baseinfo == NULL) goto failed; + + baseinfo_dn = ldb_dn_new(module, module->ldb, LTDB_BASEINFO); + if (baseinfo_dn == NULL) goto failed; + + r= ltdb_search_dn1(module, baseinfo_dn, baseinfo); + if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { + goto failed; + } + + /* possibly initialise the baseinfo */ + if (r == LDB_ERR_NO_SUCH_OBJECT) { + if (ltdb_baseinfo_init(module) != LDB_SUCCESS) { + goto failed; + } + if (ltdb_search_dn1(module, baseinfo_dn, baseinfo) != LDB_SUCCESS) { + goto failed; + } + } + + ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb); + + /* 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_uint64(baseinfo, LTDB_SEQUENCE_NUMBER, 0); + if (seq == ltdb->sequence_number) { + goto done; + } + ltdb->sequence_number = seq; + + /* Read an interpret database options */ + options = talloc(ltdb->cache, struct ldb_message); + if (options == NULL) goto failed; + + options_dn = ldb_dn_new(options, module->ldb, LTDB_OPTIONS); + if (options_dn == NULL) goto failed; + + r= ltdb_search_dn1(module, options_dn, options); + if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { + goto failed; + } + + /* set flag for checking base DN on searches */ + if (r == LDB_SUCCESS) { + ltdb->check_base = ldb_msg_find_attr_as_bool(options, LTDB_CHECK_BASE, false); + } else { + ltdb->check_base = false; + } + + talloc_free(ltdb->cache->last_attribute.name); + memset(<db->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute)); + + ltdb_attributes_unload(module); + + talloc_free(ltdb->cache->indexlist); + + ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message); + ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message); + if (ltdb->cache->indexlist == NULL || + ltdb->cache->attributes == NULL) { + goto failed; + } + + indexlist_dn = ldb_dn_new(module, module->ldb, LTDB_INDEXLIST); + if (indexlist_dn == NULL) goto failed; + + r = ltdb_search_dn1(module, indexlist_dn, ltdb->cache->indexlist); + if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { + goto failed; + } + + if (ltdb_attributes_load(module) == -1) { + goto failed; + } + +done: + talloc_free(options); + talloc_free(baseinfo); + talloc_free(baseinfo_dn); + talloc_free(indexlist_dn); + return 0; + +failed: + talloc_free(options); + talloc_free(baseinfo); + 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 = (struct ltdb_private *)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 LDB_ERR_OPERATIONS_ERROR; + } + + s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1); + if (!s) { + errno = ENOMEM; + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->num_elements = ARRAY_SIZE(el); + msg->elements = el; + msg->dn = ldb_dn_new(msg, module->ldb, LTDB_BASEINFO); + if (msg->dn == NULL) { + talloc_free(msg); + errno = ENOMEM; + return LDB_ERR_OPERATIONS_ERROR; + } + el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER); + if (el[0].name == NULL) { + talloc_free(msg); + errno = ENOMEM; + return LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_ERR_OPERATIONS_ERROR; + } + + val_time.data = (uint8_t *)s; + val_time.length = strlen(s); + + ret = ltdb_modify_internal(module, msg); + + talloc_free(msg); + + if (ret == LDB_SUCCESS) { + ltdb->sequence_number += 1; + } + + /* updating the tdb_seqnum here avoids us reloading the cache + records due to our own modification */ + ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb); + + 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/ldb/ldb_tdb/ldb_index.c b/ldb/ldb_tdb/ldb_index.c new file mode 100644 index 00000000..269305a4 --- /dev/null +++ b/ldb/ldb_tdb/ldb_index.c @@ -0,0 +1,1273 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb tdb backend - indexing + * + * Description: indexing routines for ldb tdb backend + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" + +#include "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 = (const char *)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 *ltdb_index_key(struct ldb_context *ldb, + const char *attr, const struct ldb_val *value) +{ + struct ldb_dn *ret; + struct ldb_val v; + const struct ldb_schema_attribute *a; + char *attr_folded; + int r; + + attr_folded = ldb_attr_casefold(ldb, attr); + if (!attr_folded) { + return NULL; + } + + a = ldb_schema_attribute_by_name(ldb, attr); + r = a->syntax->canonicalise_fn(ldb, ldb, value, &v); + if (r != LDB_SUCCESS) { + const char *errstr = ldb_errstring(ldb); + /* canonicalisation can be refused. For example, + a attribute that takes wildcards will refuse to canonicalise + if the value contains a wildcard */ + ldb_asprintf_errstring(ldb, "Failed to create index key for attribute '%s':%s%s%s", + attr, ldb_strerror(r), (errstr?":":""), (errstr?errstr:"")); + 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; + ret = ldb_dn_new_fmt(ldb, ldb, "%s:%s::%s", LTDB_INDEX, attr_folded, vstr); + talloc_free(vstr); + } else { + ret = ldb_dn_new_fmt(ldb, 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); + + 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]; + + if (attr == NULL) { + /* in this case we are just looking to see if key is present, + we are not spearching for a specific index */ + return 0; + } + + 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 LDB_ERR_OPERATIONS_ERROR; + } + + /* the attribute is indexed. Pull the list of DNs that match the + search criterion */ + dn = ltdb_index_key(ldb, tree->u.equality.attr, &tree->u.equality.value); + if (!dn) return LDB_ERR_OPERATIONS_ERROR; + + msg = talloc(list, struct ldb_message); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ltdb_search_dn1(module, dn, msg); + talloc_free(dn); + if (ret != LDB_SUCCESS) { + 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) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + + 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]) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + list->count++; + } + } + + talloc_free(msg); + + if (list->count > 1) { + qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) list_cmp); + } + + return LDB_SUCCESS; +} + + +static int list_union(struct ldb_context *, struct dn_list *, const struct dn_list *); + +/* + 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_dn(tree->u.equality.attr) == 0) { + list->dn = talloc_array(list, char *, 1); + if (list->dn == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + list->dn[0] = talloc_strdup(list->dn, (char *)tree->u.equality.value.data); + if (list->dn[0] == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + list->count = 1; + return LDB_SUCCESS; + } + 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 LDB_ERR_NO_SUCH_OBJECT; + } + + list3 = talloc(ldb, struct dn_list); + if (list3 == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + list3->dn = talloc_array(list3, char *, list->count); + if (!list3->dn) { + talloc_free(list3); + return LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_ERR_NO_SUCH_OBJECT; +} + + +/* + 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 LDB_ERR_NO_SUCH_OBJECT; + } + + d = talloc_realloc(list, list->dn, char *, list->count + list2->count); + if (!d) { + return LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_ERR_OPERATIONS_ERROR; + } + list->count++; + } + } + + if (list->count != count) { + qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)list_cmp); + } + + return LDB_ERR_NO_SUCH_OBJECT; +} + +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 = LDB_ERR_OPERATIONS_ERROR; + 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 LDB_ERR_OPERATIONS_ERROR; + } + + v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2); + + if (v == LDB_ERR_NO_SUCH_OBJECT) { + /* 0 || X == X */ + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + ret = v; + } + talloc_free(list2); + continue; + } + + if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) { + /* 1 || X == 1 */ + talloc_free(list->dn); + talloc_free(list2); + return v; + } + + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + ret = LDB_SUCCESS; + list->dn = talloc_move(list, &list2->dn); + list->count = list2->count; + } else { + if (list_union(ldb, list, list2) == -1) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + ret = LDB_SUCCESS; + } + talloc_free(list2); + } + + if (list->count == 0) { + return LDB_ERR_NO_SUCH_OBJECT; + } + + 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 LDB_ERR_OPERATIONS_ERROR; +} + +/* + 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 = LDB_ERR_OPERATIONS_ERROR; + 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 LDB_ERR_OPERATIONS_ERROR; + } + + v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2); + + if (v == LDB_ERR_NO_SUCH_OBJECT) { + /* 0 && X == 0 */ + talloc_free(list->dn); + talloc_free(list2); + return LDB_ERR_NO_SUCH_OBJECT; + } + + if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(list2); + continue; + } + + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + ret = LDB_SUCCESS; + 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 LDB_ERR_OPERATIONS_ERROR; + } + } + + talloc_free(list2); + + if (list->count == 0) { + talloc_free(list->dn); + return LDB_ERR_NO_SUCH_OBJECT; + } + } + + return ret; +} + +/* + AND index results and ONE level special index + */ +static int ltdb_index_dn_one(struct ldb_module *module, + struct ldb_dn *parent_dn, + struct dn_list *list) +{ + struct ldb_context *ldb = module->ldb; + struct dn_list *list2; + struct ldb_message *msg; + struct ldb_dn *key; + struct ldb_val val; + unsigned int i, j; + int ret; + + list2 = talloc_zero(module, struct dn_list); + if (list2 == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* the attribute is indexed. Pull the list of DNs that match the + search criterion */ + val.data = (uint8_t *)((uintptr_t)ldb_dn_get_casefold(parent_dn)); + val.length = strlen((char *)val.data); + key = ltdb_index_key(ldb, LTDB_IDXONE, &val); + if (!key) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + + msg = talloc(list2, struct ldb_message); + if (msg == NULL) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ltdb_search_dn1(module, key, msg); + talloc_free(key); + if (ret != LDB_SUCCESS) { + 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]; + + list2->dn = talloc_array(list2, char *, el->num_values); + if (!list2->dn) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (j = 0; j < el->num_values; j++) { + list2->dn[list2->count] = talloc_strdup(list2->dn, (char *)el->values[j].data); + if (!list2->dn[list2->count]) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + list2->count++; + } + } + + if (list2->count == 0) { + talloc_free(list2); + return LDB_ERR_NO_SUCH_OBJECT; + } + + if (list2->count > 1) { + qsort(list2->dn, list2->count, sizeof(char *), (comparison_fn_t) list_cmp); + } + + if (list->count > 0) { + if (list_intersect(ldb, list, list2) == -1) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (list->count == 0) { + talloc_free(list->dn); + talloc_free(list2); + return LDB_ERR_NO_SUCH_OBJECT; + } + } else { + list->dn = talloc_move(list, &list2->dn); + list->count = list2->count; + } + + talloc_free(list2); + + return LDB_SUCCESS; +} + +/* + return a list of dn's that might match a indexed search or + an error. return LDB_ERR_NO_SUCH_OBJECT for no matches, or LDB_SUCCESS 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 = LDB_ERR_OPERATIONS_ERROR; + + 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 = LDB_ERR_OPERATIONS_ERROR; + 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_new(ares->message, ac->module->ldb, 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 == LDB_ERR_NO_SUCH_OBJECT) { + /* the record has disappeared? yes, this can happen */ + talloc_free(ares); + continue; + } + + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + /* 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, idxattr, idxone; + + idxattr = idxone = 0; + ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXATTR); + if (ret == 0 ) { + idxattr = 1; + } + + /* We do one level indexing only if requested */ + ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXONE); + if (ret == 0 ) { + idxone = 1; + } + + if ((ac->scope == LDB_SCOPE_ONELEVEL && (idxattr+idxone == 0)) || + (ac->scope == LDB_SCOPE_SUBTREE && idxattr == 0)) { + /* no indexes? must do full search */ + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = LDB_ERR_OPERATIONS_ERROR; + + dn_list = talloc_zero(handle, struct dn_list); + if (dn_list == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + 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 LDB_ERR_OPERATIONS_ERROR; + } + dn_list->dn[0] = ldb_dn_alloc_linearized(dn_list, ac->base); + if (dn_list->dn[0] == NULL) { + ldb_oom(ac->module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + dn_list->count = 1; + ret = LDB_SUCCESS; + } + + if (ac->scope != LDB_SCOPE_BASE && idxattr == 1) { + ret = ltdb_index_dn(ac->module, ac->tree, ltdb->cache->indexlist, dn_list); + + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(dn_list); + return ret; + } + } + + if (ac->scope == LDB_SCOPE_ONELEVEL && idxone == 1) { + ret = ltdb_index_dn_one(ac->module, ac->base, dn_list); + } + + if (ret == LDB_SUCCESS) { + /* 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, + const char *dn) +{ + struct ldb_message_element *el; + + /* add another entry */ + el = talloc_realloc(msg, msg->elements, + struct ldb_message_element, msg->num_elements+1); + if (!el) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg->elements = el; + msg->elements[msg->num_elements].name = talloc_strdup(msg->elements, LTDB_IDX); + if (!msg->elements[msg->num_elements].name) { + return LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_SUCCESS; +} + + +/* + 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, + 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 LDB_SUCCESS; + } + } + + v2 = talloc_realloc(msg->elements, msg->elements[idx].values, + struct ldb_val, + msg->elements[idx].num_values+1); + if (!v2) { + return LDB_ERR_OPERATIONS_ERROR; + } + 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 LDB_SUCCESS; +} + +/* + 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 LDB_ERR_OPERATIONS_ERROR; + } + + dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]); + if (!dn_key) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + talloc_steal(msg, dn_key); + + ret = ltdb_search_dn1(module, dn_key, msg); + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(msg); + return ret; + } + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + 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, dn); + } else { + ret = ltdb_index_add1_add(ldb, msg, i, dn); + } + + if (ret == LDB_SUCCESS) { + 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 = (struct ltdb_private *)module->private_data; + int ret; + unsigned int i, j; + + if (dn[0] == '@') { + return LDB_SUCCESS; + } + + if (ltdb->cache->indexlist->num_elements == 0) { + /* no indexed fields */ + return LDB_SUCCESS; + } + + 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 != LDB_SUCCESS) { + return ret; + } + } + } + + return LDB_SUCCESS; +} + +/* + add the index entries for a new record +*/ +int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg) +{ + const char *dn; + int ret; + + dn = ldb_dn_get_linearized(msg->dn); + if (dn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements); + + 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 LDB_SUCCESS; + } + + dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]); + if (!dn_key) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg = talloc(dn_key, struct ldb_message); + if (msg == NULL) { + talloc_free(dn_key); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ltdb_search_dn1(module, dn_key, msg); + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(dn_key); + return ret; + } + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + /* it wasn't indexed. Did we have an earlier error? If we did then + its gone now */ + talloc_free(dn_key); + return LDB_SUCCESS; + } + + 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_get_linearized(dn_key)); + /* it ain't there. hmmm */ + talloc_free(dn_key); + return LDB_SUCCESS; + } + + 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 = (struct ltdb_private *)module->private_data; + int ret; + const char *dn; + unsigned int i, j; + + /* find the list of indexed fields */ + if (ltdb->cache->indexlist->num_elements == 0) { + /* no indexed fields */ + return LDB_SUCCESS; + } + + if (ldb_dn_is_special(msg->dn)) { + return LDB_SUCCESS; + } + + dn = ldb_dn_get_linearized(msg->dn); + if (dn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + 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 != LDB_SUCCESS) { + return ret; + } + } + } + + return LDB_SUCCESS; +} + +/* + handle special index for one level searches +*/ +int ltdb_index_one(struct ldb_module *module, const struct ldb_message *msg, int add) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + struct ldb_message_element el; + struct ldb_val val; + struct ldb_dn *pdn; + const char *dn; + int ret; + + /* We index for ONE Level only if requested */ + ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXONE); + if (ret != 0) { + return LDB_SUCCESS; + } + + pdn = ldb_dn_get_parent(module, msg->dn); + if (pdn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + dn = ldb_dn_get_linearized(msg->dn); + if (dn == NULL) { + talloc_free(pdn); + return LDB_ERR_OPERATIONS_ERROR; + } + + val.data = (uint8_t *)((uintptr_t)ldb_dn_get_casefold(pdn)); + if (val.data == NULL) { + talloc_free(pdn); + return LDB_ERR_OPERATIONS_ERROR; + } + + val.length = strlen((char *)val.data); + el.name = LTDB_IDXONE; + el.values = &val; + el.num_values = 1; + + if (add) { + ret = ltdb_index_add1(module, dn, &el, 0); + } else { /* delete */ + ret = ltdb_index_del_value(module, dn, &el, 0); + } + + talloc_free(pdn); + + return ret; +} + + +/* + 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 = (struct ldb_module *)state; + struct ldb_message *msg; + const 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_get_linearized(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_get_linearized(msg->dn); + } + + ret = ltdb_index_one(module, msg, 1); + if (ret == LDB_SUCCESS) { + ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements); + } else { + ldb_debug(module->ldb, LDB_DEBUG_ERROR, + "Adding special ONE LEVEL index failed (%s)!\n", + ldb_dn_get_linearized(msg->dn)); + } + + talloc_free(msg); + + if (ret != LDB_SUCCESS) return -1; + + return 0; +} + +/* + force a complete reindex of the database +*/ +int ltdb_reindex(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + int ret; + + if (ltdb_cache_reload(module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* first traverse the database deleting any @INDEX records */ + ret = tdb_traverse(ltdb->tdb, delete_index, NULL); + if (ret == -1) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* if we don't have indexes we have nothing todo */ + if (ltdb->cache->indexlist->num_elements == 0) { + return LDB_SUCCESS; + } + + /* now traverse adding any indexes for normal LDB records */ + ret = tdb_traverse(ltdb->tdb, re_index, module); + if (ret == -1) { + return LDB_ERR_OPERATIONS_ERROR; + } + + return LDB_SUCCESS; +} diff --git a/ldb/ldb_tdb/ldb_pack.c b/ldb/ldb_tdb/ldb_pack.c new file mode 100644 index 00000000..afb07dcb --- /dev/null +++ b/ldb/ldb_tdb/ldb_pack.c @@ -0,0 +1,289 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb pack/unpack + * + * Description: pack/unpack routines for ldb messages as key/value blobs + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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; + const char *dn; + uint8_t *p; + size_t len; + + dn = ldb_dn_get_linearized(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) { + 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; + } + } + + 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_new(message, ldb, (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/ldb/ldb_tdb/ldb_search.c b/ldb/ldb_tdb/ldb_search.c new file mode 100644 index 00000000..da899c36 --- /dev/null +++ b/ldb/ldb_tdb/ldb_search.c @@ -0,0 +1,616 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb search functions + * + * Description: functions to search ldb+tdb databases + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" + +#include "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_alloc_linearized(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_schema_attribute *a; + a = ldb_schema_attribute_by_name(ldb, msg->elements[i].name); + if (a->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. + return LDB_ERR_NO_SUCH_OBJECT on record-not-found + and LDB_SUCCESS on success +*/ +int ltdb_search_base(struct ldb_module *module, struct ldb_dn *dn) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + TDB_DATA tdb_key, tdb_data; + + if (ldb_dn_is_null(dn)) { + return LDB_ERR_NO_SUCH_OBJECT; + } + + /* form the key */ + tdb_key = ltdb_key(module, dn); + if (!tdb_key.dptr) { + return LDB_ERR_OPERATIONS_ERROR; + } + + tdb_data = tdb_fetch(ltdb->tdb, tdb_key); + talloc_free(tdb_key.dptr); + if (!tdb_data.dptr) { + return LDB_ERR_NO_SUCH_OBJECT; + } + + free(tdb_data.dptr); + return LDB_SUCCESS; +} + +/* + search the database for a single simple dn, returning all attributes + in a single message + + return LDB_ERR_NO_SUCH_OBJECT on record-not-found + and LDB_SUCCESS on success +*/ +int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_message *msg) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)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 LDB_ERR_OPERATIONS_ERROR; + } + + tdb_data = tdb_fetch(ltdb->tdb, tdb_key); + talloc_free(tdb_key.dptr); + if (!tdb_data.dptr) { + return LDB_ERR_NO_SUCH_OBJECT; + } + + msg->num_elements = 0; + msg->elements = NULL; + + ret = ltdb_unpack_data(module, &tdb_data, msg); + free(tdb_data.dptr); + if (ret == -1) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (!msg->dn) { + msg->dn = ldb_dn_copy(msg, dn); + } + if (!msg->dn) { + return LDB_ERR_OPERATIONS_ERROR; + } + + return LDB_SUCCESS; +} + +/* + lock the database for read - use by ltdb_search +*/ +static int ltdb_lock_read(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + if (ltdb->in_transaction == 0) { + return tdb_lockall_read(ltdb->tdb); + } + return 0; +} + +/* + unlock the database after a ltdb_lock_read() +*/ +static int ltdb_unlock_read(struct ldb_module *module) +{ + struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; + if (ltdb->in_transaction == 0) { + return tdb_unlockall_read(ltdb->tdb); + } + return 0; +} + +/* + 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_new(ares->message, ac->module->ldb, (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; + + if (ltdb->in_transaction != 0) { + ret = tdb_traverse(ltdb->tdb, search_func, handle); + } else { + 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 (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); + if (req->handle == NULL) { + ltdb_unlock_read(module); + return LDB_ERR_OPERATIONS_ERROR; + } + + if ((req->op.search.base == NULL) || (ldb_dn_is_null(req->op.search.base) == true)) { + + /* Check what we should do with a NULL dn */ + switch (req->op.search.scope) { + case LDB_SCOPE_BASE: + ldb_asprintf_errstring(module->ldb, + "NULL Base DN invalid for a base search"); + ret = LDB_ERR_INVALID_DN_SYNTAX; + break; + case LDB_SCOPE_ONELEVEL: + ldb_asprintf_errstring(module->ldb, + "NULL Base DN invalid for a one-level search"); + ret = LDB_ERR_INVALID_DN_SYNTAX; + break; + case LDB_SCOPE_SUBTREE: + default: + /* We accept subtree searches from a NULL base DN, ie over the whole DB */ + ret = LDB_SUCCESS; + } + } else if (ldb_dn_is_valid(req->op.search.base) == false) { + + /* We don't want invalid base DNs here */ + ldb_asprintf_errstring(module->ldb, + "Invalid Base DN: %s", + ldb_dn_get_linearized(req->op.search.base)); + ret = LDB_ERR_INVALID_DN_SYNTAX; + + } else if (ltdb->check_base) { + /* This database has been marked as 'checkBaseOnSearch', so do a spot check of the base dn */ + ret = ltdb_search_base(module, req->op.search.base); + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + ldb_asprintf_errstring(module->ldb, + "No such Base DN: %s", + ldb_dn_get_linearized(req->op.search.base)); + } + + } else { + /* If we are not checking the base DN life is easy */ + ret = LDB_SUCCESS; + } + + 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; + + + if (ret == LDB_SUCCESS) { + ret = ltdb_search_indexed(req->handle); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + /* Not in the index, therefore OK! */ + ret = LDB_SUCCESS; + + } else if (ret == LDB_ERR_OPERATIONS_ERROR) { + /* Not indexed, so we need to do a full scan */ + ret = ltdb_search_full(req->handle); + if (ret != LDB_SUCCESS) { + ldb_set_errstring(module->ldb, "Indexed and full searches both failed!\n"); + } + } + } + + if (ret != LDB_SUCCESS) { + 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; + + if (ret == LDB_SUCCESS) { + 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/ldb/ldb_tdb/ldb_tdb.c b/ldb/ldb_tdb/ldb_tdb.c new file mode 100644 index 00000000..01d570c8 --- /dev/null +++ b/ldb/ldb_tdb/ldb_tdb.c @@ -0,0 +1,1140 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_includes.h" + +#include "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, + struct ldb_request *req) +{ + struct ltdb_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 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 = req->context; + ac->callback = req->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, struct ldb_dn *dn) +{ + struct ldb_context *ldb = module->ldb; + TDB_DATA key; + char *key_str = NULL; + const 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_get_casefold(dn); + if (!dn_folded) { + goto failed; + } + + key_str = talloc_strdup(ldb, "DN="); + if (!key_str) { + goto failed; + } + + key_str = talloc_strdup_append_buffer(key_str, 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, struct ldb_dn *dn) +{ + int ret = LDB_SUCCESS; + + 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 == LDB_SUCCESS && + !(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 = + talloc_get_type(module->private_data, struct ltdb_private); + 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 != LDB_SUCCESS) { + 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) { + ldb_asprintf_errstring(module->ldb, + "Entry %s already exists", + ldb_dn_get_linearized(msg->dn)); + return ret; + } + + if (ret == LDB_SUCCESS) { + ret = ltdb_index_one(module, msg, 1); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ltdb_modified(module, msg->dn); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return ret; +} + +/* + add a record to the database +*/ +static int ltdb_add(struct ldb_module *module, struct ldb_request *req) +{ + struct ltdb_private *ltdb; + struct ltdb_context *ltdb_ac; + int tret, ret = LDB_SUCCESS; + + ltdb = talloc_get_type(module->private_data, struct ltdb_private); + + if (check_critical_controls(req->controls)) { + return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + + req->handle = init_ltdb_handle(ltdb, module, req); + 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, struct ldb_dn *dn) +{ + struct ltdb_private *ltdb = + talloc_get_type(module->private_data, struct ltdb_private); + 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, 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 != LDB_SUCCESS) { + /* not finding the old record is an error */ + goto done; + } + + ret = ltdb_delete_noindex(module, dn); + if (ret != LDB_SUCCESS) { + goto done; + } + + /* remove one level attribute */ + ret = ltdb_index_one(module, msg, 0); + if (ret != LDB_SUCCESS) { + goto done; + } + + /* remove any indexed attributes */ + ret = ltdb_index_del(module, msg); + if (ret != LDB_SUCCESS) { + goto done; + } + + ret = ltdb_modified(module, dn); + if (ret != LDB_SUCCESS) { + goto done; + } + +done: + talloc_free(msg); + return ret; +} + +/* + delete a record from the database +*/ +static int ltdb_delete(struct ldb_module *module, struct ldb_request *req) +{ + struct ltdb_private *ltdb; + struct ltdb_context *ltdb_ac; + int tret, ret = LDB_SUCCESS; + + ltdb = talloc_get_type(module->private_data, struct ltdb_private); + + 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); + 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) +{ + const char *dn; + unsigned int i, j; + + dn = ldb_dn_get_linearized(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); + } + } + + 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_schema_attribute *a; + + found = find_element(msg, name); + if (found == -1) { + return -1; + } + + el = &msg->elements[found]; + + a = ldb_schema_attribute_by_name(ldb, el->name); + + for (i=0;i<el->num_values;i++) { + if (a->syntax->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 = + talloc_get_type(module->private_data, struct ltdb_private); + TDB_DATA tdb_key, tdb_data; + struct ldb_message *msg2; + unsigned i, j; + int ret, idx; + + 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; + const 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 */ + idx = find_element(msg2, el->name); + + if (idx == -1) { + if (msg_add_element(ldb, msg2, el) != 0) { + ret = LDB_ERR_OTHER; + goto failed; + } + continue; + } + + el2 = &msg2->elements[idx]; + + /* An attribute with this name already exists, + * add all values if they don't already exist + * (check both the other elements to be added, + * and those already in the db). */ + + for (j=0;j<el->num_values;j++) { + if (ldb_msg_find_val(el2, &el->values[j])) { + ldb_asprintf_errstring(module->ldb, "%s: value #%d already exists", el->name, j); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto failed; + } + if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) { + ldb_asprintf_errstring(module->ldb, "%s: value #%d provided more than once", el->name, j); + 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, el->name); + + for (j=0;j<el->num_values;j++) { + if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) { + ldb_asprintf_errstring(module->ldb, "%s: value #%d provided more than once", el->name, j); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto failed; + } + } + + /* add the replacement element, if not empty */ + if (el->num_values != 0 && + msg_add_element(ldb, msg2, el) != 0) { + ret = LDB_ERR_OTHER; + goto failed; + } + break; + + case LDB_FLAG_MOD_DELETE: + + dn = ldb_dn_get_linearized(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; + } + ret = ltdb_index_del_value(module, dn, &msg->elements[i], j); + if (ret != LDB_SUCCESS) { + 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; + } + + ret = ltdb_modified(module, msg->dn); + if (ret != LDB_SUCCESS) { + 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; + struct ltdb_context *ltdb_ac; + int tret, ret = LDB_SUCCESS; + + ltdb = talloc_get_type(module->private_data, struct ltdb_private); + + if (check_critical_controls(req->controls)) { + return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + + req->handle = NULL; + + req->handle = init_ltdb_handle(ltdb, module, req); + 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; + struct ltdb_context *ltdb_ac; + struct ldb_message *msg; + int tret, ret = LDB_SUCCESS; + + ltdb = talloc_get_type(module->private_data, struct ltdb_private); + + 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); + 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 != LDB_SUCCESS) { + /* not finding the old record is an error */ + req->handle->status = tret; + goto done; + } + + msg->dn = ldb_dn_copy(msg, req->op.rename.newdn); + if (!msg->dn) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + if (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) == 0) { + /* The rename operation is apparently only changing case - + the DNs are the same. Delete the old DN before adding + the new one to avoid a TDB_ERR_EXISTS error. + + The only drawback to this is that if the delete + succeeds but the add fails, we rely on the + transaction to roll this all back. */ + ret = ltdb_delete_internal(module, req->op.rename.olddn); + if (ret != LDB_SUCCESS) { + goto done; + } + + ret = ltdb_add_internal(module, msg); + if (ret != LDB_SUCCESS) { + goto done; + } + } else { + /* The rename operation is changing DNs. Try to add the new + DN first to avoid clobbering another DN not related to + this rename operation. */ + ret = ltdb_add_internal(module, msg); + if (ret != LDB_SUCCESS) { + 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 = + talloc_get_type(module->private_data, struct ltdb_private); + + if (tdb_transaction_start(ltdb->tdb) != 0) { + return ltdb_err_map(tdb_error(ltdb->tdb)); + } + + ltdb->in_transaction++; + + return LDB_SUCCESS; +} + +static int ltdb_end_trans(struct ldb_module *module) +{ + struct ltdb_private *ltdb = + talloc_get_type(module->private_data, struct ltdb_private); + + ltdb->in_transaction--; + + 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 = + talloc_get_type(module->private_data, struct ltdb_private); + + ltdb->in_transaction--; + + 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 (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; + struct ldb_message *msg = NULL; + struct ldb_dn *dn; + const char *date; + int tret; + + tmp_ctx = talloc_new(req); + if (tmp_ctx == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + dn = ldb_dn_new(tmp_ctx, module->ldb, LTDB_BASEINFO); + + 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 != LDB_SUCCESS) { + talloc_free(tmp_ctx); + /* zero is as good as anything when we don't know */ + req->op.seq_num.seq_num = 0; + 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: + 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 | TDB_SEQNUM; + + /* check for the 'nosync' option */ + if (flags & LDB_FLG_NOSYNC) { + tdb_flags |= TDB_NOSYNC; + } + + /* and nommap option */ + if (flags & LDB_FLG_NOMMAP) { + tdb_flags |= TDB_NOMMAP; + } + + 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, + ldb->create_perms, 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; + } + talloc_set_name_const(*module, "ldb_tdb backend"); + (*module)->ldb = ldb; + (*module)->prev = (*module)->next = NULL; + (*module)->private_data = ltdb; + (*module)->ops = <db_ops; + + if (ltdb_cache_load(*module) != 0) { + talloc_free(*module); + talloc_free(ltdb); + return -1; + } + + return 0; +} + +const struct ldb_backend_ops ldb_tdb_backend_ops = { + .name = "tdb", + .connect_fn = ltdb_connect +}; diff --git a/ldb/ldb_tdb/ldb_tdb.h b/ldb/ldb_tdb/ldb_tdb.h new file mode 100644 index 00000000..4beced30 --- /dev/null +++ b/ldb/ldb_tdb/ldb_tdb.h @@ -0,0 +1,130 @@ +#if (_SAMBA_BUILD_ == 3) +#include "tdb/include/tdb.h" +#else +#include "replace.h" +#include "system/wait.h" +#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; + + /* the low level tdb seqnum - used to avoid loading BASEINFO when + possible */ + int tdb_seqnum; + + struct ltdb_cache { + struct ldb_message *indexlist; + struct ldb_message *attributes; + + struct { + char *name; + int flags; + } last_attribute; + } *cache; + + int in_transaction; + + bool check_base; +}; + +/* + 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; + 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_IDXONE "@IDXONE" +#define LTDB_BASEINFO "@BASEINFO" +#define LTDB_OPTIONS "@OPTIONS" +#define LTDB_ATTRIBUTES "@ATTRIBUTES" + +/* special attribute types */ +#define LTDB_SEQUENCE_NUMBER "sequenceNumber" +#define LTDB_CHECK_BASE "checkBaseOnSearch" +#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_index_one(struct ldb_module *module, const struct ldb_message *msg, int add); +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, 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, + struct ldb_request *req); +struct TDB_DATA ltdb_key(struct ldb_module *module, 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, 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/ldb/ldb_tdb/ldb_tdb_wrap.c b/ldb/ldb_tdb/ldb_tdb_wrap.c new file mode 100644 index 00000000..654574cd --- /dev/null +++ b/ldb/ldb_tdb/ldb_tdb_wrap.c @@ -0,0 +1,153 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb_includes.h" + +#include "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; +} + +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); +} + +/* + 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; + struct tdb_logging_context log_ctx; + + log_ctx.log_fn = ltdb_log_fn; + log_ctx.log_private = ldb; + + if (stat(path, &st) == 0) { + for (w=tdb_list;w;w=w->next) { + if (st.st_dev == w->device && st.st_ino == w->inode) { + if (!talloc_reference(mem_ctx, w)) { + return NULL; + } + 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, 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/ldb/ldb_wrap.c b/ldb/ldb_wrap.c new file mode 100644 index 00000000..f9dd8471 --- /dev/null +++ b/ldb/ldb_wrap.c @@ -0,0 +1,6586 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 1.3.35 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#define SWIGPYTHON +#define SWIG_PYTHON_NO_BUILD_NONE +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + + + +/* Python.h has to appear first */ +#include <Python.h> + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic CAPI SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "4" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the swig runtime code. + In 99.9% of the cases, swig just needs to declare them as 'static'. + + But only do this if is strictly necessary, ie, if you have problems + with your compiler or so. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 +#define SWIG_CAST_NEW_MEMORY 0x2 + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The swig conversion methods, as ConvertPtr, return and integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old swig versions, you usually write code as: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit as: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + that seems to be the same, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + requires also to SWIG_ConvertPtr to return new result values, as + + int SWIG_ConvertPtr(obj, ptr,...) { + if (<obj is ok>) { + if (<need new object>) { + *ptr = <ptr to new allocated object>; + return SWIG_NEWOBJ; + } else { + *ptr = <ptr to old object>; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + swig errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows to return the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() + + + */ +#define SWIG_OK (0) +#define SWIG_ERROR (-1) +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporal objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + + + +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *, int *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store information on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class<int>" == "Class<int >", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (int)((l1 - f1) - (l2 - f2)); +} + +/* + Check type equivalence in a name list like <name1>|<name2>|... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like <name1>|<name2>|... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCompare(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + + +/* think of this as a c++ template<> or a scheme macro */ +#define SWIG_TypeCheck_Template(comparison, ty) \ + if (ty) { \ + swig_cast_info *iter = ty->cast; \ + while (iter) { \ + if (comparison) { \ + if (iter == ty->cast) return iter; \ + /* Move iter to the top of the linked list */ \ + iter->prev->next = iter->next; \ + if (iter->next) \ + iter->next->prev = iter->prev; \ + iter->next = ty->cast; \ + iter->prev = 0; \ + if (ty->cast) ty->cast->prev = iter; \ + ty->cast = iter; \ + return iter; \ + } \ + iter = iter->next; \ + } \ + } \ + return 0 + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + SWIG_TypeCheck_Template(strcmp(iter->type->name, c) == 0, ty); +} + +/* Same as previous function, except strcmp is replaced with a pointer comparison */ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *into) { + SWIG_TypeCheck_Template(iter->type == from, into); +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + register size_t l = 0; + register size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + register size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + register int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + register size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + register const unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + register unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register char d = *(c++); + register unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + + +/* Add PyOS_snprintf for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) +# define PyOS_snprintf _snprintf +# else +# define PyOS_snprintf snprintf +# endif +#endif + +/* A crude PyString_FromFormat implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 + +#ifndef SWIG_PYBUFFER_SIZE +# define SWIG_PYBUFFER_SIZE 1024 +#endif + +static PyObject * +PyString_FromFormat(const char *fmt, ...) { + va_list ap; + char buf[SWIG_PYBUFFER_SIZE * 2]; + int res; + va_start(ap, fmt); + res = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf); +} +#endif + +/* Add PyObject_Del for old Pythons */ +#if PY_VERSION_HEX < 0x01060000 +# define PyObject_Del(op) PyMem_DEL((op)) +#endif +#ifndef PyObject_DEL +# define PyObject_DEL PyObject_Del +#endif + +/* A crude PyExc_StopIteration exception for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# ifndef PyExc_StopIteration +# define PyExc_StopIteration PyExc_RuntimeError +# endif +# ifndef PyObject_GenericGetAttr +# define PyObject_GenericGetAttr 0 +# endif +#endif +/* Py_NotImplemented is defined in 2.1 and up. */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef Py_NotImplemented +# define Py_NotImplemented PyExc_RuntimeError +# endif +#endif + + +/* A crude PyString_AsStringAndSize implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef PyString_AsStringAndSize +# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;} +# endif +#endif + +/* PySequence_Size for old Pythons */ +#if PY_VERSION_HEX < 0x02000000 +# ifndef PySequence_Size +# define PySequence_Size PySequence_Length +# endif +#endif + + +/* PyBool_FromLong for old Pythons */ +#if PY_VERSION_HEX < 0x02030000 +static +PyObject *PyBool_FromLong(long ok) +{ + PyObject *result = ok ? Py_True : Py_False; + Py_INCREF(result); + return result; +} +#endif + +/* Py_ssize_t for old Pythons */ +/* This code is as recommended by: */ +/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */ +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +# define PY_SSIZE_T_MAX INT_MAX +# define PY_SSIZE_T_MIN INT_MIN +#endif + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIME PyObject* +SWIG_Python_ErrorType(int code) { + PyObject* type = 0; + switch(code) { + case SWIG_MemoryError: + type = PyExc_MemoryError; + break; + case SWIG_IOError: + type = PyExc_IOError; + break; + case SWIG_RuntimeError: + type = PyExc_RuntimeError; + break; + case SWIG_IndexError: + type = PyExc_IndexError; + break; + case SWIG_TypeError: + type = PyExc_TypeError; + break; + case SWIG_DivisionByZero: + type = PyExc_ZeroDivisionError; + break; + case SWIG_OverflowError: + type = PyExc_OverflowError; + break; + case SWIG_SyntaxError: + type = PyExc_SyntaxError; + break; + case SWIG_ValueError: + type = PyExc_ValueError; + break; + case SWIG_SystemError: + type = PyExc_SystemError; + break; + case SWIG_AttributeError: + type = PyExc_AttributeError; + break; + default: + type = PyExc_RuntimeError; + } + return type; +} + + +SWIGRUNTIME void +SWIG_Python_AddErrorMsg(const char* mesg) +{ + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + + if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); + if (value) { + PyObject *old_str = PyObject_Str(value); + PyErr_Clear(); + Py_XINCREF(type); + PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg); + Py_DECREF(old_str); + Py_DECREF(value); + } else { + PyErr_SetString(PyExc_RuntimeError, mesg); + } +} + + + +#if defined(SWIG_PYTHON_NO_THREADS) +# if defined(SWIG_PYTHON_THREADS) +# undef SWIG_PYTHON_THREADS +# endif +#endif +#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ +# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) +# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */ +# define SWIG_PYTHON_USE_GIL +# endif +# endif +# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ +# ifndef SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() +# endif +# ifdef __cplusplus /* C++ code */ + class SWIG_Python_Thread_Block { + bool status; + PyGILState_STATE state; + public: + void end() { if (status) { PyGILState_Release(state); status = false;} } + SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} + ~SWIG_Python_Thread_Block() { end(); } + }; + class SWIG_Python_Thread_Allow { + bool status; + PyThreadState *save; + public: + void end() { if (status) { PyEval_RestoreThread(save); status = false; }} + SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} + ~SWIG_Python_Thread_Allow() { end(); } + }; +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block +# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow +# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() +# else /* C code */ +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() +# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() +# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) +# endif +# else /* Old thread way, not implemented, user must provide it */ +# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) +# define SWIG_PYTHON_INITIALIZE_THREADS +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_END_BLOCK) +# define SWIG_PYTHON_THREAD_END_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# endif +# if !defined(SWIG_PYTHON_THREAD_END_ALLOW) +# define SWIG_PYTHON_THREAD_END_ALLOW +# endif +# endif +#else /* No thread support */ +# define SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# define SWIG_PYTHON_THREAD_END_BLOCK +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# define SWIG_PYTHON_THREAD_END_ALLOW +#endif + +/* ----------------------------------------------------------------------------- + * Python API portion that goes into the runtime + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* cc-mode */ +#endif +#endif + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_PY_POINTER 4 +#define SWIG_PY_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + +#ifdef __cplusplus +#if 0 +{ /* cc-mode */ +#endif +} +#endif + + +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * pyrun.swg + * + * This file contains the runtime support for Python modules + * and includes code for managing global variables and pointer + * type checking. + * + * ----------------------------------------------------------------------------- */ + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) +#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) +#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(ptr, type, flags) +#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) +#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) +#define swig_owntype int + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) +#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Python_GetModule() +#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) +#define SWIG_NewClientData(obj) PySwigClientData_New(obj) + +#define SWIG_SetErrorObj SWIG_Python_SetErrorObj +#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg +#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) +#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) +#define SWIG_fail goto fail + + +/* Runtime API implementation */ + +/* Error manipulation */ + +SWIGINTERN void +SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetObject(errtype, obj); + Py_DECREF(obj); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +SWIGINTERN void +SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetString(errtype, (char *) msg); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) + +/* Set a constant value */ + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { + PyDict_SetItemString(d, (char*) name, obj); + Py_DECREF(obj); +} + +/* Append a value to the result obj */ + +SWIGINTERN PyObject* +SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { +#if !defined(SWIG_PYTHON_OUTPUT_TUPLE) + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyList_Check(result)) { + PyObject *o2 = result; + result = PyList_New(1); + PyList_SetItem(result, 0, o2); + } + PyList_Append(result,obj); + Py_DECREF(obj); + } + return result; +#else + PyObject* o2; + PyObject* o3; + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyTuple_Check(result)) { + o2 = result; + result = PyTuple_New(1); + PyTuple_SET_ITEM(result, 0, o2); + } + o3 = PyTuple_New(1); + PyTuple_SET_ITEM(o3, 0, obj); + o2 = result; + result = PySequence_Concat(o2, o3); + Py_DECREF(o2); + Py_DECREF(o3); + } + return result; +#endif +} + +/* Unpack the argument tuple */ + +SWIGINTERN int +SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) +{ + if (!args) { + if (!min && !max) { + return 1; + } else { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", + name, (min == max ? "" : "at least "), (int)min); + return 0; + } + } + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); + return 0; + } else { + register Py_ssize_t l = PyTuple_GET_SIZE(args); + if (l < min) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at least "), (int)min, (int)l); + return 0; + } else if (l > max) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at most "), (int)max, (int)l); + return 0; + } else { + register int i; + for (i = 0; i < l; ++i) { + objs[i] = PyTuple_GET_ITEM(args, i); + } + for (; l < max; ++l) { + objs[l] = 0; + } + return i + 1; + } + } +} + +/* A functor is a function object with one single object argument */ +#if PY_VERSION_HEX >= 0x02020000 +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); +#else +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj); +#endif + +/* + Helper for static pointer initialization for both C and C++ code, for example + static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); +*/ +#ifdef __cplusplus +#define SWIG_STATIC_POINTER(var) var +#else +#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var +#endif + +/* ----------------------------------------------------------------------------- + * Pointer declarations + * ----------------------------------------------------------------------------- */ + +/* Flags for new pointer objects */ +#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) +#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) + +#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* cc-mode */ +#endif +#endif + +/* How to access Py_None */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# ifndef SWIG_PYTHON_NO_BUILD_NONE +# ifndef SWIG_PYTHON_BUILD_NONE +# define SWIG_PYTHON_BUILD_NONE +# endif +# endif +#endif + +#ifdef SWIG_PYTHON_BUILD_NONE +# ifdef Py_None +# undef Py_None +# define Py_None SWIG_Py_None() +# endif +SWIGRUNTIMEINLINE PyObject * +_SWIG_Py_None(void) +{ + PyObject *none = Py_BuildValue((char*)""); + Py_DECREF(none); + return none; +} +SWIGRUNTIME PyObject * +SWIG_Py_None(void) +{ + static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None(); + return none; +} +#endif + +/* The python void return value */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Py_Void(void) +{ + PyObject *none = Py_None; + Py_INCREF(none); + return none; +} + +/* PySwigClientData */ + +typedef struct { + PyObject *klass; + PyObject *newraw; + PyObject *newargs; + PyObject *destroy; + int delargs; + int implicitconv; +} PySwigClientData; + +SWIGRUNTIMEINLINE int +SWIG_Python_CheckImplicit(swig_type_info *ty) +{ + PySwigClientData *data = (PySwigClientData *)ty->clientdata; + return data ? data->implicitconv : 0; +} + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_ExceptionType(swig_type_info *desc) { + PySwigClientData *data = desc ? (PySwigClientData *) desc->clientdata : 0; + PyObject *klass = data ? data->klass : 0; + return (klass ? klass : PyExc_RuntimeError); +} + + +SWIGRUNTIME PySwigClientData * +PySwigClientData_New(PyObject* obj) +{ + if (!obj) { + return 0; + } else { + PySwigClientData *data = (PySwigClientData *)malloc(sizeof(PySwigClientData)); + /* the klass element */ + data->klass = obj; + Py_INCREF(data->klass); + /* the newraw method and newargs arguments used to create a new raw instance */ + if (PyClass_Check(obj)) { + data->newraw = 0; + data->newargs = obj; + Py_INCREF(obj); + } else { +#if (PY_VERSION_HEX < 0x02020000) + data->newraw = 0; +#else + data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__"); +#endif + if (data->newraw) { + Py_INCREF(data->newraw); + data->newargs = PyTuple_New(1); + PyTuple_SetItem(data->newargs, 0, obj); + } else { + data->newargs = obj; + } + Py_INCREF(data->newargs); + } + /* the destroy method, aka as the C++ delete method */ + data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__"); + if (PyErr_Occurred()) { + PyErr_Clear(); + data->destroy = 0; + } + if (data->destroy) { + int flags; + Py_INCREF(data->destroy); + flags = PyCFunction_GET_FLAGS(data->destroy); +#ifdef METH_O + data->delargs = !(flags & (METH_O)); +#else + data->delargs = 0; +#endif + } else { + data->delargs = 0; + } + data->implicitconv = 0; + return data; + } +} + +SWIGRUNTIME void +PySwigClientData_Del(PySwigClientData* data) +{ + Py_XDECREF(data->newraw); + Py_XDECREF(data->newargs); + Py_XDECREF(data->destroy); +} + +/* =============== PySwigObject =====================*/ + +typedef struct { + PyObject_HEAD + void *ptr; + swig_type_info *ty; + int own; + PyObject *next; +} PySwigObject; + +SWIGRUNTIME PyObject * +PySwigObject_long(PySwigObject *v) +{ + return PyLong_FromVoidPtr(v->ptr); +} + +SWIGRUNTIME PyObject * +PySwigObject_format(const char* fmt, PySwigObject *v) +{ + PyObject *res = NULL; + PyObject *args = PyTuple_New(1); + if (args) { + if (PyTuple_SetItem(args, 0, PySwigObject_long(v)) == 0) { + PyObject *ofmt = PyString_FromString(fmt); + if (ofmt) { + res = PyString_Format(ofmt,args); + Py_DECREF(ofmt); + } + Py_DECREF(args); + } + } + return res; +} + +SWIGRUNTIME PyObject * +PySwigObject_oct(PySwigObject *v) +{ + return PySwigObject_format("%o",v); +} + +SWIGRUNTIME PyObject * +PySwigObject_hex(PySwigObject *v) +{ + return PySwigObject_format("%x",v); +} + +SWIGRUNTIME PyObject * +#ifdef METH_NOARGS +PySwigObject_repr(PySwigObject *v) +#else +PySwigObject_repr(PySwigObject *v, PyObject *args) +#endif +{ + const char *name = SWIG_TypePrettyName(v->ty); + PyObject *hex = PySwigObject_hex(v); + PyObject *repr = PyString_FromFormat("<Swig Object of type '%s' at 0x%s>", name, PyString_AsString(hex)); + Py_DECREF(hex); + if (v->next) { +#ifdef METH_NOARGS + PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next); +#else + PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next, args); +#endif + PyString_ConcatAndDel(&repr,nrep); + } + return repr; +} + +SWIGRUNTIME int +PySwigObject_print(PySwigObject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) +{ +#ifdef METH_NOARGS + PyObject *repr = PySwigObject_repr(v); +#else + PyObject *repr = PySwigObject_repr(v, NULL); +#endif + if (repr) { + fputs(PyString_AsString(repr), fp); + Py_DECREF(repr); + return 0; + } else { + return 1; + } +} + +SWIGRUNTIME PyObject * +PySwigObject_str(PySwigObject *v) +{ + char result[SWIG_BUFFER_SIZE]; + return SWIG_PackVoidPtr(result, v->ptr, v->ty->name, sizeof(result)) ? + PyString_FromString(result) : 0; +} + +SWIGRUNTIME int +PySwigObject_compare(PySwigObject *v, PySwigObject *w) +{ + void *i = v->ptr; + void *j = w->ptr; + return (i < j) ? -1 : ((i > j) ? 1 : 0); +} + +SWIGRUNTIME PyTypeObject* _PySwigObject_type(void); + +SWIGRUNTIME PyTypeObject* +PySwigObject_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigObject_type(); + return type; +} + +SWIGRUNTIMEINLINE int +PySwigObject_Check(PyObject *op) { + return ((op)->ob_type == PySwigObject_type()) + || (strcmp((op)->ob_type->tp_name,"PySwigObject") == 0); +} + +SWIGRUNTIME PyObject * +PySwigObject_New(void *ptr, swig_type_info *ty, int own); + +SWIGRUNTIME void +PySwigObject_dealloc(PyObject *v) +{ + PySwigObject *sobj = (PySwigObject *) v; + PyObject *next = sobj->next; + if (sobj->own == SWIG_POINTER_OWN) { + swig_type_info *ty = sobj->ty; + PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0; + PyObject *destroy = data ? data->destroy : 0; + if (destroy) { + /* destroy is always a VARARGS method */ + PyObject *res; + if (data->delargs) { + /* we need to create a temporal object to carry the destroy operation */ + PyObject *tmp = PySwigObject_New(sobj->ptr, ty, 0); + res = SWIG_Python_CallFunctor(destroy, tmp); + Py_DECREF(tmp); + } else { + PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); + PyObject *mself = PyCFunction_GET_SELF(destroy); + res = ((*meth)(mself, v)); + } + Py_XDECREF(res); + } +#if !defined(SWIG_PYTHON_SILENT_MEMLEAK) + else { + const char *name = SWIG_TypePrettyName(ty); + printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); + } +#endif + } + Py_XDECREF(next); + PyObject_DEL(v); +} + +SWIGRUNTIME PyObject* +PySwigObject_append(PyObject* v, PyObject* next) +{ + PySwigObject *sobj = (PySwigObject *) v; +#ifndef METH_O + PyObject *tmp = 0; + if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL; + next = tmp; +#endif + if (!PySwigObject_Check(next)) { + return NULL; + } + sobj->next = next; + Py_INCREF(next); + return SWIG_Py_Void(); +} + +SWIGRUNTIME PyObject* +#ifdef METH_NOARGS +PySwigObject_next(PyObject* v) +#else +PySwigObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + PySwigObject *sobj = (PySwigObject *) v; + if (sobj->next) { + Py_INCREF(sobj->next); + return sobj->next; + } else { + return SWIG_Py_Void(); + } +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +PySwigObject_disown(PyObject *v) +#else +PySwigObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + PySwigObject *sobj = (PySwigObject *)v; + sobj->own = 0; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +PySwigObject_acquire(PyObject *v) +#else +PySwigObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + PySwigObject *sobj = (PySwigObject *)v; + sobj->own = SWIG_POINTER_OWN; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +PySwigObject_own(PyObject *v, PyObject *args) +{ + PyObject *val = 0; +#if (PY_VERSION_HEX < 0x02020000) + if (!PyArg_ParseTuple(args,(char *)"|O:own",&val)) +#else + if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val)) +#endif + { + return NULL; + } + else + { + PySwigObject *sobj = (PySwigObject *)v; + PyObject *obj = PyBool_FromLong(sobj->own); + if (val) { +#ifdef METH_NOARGS + if (PyObject_IsTrue(val)) { + PySwigObject_acquire(v); + } else { + PySwigObject_disown(v); + } +#else + if (PyObject_IsTrue(val)) { + PySwigObject_acquire(v,args); + } else { + PySwigObject_disown(v,args); + } +#endif + } + return obj; + } +} + +#ifdef METH_O +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_NOARGS, (char *)"aquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)PySwigObject_append, METH_O, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)PySwigObject_next, METH_NOARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_NOARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#else +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_VARARGS, (char *)"aquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)PySwigObject_append, METH_VARARGS, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)PySwigObject_next, METH_VARARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_VARARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#endif + +#if PY_VERSION_HEX < 0x02020000 +SWIGINTERN PyObject * +PySwigObject_getattr(PySwigObject *sobj,char *name) +{ + return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name); +} +#endif + +SWIGRUNTIME PyTypeObject* +_PySwigObject_type(void) { + static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; + + static PyNumberMethods PySwigObject_as_number = { + (binaryfunc)0, /*nb_add*/ + (binaryfunc)0, /*nb_subtract*/ + (binaryfunc)0, /*nb_multiply*/ + (binaryfunc)0, /*nb_divide*/ + (binaryfunc)0, /*nb_remainder*/ + (binaryfunc)0, /*nb_divmod*/ + (ternaryfunc)0,/*nb_power*/ + (unaryfunc)0, /*nb_negative*/ + (unaryfunc)0, /*nb_positive*/ + (unaryfunc)0, /*nb_absolute*/ + (inquiry)0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + (coercion)0, /*nb_coerce*/ + (unaryfunc)PySwigObject_long, /*nb_int*/ + (unaryfunc)PySwigObject_long, /*nb_long*/ + (unaryfunc)0, /*nb_float*/ + (unaryfunc)PySwigObject_oct, /*nb_oct*/ + (unaryfunc)PySwigObject_hex, /*nb_hex*/ +#if PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ +#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */ +#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */ + 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */ +#endif + }; + + static PyTypeObject pyswigobject_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp + = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + (char *)"PySwigObject", /* tp_name */ + sizeof(PySwigObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PySwigObject_dealloc, /* tp_dealloc */ + (printfunc)PySwigObject_print, /* tp_print */ +#if PY_VERSION_HEX < 0x02020000 + (getattrfunc)PySwigObject_getattr, /* tp_getattr */ +#else + (getattrfunc)0, /* tp_getattr */ +#endif + (setattrfunc)0, /* tp_setattr */ + (cmpfunc)PySwigObject_compare, /* tp_compare */ + (reprfunc)PySwigObject_repr, /* tp_repr */ + &PySwigObject_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)PySwigObject_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigobject_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + swigobject_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#ifdef COUNT_ALLOCS + 0,0,0,0 /* tp_alloc -> tp_next */ +#endif + }; + pyswigobject_type = tmp; + pyswigobject_type.ob_type = &PyType_Type; + type_init = 1; + } + return &pyswigobject_type; +} + +SWIGRUNTIME PyObject * +PySwigObject_New(void *ptr, swig_type_info *ty, int own) +{ + PySwigObject *sobj = PyObject_NEW(PySwigObject, PySwigObject_type()); + if (sobj) { + sobj->ptr = ptr; + sobj->ty = ty; + sobj->own = own; + sobj->next = 0; + } + return (PyObject *)sobj; +} + +/* ----------------------------------------------------------------------------- + * Implements a simple Swig Packed type, and use it instead of string + * ----------------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + void *pack; + swig_type_info *ty; + size_t size; +} PySwigPacked; + +SWIGRUNTIME int +PySwigPacked_print(PySwigPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags)) +{ + char result[SWIG_BUFFER_SIZE]; + fputs("<Swig Packed ", fp); + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { + fputs("at ", fp); + fputs(result, fp); + } + fputs(v->ty->name,fp); + fputs(">", fp); + return 0; +} + +SWIGRUNTIME PyObject * +PySwigPacked_repr(PySwigPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { + return PyString_FromFormat("<Swig Packed at %s%s>", result, v->ty->name); + } else { + return PyString_FromFormat("<Swig Packed %s>", v->ty->name); + } +} + +SWIGRUNTIME PyObject * +PySwigPacked_str(PySwigPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ + return PyString_FromFormat("%s%s", result, v->ty->name); + } else { + return PyString_FromString(v->ty->name); + } +} + +SWIGRUNTIME int +PySwigPacked_compare(PySwigPacked *v, PySwigPacked *w) +{ + size_t i = v->size; + size_t j = w->size; + int s = (i < j) ? -1 : ((i > j) ? 1 : 0); + return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size); +} + +SWIGRUNTIME PyTypeObject* _PySwigPacked_type(void); + +SWIGRUNTIME PyTypeObject* +PySwigPacked_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigPacked_type(); + return type; +} + +SWIGRUNTIMEINLINE int +PySwigPacked_Check(PyObject *op) { + return ((op)->ob_type == _PySwigPacked_type()) + || (strcmp((op)->ob_type->tp_name,"PySwigPacked") == 0); +} + +SWIGRUNTIME void +PySwigPacked_dealloc(PyObject *v) +{ + if (PySwigPacked_Check(v)) { + PySwigPacked *sobj = (PySwigPacked *) v; + free(sobj->pack); + } + PyObject_DEL(v); +} + +SWIGRUNTIME PyTypeObject* +_PySwigPacked_type(void) { + static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; + static PyTypeObject pyswigpacked_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp + = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + (char *)"PySwigPacked", /* tp_name */ + sizeof(PySwigPacked), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PySwigPacked_dealloc, /* tp_dealloc */ + (printfunc)PySwigPacked_print, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ + (cmpfunc)PySwigPacked_compare, /* tp_compare */ + (reprfunc)PySwigPacked_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)PySwigPacked_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigpacked_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#ifdef COUNT_ALLOCS + 0,0,0,0 /* tp_alloc -> tp_next */ +#endif + }; + pyswigpacked_type = tmp; + pyswigpacked_type.ob_type = &PyType_Type; + type_init = 1; + } + return &pyswigpacked_type; +} + +SWIGRUNTIME PyObject * +PySwigPacked_New(void *ptr, size_t size, swig_type_info *ty) +{ + PySwigPacked *sobj = PyObject_NEW(PySwigPacked, PySwigPacked_type()); + if (sobj) { + void *pack = malloc(size); + if (pack) { + memcpy(pack, ptr, size); + sobj->pack = pack; + sobj->ty = ty; + sobj->size = size; + } else { + PyObject_DEL((PyObject *) sobj); + sobj = 0; + } + } + return (PyObject *) sobj; +} + +SWIGRUNTIME swig_type_info * +PySwigPacked_UnpackData(PyObject *obj, void *ptr, size_t size) +{ + if (PySwigPacked_Check(obj)) { + PySwigPacked *sobj = (PySwigPacked *)obj; + if (sobj->size != size) return 0; + memcpy(ptr, sobj->pack, size); + return sobj->ty; + } else { + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIMEINLINE PyObject * +_SWIG_This(void) +{ + return PyString_FromString("this"); +} + +SWIGRUNTIME PyObject * +SWIG_This(void) +{ + static PyObject *SWIG_STATIC_POINTER(swig_this) = _SWIG_This(); + return swig_this; +} + +/* #define SWIG_PYTHON_SLOW_GETSET_THIS */ + +SWIGRUNTIME PySwigObject * +SWIG_Python_GetSwigThis(PyObject *pyobj) +{ + if (PySwigObject_Check(pyobj)) { + return (PySwigObject *) pyobj; + } else { + PyObject *obj = 0; +#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000)) + if (PyInstance_Check(pyobj)) { + obj = _PyInstance_Lookup(pyobj, SWIG_This()); + } else { + PyObject **dictptr = _PyObject_GetDictPtr(pyobj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; + } else { +#ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); + return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; + } +#endif + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } + } + } +#else + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } +#endif + if (obj && !PySwigObject_Check(obj)) { + /* a PyObject is called 'this', try to get the 'real this' + PySwigObject from it */ + return SWIG_Python_GetSwigThis(obj); + } + return (PySwigObject *)obj; + } +} + +/* Acquire a pointer value */ + +SWIGRUNTIME int +SWIG_Python_AcquirePtr(PyObject *obj, int own) { + if (own == SWIG_POINTER_OWN) { + PySwigObject *sobj = SWIG_Python_GetSwigThis(obj); + if (sobj) { + int oldown = sobj->own; + sobj->own = own; + return oldown; + } + } + return 0; +} + +/* Convert a pointer value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { + if (!obj) return SWIG_ERROR; + if (obj == Py_None) { + if (ptr) *ptr = 0; + return SWIG_OK; + } else { + PySwigObject *sobj = SWIG_Python_GetSwigThis(obj); + if (own) + *own = 0; + while (sobj) { + void *vptr = sobj->ptr; + if (ty) { + swig_type_info *to = sobj->ty; + if (to == ty) { + /* no type cast needed */ + if (ptr) *ptr = vptr; + break; + } else { + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) { + sobj = (PySwigObject *)sobj->next; + } else { + if (ptr) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + if (newmemory == SWIG_CAST_NEW_MEMORY) { + assert(own); + if (own) + *own = *own | SWIG_CAST_NEW_MEMORY; + } + } + break; + } + } + } else { + if (ptr) *ptr = vptr; + break; + } + } + if (sobj) { + if (own) + *own = *own | sobj->own; + if (flags & SWIG_POINTER_DISOWN) { + sobj->own = 0; + } + return SWIG_OK; + } else { + int res = SWIG_ERROR; + if (flags & SWIG_POINTER_IMPLICIT_CONV) { + PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0; + if (data && !data->implicitconv) { + PyObject *klass = data->klass; + if (klass) { + PyObject *impconv; + data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ + impconv = SWIG_Python_CallFunctor(klass, obj); + data->implicitconv = 0; + if (PyErr_Occurred()) { + PyErr_Clear(); + impconv = 0; + } + if (impconv) { + PySwigObject *iobj = SWIG_Python_GetSwigThis(impconv); + if (iobj) { + void *vptr; + res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); + if (SWIG_IsOK(res)) { + if (ptr) { + *ptr = vptr; + /* transfer the ownership to 'ptr' */ + iobj->own = 0; + res = SWIG_AddCast(res); + res = SWIG_AddNewMask(res); + } else { + res = SWIG_AddCast(res); + } + } + } + Py_DECREF(impconv); + } + } + } + } + return res; + } + } +} + +/* Convert a function ptr value */ + +SWIGRUNTIME int +SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { + if (!PyCFunction_Check(obj)) { + return SWIG_ConvertPtr(obj, ptr, ty, 0); + } else { + void *vptr = 0; + + /* here we get the method pointer for callbacks */ + const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); + const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; + if (desc) { + desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; + if (!desc) return SWIG_ERROR; + } + if (ty) { + swig_cast_info *tc = SWIG_TypeCheck(desc,ty); + if (tc) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + assert(!newmemory); /* newmemory handling not yet implemented */ + } else { + return SWIG_ERROR; + } + } else { + *ptr = vptr; + } + return SWIG_OK; + } +} + +/* Convert a packed value value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { + swig_type_info *to = PySwigPacked_UnpackData(obj, ptr, sz); + if (!to) return SWIG_ERROR; + if (ty) { + if (to != ty) { + /* check type cast? */ + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) return SWIG_ERROR; + } + } + return SWIG_OK; +} + +/* ----------------------------------------------------------------------------- + * Create a new pointer object + * ----------------------------------------------------------------------------- */ + +/* + Create a new instance object, whitout calling __init__, and set the + 'this' attribute. +*/ + +SWIGRUNTIME PyObject* +SWIG_Python_NewShadowInstance(PySwigClientData *data, PyObject *swig_this) +{ +#if (PY_VERSION_HEX >= 0x02020000) + PyObject *inst = 0; + PyObject *newraw = data->newraw; + if (newraw) { + inst = PyObject_Call(newraw, data->newargs, NULL); + if (inst) { +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + PyDict_SetItem(dict, SWIG_This(), swig_this); + } + } +#else + PyObject *key = SWIG_This(); + PyObject_SetAttr(inst, key, swig_this); +#endif + } + } else { + PyObject *dict = PyDict_New(); + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + } + return inst; +#else +#if (PY_VERSION_HEX >= 0x02010000) + PyObject *inst; + PyObject *dict = PyDict_New(); + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + return (PyObject *) inst; +#else + PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); + if (inst == NULL) { + return NULL; + } + inst->in_class = (PyClassObject *)data->newargs; + Py_INCREF(inst->in_class); + inst->in_dict = PyDict_New(); + if (inst->in_dict == NULL) { + Py_DECREF(inst); + return NULL; + } +#ifdef Py_TPFLAGS_HAVE_WEAKREFS + inst->in_weakreflist = NULL; +#endif +#ifdef Py_TPFLAGS_GC + PyObject_GC_Init(inst); +#endif + PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this); + return (PyObject *) inst; +#endif +#endif +} + +SWIGRUNTIME void +SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) +{ + PyObject *dict; +#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + } + PyDict_SetItem(dict, SWIG_This(), swig_this); + return; + } +#endif + dict = PyObject_GetAttrString(inst, (char*)"__dict__"); + PyDict_SetItem(dict, SWIG_This(), swig_this); + Py_DECREF(dict); +} + + +SWIGINTERN PyObject * +SWIG_Python_InitShadowInstance(PyObject *args) { + PyObject *obj[2]; + if (!SWIG_Python_UnpackTuple(args,(char*)"swiginit", 2, 2, obj)) { + return NULL; + } else { + PySwigObject *sthis = SWIG_Python_GetSwigThis(obj[0]); + if (sthis) { + PySwigObject_append((PyObject*) sthis, obj[1]); + } else { + SWIG_Python_SetSwigThis(obj[0], obj[1]); + } + return SWIG_Py_Void(); + } +} + +/* Create a new pointer object */ + +SWIGRUNTIME PyObject * +SWIG_Python_NewPointerObj(void *ptr, swig_type_info *type, int flags) { + if (!ptr) { + return SWIG_Py_Void(); + } else { + int own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; + PyObject *robj = PySwigObject_New(ptr, type, own); + PySwigClientData *clientdata = type ? (PySwigClientData *)(type->clientdata) : 0; + if (clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { + PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); + if (inst) { + Py_DECREF(robj); + robj = inst; + } + } + return robj; + } +} + +/* Create a new packed object */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { + return ptr ? PySwigPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); +} + +/* -----------------------------------------------------------------------------* + * Get type list + * -----------------------------------------------------------------------------*/ + +#ifdef SWIG_LINK_RUNTIME +void *SWIG_ReturnGlobalTypeList(void *); +#endif + +SWIGRUNTIME swig_module_info * +SWIG_Python_GetModule(void) { + static void *type_pointer = (void *)0; + /* first check if module already created */ + if (!type_pointer) { +#ifdef SWIG_LINK_RUNTIME + type_pointer = SWIG_ReturnGlobalTypeList((void *)0); +#else + type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, + (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); + if (PyErr_Occurred()) { + PyErr_Clear(); + type_pointer = (void *)0; + } +#endif + } + return (swig_module_info *) type_pointer; +} + +#if PY_MAJOR_VERSION < 2 +/* PyModule_AddObject function was introduced in Python 2.0. The following function + is copied out of Python/modsupport.c in python version 2.3.4 */ +SWIGINTERN int +PyModule_AddObject(PyObject *m, char *name, PyObject *o) +{ + PyObject *dict; + if (!PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs module as first arg"); + return SWIG_ERROR; + } + if (!o) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs non-NULL value"); + return SWIG_ERROR; + } + + dict = PyModule_GetDict(m); + if (dict == NULL) { + /* Internal error -- modules must have a dict! */ + PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", + PyModule_GetName(m)); + return SWIG_ERROR; + } + if (PyDict_SetItemString(dict, name, o)) + return SWIG_ERROR; + Py_DECREF(o); + return SWIG_OK; +} +#endif + +SWIGRUNTIME void +SWIG_Python_DestroyModule(void *vptr) +{ + swig_module_info *swig_module = (swig_module_info *) vptr; + swig_type_info **types = swig_module->types; + size_t i; + for (i =0; i < swig_module->size; ++i) { + swig_type_info *ty = types[i]; + if (ty->owndata) { + PySwigClientData *data = (PySwigClientData *) ty->clientdata; + if (data) PySwigClientData_Del(data); + } + } + Py_DECREF(SWIG_This()); +} + +SWIGRUNTIME void +SWIG_Python_SetModule(swig_module_info *swig_module) { + static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} };/* Sentinel */ + + PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, + swig_empty_runtime_method_table); + PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); + } else { + Py_XDECREF(pointer); + } +} + +/* The python cached type query */ +SWIGRUNTIME PyObject * +SWIG_Python_TypeCache(void) { + static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); + return cache; +} + +SWIGRUNTIME swig_type_info * +SWIG_Python_TypeQuery(const char *type) +{ + PyObject *cache = SWIG_Python_TypeCache(); + PyObject *key = PyString_FromString(type); + PyObject *obj = PyDict_GetItem(cache, key); + swig_type_info *descriptor; + if (obj) { + descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj); + } else { + swig_module_info *swig_module = SWIG_Python_GetModule(); + descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); + if (descriptor) { + obj = PyCObject_FromVoidPtr(descriptor, NULL); + PyDict_SetItem(cache, key, obj); + Py_DECREF(obj); + } + } + Py_DECREF(key); + return descriptor; +} + +/* + For backward compatibility only +*/ +#define SWIG_POINTER_EXCEPTION 0 +#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) +#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) + +SWIGRUNTIME int +SWIG_Python_AddErrMesg(const char* mesg, int infront) +{ + if (PyErr_Occurred()) { + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + PyErr_Fetch(&type, &value, &traceback); + if (value) { + PyObject *old_str = PyObject_Str(value); + Py_XINCREF(type); + PyErr_Clear(); + if (infront) { + PyErr_Format(type, "%s %s", mesg, PyString_AsString(old_str)); + } else { + PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg); + } + Py_DECREF(old_str); + } + return 1; + } else { + return 0; + } +} + +SWIGRUNTIME int +SWIG_Python_ArgFail(int argnum) +{ + if (PyErr_Occurred()) { + /* add information about failing argument */ + char mesg[256]; + PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); + return SWIG_Python_AddErrMesg(mesg, 1); + } else { + return 0; + } +} + +SWIGRUNTIMEINLINE const char * +PySwigObject_GetDesc(PyObject *self) +{ + PySwigObject *v = (PySwigObject *)self; + swig_type_info *ty = v ? v->ty : 0; + return ty ? ty->str : (char*)""; +} + +SWIGRUNTIME void +SWIG_Python_TypeError(const char *type, PyObject *obj) +{ + if (type) { +#if defined(SWIG_COBJECT_TYPES) + if (obj && PySwigObject_Check(obj)) { + const char *otype = (const char *) PySwigObject_GetDesc(obj); + if (otype) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'PySwigObject(%s)' is received", + type, otype); + return; + } + } else +#endif + { + const char *otype = (obj ? obj->ob_type->tp_name : 0); + if (otype) { + PyObject *str = PyObject_Str(obj); + const char *cstr = str ? PyString_AsString(str) : 0; + if (cstr) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", + type, otype, cstr); + } else { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", + type, otype); + } + Py_XDECREF(str); + return; + } + } + PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); + } else { + PyErr_Format(PyExc_TypeError, "unexpected type is received"); + } +} + + +/* Convert a pointer value, signal an exception on a type mismatch */ +SWIGRUNTIME void * +SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int argnum, int flags) { + void *result; + if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { + PyErr_Clear(); + if (flags & SWIG_POINTER_EXCEPTION) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + SWIG_Python_ArgFail(argnum); + } + } + return result; +} + + +#ifdef __cplusplus +#if 0 +{ /* cc-mode */ +#endif +} +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else + + + + #define SWIG_exception(code, msg) do { SWIG_Error(code, msg); SWIG_fail;; } while(0) + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_TALLOC_CTX swig_types[0] +#define SWIGTYPE_p_char swig_types[1] +#define SWIGTYPE_p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void swig_types[2] +#define SWIGTYPE_p_int swig_types[3] +#define SWIGTYPE_p_ldb_context swig_types[4] +#define SWIGTYPE_p_ldb_dn swig_types[5] +#define SWIGTYPE_p_ldb_ldif swig_types[6] +#define SWIGTYPE_p_ldb_message swig_types[7] +#define SWIGTYPE_p_ldb_message_element swig_types[8] +#define SWIGTYPE_p_ldb_module_ops swig_types[9] +#define SWIGTYPE_p_ldb_result swig_types[10] +#define SWIGTYPE_p_ldb_val swig_types[11] +#define SWIGTYPE_p_long_long swig_types[12] +#define SWIGTYPE_p_p_char swig_types[13] +#define SWIGTYPE_p_p_ldb_control swig_types[14] +#define SWIGTYPE_p_p_ldb_result swig_types[15] +#define SWIGTYPE_p_short swig_types[16] +#define SWIGTYPE_p_signed_char swig_types[17] +#define SWIGTYPE_p_unsigned_char swig_types[18] +#define SWIGTYPE_p_unsigned_int swig_types[19] +#define SWIGTYPE_p_unsigned_long swig_types[20] +#define SWIGTYPE_p_unsigned_long_long swig_types[21] +#define SWIGTYPE_p_unsigned_short swig_types[22] +#define SWIGTYPE_p_void swig_types[23] +static swig_type_info *swig_types[25]; +static swig_module_info swig_module = {swig_types, 24, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#if (PY_VERSION_HEX <= 0x02000000) +# if !defined(SWIG_PYTHON_CLASSIC) +# error "This python version requires swig to be run with the '-classic' option" +# endif +#endif +#if (PY_VERSION_HEX <= 0x02020000) +# error "This python version requires swig to be run with the '-nomodern' option" +#endif +#if (PY_VERSION_HEX <= 0x02020000) +# error "This python version requires swig to be run with the '-nomodernargs' option" +#endif +#ifndef METH_O +# error "This python version requires swig to be run with the '-nofastunpack' option" +#endif +#ifdef SWIG_TypeQuery +# undef SWIG_TypeQuery +#endif +#define SWIG_TypeQuery SWIG_Python_TypeQuery + +/*----------------------------------------------- + @(target):= _ldb.so + ------------------------------------------------*/ +#define SWIG_init init_ldb + +#define SWIG_name "_ldb" + +#define SWIGVERSION 0x010335 +#define SWIG_VERSION SWIGVERSION + + +#define SWIG_as_voidptr(a) (void *)((const void *)(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) + + + +#include <stdint.h> +#include <stdbool.h> +#include "talloc.h" +#include "events.h" +#include "ldb.h" +#include "ldb_errors.h" +#include "ldb_private.h" + +typedef struct ldb_message ldb_msg; +typedef struct ldb_context ldb; +typedef struct ldb_dn ldb_dn; +typedef struct ldb_ldif ldb_ldif; +typedef struct ldb_message_element ldb_message_element; +typedef int ldb_error; +typedef int ldb_int_error; + + + + #define SWIG_From_long PyInt_FromLong + + +SWIGINTERNINLINE PyObject * +SWIG_From_int (int value) +{ + return SWIG_From_long (value); +} + + +PyObject *ldb_val_to_py_object(struct ldb_context *ldb_ctx, + struct ldb_message_element *el, + struct ldb_val *val) +{ + const struct ldb_schema_attribute *a; + struct ldb_val new_val; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + PyObject *ret; + + new_val = *val; + + if (ldb_ctx != NULL) { + a = ldb_schema_attribute_by_name(ldb_ctx, el->name); + + if (a != NULL) { + if (a->syntax->ldif_write_fn(ldb_ctx, mem_ctx, val, &new_val) != 0) { + talloc_free(mem_ctx); + return NULL; + } + } + } + + ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length); + + talloc_free(mem_ctx); + + return ret; +} + + + +SWIGINTERN swig_type_info* +SWIG_pchar_descriptor(void) +{ + static int init = 0; + static swig_type_info* info = 0; + if (!init) { + info = SWIG_TypeQuery("_p_char"); + init = 1; + } + return info; +} + + +SWIGINTERN int +SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) +{ + if (PyString_Check(obj)) { + char *cstr; Py_ssize_t len; + PyString_AsStringAndSize(obj, &cstr, &len); + if (cptr) { + if (alloc) { + /* + In python the user should not be able to modify the inner + string representation. To warranty that, if you define + SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string + buffer is always returned. + + The default behavior is just to return the pointer value, + so, be careful. + */ +#if defined(SWIG_PYTHON_SAFE_CSTRINGS) + if (*alloc != SWIG_OLDOBJ) +#else + if (*alloc == SWIG_NEWOBJ) +#endif + { + *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); + *alloc = SWIG_NEWOBJ; + } + else { + *cptr = cstr; + *alloc = SWIG_OLDOBJ; + } + } else { + *cptr = PyString_AsString(obj); + } + } + if (psize) *psize = len + 1; + return SWIG_OK; + } else { + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + void* vptr = 0; + if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = (char *) vptr; + if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; +} + + + + +SWIGINTERN ldb_dn *new_ldb_dn(ldb *ldb_ctx,char const *str){ + ldb_dn *ret = ldb_dn_new(ldb_ctx, ldb_ctx, str); + /* ldb_dn_new() doesn't accept NULL as memory context, so + we do it this way... */ + talloc_steal(NULL, ret); + + if (ret == NULL) + SWIG_exception(SWIG_ValueError, + "unable to parse dn string"); +fail: + return ret; + } +SWIGINTERN void delete_ldb_dn(ldb_dn *self){ talloc_free(self); } + +SWIGINTERNINLINE PyObject* + SWIG_From_bool (bool value) +{ + return PyBool_FromLong(value ? 1 : 0); +} + + +SWIGINTERNINLINE PyObject * +SWIG_FromCharPtrAndSize(const char* carray, size_t size) +{ + if (carray) { + if (size > INT_MAX) { + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + return pchar_descriptor ? + SWIG_NewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void(); + } else { + return PyString_FromStringAndSize(carray, (int)(size)); + } + } else { + return SWIG_Py_Void(); + } +} + + +SWIGINTERNINLINE PyObject * +SWIG_FromCharPtr(const char *cptr) +{ + return SWIG_FromCharPtrAndSize(cptr, (cptr ? strlen(cptr) : 0)); +} + +SWIGINTERN ldb_dn *ldb_dn_parent(ldb_dn *self){ return ldb_dn_get_parent(NULL, self); } +SWIGINTERN char const *ldb_dn_canonical_str(ldb_dn *self){ + return ldb_dn_canonical_string(self, self); + } +SWIGINTERN char const *ldb_dn_canonical_ex_str(ldb_dn *self){ + return ldb_dn_canonical_ex_string(self, self); + } +SWIGINTERN char *ldb_dn___repr__(ldb_dn *self){ + char *dn = ldb_dn_get_linearized(self), *ret; + asprintf(&ret, "Dn('%s')", dn); + talloc_free(dn); + return ret; + } +SWIGINTERN ldb_dn *ldb_dn___add__(ldb_dn *self,ldb_dn *other){ + ldb_dn *ret = ldb_dn_copy(NULL, self); + ldb_dn_add_child(ret, other); + return ret; + } + +struct ldb_context *ldb_context_from_py_object(PyObject *py_obj) +{ + struct ldb_context *ldb_ctx; + if (SWIG_ConvertPtr(py_obj, (void *)&ldb_ctx, SWIGTYPE_p_ldb_context, 0 | 0 ) < 0) + return NULL; + return ldb_ctx; +} + +int ldb_dn_from_pyobject(TALLOC_CTX *mem_ctx, PyObject *object, + struct ldb_context *ldb_ctx, ldb_dn **dn) +{ + int ret; + struct ldb_dn *odn; + if (ldb_ctx != NULL && PyString_Check(object)) { + odn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object)); + if (!odn) { + return SWIG_ERROR; + } + *dn = odn; + return 0; + } + ret = SWIG_ConvertPtr(object, (void **)&odn, SWIGTYPE_p_ldb_dn, + SWIG_POINTER_EXCEPTION); + *dn = ldb_dn_copy(mem_ctx, odn); + if (odn && !*dn) { + return SWIG_ERROR; + } + return ret; +} + +ldb_message_element *ldb_msg_element_from_pyobject(TALLOC_CTX *mem_ctx, + PyObject *set_obj, int flags, + const char *attr_name) +{ + struct ldb_message_element *me = talloc(mem_ctx, struct ldb_message_element); + me->name = attr_name; + me->flags = flags; + if (PyString_Check(set_obj)) { + me->num_values = 1; + me->values = talloc_array(me, struct ldb_val, me->num_values); + me->values[0].length = PyString_Size(set_obj); + me->values[0].data = (uint8_t *)talloc_strdup(me->values, + PyString_AsString(set_obj)); + } else if (PySequence_Check(set_obj)) { + int i; + me->num_values = PySequence_Size(set_obj); + me->values = talloc_array(me, struct ldb_val, me->num_values); + for (i = 0; i < me->num_values; i++) { + PyObject *obj = PySequence_GetItem(set_obj, i); + me->values[i].length = PyString_Size(obj); + me->values[i].data = (uint8_t *)PyString_AsString(obj); + } + } else { + talloc_free(me); + me = NULL; + } + + return me; +} + +PyObject *ldb_msg_element_to_set(struct ldb_context *ldb_ctx, + ldb_message_element *me) +{ + int i; + PyObject *result; + + /* Python << 2.5 doesn't have PySet_New and PySet_Add. */ + result = PyList_New(me->num_values); + + for (i = 0; i < me->num_values; i++) { + PyList_SetItem(result, i, + ldb_val_to_py_object(ldb_ctx, me, &me->values[i])); + } + + return result; +} + + +SWIGINTERN int ldb_message_element___cmp__(ldb_message_element *self,ldb_message_element *other){ + return ldb_msg_element_compare(self, other); + } +SWIGINTERN PyObject *ldb_message_element___iter__(ldb_message_element *self){ + return PyObject_GetIter(ldb_msg_element_to_set(NULL, self)); + } +SWIGINTERN PyObject *ldb_message_element___set__(ldb_message_element *self){ + return ldb_msg_element_to_set(NULL, self); + } + +#include <limits.h> +#if !defined(SWIG_NO_LLONG_MAX) +# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) +# define LLONG_MAX __LONG_LONG_MAX__ +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +# endif +#endif + + +SWIGINTERN int +SWIG_AsVal_double (PyObject *obj, double *val) +{ + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; + } else if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include <float.h> + + +#include <math.h> + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx = floor(x); + double cx = ceil(x); + double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff/summ; + if (reps < 8*DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_long (PyObject *obj, long* val) +{ + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int (PyObject * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = (int)(v); + } + } + return res; +} + +SWIGINTERN ldb_message_element *new_ldb_message_element(PyObject *set_obj,int flags,char const *name){ + return ldb_msg_element_from_pyobject(NULL, set_obj, flags, name); + } +SWIGINTERN int ldb_message_element___len__(ldb_message_element *self){ + return self->num_values; + } +SWIGINTERN PyObject *ldb_message_element_get(ldb_message_element *self,int i){ + if (i < 0 || i >= self->num_values) + return Py_None; + + return ldb_val_to_py_object(NULL, self, &self->values[i]); + } +SWIGINTERN void delete_ldb_message_element(ldb_message_element *self){ talloc_free(self); } + + PyObject *ldb_msg_list_elements(ldb_msg *msg) + { + int i, j = 0; + PyObject *obj = PyList_New(msg->num_elements+(msg->dn != NULL?1:0)); + if (msg->dn != NULL) { + PyList_SetItem(obj, j, PyString_FromString("dn")); + j++; + } + for (i = 0; i < msg->num_elements; i++) { + PyList_SetItem(obj, j, PyString_FromString(msg->elements[i].name)); + j++; + } + return obj; + } + +SWIGINTERN ldb_msg *new_ldb_msg(ldb_dn *dn){ + ldb_msg *ret = ldb_msg_new(NULL); + ret->dn = talloc_reference(ret, dn); + return ret; + } +SWIGINTERN void delete_ldb_msg(ldb_msg *self){ talloc_free(self); } +SWIGINTERN void ldb_msg___setitem____SWIG_0(ldb_msg *self,char const *attr_name,ldb_message_element *val){ + struct ldb_message_element *el; + + ldb_msg_remove_attr(self, attr_name); + + el = talloc(self, struct ldb_message_element); + el->name = talloc_strdup(el, attr_name); + el->num_values = val->num_values; + el->values = talloc_reference(el, val->values); + + ldb_msg_add(self, el, val->flags); + } +SWIGINTERN void ldb_msg___setitem____SWIG_1(ldb_msg *self,char const *attr_name,PyObject *val){ + struct ldb_message_element *el = ldb_msg_element_from_pyobject(NULL, + val, 0, attr_name); + talloc_steal(self, el); + ldb_msg_remove_attr(self, attr_name); + ldb_msg_add(self, el, el->flags); + } +SWIGINTERN unsigned int ldb_msg___len__(ldb_msg *self){ return self->num_elements; } + +SWIGINTERNINLINE PyObject* +SWIG_From_unsigned_SS_long (unsigned long value) +{ + return (value > LONG_MAX) ? + PyLong_FromUnsignedLong(value) : PyInt_FromLong((long)(value)); +} + + +SWIGINTERNINLINE PyObject * +SWIG_From_unsigned_SS_int (unsigned int value) +{ + return SWIG_From_unsigned_SS_long (value); +} + +SWIGINTERN PyObject *ldb_msg_keys(ldb_msg *self){ + return ldb_msg_list_elements(self); + } +SWIGINTERN PyObject *ldb_msg___iter__(ldb_msg *self){ + return PyObject_GetIter(ldb_msg_list_elements(self)); + } + +static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3, 0); + +static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) +{ + char *text; + PyObject *fn = context; + + vasprintf(&text, fmt, ap); + PyObject_CallFunction(fn, (char *)"(i,s)", level, text); + free(text); +} + + + static PyObject *ldb_ldif_to_pyobject(ldb_ldif *ldif) + { + if (ldif == NULL) { + return Py_None; + } else { + return Py_BuildValue((char *)"(iO)", ldif->changetype, + SWIG_NewPointerObj(ldif->msg, SWIGTYPE_p_ldb_message, 0)); + } + } + + +PyObject *PyExc_LdbError; + +SWIGINTERN ldb *new_ldb(){ + return ldb_init(NULL, event_context_init(NULL)); + } + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) +{ + if (PyInt_Check(obj)) { + long v = PyInt_AsLong(obj); + if (v >= 0) { + if (val) *val = v; + return SWIG_OK; + } else { + return SWIG_OverflowError; + } + } else if (PyLong_Check(obj)) { + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) { + if (val) *val = (unsigned long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_int (PyObject * obj, unsigned int *val) +{ + unsigned long v; + int res = SWIG_AsVal_unsigned_SS_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v > UINT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = (unsigned int)(v); + } + } + return res; +} + +SWIGINTERN void delete_ldb(ldb *self){ talloc_free(self); } +SWIGINTERN ldb_error ldb_search_ex(ldb *self,TALLOC_CTX *mem_ctx,ldb_dn *base,enum ldb_scope scope,char const *expression,char const *const *attrs,struct ldb_control **controls,struct ldb_result **OUT){ + int ret; + struct ldb_result *res; + struct ldb_request *req; + res = talloc_zero(mem_ctx, struct ldb_result); + if (!res) { + return 1; + } + + ret = ldb_build_search_req(&req, self, mem_ctx, + base?base:ldb_get_default_basedn(self), + scope, + expression, + attrs, + controls, + res, + ldb_search_default_callback); + + if (ret != 0) { + talloc_free(res); + return ret; + } + + ldb_set_timeout(self, req, 0); /* use default timeout */ + + ret = ldb_request(self, req); + + if (ret == 0) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(req); + + *OUT = res; + return ret; + } +SWIGINTERN PyObject *ldb_schema_format_value(ldb *self,char const *element_name,PyObject *val){ + const struct ldb_schema_attribute *a; + struct ldb_val old_val; + struct ldb_val new_val; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + PyObject *ret; + + old_val.data = PyString_AsString(val); + old_val.length = PyString_Size(val); + + a = ldb_schema_attribute_by_name(self, element_name); + + if (a == NULL) { + return Py_None; + } + + if (a->syntax->ldif_write_fn(self, mem_ctx, &old_val, &new_val) != 0) { + talloc_free(mem_ctx); + return Py_None; + } + + ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length); + + talloc_free(mem_ctx); + + return ret; + } +SWIGINTERN ldb_error ldb___contains__(ldb *self,ldb_dn *dn,struct ldb_result **result_as_bool){ + return ldb_search(self, dn, LDB_SCOPE_BASE, NULL, NULL, + result_as_bool); + } +SWIGINTERN PyObject *ldb_parse_ldif(ldb *self,char const *s){ + PyObject *list = PyList_New(0); + struct ldb_ldif *ldif; + while ((ldif = ldb_ldif_read_string(self, &s)) != NULL) { + PyList_Append(list, ldb_ldif_to_pyobject(ldif)); + } + return PyObject_GetIter(list); + } +SWIGINTERN char *ldb___repr__(ldb *self){ + char *ret; + asprintf(&ret, "<ldb connection at 0x%x>", ret); + return ret; + } + +static char *timestring(time_t t) +{ + char *tresult = ldb_timestring(NULL, t); + char *result = strdup(tresult); + talloc_free(tresult); + return result; +} + +#ifdef __cplusplus +extern "C" { +#endif +SWIGINTERN PyObject *_wrap_ldb_val_to_py_object(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + struct ldb_context *arg1 = (struct ldb_context *) 0 ; + struct ldb_message_element *arg2 = (struct ldb_message_element *) 0 ; + struct ldb_val *arg3 = (struct ldb_val *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + void *argp3 = 0 ; + int res3 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + char * kwnames[] = { + (char *) "ldb_ctx",(char *) "el",(char *) "val", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:ldb_val_to_py_object",kwnames,&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ldb_val_to_py_object" "', argument " "1"" of type '" "struct ldb_context *""'"); + } + arg1 = (struct ldb_context *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ldb_val_to_py_object" "', argument " "2"" of type '" "struct ldb_message_element *""'"); + } + arg2 = (struct ldb_message_element *)(argp2); + res3 = SWIG_ConvertPtr(obj2, &argp3,SWIGTYPE_p_ldb_val, 0 | 0 ); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "ldb_val_to_py_object" "', argument " "3"" of type '" "struct ldb_val *""'"); + } + arg3 = (struct ldb_val *)(argp3); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (PyObject *)ldb_val_to_py_object(arg1,arg2,arg3); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_Dn(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "ldb_ctx",(char *) "str", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:new_Dn",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_Dn" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "new_Dn" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (ldb_dn *)new_ldb_dn(arg1,(char const *)arg2); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, SWIG_POINTER_NEW | 0 ); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_Dn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_Dn" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + delete_ldb_dn(arg1); + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_validate(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_validate" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (bool)ldb_dn_validate(arg1); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_get_casefold(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_get_casefold" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (char *)ldb_dn_get_casefold(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn___str__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn___str__" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (char *)ldb_dn_get_linearized(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_parent(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_parent" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (ldb_dn *)ldb_dn_parent(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn___cmp__(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "other", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Dn___cmp__",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn___cmp__" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Dn___cmp__" "', argument " "2"" of type '" "ldb_dn *""'"); + } + arg2 = (ldb_dn *)(argp2); + result = (int)ldb_dn_compare(arg1,arg2); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_is_valid(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_is_valid" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (bool)ldb_dn_is_valid(arg1); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_is_special(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_is_special" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (bool)ldb_dn_is_special(arg1); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_is_null(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_is_null" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (bool)ldb_dn_is_null(arg1); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_check_special(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *arg2 = (char *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Dn_check_special",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_check_special" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Dn_check_special" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + result = (bool)ldb_dn_check_special(arg1,(char const *)arg2); + resultobj = SWIG_From_bool((bool)(result)); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn___len__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn___len__" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (int)ldb_dn_get_comp_num(arg1); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_add_child(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "child", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Dn_add_child",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_add_child" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Dn_add_child" "', argument " "2"" of type '" "ldb_dn *""'"); + } + arg2 = (ldb_dn *)(argp2); + result = (bool)ldb_dn_add_child(arg1,arg2); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_add_base(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "base", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Dn_add_base",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_add_base" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Dn_add_base" "', argument " "2"" of type '" "ldb_dn *""'"); + } + arg2 = (ldb_dn *)(argp2); + result = (bool)ldb_dn_add_base(arg1,arg2); + resultobj = SWIG_From_bool((bool)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_canonical_str(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_canonical_str" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (char *)ldb_dn_canonical_str(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn_canonical_ex_str(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn_canonical_ex_str" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (char *)ldb_dn_canonical_ex_str(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn___repr__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn___repr__" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + result = (char *)ldb_dn___repr__(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Dn___add__(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "other", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Dn___add__",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Dn___add__" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Dn___add__" "', argument " "2"" of type '" "ldb_dn *""'"); + } + arg2 = (ldb_dn *)(argp2); + result = (ldb_dn *)ldb_dn___add__(arg1,arg2); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *Dn_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj; + if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_ldb_dn, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *Dn_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_MessageElement___cmp__(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + ldb_message_element *arg2 = (ldb_message_element *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "other", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:MessageElement___cmp__",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "MessageElement___cmp__" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "MessageElement___cmp__" "', argument " "2"" of type '" "ldb_message_element *""'"); + } + arg2 = (ldb_message_element *)(argp2); + result = (int)ldb_message_element___cmp__(arg1,arg2); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_MessageElement___iter__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "MessageElement___iter__" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + result = (PyObject *)ldb_message_element___iter__(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_MessageElement___set__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "MessageElement___set__" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + result = (PyObject *)ldb_message_element___set__(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_MessageElement(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + PyObject *arg1 = (PyObject *) 0 ; + int arg2 = (int) 0 ; + char *arg3 = (char *) NULL ; + ldb_message_element *result = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + char * kwnames[] = { + (char *) "set_obj",(char *) "flags",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O|OO:new_MessageElement",kwnames,&obj0,&obj1,&obj2)) SWIG_fail; + arg1 = obj0; + if (obj1) { + ecode2 = SWIG_AsVal_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_MessageElement" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + } + if (obj2) { + res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "new_MessageElement" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = (char *)(buf3); + } + result = (ldb_message_element *)new_ldb_message_element(arg1,arg2,(char const *)arg3); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_message_element, SWIG_POINTER_NEW | 0 ); + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return resultobj; +fail: + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_MessageElement___len__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "MessageElement___len__" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + result = (int)ldb_message_element___len__(arg1); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_MessageElement_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + int arg2 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "i", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:MessageElement_get",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "MessageElement_get" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + ecode2 = SWIG_AsVal_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "MessageElement_get" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + result = (PyObject *)ldb_message_element_get(arg1,arg2); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_MessageElement(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_message_element *arg1 = (ldb_message_element *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message_element, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_MessageElement" "', argument " "1"" of type '" "ldb_message_element *""'"); + } + arg1 = (ldb_message_element *)(argp1); + delete_ldb_message_element(arg1); + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *MessageElement_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj; + if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_ldb_message_element, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *MessageElement_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_ldb_msg_list_elements(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "msg", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:ldb_msg_list_elements",kwnames,&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ldb_msg_list_elements" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (PyObject *)ldb_msg_list_elements(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message_dn_set(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject *swig_obj[2] ; + + if (!SWIG_Python_UnpackTuple(args,"Message_dn_set",2,2,swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message_dn_set" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + res2 = SWIG_ConvertPtr(swig_obj[1], &argp2,SWIGTYPE_p_ldb_dn, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Message_dn_set" "', argument " "2"" of type '" "ldb_dn *""'"); + } + arg2 = (ldb_dn *)(argp2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + if (arg1) (arg1)->dn = arg2; + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message_dn_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message_dn_get" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (ldb_dn *) ((arg1)->dn); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_Message(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_dn *arg1 = (ldb_dn *) NULL ; + ldb_msg *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "dn", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"|O:new_Message",kwnames,&obj0)) SWIG_fail; + if (obj0) { + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_dn, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_Message" "', argument " "1"" of type '" "ldb_dn *""'"); + } + arg1 = (ldb_dn *)(argp1); + } + result = (ldb_msg *)new_ldb_msg(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_message, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_Message(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_Message" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + delete_ldb_msg(arg1); + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message_find_element(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + char *arg2 = (char *) 0 ; + ldb_message_element *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Message_find_element",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message_find_element" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Message_find_element" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (ldb_message_element *)ldb_msg_find_element(arg1,(char const *)arg2); + { + if (result == NULL) + PyErr_SetString(PyExc_KeyError, "no such element"); + else + resultobj = SWIG_NewPointerObj(result, SWIGTYPE_p_ldb_message_element, 0); + } + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___setitem____SWIG_0(PyObject *SWIGUNUSEDPARM(self), int nobjs, PyObject **swig_obj) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + char *arg2 = (char *) 0 ; + ldb_message_element *arg3 = (ldb_message_element *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + void *argp3 = 0 ; + int res3 = 0 ; + + if ((nobjs < 3) || (nobjs > 3)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message___setitem__" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + res2 = SWIG_AsCharPtrAndSize(swig_obj[1], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Message___setitem__" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_ConvertPtr(swig_obj[2], &argp3,SWIGTYPE_p_ldb_message_element, 0 | 0 ); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "Message___setitem__" "', argument " "3"" of type '" "ldb_message_element *""'"); + } + arg3 = (ldb_message_element *)(argp3); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + ldb_msg___setitem____SWIG_0(arg1,(char const *)arg2,arg3); + resultobj = SWIG_Py_Void(); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___setitem____SWIG_1(PyObject *SWIGUNUSEDPARM(self), int nobjs, PyObject **swig_obj) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + char *arg2 = (char *) 0 ; + PyObject *arg3 = (PyObject *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + + if ((nobjs < 3) || (nobjs > 3)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message___setitem__" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + res2 = SWIG_AsCharPtrAndSize(swig_obj[1], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Message___setitem__" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + arg3 = swig_obj[2]; + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + ldb_msg___setitem____SWIG_1(arg1,(char const *)arg2,arg3); + resultobj = SWIG_Py_Void(); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___setitem__(PyObject *self, PyObject *args) { + int argc; + PyObject *argv[4]; + + if (!(argc = SWIG_Python_UnpackTuple(args,"Message___setitem__",0,3,argv))) SWIG_fail; + --argc; + if (argc == 3) { + int _v = 0; + { + void *vptr = 0; + int res = SWIG_ConvertPtr(argv[2], &vptr, SWIGTYPE_p_ldb_message_element, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_1; + return _wrap_Message___setitem____SWIG_0(self, argc, argv); + } +check_1: + + if (argc == 3) { + return _wrap_Message___setitem____SWIG_1(self, argc, argv); + } + +fail: + SWIG_SetErrorMsg(PyExc_NotImplementedError,"Wrong number of arguments for overloaded function 'Message___setitem__'.\n" + " Possible C/C++ prototypes are:\n" + " __setitem__(ldb_msg *,char const *,ldb_message_element *)\n" + " __setitem__(ldb_msg *,char const *,PyObject *)\n"); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___len__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + unsigned int result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message___len__" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (unsigned int)ldb_msg___len__(arg1); + resultobj = SWIG_From_unsigned_SS_int((unsigned int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message_keys(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message_keys" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (PyObject *)ldb_msg_keys(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___iter__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message___iter__" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = (PyObject *)ldb_msg___iter__(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Message___delitem__(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_msg *arg1 = (ldb_msg *) 0 ; + char *arg2 = (char *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Message___delitem__",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Message___delitem__" "', argument " "1"" of type '" "ldb_msg *""'"); + } + arg1 = (ldb_msg *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Message___delitem__" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + ldb_msg_remove_attr(arg1,(char const *)arg2); + resultobj = SWIG_Py_Void(); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *Message_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj; + if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_ldb_message, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *Message_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_ldb_ldif_to_pyobject(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb_ldif *arg1 = (ldb_ldif *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "ldif", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:ldb_ldif_to_pyobject",kwnames,&obj0)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_ldif, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ldb_ldif_to_pyobject" "', argument " "1"" of type '" "ldb_ldif *""'"); + } + arg1 = (ldb_ldif *)(argp1); + result = (PyObject *)ldb_ldif_to_pyobject(arg1); + resultobj = result; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_Ldb(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *result = 0 ; + + if (!SWIG_Python_UnpackTuple(args,"new_Ldb",0,0,0)) SWIG_fail; + result = (ldb *)new_ldb(); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_context, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_connect(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + unsigned int arg3 = (unsigned int) 0 ; + char **arg4 = (char **) (char **)NULL ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + unsigned int val3 ; + int ecode3 = 0 ; + void *argp4 = 0 ; + int res4 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "url",(char *) "flags",(char *) "options", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO|OO:Ldb_connect",kwnames,&obj0,&obj1,&obj2,&obj3)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_connect" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_connect" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (obj2) { + ecode3 = SWIG_AsVal_unsigned_SS_int(obj2, &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "Ldb_connect" "', argument " "3"" of type '" "unsigned int""'"); + } + arg3 = (unsigned int)(val3); + } + if (obj3) { + res4 = SWIG_ConvertPtr(obj3, &argp4,SWIGTYPE_p_p_char, 0 | 0 ); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "Ldb_connect" "', argument " "4"" of type '" "char const *[]""'"); + } + arg4 = (char **)(argp4); + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_connect(arg1,(char const *)arg2,arg3,(char const *(*))arg4); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_Ldb(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_Ldb" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + delete_ldb(arg1); + + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_search_ex(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ; + ldb_dn *arg3 = (ldb_dn *) NULL ; + enum ldb_scope arg4 = (enum ldb_scope) LDB_SCOPE_DEFAULT ; + char *arg5 = (char *) NULL ; + char **arg6 = (char **) NULL ; + struct ldb_control **arg7 = (struct ldb_control **) NULL ; + struct ldb_result **arg8 = (struct ldb_result **) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + int val4 ; + int ecode4 = 0 ; + int res5 ; + char *buf5 = 0 ; + int alloc5 = 0 ; + void *argp7 = 0 ; + int res7 = 0 ; + struct ldb_result *temp_ldb_result8 ; + int i8 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "base",(char *) "scope",(char *) "expression",(char *) "attrs",(char *) "controls", NULL + }; + + arg2 = NULL; + arg8 = &temp_ldb_result8; + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O|OOOOO:Ldb_search_ex",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_search_ex" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (obj1) { + if (ldb_dn_from_pyobject(NULL, obj1, arg1, &arg3) != 0) { + SWIG_fail; + } + } + if (obj2) { + ecode4 = SWIG_AsVal_int(obj2, &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "Ldb_search_ex" "', argument " "4"" of type '" "enum ldb_scope""'"); + } + arg4 = (enum ldb_scope)(val4); + } + if (obj3) { + res5 = SWIG_AsCharPtrAndSize(obj3, &buf5, NULL, &alloc5); + if (!SWIG_IsOK(res5)) { + SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "Ldb_search_ex" "', argument " "5"" of type '" "char const *""'"); + } + arg5 = (char *)(buf5); + } + if (obj4) { + if (obj4 == Py_None) { + arg6 = NULL; + } else if (PySequence_Check(obj4)) { + int i; + arg6 = talloc_array(NULL, char *, PySequence_Size(obj4)+1); + for(i = 0; i < PySequence_Size(obj4); i++) + arg6[i] = PyString_AsString(PySequence_GetItem(obj4, i)); + arg6[i] = NULL; + } else { + SWIG_exception(SWIG_TypeError, "expected sequence"); + } + } + if (obj5) { + res7 = SWIG_ConvertPtr(obj5, &argp7,SWIGTYPE_p_p_ldb_control, 0 | 0 ); + if (!SWIG_IsOK(res7)) { + SWIG_exception_fail(SWIG_ArgError(res7), "in method '" "Ldb_search_ex" "', argument " "7"" of type '" "struct ldb_control **""'"); + } + arg7 = (struct ldb_control **)(argp7); + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_search_ex(arg1,arg2,arg3,arg4,(char const *)arg5,(char const *const *)arg6,arg7,arg8); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + resultobj = PyList_New((*arg8)->count); + for (i8 = 0; i8 < (*arg8)->count; i8++) { + PyList_SetItem(resultobj, i8, + SWIG_NewPointerObj((*arg8)->msgs[i8], SWIGTYPE_p_ldb_message, 0) + ); + } + talloc_free(arg3); + if (alloc5 == SWIG_NEWOBJ) free((char*)buf5); + talloc_free(arg6); + return resultobj; +fail: + talloc_free(arg3); + if (alloc5 == SWIG_NEWOBJ) free((char*)buf5); + talloc_free(arg6); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_delete(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "dn", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_delete",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_delete" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (ldb_dn_from_pyobject(NULL, obj1, arg1, &arg2) != 0) { + SWIG_fail; + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_delete(arg1,arg2); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + talloc_free(arg2); + return resultobj; +fail: + talloc_free(arg2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_rename(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + ldb_dn *arg3 = (ldb_dn *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "olddn",(char *) "newdn", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:Ldb_rename",kwnames,&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_rename" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (ldb_dn_from_pyobject(NULL, obj1, arg1, &arg2) != 0) { + SWIG_fail; + } + if (ldb_dn_from_pyobject(NULL, obj2, arg1, &arg3) != 0) { + SWIG_fail; + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_rename(arg1,arg2,arg3); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + talloc_free(arg2); + talloc_free(arg3); + return resultobj; +fail: + talloc_free(arg2); + talloc_free(arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_parse_control_strings(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ; + char **arg3 = (char **) 0 ; + struct ldb_control **result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "control_strings", NULL + }; + + arg2 = NULL; + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_parse_control_strings",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_parse_control_strings" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (obj1 == Py_None) { + arg3 = NULL; + } else if (PySequence_Check(obj1)) { + int i; + arg3 = talloc_array(NULL, char *, PySequence_Size(obj1)+1); + for(i = 0; i < PySequence_Size(obj1); i++) + arg3[i] = PyString_AsString(PySequence_GetItem(obj1, i)); + arg3[i] = NULL; + } else { + SWIG_exception(SWIG_TypeError, "expected sequence"); + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (struct ldb_control **)ldb_parse_control_strings(arg1,arg2,(char const *const *)arg3); + if (result == NULL) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(s)", ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = SWIG_NewPointerObj(result, SWIGTYPE_p_p_ldb_control, 0); + talloc_free(arg3); + return resultobj; +fail: + talloc_free(arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_add(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_msg *arg2 = (ldb_msg *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "add_msg", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_add",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_add" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + { + Py_ssize_t dict_pos, msg_pos; + ldb_message_element *msgel; + PyObject *key, *value; + + if (PyDict_Check(obj1)) { + PyObject *dn_value = PyDict_GetItemString(obj1, "dn"); + arg2 = ldb_msg_new(NULL); + arg2->elements = talloc_zero_array(arg2, struct ldb_message_element, PyDict_Size(obj1)); + msg_pos = dict_pos = 0; + if (dn_value) { + /* using argp1 (magic SWIG value) here is a hack */ + if (ldb_dn_from_pyobject(arg2, dn_value, argp1, &arg2->dn) != 0) { + SWIG_exception(SWIG_TypeError, "unable to import dn object"); + } + if (arg2->dn == NULL) { + SWIG_exception(SWIG_TypeError, "dn set but not found"); + } + } + + while (PyDict_Next(obj1, &dict_pos, &key, &value)) { + char *key_str = PyString_AsString(key); + if (strcmp(key_str, "dn") != 0) { + msgel = ldb_msg_element_from_pyobject(arg2->elements, value, 0, key_str); + if (msgel == NULL) { + SWIG_exception(SWIG_TypeError, "unable to import element"); + } + memcpy(&arg2->elements[msg_pos], msgel, sizeof(*msgel)); + msg_pos++; + } + } + + if (arg2->dn == NULL) { + SWIG_exception(SWIG_TypeError, "no dn set"); + } + + arg2->num_elements = msg_pos; + } else { + if (SWIG_ConvertPtr(obj1, (void **)&arg2, SWIGTYPE_p_ldb_message, 0) != 0) { + SWIG_exception(SWIG_TypeError, "unable to convert ldb message"); + } + } + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + if (arg2 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = ldb_add(arg1,arg2); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_modify(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_msg *arg2 = (ldb_msg *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "message", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_modify",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_modify" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ldb_message, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_modify" "', argument " "2"" of type '" "ldb_msg *""'"); + } + arg2 = (ldb_msg *)(argp2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + if (arg2 == NULL) + SWIG_exception(SWIG_ValueError, + "Message can not be None"); + result = ldb_modify(arg1,arg2); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_get_config_basedn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_get_config_basedn" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (ldb_dn *)ldb_get_config_basedn(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_get_root_basedn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_get_root_basedn" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (ldb_dn *)ldb_get_root_basedn(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_get_schema_basedn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_get_schema_basedn" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (ldb_dn *)ldb_get_schema_basedn(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_get_default_basedn(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_get_default_basedn" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (ldb_dn *)ldb_get_default_basedn(arg1); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ldb_dn, 0 | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_schema_format_value(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + PyObject *arg3 = (PyObject *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "element_name",(char *) "val", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:Ldb_schema_format_value",kwnames,&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_schema_format_value" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_schema_format_value" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + arg3 = obj2; + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (PyObject *)ldb_schema_format_value(arg1,(char const *)arg2,arg3); + resultobj = result; + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_errstring(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_errstring" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (char *)ldb_errstring(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_set_create_perms(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + unsigned int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + unsigned int val2 ; + int ecode2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "perms", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_set_create_perms",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_set_create_perms" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + ecode2 = SWIG_AsVal_unsigned_SS_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "Ldb_set_create_perms" "', argument " "2"" of type '" "unsigned int""'"); + } + arg2 = (unsigned int)(val2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + ldb_set_create_perms(arg1,arg2); + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_set_modules_dir(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "path", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_set_modules_dir",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_set_modules_dir" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_set_modules_dir" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + ldb_set_modules_dir(arg1,(char const *)arg2); + resultobj = SWIG_Py_Void(); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_set_debug(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + void (*arg2)(void *,enum ldb_debug_level,char const *,va_list) = (void (*)(void *,enum ldb_debug_level,char const *,va_list)) 0 ; + void *arg3 = (void *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "debug", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_set_debug",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_set_debug" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + arg2 = py_ldb_debug; + /* FIXME: Should be decreased somewhere as well. Perhaps register a + destructor and tie it to the ldb context ? */ + Py_INCREF(obj1); + arg3 = obj1; + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_set_debug(arg1,arg2,arg3); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_set_opaque(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + void *arg3 = (void *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name",(char *) "value", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:Ldb_set_opaque",kwnames,&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_set_opaque" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_set_opaque" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3), 0, 0); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "Ldb_set_opaque" "', argument " "3"" of type '" "void *""'"); + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_set_opaque(arg1,(char const *)arg2,arg3); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_get_opaque(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + void *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_get_opaque",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_get_opaque" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_get_opaque" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (void *)ldb_get_opaque(arg1,(char const *)arg2); + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_void, 0 | 0 ); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_transaction_start(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_transaction_start" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_transaction_start(arg1); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_transaction_commit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_transaction_commit" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_transaction_commit(arg1); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_transaction_cancel(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_transaction_cancel" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_transaction_cancel(arg1); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_schema_attribute_remove(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "name", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_schema_attribute_remove",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_schema_attribute_remove" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_schema_attribute_remove" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + ldb_schema_attribute_remove(arg1,(char const *)arg2); + resultobj = SWIG_Py_Void(); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_schema_attribute_add(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + unsigned int arg3 ; + char *arg4 = (char *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + unsigned int val3 ; + int ecode3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "attribute",(char *) "flags",(char *) "syntax", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOO:Ldb_schema_attribute_add",kwnames,&obj0,&obj1,&obj2,&obj3)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_schema_attribute_add" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_schema_attribute_add" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + ecode3 = SWIG_AsVal_unsigned_SS_int(obj2, &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "Ldb_schema_attribute_add" "', argument " "3"" of type '" "unsigned int""'"); + } + arg3 = (unsigned int)(val3); + res4 = SWIG_AsCharPtrAndSize(obj3, &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "Ldb_schema_attribute_add" "', argument " "4"" of type '" "char const *""'"); + } + arg4 = (char *)(buf4); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_schema_attribute_add(arg1,(char const *)arg2,arg3,(char const *)arg4); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc4 == SWIG_NEWOBJ) free((char*)buf4); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc4 == SWIG_NEWOBJ) free((char*)buf4); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_setup_wellknown_attributes(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_setup_wellknown_attributes" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb_setup_wellknown_attributes(arg1); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb___contains__(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + ldb_dn *arg2 = (ldb_dn *) 0 ; + struct ldb_result **arg3 = (struct ldb_result **) 0 ; + ldb_error result; + void *argp1 = 0 ; + int res1 = 0 ; + struct ldb_result *tmp3 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "dn", NULL + }; + + arg3 = &tmp3; + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb___contains__",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb___contains__" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (ldb_dn_from_pyobject(NULL, obj1, arg1, &arg2) != 0) { + SWIG_fail; + } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = ldb___contains__(arg1,arg2,arg3); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_errstring(arg1))); + SWIG_fail; + } + resultobj = Py_None; + resultobj = ((*arg3)->count > 0)?Py_True:Py_False; + talloc_free(arg2); + talloc_free(*arg3); + return resultobj; +fail: + talloc_free(arg2); + talloc_free(*arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb_parse_ldif(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *arg2 = (char *) 0 ; + PyObject *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "self",(char *) "s", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:Ldb_parse_ldif",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb_parse_ldif" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Ldb_parse_ldif" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (PyObject *)ldb_parse_ldif(arg1,(char const *)arg2); + resultobj = result; + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_Ldb___repr__(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ldb *arg1 = (ldb *) 0 ; + char *result = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Ldb___repr__" "', argument " "1"" of type '" "ldb *""'"); + } + arg1 = (ldb *)(argp1); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (char *)ldb___repr__(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *Ldb_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj; + if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_ldb_context, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *Ldb_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_valid_attr_name(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + char *arg1 = (char *) 0 ; + int result; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "s", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:valid_attr_name",kwnames,&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "valid_attr_name" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = (char *)(buf1); + result = (int)ldb_valid_attr_name((char const *)arg1); + resultobj = SWIG_From_int((int)(result)); + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_timestring(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + time_t arg1 ; + char *result = 0 ; + unsigned long val1 ; + int ecode1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "t", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:timestring",kwnames,&obj0)) SWIG_fail; + ecode1 = SWIG_AsVal_unsigned_SS_long(obj0, &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "timestring" "', argument " "1"" of type '" "time_t""'"); + } + arg1 = (time_t)(val1); + result = (char *)timestring(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_string_to_time(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + char *arg1 = (char *) 0 ; + time_t result; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *) "s", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:string_to_time",kwnames,&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "string_to_time" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = (char *)(buf1); + result = (time_t)ldb_string_to_time((char const *)arg1); + resultobj = SWIG_From_unsigned_SS_long((unsigned long)(result)); + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_register_module(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + struct ldb_module_ops *arg1 = (struct ldb_module_ops *) 0 ; + ldb_int_error result; + PyObject * obj0 = 0 ; + char * kwnames[] = { + (char *)"arg1", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:register_module",kwnames,&obj0)) SWIG_fail; + arg1 = talloc_zero(talloc_autofree_context(), struct ldb_module_ops); + + arg1->name = (char *)PyObject_GetAttrString(obj0, (char *)"name"); + result = ldb_register_module((struct ldb_module_ops const *)arg1); + if (result != 0) { + PyErr_SetObject(PyExc_LdbError, Py_BuildValue((char *)"(i,s)", result, ldb_strerror(result))); + SWIG_fail; + } + resultobj = Py_None; + return resultobj; +fail: + return NULL; +} + + +static PyMethodDef SwigMethods[] = { + { (char *)"ldb_val_to_py_object", (PyCFunction) _wrap_ldb_val_to_py_object, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"new_Dn", (PyCFunction) _wrap_new_Dn, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.__init__(ldb, string)\n" + "Create a new DN.\n" + ""}, + { (char *)"delete_Dn", (PyCFunction)_wrap_delete_Dn, METH_O, NULL}, + { (char *)"Dn_validate", (PyCFunction)_wrap_Dn_validate, METH_O, (char *)"\n" + "S.validate() -> bool\n" + "Validate DN is correct.\n" + ""}, + { (char *)"Dn_get_casefold", (PyCFunction)_wrap_Dn_get_casefold, METH_O, NULL}, + { (char *)"Dn___str__", (PyCFunction)_wrap_Dn___str__, METH_O, NULL}, + { (char *)"Dn_parent", (PyCFunction)_wrap_Dn_parent, METH_O, (char *)"\n" + "S.parent() -> dn\n" + "Get the parent for this DN.\n" + ""}, + { (char *)"Dn___cmp__", (PyCFunction) _wrap_Dn___cmp__, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Dn_is_valid", (PyCFunction)_wrap_Dn_is_valid, METH_O, NULL}, + { (char *)"Dn_is_special", (PyCFunction)_wrap_Dn_is_special, METH_O, (char *)"\n" + "S.is_special() -> bool\n" + "Check whether this is a special LDB DN.\n" + ""}, + { (char *)"Dn_is_null", (PyCFunction)_wrap_Dn_is_null, METH_O, (char *)"\n" + "S.is_null() -> bool\n" + "Check whether this is a null DN.\n" + ""}, + { (char *)"Dn_check_special", (PyCFunction) _wrap_Dn_check_special, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Dn___len__", (PyCFunction)_wrap_Dn___len__, METH_O, NULL}, + { (char *)"Dn_add_child", (PyCFunction) _wrap_Dn_add_child, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.add_child(dn) -> None\n" + "Add a child DN to this DN.\n" + ""}, + { (char *)"Dn_add_base", (PyCFunction) _wrap_Dn_add_base, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.add_base(dn) -> None\n" + "Add a base DN to this DN.\n" + ""}, + { (char *)"Dn_canonical_str", (PyCFunction)_wrap_Dn_canonical_str, METH_O, (char *)"\n" + "S.canonical_str() -> string\n" + "Canonical version of this DN (like a posix path).\n" + ""}, + { (char *)"Dn_canonical_ex_str", (PyCFunction)_wrap_Dn_canonical_ex_str, METH_O, (char *)"\n" + "S.canonical_ex_str() -> string\n" + "Canonical version of this DN (like a posix path, with terminating newline).\n" + ""}, + { (char *)"Dn___repr__", (PyCFunction)_wrap_Dn___repr__, METH_O, NULL}, + { (char *)"Dn___add__", (PyCFunction) _wrap_Dn___add__, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Dn_swigregister", Dn_swigregister, METH_VARARGS, NULL}, + { (char *)"Dn_swiginit", Dn_swiginit, METH_VARARGS, NULL}, + { (char *)"MessageElement___cmp__", (PyCFunction) _wrap_MessageElement___cmp__, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"MessageElement___iter__", (PyCFunction)_wrap_MessageElement___iter__, METH_O, NULL}, + { (char *)"MessageElement___set__", (PyCFunction)_wrap_MessageElement___set__, METH_O, NULL}, + { (char *)"new_MessageElement", (PyCFunction) _wrap_new_MessageElement, METH_VARARGS | METH_KEYWORDS, (char *)"Message element."}, + { (char *)"MessageElement___len__", (PyCFunction)_wrap_MessageElement___len__, METH_O, NULL}, + { (char *)"MessageElement_get", (PyCFunction) _wrap_MessageElement_get, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"delete_MessageElement", (PyCFunction)_wrap_delete_MessageElement, METH_O, NULL}, + { (char *)"MessageElement_swigregister", MessageElement_swigregister, METH_VARARGS, NULL}, + { (char *)"MessageElement_swiginit", MessageElement_swiginit, METH_VARARGS, NULL}, + { (char *)"ldb_msg_list_elements", (PyCFunction) _wrap_ldb_msg_list_elements, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Message_dn_set", _wrap_Message_dn_set, METH_VARARGS, NULL}, + { (char *)"Message_dn_get", (PyCFunction)_wrap_Message_dn_get, METH_O, NULL}, + { (char *)"new_Message", (PyCFunction) _wrap_new_Message, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"delete_Message", (PyCFunction)_wrap_delete_Message, METH_O, NULL}, + { (char *)"Message_find_element", (PyCFunction) _wrap_Message_find_element, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Message___setitem__", _wrap_Message___setitem__, METH_VARARGS, NULL}, + { (char *)"Message___len__", (PyCFunction)_wrap_Message___len__, METH_O, NULL}, + { (char *)"Message_keys", (PyCFunction)_wrap_Message_keys, METH_O, NULL}, + { (char *)"Message___iter__", (PyCFunction)_wrap_Message___iter__, METH_O, NULL}, + { (char *)"Message___delitem__", (PyCFunction) _wrap_Message___delitem__, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Message_swigregister", Message_swigregister, METH_VARARGS, NULL}, + { (char *)"Message_swiginit", Message_swiginit, METH_VARARGS, NULL}, + { (char *)"ldb_ldif_to_pyobject", (PyCFunction) _wrap_ldb_ldif_to_pyobject, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"new_Ldb", (PyCFunction)_wrap_new_Ldb, METH_NOARGS, NULL}, + { (char *)"Ldb_connect", (PyCFunction) _wrap_Ldb_connect, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.connect(url,flags=0,options=None) -> None\n" + "Connect to a LDB URL.\n" + ""}, + { (char *)"delete_Ldb", (PyCFunction)_wrap_delete_Ldb, METH_O, NULL}, + { (char *)"Ldb_search_ex", (PyCFunction) _wrap_Ldb_search_ex, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_delete", (PyCFunction) _wrap_Ldb_delete, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.delete(dn) -> None\n" + "Remove an entry.\n" + ""}, + { (char *)"Ldb_rename", (PyCFunction) _wrap_Ldb_rename, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.rename(old_dn, new_dn) -> None\n" + "Rename an entry.\n" + ""}, + { (char *)"Ldb_parse_control_strings", (PyCFunction) _wrap_Ldb_parse_control_strings, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_add", (PyCFunction) _wrap_Ldb_add, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.add(message) -> None\n" + "Add an entry.\n" + ""}, + { (char *)"Ldb_modify", (PyCFunction) _wrap_Ldb_modify, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.modify(message) -> None\n" + "Modify an entry.\n" + ""}, + { (char *)"Ldb_get_config_basedn", (PyCFunction)_wrap_Ldb_get_config_basedn, METH_O, NULL}, + { (char *)"Ldb_get_root_basedn", (PyCFunction)_wrap_Ldb_get_root_basedn, METH_O, NULL}, + { (char *)"Ldb_get_schema_basedn", (PyCFunction)_wrap_Ldb_get_schema_basedn, METH_O, NULL}, + { (char *)"Ldb_get_default_basedn", (PyCFunction)_wrap_Ldb_get_default_basedn, METH_O, NULL}, + { (char *)"Ldb_schema_format_value", (PyCFunction) _wrap_Ldb_schema_format_value, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_errstring", (PyCFunction)_wrap_Ldb_errstring, METH_O, NULL}, + { (char *)"Ldb_set_create_perms", (PyCFunction) _wrap_Ldb_set_create_perms, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.set_create_perms(mode) -> None\n" + "Set mode to use when creating new LDB files.\n" + ""}, + { (char *)"Ldb_set_modules_dir", (PyCFunction) _wrap_Ldb_set_modules_dir, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.set_modules_dir(path) -> None\n" + "Set path LDB should search for modules\n" + ""}, + { (char *)"Ldb_set_debug", (PyCFunction) _wrap_Ldb_set_debug, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.set_debug(callback) -> None\n" + "Set callback for LDB debug messages.\n" + "The callback should accept a debug level and debug text.\n" + ""}, + { (char *)"Ldb_set_opaque", (PyCFunction) _wrap_Ldb_set_opaque, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.set_opaque(name, value) -> None\n" + "Set an opaque value on this LDB connection. \n" + ":note: Passing incorrect values may cause crashes.\n" + ""}, + { (char *)"Ldb_get_opaque", (PyCFunction) _wrap_Ldb_get_opaque, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.get_opaque(name) -> value\n" + "Get an opaque value set on this LDB connection. \n" + ":note: The returned value may not be useful in Python.\n" + ""}, + { (char *)"Ldb_transaction_start", (PyCFunction)_wrap_Ldb_transaction_start, METH_O, (char *)"\n" + "S.transaction_start() -> None\n" + "Start a new transaction.\n" + ""}, + { (char *)"Ldb_transaction_commit", (PyCFunction)_wrap_Ldb_transaction_commit, METH_O, (char *)"\n" + "S.transaction_commit() -> None\n" + "Commit currently active transaction.\n" + ""}, + { (char *)"Ldb_transaction_cancel", (PyCFunction)_wrap_Ldb_transaction_cancel, METH_O, (char *)"\n" + "S.transaction_cancel() -> None\n" + "Cancel currently active transaction.\n" + ""}, + { (char *)"Ldb_schema_attribute_remove", (PyCFunction) _wrap_Ldb_schema_attribute_remove, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_schema_attribute_add", (PyCFunction) _wrap_Ldb_schema_attribute_add, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_setup_wellknown_attributes", (PyCFunction)_wrap_Ldb_setup_wellknown_attributes, METH_O, NULL}, + { (char *)"Ldb___contains__", (PyCFunction) _wrap_Ldb___contains__, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"Ldb_parse_ldif", (PyCFunction) _wrap_Ldb_parse_ldif, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.parse_ldif(ldif) -> iter(messages)\n" + "Parse a string formatted using LDIF.\n" + ""}, + { (char *)"Ldb___repr__", (PyCFunction)_wrap_Ldb___repr__, METH_O, NULL}, + { (char *)"Ldb_swigregister", Ldb_swigregister, METH_VARARGS, NULL}, + { (char *)"Ldb_swiginit", Ldb_swiginit, METH_VARARGS, NULL}, + { (char *)"valid_attr_name", (PyCFunction) _wrap_valid_attr_name, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.valid_attr_name(name) -> bool\n" + "Check whether the supplied name is a valid attribute name.\n" + ""}, + { (char *)"timestring", (PyCFunction) _wrap_timestring, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.timestring(int) -> string\n" + "Generate a LDAP time string from a UNIX timestamp\n" + ""}, + { (char *)"string_to_time", (PyCFunction) _wrap_string_to_time, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.string_to_time(string) -> int\n" + "Parse a LDAP time string into a UNIX timestamp.\n" + ""}, + { (char *)"register_module", (PyCFunction) _wrap_register_module, METH_VARARGS | METH_KEYWORDS, (char *)"\n" + "S.register_module(module) -> None\n" + "Register a LDB module.\n" + ""}, + { NULL, NULL, 0, NULL } +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_TALLOC_CTX = {"_p_TALLOC_CTX", "TALLOC_CTX *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void = {"_p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void", "void (*)(void *,enum ldb_debug_level,char const *,va_list)", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_int = {"_p_int", "intptr_t *|int *|int_least32_t *|int_fast32_t *|int32_t *|int_fast16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_context = {"_p_ldb_context", "struct ldb_context *|ldb *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_dn = {"_p_ldb_dn", "struct ldb_dn *|ldb_dn *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_ldif = {"_p_ldb_ldif", "struct ldb_ldif *|ldb_ldif *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_message = {"_p_ldb_message", "ldb_msg *|struct ldb_message *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_message_element = {"_p_ldb_message_element", "struct ldb_message_element *|ldb_message_element *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_module_ops = {"_p_ldb_module_ops", "struct ldb_module_ops *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_result = {"_p_ldb_result", "struct ldb_result *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_ldb_val = {"_p_ldb_val", "struct ldb_val *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_long_long = {"_p_long_long", "int_least64_t *|int_fast64_t *|int64_t *|long long *|intmax_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_p_char = {"_p_p_char", "char **", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_p_ldb_control = {"_p_p_ldb_control", "struct ldb_control **", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_p_ldb_result = {"_p_p_ldb_result", "struct ldb_result **", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_short = {"_p_short", "short *|int_least16_t *|int16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_signed_char = {"_p_signed_char", "signed char *|int_least8_t *|int_fast8_t *|int8_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_char = {"_p_unsigned_char", "unsigned char *|uint_least8_t *|uint_fast8_t *|uint8_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_int = {"_p_unsigned_int", "uintptr_t *|uint_least32_t *|uint_fast32_t *|uint32_t *|unsigned int *|uint_fast16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_long = {"_p_unsigned_long", "unsigned long *|time_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_long_long = {"_p_unsigned_long_long", "uint_least64_t *|uint_fast64_t *|uint64_t *|unsigned long long *|uintmax_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_unsigned_short = {"_p_unsigned_short", "unsigned short *|uint_least16_t *|uint16_t *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_void = {"_p_void", "void *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p_TALLOC_CTX, + &_swigt__p_char, + &_swigt__p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void, + &_swigt__p_int, + &_swigt__p_ldb_context, + &_swigt__p_ldb_dn, + &_swigt__p_ldb_ldif, + &_swigt__p_ldb_message, + &_swigt__p_ldb_message_element, + &_swigt__p_ldb_module_ops, + &_swigt__p_ldb_result, + &_swigt__p_ldb_val, + &_swigt__p_long_long, + &_swigt__p_p_char, + &_swigt__p_p_ldb_control, + &_swigt__p_p_ldb_result, + &_swigt__p_short, + &_swigt__p_signed_char, + &_swigt__p_unsigned_char, + &_swigt__p_unsigned_int, + &_swigt__p_unsigned_long, + &_swigt__p_unsigned_long_long, + &_swigt__p_unsigned_short, + &_swigt__p_void, +}; + +static swig_cast_info _swigc__p_TALLOC_CTX[] = { {&_swigt__p_TALLOC_CTX, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void[] = { {&_swigt__p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_context[] = { {&_swigt__p_ldb_context, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_dn[] = { {&_swigt__p_ldb_dn, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_ldif[] = { {&_swigt__p_ldb_ldif, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_message[] = { {&_swigt__p_ldb_message, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_message_element[] = { {&_swigt__p_ldb_message_element, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_module_ops[] = { {&_swigt__p_ldb_module_ops, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_result[] = { {&_swigt__p_ldb_result, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_ldb_val[] = { {&_swigt__p_ldb_val, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_long_long[] = { {&_swigt__p_long_long, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_p_char[] = { {&_swigt__p_p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_p_ldb_control[] = { {&_swigt__p_p_ldb_control, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_p_ldb_result[] = { {&_swigt__p_p_ldb_result, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_short[] = { {&_swigt__p_short, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_signed_char[] = { {&_swigt__p_signed_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_char[] = { {&_swigt__p_unsigned_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_int[] = { {&_swigt__p_unsigned_int, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_long[] = { {&_swigt__p_unsigned_long, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_long_long[] = { {&_swigt__p_unsigned_long_long, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_unsigned_short[] = { {&_swigt__p_unsigned_short, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_void[] = { {&_swigt__p_void, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p_TALLOC_CTX, + _swigc__p_char, + _swigc__p_f_p_void_enum_ldb_debug_level_p_q_const__char_va_list__void, + _swigc__p_int, + _swigc__p_ldb_context, + _swigc__p_ldb_dn, + _swigc__p_ldb_ldif, + _swigc__p_ldb_message, + _swigc__p_ldb_message_element, + _swigc__p_ldb_module_ops, + _swigc__p_ldb_result, + _swigc__p_ldb_val, + _swigc__p_long_long, + _swigc__p_p_char, + _swigc__p_p_ldb_control, + _swigc__p_p_ldb_result, + _swigc__p_short, + _swigc__p_signed_char, + _swigc__p_unsigned_char, + _swigc__p_unsigned_int, + _swigc__p_unsigned_long, + _swigc__p_unsigned_long_long, + _swigc__p_unsigned_short, + _swigc__p_void, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_const_info swig_const_table[] = { +{0, 0, 0, 0.0, 0, 0}}; + +#ifdef __cplusplus +} +#endif +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned staticly to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + + +SWIGRUNTIME void +SWIG_InitializeModule(void *clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int found, init; + + clientdata = clientdata; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + init = 1; + } else { + init = 0; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + module_head = &swig_module; + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + found=0; + iter=module_head; + do { + if (iter==&swig_module) { + found=1; + break; + } + iter=iter->next; + } while (iter!= module_head); + + /* if the is found in the list, then all is done and we may leave */ + if (found) return; + /* otherwise we must add out module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* When multiple interpeters are used, a module could have already been initialized in + a different interpreter, but not yet have a pointer in this interpreter. + In this case, we do not want to continue adding types... everything should be + set up already */ + if (init == 0) return; + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %d\n", swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + /* Python-specific SWIG API */ +#define SWIG_newvarlink() SWIG_Python_newvarlink() +#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) +#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) + + /* ----------------------------------------------------------------------------- + * global variable support code. + * ----------------------------------------------------------------------------- */ + + typedef struct swig_globalvar { + char *name; /* Name of global variable */ + PyObject *(*get_attr)(void); /* Return the current value */ + int (*set_attr)(PyObject *); /* Set the value */ + struct swig_globalvar *next; + } swig_globalvar; + + typedef struct swig_varlinkobject { + PyObject_HEAD + swig_globalvar *vars; + } swig_varlinkobject; + + SWIGINTERN PyObject * + swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { + return PyString_FromString("<Swig global variables>"); + } + + SWIGINTERN PyObject * + swig_varlink_str(swig_varlinkobject *v) { + PyObject *str = PyString_FromString("("); + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + PyString_ConcatAndDel(&str,PyString_FromString(var->name)); + if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); + } + PyString_ConcatAndDel(&str,PyString_FromString(")")); + return str; + } + + SWIGINTERN int + swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { + PyObject *str = swig_varlink_str(v); + fprintf(fp,"Swig global variables "); + fprintf(fp,"%s\n", PyString_AsString(str)); + Py_DECREF(str); + return 0; + } + + SWIGINTERN void + swig_varlink_dealloc(swig_varlinkobject *v) { + swig_globalvar *var = v->vars; + while (var) { + swig_globalvar *n = var->next; + free(var->name); + free(var); + var = n; + } + } + + SWIGINTERN PyObject * + swig_varlink_getattr(swig_varlinkobject *v, char *n) { + PyObject *res = NULL; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->get_attr)(); + break; + } + var = var->next; + } + if (res == NULL && !PyErr_Occurred()) { + PyErr_SetString(PyExc_NameError,"Unknown C global variable"); + } + return res; + } + + SWIGINTERN int + swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { + int res = 1; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->set_attr)(p); + break; + } + var = var->next; + } + if (res == 1 && !PyErr_Occurred()) { + PyErr_SetString(PyExc_NameError,"Unknown C global variable"); + } + return res; + } + + SWIGINTERN PyTypeObject* + swig_varlink_type(void) { + static char varlink__doc__[] = "Swig var link object"; + static PyTypeObject varlink_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp + = { + PyObject_HEAD_INIT(NULL) + 0, /* Number of items in variable part (ob_size) */ + (char *)"swigvarlink", /* Type name (tp_name) */ + sizeof(swig_varlinkobject), /* Basic size (tp_basicsize) */ + 0, /* Itemsize (tp_itemsize) */ + (destructor) swig_varlink_dealloc, /* Deallocator (tp_dealloc) */ + (printfunc) swig_varlink_print, /* Print (tp_print) */ + (getattrfunc) swig_varlink_getattr, /* get attr (tp_getattr) */ + (setattrfunc) swig_varlink_setattr, /* Set attr (tp_setattr) */ + 0, /* tp_compare */ + (reprfunc) swig_varlink_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)swig_varlink_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + varlink__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#ifdef COUNT_ALLOCS + 0,0,0,0 /* tp_alloc -> tp_next */ +#endif + }; + varlink_type = tmp; + varlink_type.ob_type = &PyType_Type; + type_init = 1; + } + return &varlink_type; + } + + /* Create a variable linking object for use later */ + SWIGINTERN PyObject * + SWIG_Python_newvarlink(void) { + swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); + if (result) { + result->vars = 0; + } + return ((PyObject*) result); + } + + SWIGINTERN void + SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { + swig_varlinkobject *v = (swig_varlinkobject *) p; + swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); + if (gv) { + size_t size = strlen(name)+1; + gv->name = (char *)malloc(size); + if (gv->name) { + strncpy(gv->name,name,size); + gv->get_attr = get_attr; + gv->set_attr = set_attr; + gv->next = v->vars; + } + } + v->vars = gv; + } + + SWIGINTERN PyObject * + SWIG_globals(void) { + static PyObject *_SWIG_globals = 0; + if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink(); + return _SWIG_globals; + } + + /* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + + /* Install Constants */ + SWIGINTERN void + SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { + PyObject *obj = 0; + size_t i; + for (i = 0; constants[i].type; ++i) { + switch(constants[i].type) { + case SWIG_PY_POINTER: + obj = SWIG_NewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_PY_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + PyDict_SetItemString(d, constants[i].name, obj); + Py_DECREF(obj); + } + } + } + + /* -----------------------------------------------------------------------------*/ + /* Fix SwigMethods to carry the callback ptrs when needed */ + /* -----------------------------------------------------------------------------*/ + + SWIGINTERN void + SWIG_Python_FixMethods(PyMethodDef *methods, + swig_const_info *const_table, + swig_type_info **types, + swig_type_info **types_initial) { + size_t i; + for (i = 0; methods[i].ml_name; ++i) { + const char *c = methods[i].ml_doc; + if (c && (c = strstr(c, "swig_ptr: "))) { + int j; + swig_const_info *ci = 0; + const char *name = c + 10; + for (j = 0; const_table[j].type; ++j) { + if (strncmp(const_table[j].name, name, + strlen(const_table[j].name)) == 0) { + ci = &(const_table[j]); + break; + } + } + if (ci) { + size_t shift = (ci->ptype) - types; + swig_type_info *ty = types_initial[shift]; + size_t ldoc = (c - methods[i].ml_doc); + size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; + char *ndoc = (char*)malloc(ldoc + lptr + 10); + if (ndoc) { + char *buff = ndoc; + void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; + if (ptr) { + strncpy(buff, methods[i].ml_doc, ldoc); + buff += ldoc; + strncpy(buff, "swig_ptr: ", 10); + buff += 10; + SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); + methods[i].ml_doc = ndoc; + } + } + } + } + } + } + +#ifdef __cplusplus +} +#endif + +/* -----------------------------------------------------------------------------* + * Partial Init method + * -----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT void SWIG_init(void) { + PyObject *m, *d; + + /* Fix SwigMethods to carry the callback ptrs when needed */ + SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); + + m = Py_InitModule((char *) SWIG_name, SwigMethods); + d = PyModule_GetDict(m); + + SWIG_InitializeModule(0); + SWIG_InstallConstants(d,swig_const_table); + + + SWIG_Python_SetConstant(d, "SCOPE_DEFAULT",SWIG_From_int((int)(LDB_SCOPE_DEFAULT))); + SWIG_Python_SetConstant(d, "SCOPE_BASE",SWIG_From_int((int)(LDB_SCOPE_BASE))); + SWIG_Python_SetConstant(d, "SCOPE_ONELEVEL",SWIG_From_int((int)(LDB_SCOPE_ONELEVEL))); + SWIG_Python_SetConstant(d, "SCOPE_SUBTREE",SWIG_From_int((int)(LDB_SCOPE_SUBTREE))); + SWIG_Python_SetConstant(d, "CHANGETYPE_NONE",SWIG_From_int((int)(LDB_CHANGETYPE_NONE))); + SWIG_Python_SetConstant(d, "CHANGETYPE_ADD",SWIG_From_int((int)(LDB_CHANGETYPE_ADD))); + SWIG_Python_SetConstant(d, "CHANGETYPE_DELETE",SWIG_From_int((int)(LDB_CHANGETYPE_DELETE))); + SWIG_Python_SetConstant(d, "CHANGETYPE_MODIFY",SWIG_From_int((int)(LDB_CHANGETYPE_MODIFY))); + + PyExc_LdbError = PyErr_NewException((char *)"_ldb.LdbError", NULL, NULL); + PyDict_SetItemString(d, "LdbError", PyExc_LdbError); + + SWIG_Python_SetConstant(d, "LDB_ERR_OPERATIONS_ERROR",SWIG_From_int((int)(1))); + SWIG_Python_SetConstant(d, "LDB_ERR_PROTOCOL_ERROR",SWIG_From_int((int)(2))); + SWIG_Python_SetConstant(d, "LDB_ERR_TIME_LIMIT_EXCEEDED",SWIG_From_int((int)(3))); + SWIG_Python_SetConstant(d, "LDB_ERR_SIZE_LIMIT_EXCEEDED",SWIG_From_int((int)(4))); + SWIG_Python_SetConstant(d, "LDB_ERR_COMPARE_FALSE",SWIG_From_int((int)(5))); + SWIG_Python_SetConstant(d, "LDB_ERR_COMPARE_TRUE",SWIG_From_int((int)(6))); + SWIG_Python_SetConstant(d, "LDB_ERR_AUTH_METHOD_NOT_SUPPORTED",SWIG_From_int((int)(7))); + SWIG_Python_SetConstant(d, "LDB_ERR_STRONG_AUTH_REQUIRED",SWIG_From_int((int)(8))); + SWIG_Python_SetConstant(d, "LDB_ERR_REFERRAL",SWIG_From_int((int)(10))); + SWIG_Python_SetConstant(d, "LDB_ERR_ADMIN_LIMIT_EXCEEDED",SWIG_From_int((int)(11))); + SWIG_Python_SetConstant(d, "LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION",SWIG_From_int((int)(12))); + SWIG_Python_SetConstant(d, "LDB_ERR_CONFIDENTIALITY_REQUIRED",SWIG_From_int((int)(13))); + SWIG_Python_SetConstant(d, "LDB_ERR_SASL_BIND_IN_PROGRESS",SWIG_From_int((int)(14))); + SWIG_Python_SetConstant(d, "LDB_ERR_NO_SUCH_ATTRIBUTE",SWIG_From_int((int)(16))); + SWIG_Python_SetConstant(d, "LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE",SWIG_From_int((int)(17))); + SWIG_Python_SetConstant(d, "LDB_ERR_INAPPROPRIATE_MATCHING",SWIG_From_int((int)(18))); + SWIG_Python_SetConstant(d, "LDB_ERR_CONSTRAINT_VIOLATION",SWIG_From_int((int)(19))); + SWIG_Python_SetConstant(d, "LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS",SWIG_From_int((int)(20))); + SWIG_Python_SetConstant(d, "LDB_ERR_INVALID_ATTRIBUTE_SYNTAX",SWIG_From_int((int)(21))); + SWIG_Python_SetConstant(d, "LDB_ERR_NO_SUCH_OBJECT",SWIG_From_int((int)(32))); + SWIG_Python_SetConstant(d, "LDB_ERR_ALIAS_PROBLEM",SWIG_From_int((int)(33))); + SWIG_Python_SetConstant(d, "LDB_ERR_INVALID_DN_SYNTAX",SWIG_From_int((int)(34))); + SWIG_Python_SetConstant(d, "LDB_ERR_ALIAS_DEREFERENCING_PROBLEM",SWIG_From_int((int)(36))); + SWIG_Python_SetConstant(d, "LDB_ERR_INAPPROPRIATE_AUTHENTICATION",SWIG_From_int((int)(48))); + SWIG_Python_SetConstant(d, "LDB_ERR_INVALID_CREDENTIALS",SWIG_From_int((int)(49))); + SWIG_Python_SetConstant(d, "LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS",SWIG_From_int((int)(50))); + SWIG_Python_SetConstant(d, "LDB_ERR_BUSY",SWIG_From_int((int)(51))); + SWIG_Python_SetConstant(d, "LDB_ERR_UNAVAILABLE",SWIG_From_int((int)(52))); + SWIG_Python_SetConstant(d, "LDB_ERR_UNWILLING_TO_PERFORM",SWIG_From_int((int)(53))); + SWIG_Python_SetConstant(d, "LDB_ERR_LOOP_DETECT",SWIG_From_int((int)(54))); + SWIG_Python_SetConstant(d, "LDB_ERR_NAMING_VIOLATION",SWIG_From_int((int)(64))); + SWIG_Python_SetConstant(d, "LDB_ERR_OBJECT_CLASS_VIOLATION",SWIG_From_int((int)(65))); + SWIG_Python_SetConstant(d, "LDB_ERR_NOT_ALLOWED_ON_NON_LEAF",SWIG_From_int((int)(66))); + SWIG_Python_SetConstant(d, "LDB_ERR_NOT_ALLOWED_ON_RDN",SWIG_From_int((int)(67))); + SWIG_Python_SetConstant(d, "LDB_ERR_ENTRY_ALREADY_EXISTS",SWIG_From_int((int)(68))); + SWIG_Python_SetConstant(d, "LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED",SWIG_From_int((int)(69))); + SWIG_Python_SetConstant(d, "LDB_ERR_AFFECTS_MULTIPLE_DSAS",SWIG_From_int((int)(71))); + SWIG_Python_SetConstant(d, "LDB_ERR_OTHER",SWIG_From_int((int)(80))); +} + diff --git a/ldb/libldb.m4 b/ldb/libldb.m4 new file mode 100644 index 00000000..77ebcc5f --- /dev/null +++ b/ldb/libldb.m4 @@ -0,0 +1,7 @@ + +# disable ldb_sqlite3 by default +SMB_ENABLE(ldb_sqlite3, NO) + +if test x"$with_sqlite3_support" = x"yes"; then + SMB_ENABLE(ldb_sqlite3, YES) +fi diff --git a/ldb/mainpage.dox b/ldb/mainpage.dox new file mode 100644 index 00000000..bbd8d9c5 --- /dev/null +++ b/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/ldb/man/ad2oLschema.1.xml b/ldb/man/ad2oLschema.1.xml new file mode 100644 index 00000000..6ae89964 --- /dev/null +++ b/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/ldb/man/ldb.3.xml b/ldb/man/ldb.3.xml new file mode 100644 index 00000000..19d9a89e --- /dev/null +++ b/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> +In 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/ldb/man/ldbadd.1.xml b/ldb/man/ldbadd.1.xml new file mode 100644 index 00000000..7ad0f835 --- /dev/null +++ b/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/ldb/man/ldbdel.1.xml b/ldb/man/ldbdel.1.xml new file mode 100644 index 00000000..7dfc7366 --- /dev/null +++ b/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/ldb/man/ldbedit.1.xml b/ldb/man/ldbedit.1.xml new file mode 100644 index 00000000..15c69b1b --- /dev/null +++ b/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/ldb/man/ldbmodify.1.xml b/ldb/man/ldbmodify.1.xml new file mode 100644 index 00000000..bc196477 --- /dev/null +++ b/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/ldb/man/ldbrename.1.xml b/ldb/man/ldbrename.1.xml new file mode 100644 index 00000000..391ec84c --- /dev/null +++ b/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/ldb/man/ldbsearch.1.xml b/ldb/man/ldbsearch.1.xml new file mode 100644 index 00000000..ed3749b9 --- /dev/null +++ b/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/ldb/man/oLschema2ldif.1.xml b/ldb/man/oLschema2ldif.1.xml new file mode 100644 index 00000000..b1e681be --- /dev/null +++ b/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/ldb/modules/asq.c b/ldb/modules/asq.c new file mode 100644 index 00000000..eb27263b --- /dev/null +++ b/ldb/modules/asq.c @@ -0,0 +1,481 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_includes.h" + +struct asq_context { + + enum {ASQ_INIT, ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step; + + struct ldb_module *module; + struct ldb_request *orig_req; + + struct ldb_asq_control *asq_ctrl; + + 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(struct ldb_request *req, struct ldb_module *module) +{ + struct asq_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 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->step = ASQ_INIT; + ac->module = module; + ac->orig_req = req; + + 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); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + 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->orig_req->callback(ac->module->ldb, ac->orig_req->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); + if (ac == NULL) { + goto error; + } + + /* 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); + if (ac == NULL) { + goto error; + } + + /* 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->orig_req->callback(ac->module->ldb, ac->orig_req->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_build_first_request(struct asq_context *ac) +{ + char **base_attrs; + + ac->base_req = talloc_zero(ac, struct ldb_request); + if (ac->base_req == NULL) return LDB_ERR_OPERATIONS_ERROR; + + ac->base_req->operation = ac->orig_req->operation; + ac->base_req->op.search.base = ac->orig_req->op.search.base; + ac->base_req->op.search.scope = LDB_SCOPE_BASE; + ac->base_req->op.search.tree = ac->orig_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, ac->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(ac->module->ldb, ac->orig_req, ac->base_req); + + return LDB_SUCCESS; +} + +static int asq_build_multiple_requests(struct asq_context *ac, struct ldb_handle *handle) +{ + struct ldb_message_element *el; + int i; + + /* 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); + } + + 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_new(ac->reqs[i], ac->module->ldb, (const char *)el->values[i].data); + if ( ! ldb_dn_validate(ac->reqs[i]->op.search.base)) { + 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]); + } + + return LDB_SUCCESS; +} + +static int asq_search_continue(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; + } + + ac = talloc_get_type(handle->private_data, struct asq_context); + if (ac == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + switch (ac->step) { + case ASQ_INIT: + /* check the search is well formed */ + if (ac->orig_req->op.search.scope != LDB_SCOPE_BASE) { + ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM; + return asq_terminate(handle); + } + + ac->req_attrs = ac->orig_req->op.search.attrs; + ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute); + if (ac->req_attribute == NULL) + return LDB_ERR_OPERATIONS_ERROR; + + /* get the object to retrieve the DNs to search */ + ret = asq_build_first_request(ac); + if (ret != LDB_SUCCESS) { + return ret; + } + + ac->step = ASQ_SEARCH_BASE; + + handle->state = LDB_ASYNC_PENDING; + handle->status = LDB_SUCCESS; + + return ldb_request(ac->module->ldb, ac->base_req); + + 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) { + + /* build up the requests call chain */ + ret = asq_build_multiple_requests(ac, handle); + if (ret != LDB_SUCCESS) { + return ret; + } + if (handle->state == LDB_ASYNC_DONE) { + return LDB_SUCCESS; + } + + ac->step = ASQ_SEARCH_MULTI; + + return ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]); + } + + /* request still pending, return to cycle again */ + return LDB_SUCCESS; + + case ASQ_SEARCH_MULTI: + + 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_request(ac->module->ldb, ac->reqs[ac->cur_req]); + } + + return asq_terminate(handle); + } + + /* request still pending, return to cycle again */ + return LDB_SUCCESS; + + default: + ret = LDB_ERR_OPERATIONS_ERROR; + break; + } + +done: + handle->state = LDB_ASYNC_DONE; + return ret; +} + +static int asq_search(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_control *control; + struct asq_context *ac; + struct ldb_handle *h; + + /* check if there's a paged request control */ + control = ldb_request_get_control(req, 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; + } + + h = init_handle(req, module); + if (!h) { + return LDB_ERR_OPERATIONS_ERROR; + } + ac = talloc_get_type(h->private_data, struct asq_context); + + ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control); + if (!ac->asq_ctrl) { + return LDB_ERR_PROTOCOL_ERROR; + } + + req->handle = h; + + return asq_search_continue(h); +} + +static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + int ret; + + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (type == LDB_WAIT_ALL) { + while (handle->state != LDB_ASYNC_DONE) { + ret = asq_search_continue(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; + } + + return asq_search_continue(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_WARNING, "asq: Unable to register control with rootdse!\n"); + } + + return ldb_next_init(module); +} + +const struct ldb_module_ops ldb_asq_module_ops = { + .name = "asq", + .search = asq_search, + .wait = asq_wait, + .init_context = asq_init +}; diff --git a/ldb/modules/operational.c b/ldb/modules/operational.c new file mode 100644 index 00000000..a59e81be --- /dev/null +++ b/ldb/modules/operational.c @@ -0,0 +1,305 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ +/* + 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,$SCHEMADN + + 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 "ldb_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) +{ + int ret = 0; + + if (ret != 0) { + return ret; + } + + return ldb_next_init(ctx); +} + +const struct ldb_module_ops ldb_operational_module_ops = { + .name = "operational", + .search = operational_search, + .init_context = operational_init +}; diff --git a/ldb/modules/paged_results.c b/ldb/modules/paged_results.c new file mode 100644 index 00000000..b62b1f92 --- /dev/null +++ b/ldb/modules/paged_results.c @@ -0,0 +1,557 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_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 = ldb_request_get_control(req, 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_once(struct ldb_handle *handle) { + 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; + } + + ret = ldb_wait(ac->store->req->handle, LDB_WAIT_NONE); + 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_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + int ret; + + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (type == LDB_WAIT_ALL) { + while (handle->state != LDB_ASYNC_DONE) { + ret = paged_wait_once(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; + } + + return paged_wait_once(handle); +} + +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_WARNING, "paged_request: Unable to register control with rootdse!\n"); + } + + talloc_free(req); + return ldb_next_init(module); +} + +const struct ldb_module_ops ldb_paged_results_module_ops = { + .name = "paged_results", + .search = paged_search, + .wait = paged_wait, + .init_context = paged_request_init +}; diff --git a/ldb/modules/paged_searches.c b/ldb/modules/paged_searches.c new file mode 100644 index 00000000..40e87f70 --- /dev/null +++ b/ldb/modules/paged_searches.c @@ -0,0 +1,463 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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_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_once(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(struct ldb_handle *handle, enum ldb_wait_type type) +{ + int ret; + + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (type == LDB_WAIT_ALL) { + while (handle->state != LDB_ASYNC_DONE) { + ret = ps_wait_once(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; + } + return ps_wait_once(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) { + ldb_set_errstring(module->ldb, "Out of Memory"); + 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, module->ldb, ""); + 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); +} + +_PUBLIC_ const struct ldb_module_ops ldb_paged_searches_module_ops = { + .name = "paged_searches", + .search = ps_search, + .wait = ps_wait, + .init_context = ps_init +}; diff --git a/ldb/modules/rdn_name.c b/ldb/modules/rdn_name.c new file mode 100644 index 00000000..65c044c0 --- /dev/null +++ b/ldb/modules/rdn_name.c @@ -0,0 +1,335 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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 "ldb_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; + const char *rdn_name; + struct ldb_val rdn_val; + 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_name = ldb_dn_get_rdn_name(msg->dn); + if (rdn_name == NULL) { + talloc_free(down_req); + return LDB_ERR_OPERATIONS_ERROR; + } + + rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(msg->dn)); + + /* 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_val, NULL) != 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_val, NULL) != 0) { + talloc_free(down_req); + return LDB_ERR_OPERATIONS_ERROR; + } + } else { + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(module->ldb, rdn_name); + + for (i = 0; i < attribute->num_values; i++) { + if (a->syntax->comparison_fn(module->ldb, msg, &rdn_val, &attribute->values[i]) == 0) { + /* overwrite so it matches in case */ + attribute->values[i] = rdn_val; + break; + } + } + if (i == attribute->num_values) { + ldb_debug_set(module->ldb, LDB_DEBUG_FATAL, + "RDN mismatch on %s: %s (%s)", + ldb_dn_get_linearized(msg->dn), rdn_name, rdn_val.data); + talloc_free(down_req); + /* Match AD's error here */ + return LDB_ERR_INVALID_DN_SYNTAX; + } + } + + /* 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; + const char *rdn_name; + struct ldb_val rdn_val; + struct ldb_message *msg; + + ac = talloc_get_type(h->private_data, struct rename_context); + + 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; + } + + rdn_name = ldb_dn_get_rdn_name(ac->orig_req->op.rename.newdn); + if (rdn_name == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(ac->orig_req->op.rename.newdn)); + + if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 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 rdn_name_wait_once(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 rdn_name_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + int ret; + + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (type == LDB_WAIT_ALL) { + while (handle->state != LDB_ASYNC_DONE) { + ret = rdn_name_wait_once(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; + } + + return rdn_name_wait_once(handle); +} + +const struct ldb_module_ops ldb_rdn_name_module_ops = { + .name = "rdn_name", + .add = rdn_name_add, + .rename = rdn_name_rename, + .wait = rdn_name_wait +}; diff --git a/ldb/modules/skel.c b/ldb/modules/skel.c new file mode 100644 index 00000000..0cd29ac4 --- /dev/null +++ b/ldb/modules/skel.c @@ -0,0 +1,131 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb skel module + * + * Description: example module + * + * Author: Simo Sorce + */ + +#include "ldb_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 *module) +{ + struct private_data *data; + + data = talloc(module, struct private_data); + if (data == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + data->some_private_data = NULL; + module->private_data = data; + + talloc_set_destructor (module, skel_destructor); + + return ldb_next_init(module); +} + +const struct ldb_module_ops ldb_skel_module_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, +}; diff --git a/ldb/modules/sort.c b/ldb/modules/sort.c new file mode 100644 index 00000000..746befa5 --- /dev/null +++ b/ldb/modules/sort.c @@ -0,0 +1,458 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldb server side sort control module + * + * Description: this module sorts the results of a search + * + * Author: Simo Sorce + */ + +#include "ldb_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_schema_attribute *a; + 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->a->syntax->comparison_fn(ac->module->ldb, ac, &el2->values[0], &el1->values[0]); + + return ac->a->syntax->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 = ldb_request_get_control(req, 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->a = ldb_schema_attribute_by_name(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_once(struct ldb_handle *handle) +{ + 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, LDB_WAIT_NONE); + + 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_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + int ret; + + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (type == LDB_WAIT_ALL) { + while (handle->state != LDB_ASYNC_DONE) { + ret = server_sort_wait_once(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; + } + + return server_sort_wait_once(handle); +} + +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_WARNING, "server_sort: Unable to register control with rootdse!\n"); + } + + talloc_free(req); + return ldb_next_init(module); +} + +const struct ldb_module_ops ldb_server_sort_module_ops = { + .name = "server_sort", + .search = server_sort_search, + .wait = server_sort_wait, + .init_context = server_sort_init +}; diff --git a/ldb/nssldb/README.txt b/ldb/nssldb/README.txt new file mode 100644 index 00000000..ddba62b3 --- /dev/null +++ b/ldb/nssldb/README.txt @@ -0,0 +1,34 @@ + +This test code requires a tdb that is configured for to use the asq module. +You can do that adding the following record to a tdb: + +dn: @MODULES +@LIST: asq + +Other modules can be used as well (like rdn_name for example) + +The uidNumber 0 and the gidNumber 0 are considered invalid. + +The user records should contain the followin attributes: +uid (required) the user name +userPassword (optional) the user password (if not present "LDB" is + returned in the password field) +uidNumber (required) the user uid +gidNumber (required) the user primary gid +gecos (optional) the GECOS +homeDirectory (required) the home directory +loginShell (required) the login shell +memberOf (required) all the groups the user is member of should + be reported here using their DNs. The + primary group as well. + +The group accounts should contain the following attributes: +cn (required) the group name +uesrPassword (optional) the group password (if not present "LDB" is + returned in the password field) +gidNumber (required) the group gid +member (optional) the DNs of the member users, also the ones + that have this group as primary + + +SSS diff --git a/ldb/nssldb/ldb-grp.c b/ldb/nssldb/ldb-grp.c new file mode 100644 index 00000000..71e27a91 --- /dev/null +++ b/ldb/nssldb/ldb-grp.c @@ -0,0 +1,425 @@ +/* + LDB nsswitch module + + Copyright (C) Simo Sorce 2006 + + 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 3 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb-nss.h" + +extern struct _ldb_nss_context *_ldb_nss_ctx; + +const char *_ldb_nss_gr_attrs[] = { + "cn", + "userPassword", + "gidNumber", + NULL +}; + +const char *_ldb_nss_mem_attrs[] = { + "uid", + NULL +}; + +#define _NSS_LDB_ENOMEM(amem) \ + do { \ + if ( ! amem) { \ + errno = ENOMEM; \ + talloc_free(memctx); \ + return NSS_STATUS_UNAVAIL; \ + } \ + } while(0) + +/* This setgrent, getgrent, endgrent is not very efficient */ + +NSS_STATUS _nss_ldb_setgrent(void) +{ + int ret; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + _ldb_nss_ctx->gr_cur = 0; + if (_ldb_nss_ctx->gr_res != NULL) { + talloc_free(_ldb_nss_ctx->gr_res); + _ldb_nss_ctx->gr_res = NULL; + } + + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + _LDB_NSS_GRENT_FILTER, + _ldb_nss_gr_attrs, + &_ldb_nss_ctx->gr_res); + if (ret != LDB_SUCCESS) { + return NSS_STATUS_UNAVAIL; + } + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_endgrent(void) +{ + int ret; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + _ldb_nss_ctx->gr_cur = 0; + if (_ldb_nss_ctx->gr_res) { + talloc_free(_ldb_nss_ctx->gr_res); + _ldb_nss_ctx->gr_res = NULL; + } + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_getgrent_r(struct group *result_buf, char *buffer, size_t buflen, int *errnop) +{ + int ret; + struct ldb_result *res; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + *errnop = 0; + + if (_ldb_nss_ctx->gr_cur >= _ldb_nss_ctx->gr_res->count) { + /* already returned all entries */ + return NSS_STATUS_NOTFOUND; + } + + res = talloc_zero(_ldb_nss_ctx->gr_res, struct ldb_result); + if ( ! res) { + errno = *errnop = ENOMEM; + _ldb_nss_ctx->gr_cur++; /* skip this entry */ + return NSS_STATUS_UNAVAIL; + } + + ret = _ldb_nss_group_request(&res, + _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur]->dn, + _ldb_nss_mem_attrs, + "member"); + + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno; + talloc_free(res); + _ldb_nss_ctx->gr_cur++; /* skip this entry */ + return ret; + } + + ret = _ldb_nss_fill_group(result_buf, + buffer, + buflen, + errnop, + _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur], + res); + + talloc_free(res); + + if (ret != NSS_STATUS_SUCCESS) { + if (ret != NSS_STATUS_TRYAGAIN) { + _ldb_nss_ctx->gr_cur++; /* skip this entry */ + } + return ret; + } + + /* this entry is ok, increment counter to nex entry */ + _ldb_nss_ctx->gr_cur++; + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_getgrnam_r(const char *name, struct group *result_buf, char *buffer, size_t buflen, int *errnop) +{ + int ret; + char *filter; + TALLOC_CTX *ctx; + struct ldb_result *gr_res; + struct ldb_result *mem_res; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + ctx = talloc_new(_ldb_nss_ctx->ldb); + if ( ! ctx) { + *errnop = errno = ENOMEM; + return NSS_STATUS_UNAVAIL; + } + + /* build the filter for this uid */ + filter = talloc_asprintf(ctx, _LDB_NSS_GRNAM_FILTER, name); + if (filter == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* search the entry */ + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_gr_attrs, + &gr_res); + if (ret != LDB_SUCCESS) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + talloc_steal(ctx, gr_res); + + /* if none found return */ + if (gr_res->count == 0) { + *errnop = errno = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + if (gr_res->count != 1) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + mem_res = talloc_zero(ctx, struct ldb_result); + if ( ! mem_res) { + errno = *errnop = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + ret = _ldb_nss_group_request(&mem_res, + gr_res->msgs[0]->dn, + _ldb_nss_mem_attrs, + "member"); + + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno; + goto done; + } + + ret = _ldb_nss_fill_group(result_buf, + buffer, + buflen, + errnop, + gr_res->msgs[0], + mem_res); + + if (ret != NSS_STATUS_SUCCESS) { + goto done; + } + + ret = NSS_STATUS_SUCCESS; +done: + talloc_free(ctx); + return ret; +} + +NSS_STATUS _nss_ldb_getgrgid_r(gid_t gid, struct group *result_buf, char *buffer, size_t buflen, int *errnop) +{ + int ret; + char *filter; + TALLOC_CTX *ctx; + struct ldb_result *gr_res; + struct ldb_result *mem_res; + + if (gid == 0) { /* we don't serve root gid by policy */ + *errnop = errno = ENOENT; + return NSS_STATUS_NOTFOUND; + } + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + ctx = talloc_new(_ldb_nss_ctx->ldb); + if ( ! ctx) { + *errnop = errno = ENOMEM; + return NSS_STATUS_UNAVAIL; + } + + /* build the filter for this uid */ + filter = talloc_asprintf(ctx, _LDB_NSS_GRGID_FILTER, gid); + if (filter == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* search the entry */ + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_gr_attrs, + &gr_res); + if (ret != LDB_SUCCESS) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + talloc_steal(ctx, gr_res); + + /* if none found return */ + if (gr_res->count == 0) { + *errnop = errno = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + if (gr_res->count != 1) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + mem_res = talloc_zero(ctx, struct ldb_result); + if ( ! mem_res) { + errno = *errnop = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + ret = _ldb_nss_group_request(&mem_res, + gr_res->msgs[0]->dn, + _ldb_nss_mem_attrs, + "member"); + + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno; + goto done; + } + + ret = _ldb_nss_fill_group(result_buf, + buffer, + buflen, + errnop, + gr_res->msgs[0], + mem_res); + + if (ret != NSS_STATUS_SUCCESS) { + goto done; + } + + ret = NSS_STATUS_SUCCESS; +done: + talloc_free(ctx); + return ret; +} + +NSS_STATUS _nss_ldb_initgroups_dyn(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop) +{ + int ret; + char *filter; + const char * attrs[] = { "uidNumber", "gidNumber", NULL }; + struct ldb_result *uid_res; + struct ldb_result *mem_res; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + mem_res = talloc_zero(_ldb_nss_ctx, struct ldb_result); + if ( ! mem_res) { + errno = *errnop = ENOMEM; + return NSS_STATUS_UNAVAIL; + } + + /* build the filter for this name */ + filter = talloc_asprintf(mem_res, _LDB_NSS_PWNAM_FILTER, user); + if (filter == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* search the entry */ + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + filter, + attrs, + &uid_res); + if (ret != LDB_SUCCESS) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + talloc_steal(mem_res, uid_res); + + /* if none found return */ + if (uid_res->count == 0) { + *errnop = errno = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + if (uid_res->count != 1) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + ret = _ldb_nss_group_request(&mem_res, + uid_res->msgs[0]->dn, + attrs, + "memberOf"); + + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno; + goto done; + } + + ret = _ldb_nss_fill_initgr(group, + limit, + start, + size, + groups, + errnop, + mem_res); + + if (ret != NSS_STATUS_SUCCESS) { + goto done; + } + + ret = NSS_STATUS_SUCCESS; + +done: + talloc_free(mem_res); + return ret; +} diff --git a/ldb/nssldb/ldb-nss.c b/ldb/nssldb/ldb-nss.c new file mode 100644 index 00000000..8f732103 --- /dev/null +++ b/ldb/nssldb/ldb-nss.c @@ -0,0 +1,395 @@ +/* + LDB nsswitch module + + Copyright (C) Simo Sorce 2006 + + 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 3 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb-nss.h" + +struct _ldb_nss_context *_ldb_nss_ctx = NULL; + +NSS_STATUS _ldb_nss_init(void) +{ + int ret; + + pid_t mypid = getpid(); + + if (_ldb_nss_ctx != NULL) { + if (_ldb_nss_ctx->pid == mypid) { + /* already initialized */ + return NSS_STATUS_SUCCESS; + } else { + /* we are in a forked child now, reinitialize */ + talloc_free(_ldb_nss_ctx); + _ldb_nss_ctx = NULL; + } + } + + _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid); + if (_ldb_nss_ctx == NULL) { + return NSS_STATUS_UNAVAIL; + } + + _ldb_nss_ctx->pid = mypid; + + _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx, NULL); + if (_ldb_nss_ctx->ldb == NULL) { + goto failed; + } + + ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL); + if (ret != LDB_SUCCESS) { + goto failed; + } + + _ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN); + if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) { + goto failed; + } + + _ldb_nss_ctx->pw_cur = 0; + _ldb_nss_ctx->pw_res = NULL; + _ldb_nss_ctx->gr_cur = 0; + _ldb_nss_ctx->gr_res = NULL; + + return NSS_STATUS_SUCCESS; + +failed: + /* talloc_free(_ldb_nss_ctx); */ + _ldb_nss_ctx = NULL; + return NSS_STATUS_UNAVAIL; +} + +NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result, + char *buffer, + int buflen, + int *errnop, + struct ldb_message *msg) +{ + int len; + int bufpos; + const char *tmp; + + bufpos = 0; + + /* get username */ + tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL); + if (tmp == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->pw_name = &buffer[bufpos]; + bufpos += len; + + /* get userPassword */ + tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL); + if (tmp == NULL) { + tmp = "LDB"; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->pw_passwd = &buffer[bufpos]; + bufpos += len; + + /* this backend never serves an uid 0 user */ + result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0); + if (result->pw_uid == 0) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + + result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0); + if (result->pw_gid == 0) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + + /* get gecos */ + tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL); + if (tmp == NULL) { + tmp = ""; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->pw_gecos = &buffer[bufpos]; + bufpos += len; + + /* get homeDirectory */ + tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL); + if (tmp == NULL) { + tmp = ""; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->pw_dir = &buffer[bufpos]; + bufpos += len; + + /* get shell */ + tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL); + if (tmp == NULL) { + tmp = ""; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->pw_shell = &buffer[bufpos]; + bufpos += len; + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _ldb_nss_fill_group(struct group *result, + char *buffer, + int buflen, + int *errnop, + struct ldb_message *group, + struct ldb_result *members) +{ + const char *tmp; + size_t len; + size_t bufpos; + size_t lsize; + int i; + + bufpos = 0; + + /* get group name */ + tmp = ldb_msg_find_attr_as_string(group, "cn", NULL); + if (tmp == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->gr_name = &buffer[bufpos]; + bufpos += len; + + /* get userPassword */ + tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL); + if (tmp == NULL) { + tmp = "LDB"; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->gr_passwd = &buffer[bufpos]; + bufpos += len; + + result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0); + if (result->gr_gid == 0) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + + /* check if there is enough memory for the list of pointers */ + lsize = (members->count + 1) * sizeof(char *); + + /* align buffer on pointer boundary */ + bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*))); + if ((buflen - bufpos) < lsize) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + + result->gr_mem = (char **)&buffer[bufpos]; + bufpos += lsize; + + for (i = 0; i < members->count; i++) { + tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL); + if (tmp == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + len = strlen(tmp)+1; + if (bufpos + len > buflen) { + /* buffer too small */ + *errnop = errno = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + memcpy(&buffer[bufpos], tmp, len); + result->gr_mem[i] = &buffer[bufpos]; + bufpos += len; + } + + result->gr_mem[i] = NULL; + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _ldb_nss_fill_initgr(gid_t group, + long int limit, + long int *start, + long int *size, + gid_t **groups, + int *errnop, + struct ldb_result *grlist) +{ + NSS_STATUS ret; + int i; + + for (i = 0; i < grlist->count; i++) { + + if (limit && (*start > limit)) { + /* TODO: warn no all groups were reported */ + *errnop = 0; + ret = NSS_STATUS_SUCCESS; + goto done; + } + + if (*start == *size) { + /* buffer full, enlarge it */ + long int gs; + gid_t *gm; + + gs = (*size) + 32; + if (limit && (gs > limit)) { + gs = limit; + } + + gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t)); + if ( ! gm) { + *errnop = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + *groups = gm; + *size = gs; + } + + (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0); + if ((*groups)[*start] == 0 || (*groups)[*start] == group) { + /* skip root group or primary group */ + continue; + } + (*start)++; + + } + + *errnop = 0; + ret = NSS_STATUS_SUCCESS; +done: + return ret; +} + +#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0) + +NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res, + struct ldb_dn *group_dn, + const char * const *attrs, + const char *mattr) +{ + struct ldb_control **ctrls; + struct ldb_control *ctrl; + struct ldb_asq_control *asqc; + struct ldb_request *req; + int ret; + struct ldb_result *res = *_res; + + ctrls = talloc_array(res, struct ldb_control *, 2); + _LDB_NSS_ALLOC_CHECK(ctrls); + + ctrl = talloc(ctrls, struct ldb_control); + _LDB_NSS_ALLOC_CHECK(ctrl); + + asqc = talloc(ctrl, struct ldb_asq_control); + _LDB_NSS_ALLOC_CHECK(asqc); + + asqc->source_attribute = talloc_strdup(asqc, mattr); + _LDB_NSS_ALLOC_CHECK(asqc->source_attribute); + + asqc->request = 1; + asqc->src_attr_len = strlen(asqc->source_attribute); + ctrl->oid = LDB_CONTROL_ASQ_OID; + ctrl->critical = 1; + ctrl->data = asqc; + ctrls[0] = ctrl; + ctrls[1] = NULL; + + ret = ldb_build_search_req( + &req, + _ldb_nss_ctx->ldb, + res, + group_dn, + LDB_SCOPE_BASE, + "(objectClass=*)", + attrs, + ctrls, + res, + ldb_search_default_callback); + + if (ret != LDB_SUCCESS) { + errno = ENOENT; + return NSS_STATUS_UNAVAIL; + } + + ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0); + + ret = ldb_request(_ldb_nss_ctx->ldb, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } else { + talloc_free(req); + return NSS_STATUS_UNAVAIL; + } + + talloc_free(req); + return NSS_STATUS_SUCCESS; +} + diff --git a/ldb/nssldb/ldb-nss.h b/ldb/nssldb/ldb-nss.h new file mode 100644 index 00000000..583876fe --- /dev/null +++ b/ldb/nssldb/ldb-nss.h @@ -0,0 +1,84 @@ +/* + LDB nsswitch module + + Copyright (C) Simo Sorce 2006 + + 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 3 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LDB_NSS +#define _LDB_NSS + +#include "includes.h" +#include "ldb/include/includes.h" + +#include <nss.h> +#include <pwd.h> +#include <grp.h> + +#define _LDB_NSS_URL "etc/users.ldb" +#define _LDB_NSS_BASEDN "CN=Users,CN=System" +#define _LDB_NSS_PWENT_FILTER "(&(objectClass=posixAccount)(!(uidNumber=0))(!(gidNumber=0)))" +#define _LDB_NSS_PWUID_FILTER "(&(objectClass=posixAccount)(uidNumber=%d)(!(gidNumber=0)))" +#define _LDB_NSS_PWNAM_FILTER "(&(objectClass=posixAccount)(uid=%s)(!(uidNumber=0))(!(gidNumber=0)))" + +#define _LDB_NSS_GRENT_FILTER "(&(objectClass=posixGroup)(!(gidNumber=0)))" +#define _LDB_NSS_GRGID_FILTER "(&(objectClass=posixGroup)(gidNumber=%d)))" +#define _LDB_NSS_GRNAM_FILTER "(&(objectClass=posixGroup)(cn=%s)(!(gidNumber=0)))" + +typedef enum nss_status NSS_STATUS; + +struct _ldb_nss_context { + + pid_t pid; + + struct ldb_context *ldb; + struct ldb_dn *base; + + int pw_cur; + struct ldb_result *pw_res; + + int gr_cur; + struct ldb_result *gr_res; +}; + +NSS_STATUS _ldb_nss_init(void); + +NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result, + char *buffer, + int buflen, + int *errnop, + struct ldb_message *msg); + +NSS_STATUS _ldb_nss_fill_group(struct group *result, + char *buffer, + int buflen, + int *errnop, + struct ldb_message *group, + struct ldb_result *members); + +NSS_STATUS _ldb_nss_fill_initgr(gid_t group, + long int limit, + long int *start, + long int *size, + gid_t **groups, + int *errnop, + struct ldb_result *grlist); + +NSS_STATUS _ldb_nss_group_request(struct ldb_result **res, + struct ldb_dn *group_dn, + const char * const *attrs, + const char *mattr); + +#endif /* _LDB_NSS */ diff --git a/ldb/nssldb/ldb-pwd.c b/ldb/nssldb/ldb-pwd.c new file mode 100644 index 00000000..44b0ab21 --- /dev/null +++ b/ldb/nssldb/ldb-pwd.c @@ -0,0 +1,239 @@ +/* + LDB nsswitch module + + Copyright (C) Simo Sorce 2006 + + 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 3 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb-nss.h" + +extern struct _ldb_nss_context *_ldb_nss_ctx; + +const char *_ldb_nss_pw_attrs[] = { + "uid", + "userPassword", + "uidNumber", + "gidNumber", + "gecos", + "homeDirectory", + "loginShell", + NULL +}; + +NSS_STATUS _nss_ldb_setpwent(void) +{ + int ret; + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + _ldb_nss_ctx->pw_cur = 0; + if (_ldb_nss_ctx->pw_res != NULL) { + talloc_free(_ldb_nss_ctx->pw_res); + _ldb_nss_ctx->pw_res = NULL; + } + + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + _LDB_NSS_PWENT_FILTER, + _ldb_nss_pw_attrs, + &_ldb_nss_ctx->pw_res); + if (ret != LDB_SUCCESS) { + return NSS_STATUS_UNAVAIL; + } + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_endpwent(void) +{ + int ret; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + _ldb_nss_ctx->pw_cur = 0; + if (_ldb_nss_ctx->pw_res) { + talloc_free(_ldb_nss_ctx->pw_res); + _ldb_nss_ctx->pw_res = NULL; + } + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_getpwent_r(struct passwd *result_buf, + char *buffer, + int buflen, + int *errnop) +{ + int ret; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + *errnop = 0; + + if (_ldb_nss_ctx->pw_cur >= _ldb_nss_ctx->pw_res->count) { + /* already returned all entries */ + return NSS_STATUS_NOTFOUND; + } + + ret = _ldb_nss_fill_passwd(result_buf, + buffer, + buflen, + errnop, + _ldb_nss_ctx->pw_res->msgs[_ldb_nss_ctx->pw_cur]); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + _ldb_nss_ctx->pw_cur++; + + return NSS_STATUS_SUCCESS; +} + +NSS_STATUS _nss_ldb_getpwuid_r(uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) +{ + int ret; + char *filter; + struct ldb_result *res; + + if (uid == 0) { /* we don't serve root uid by policy */ + *errnop = errno = ENOENT; + return NSS_STATUS_NOTFOUND; + } + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + /* build the filter for this uid */ + filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWUID_FILTER, uid); + if (filter == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOMEM; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* search the entry */ + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_pw_attrs, + &res); + if (ret != LDB_SUCCESS) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* if none found return */ + if (res->count == 0) { + *errnop = errno = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + if (res->count != 1) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* fill in the passwd struct */ + ret = _ldb_nss_fill_passwd(result_buf, + buffer, + buflen, + errnop, + res->msgs[0]); + +done: + talloc_free(filter); + talloc_free(res); + return ret; +} + +NSS_STATUS _nss_ldb_getpwnam_r(const char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) +{ + int ret; + char *filter; + struct ldb_result *res; + + ret = _ldb_nss_init(); + if (ret != NSS_STATUS_SUCCESS) { + return ret; + } + + /* build the filter for this name */ + filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWNAM_FILTER, name); + if (filter == NULL) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* search the entry */ + ret = ldb_search(_ldb_nss_ctx->ldb, + _ldb_nss_ctx->base, + LDB_SCOPE_SUBTREE, + filter, + _ldb_nss_pw_attrs, + &res); + if (ret != LDB_SUCCESS) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* if none found return */ + if (res->count == 0) { + *errnop = errno = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + + if (res->count != 1) { + /* this is a fatal error */ + *errnop = errno = ENOENT; + ret = NSS_STATUS_UNAVAIL; + goto done; + } + + /* fill in the passwd struct */ + ret = _ldb_nss_fill_passwd(result_buf, + buffer, + buflen, + errnop, + res->msgs[0]); + +done: + talloc_free(filter); + talloc_free(res); + return ret; +} + diff --git a/ldb/python.mk b/ldb/python.mk new file mode 100644 index 00000000..45f4f044 --- /dev/null +++ b/ldb/python.mk @@ -0,0 +1,10 @@ +[PYTHON::swig_ldb] +LIBRARY_REALNAME = _ldb.$(SHLIBEXT) +PUBLIC_DEPENDENCIES = LIBLDB LIBEVENTS + +swig_ldb_OBJ_FILES = $(ldbsrcdir)/ldb_wrap.o +$(swig_ldb_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/include + +$(eval $(call python_py_module_template,ldb.py,$(ldbsrcdir)/ldb.py)) + +$(swig_ldb_OBJ_FILES): CFLAGS+=$(CFLAG_NO_UNUSED_MACROS) $(CFLAG_NO_CAST_QUAL) diff --git a/ldb/rules.mk b/ldb/rules.mk new file mode 100644 index 00000000..ff5dc027 --- /dev/null +++ b/ldb/rules.mk @@ -0,0 +1,30 @@ +etags: + etags `find $(srcdir) -name "*.[ch]"` + +ctags: + ctags `find $(srcdir) -name "*.[ch]"` + +.SUFFIXES: _wrap.c .i + +.i_wrap.c: + [ "$(SWIG)" = "no" ] || $(SWIG) -O -Wall -python -keyword $< + +.SUFFIXES: .1 .1.xml .3 .3.xml .xml .html .c .o + +.c.o: + @echo Compiling $*.c + @mkdir -p `dirname $@` + @$(CC) $(CFLAGS) $(PICFLAG) -c $< -o $@ + +.c.po: + @echo Compiling $*.c + @mkdir -p `dirname $@` + @$(CC) -fPIC $(CFLAGS) -c $< -o $@ + +showflags:: + @echo 'ldb will be compiled with flags:' + @echo ' CFLAGS = $(CFLAGS)' + @echo ' LIBS = $(LIBS)' + +distclean:: + rm -f *~ */*~ diff --git a/ldb/sqlite3.m4 b/ldb/sqlite3.m4 new file mode 100644 index 00000000..d0a74ee5 --- /dev/null +++ b/ldb/sqlite3.m4 @@ -0,0 +1,62 @@ +######################################################## +# 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_DEFINE(HAVE_LDB_SQLITE3,1,[Whether ldb_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/ldb/standalone.sh b/ldb/standalone.sh new file mode 100755 index 00000000..8ab081e0 --- /dev/null +++ b/ldb/standalone.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +cd ../replace +make clean + +cd ../talloc +make clean + +cd ../tdb +make clean + +cd ../events +make clean + +cd ../ldb +make clean + +./autogen.sh + +rm -fr build +mkdir build +cd build + +../configure $* +make dirs +make all + +cd .. diff --git a/ldb/tests/init.ldif b/ldb/tests/init.ldif new file mode 100644 index 00000000..2e0b83c7 --- /dev/null +++ b/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/ldb/tests/init_slapd.sh b/ldb/tests/init_slapd.sh new file mode 100755 index 00000000..cf06acd0 --- /dev/null +++ b/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/ldb/tests/kill_slapd.sh b/ldb/tests/kill_slapd.sh new file mode 100755 index 00000000..91beb108 --- /dev/null +++ b/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/ldb/tests/ldapi_url.sh b/ldb/tests/ldapi_url.sh new file mode 100755 index 00000000..fef6c35f --- /dev/null +++ b/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/ldb/tests/photo.ldif b/ldb/tests/photo.ldif new file mode 100644 index 00000000..28981b1f --- /dev/null +++ b/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/ldb/tests/python/api.py b/ldb/tests/python/api.py new file mode 100755 index 00000000..1ae3fde7 --- /dev/null +++ b/ldb/tests/python/api.py @@ -0,0 +1,463 @@ +#!/usr/bin/python +# Simple tests for the ldb python bindings. +# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org> + +import sys +import unittest + +# Required for the standalone LDB build +sys.path.append("swig") +sys.path.append("build/lib.linux-i686-2.4") + +import ldb + +class NoContextTests(unittest.TestCase): + def test_valid_attr_name(self): + self.assertTrue(ldb.valid_attr_name("foo")) + self.assertFalse(ldb.valid_attr_name("24foo")) + + def test_timestring(self): + self.assertEquals("19700101000000.0Z", ldb.timestring(0)) + self.assertEquals("20071119191012.0Z", ldb.timestring(1195499412)) + + def test_string_to_time(self): + self.assertEquals(0, ldb.string_to_time("19700101000000.0Z")) + self.assertEquals(1195499412, ldb.string_to_time("20071119191012.0Z")) + + +class SimpleLdb(unittest.TestCase): + def test_connect(self): + ldb.Ldb("foo.tdb") + + def test_connect_none(self): + ldb.Ldb() + + def test_connect_later(self): + x = ldb.Ldb() + x.connect("foo.tdb") + + def test_repr(self): + x = ldb.Ldb() + self.assertTrue(repr(x).startswith("<ldb connection")) + + def test_set_create_perms(self): + x = ldb.Ldb() + x.set_create_perms(0600) + + def test_set_modules_dir(self): + x = ldb.Ldb() + x.set_modules_dir("/tmp") + + def test_search(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(len(l.search()), 1) + + def test_search_controls(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(len(l.search(controls=["paged_results:1:5"])), 1) + + def test_search_attrs(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0) + + def test_search_string_dn(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0) + + def test_search_attr_string(self): + l = ldb.Ldb("foo.tdb") + self.assertRaises(TypeError, l.search, attrs="dc") + + def test_opaque(self): + l = ldb.Ldb("foo.tdb") + l.set_opaque("my_opaque", l) + self.assertTrue(l.get_opaque("my_opaque") is not None) + self.assertEquals(None, l.get_opaque("unknown")) + + def test_parse_control_strings(self): + l = ldb.Ldb("foo.tdb") + self.assertRaises(ldb.LdbError, l.parse_control_strings, ["foo", "bar"]) + self.assertTrue(l.parse_control_strings(["paged_results:1:5"]) is not None) + + def test_search_scope_base(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo"), + ldb.SCOPE_ONELEVEL)), 0) + + def test_delete(self): + l = ldb.Ldb("foo.tdb") + self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo"))) + + def test_contains(self): + l = ldb.Ldb("foo.tdb") + self.assertFalse(ldb.Dn(l, "dc=foo") in l) + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=foo") + m["b"] = ["a"] + l.add(m) + try: + self.assertTrue(ldb.Dn(l, "dc=foo") in l) + finally: + l.delete(m.dn) + + def test_get_config_basedn(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(None, l.get_config_basedn()) + + def test_get_root_basedn(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(None, l.get_root_basedn()) + + def test_get_schema_basedn(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(None, l.get_schema_basedn()) + + def test_get_default_basedn(self): + l = ldb.Ldb("foo.tdb") + self.assertEquals(None, l.get_default_basedn()) + + def test_add(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=foo") + m["bla"] = "bla" + self.assertEquals(len(l.search()), 1) + l.add(m) + try: + self.assertEquals(len(l.search()), 2) + finally: + l.delete(ldb.Dn(l, "dc=foo")) + + def test_add_dict(self): + l = ldb.Ldb("foo.tdb") + m = {"dn": ldb.Dn(l, "dc=foo"), + "bla": "bla"} + self.assertEquals(len(l.search()), 1) + l.add(m) + try: + self.assertEquals(len(l.search()), 2) + finally: + l.delete(ldb.Dn(l, "dc=foo")) + + def test_add_dict_string_dn(self): + l = ldb.Ldb("foo.tdb") + m = {"dn": "dc=foo", "bla": "bla"} + self.assertEquals(len(l.search()), 1) + l.add(m) + try: + self.assertEquals(len(l.search()), 2) + finally: + l.delete(ldb.Dn(l, "dc=foo")) + + def test_rename(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=foo") + m["bla"] = "bla" + self.assertEquals(len(l.search()), 1) + l.add(m) + try: + l.rename(ldb.Dn(l, "dc=foo"), ldb.Dn(l, "dc=bar")) + self.assertEquals(len(l.search()), 2) + finally: + l.delete(ldb.Dn(l, "dc=bar")) + + def test_rename_string_dns(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=foo") + m["bla"] = "bla" + self.assertEquals(len(l.search()), 1) + l.add(m) + try: + l.rename("dc=foo", "dc=bar") + self.assertEquals(len(l.search()), 2) + finally: + l.delete(ldb.Dn(l, "dc=bar")) + + def test_modify_delete(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=modifydelete") + m["bla"] = ["1234"] + l.add(m) + rm = l.search(m.dn)[0] + self.assertEquals(["1234"], list(rm["bla"])) + try: + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=modifydelete") + m["bla"] = ldb.MessageElement([], ldb.CHANGETYPE_DELETE, "bla") + l.modify(m) + rm = l.search(m.dn)[0] + self.assertEquals(1, len(rm)) + finally: + l.delete(ldb.Dn(l, "dc=modifydelete")) + + def test_modify_add(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=add") + m["bla"] = ["1234"] + l.add(m) + try: + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=add") + m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_ADD, "bla") + l.modify(m) + rm = l.search(m.dn)[0] + self.assertEquals(2, len(rm)) + self.assertEquals(["1234", "456"], list(rm["bla"])) + finally: + l.delete(ldb.Dn(l, "dc=add")) + + def test_modify_modify(self): + l = ldb.Ldb("foo.tdb") + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=modify2") + m["bla"] = ["1234", "456"] + l.add(m) + try: + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=modify2") + m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_MODIFY, "bla") + l.modify(m) + rm = l.search(m.dn)[0] + self.assertEquals(2, len(rm)) + self.assertEquals(["1234"], list(rm["bla"])) + finally: + l.delete(ldb.Dn(l, "dc=modify2")) + + def test_transaction_commit(self): + l = ldb.Ldb("foo.tdb") + l.transaction_start() + m = ldb.Message(ldb.Dn(l, "dc=foo")) + m["foo"] = ["bar"] + l.add(m) + l.transaction_commit() + l.delete(m.dn) + + def test_transaction_cancel(self): + l = ldb.Ldb("foo.tdb") + l.transaction_start() + m = ldb.Message(ldb.Dn(l, "dc=foo")) + m["foo"] = ["bar"] + l.add(m) + l.transaction_cancel() + self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo")))) + + def test_set_debug(self): + def my_report_fn(level, text): + pass + l = ldb.Ldb("foo.tdb") + l.set_debug(my_report_fn) + + +class DnTests(unittest.TestCase): + def setUp(self): + self.ldb = ldb.Ldb("foo.tdb") + + def test_eq_str(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals("dc=foo,bar=bloe", x) + + def test_str(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals(x.__str__(), "dc=foo,bar=bloe") + + def test_repr(self): + x = ldb.Dn(self.ldb, "dc=foo,bla=blie") + self.assertEquals(x.__repr__(), "Dn('dc=foo,bla=blie')") + + def test_get_casefold(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals(x.get_casefold(), "DC=FOO,BAR=bloe") + + def test_validate(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertTrue(x.validate()) + + def test_parent(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals("bar=bloe", x.parent().__str__()) + + def test_compare(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + y = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals(x, y) + z = ldb.Dn(self.ldb, "dc=foo,bar=blie") + self.assertNotEquals(z, y) + + def test_is_valid(self): + x = ldb.Dn(self.ldb, "dc=foo,dc=bloe") + self.assertTrue(x.is_valid()) + x = ldb.Dn(self.ldb, "") + # is_valid()'s return values appears to be a side effect of + # some other ldb functions. yuck. + # self.assertFalse(x.is_valid()) + + def test_is_special(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertFalse(x.is_special()) + x = ldb.Dn(self.ldb, "@FOOBAR") + self.assertTrue(x.is_special()) + + def test_check_special(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertFalse(x.check_special("FOOBAR")) + x = ldb.Dn(self.ldb, "@FOOBAR") + self.assertTrue(x.check_special("@FOOBAR")) + + def test_len(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals(2, len(x)) + x = ldb.Dn(self.ldb, "dc=foo") + self.assertEquals(1, len(x)) + + def test_add_child(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe"))) + self.assertEquals("bla=bloe,dc=foo,bar=bloe", x.__str__()) + + def test_add_base(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertTrue(x.add_base(ldb.Dn(self.ldb, "bla=bloe"))) + self.assertEquals("dc=foo,bar=bloe,bla=bloe", x.__str__()) + + def test_add(self): + x = ldb.Dn(self.ldb, "dc=foo") + y = ldb.Dn(self.ldb, "bar=bla") + self.assertEquals("dc=foo,bar=bla", str(y + x)) + + def test_parse_ldif(self): + msgs = self.ldb.parse_ldif("dn: foo=bar\n") + msg = msgs.next() + self.assertEquals("foo=bar", str(msg[1].dn)) + self.assertTrue(isinstance(msg[1], ldb.Message)) + + def test_parse_ldif_more(self): + msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar") + msg = msgs.next() + self.assertEquals("foo=bar", str(msg[1].dn)) + msg = msgs.next() + self.assertEquals("bar=bar", str(msg[1].dn)) + + def test_canonical_string(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals("/bloe/foo", x.canonical_str()) + + def test_canonical_ex_string(self): + x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + self.assertEquals("/bloe\nfoo", x.canonical_ex_str()) + + +class LdbMsgTests(unittest.TestCase): + def setUp(self): + self.msg = ldb.Message() + + def test_init_dn(self): + self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo")) + self.assertEquals("dc=foo", str(self.msg.dn)) + + def test_iter_items(self): + self.assertEquals(0, len(self.msg.items())) + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo") + self.assertEquals(1, len(self.msg.items())) + + def test_repr(self): + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo") + self.msg["dc"] = "foo" + self.assertEquals("Message({'dn': Dn('dc=foo'), 'dc': MessageElement(['foo'])})", repr(self.msg)) + + def test_len(self): + self.assertEquals(0, len(self.msg)) + + def test_notpresent(self): + self.assertRaises(KeyError, lambda: self.msg["foo"]) + + def test_del(self): + del self.msg["foo"] + + def test_add_value(self): + self.assertEquals(0, len(self.msg)) + self.msg["foo"] = ["foo"] + self.assertEquals(1, len(self.msg)) + + def test_add_value_multiple(self): + self.assertEquals(0, len(self.msg)) + self.msg["foo"] = ["foo", "bla"] + self.assertEquals(1, len(self.msg)) + self.assertEquals(["foo", "bla"], list(self.msg["foo"])) + + def test_set_value(self): + self.msg["foo"] = ["fool"] + self.assertEquals(["fool"], list(self.msg["foo"])) + self.msg["foo"] = ["bar"] + self.assertEquals(["bar"], list(self.msg["foo"])) + + def test_keys(self): + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO") + self.msg["foo"] = ["bla"] + self.msg["bar"] = ["bla"] + self.assertEquals(["dn", "foo", "bar"], self.msg.keys()) + + def test_dn(self): + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO") + self.assertEquals("@BASEINFO", self.msg.dn.__str__()) + + def test_get_dn(self): + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO") + self.assertEquals("@BASEINFO", self.msg.get("dn").__str__()) + + def test_get_other(self): + self.msg["foo"] = ["bar"] + self.assertEquals("bar", self.msg.get("foo")[0]) + + def test_get_unknown(self): + self.assertRaises(KeyError, self.msg.get, "lalalala") + + +class MessageElementTests(unittest.TestCase): + def test_cmp_element(self): + x = ldb.MessageElement(["foo"]) + y = ldb.MessageElement(["foo"]) + z = ldb.MessageElement(["bzr"]) + self.assertEquals(x, y) + self.assertNotEquals(x, z) + + def test_create_iterable(self): + x = ldb.MessageElement(["foo"]) + self.assertEquals(["foo"], list(x)) + + def test_repr(self): + x = ldb.MessageElement(["foo"]) + self.assertEquals("MessageElement(['foo'])", repr(x)) + x = ldb.MessageElement(["foo", "bla"]) + self.assertEquals("MessageElement(['foo','bla'])", repr(x)) + + def test_get_item(self): + x = ldb.MessageElement(["foo", "bar"]) + self.assertEquals("foo", x[0]) + self.assertEquals("bar", x[1]) + self.assertRaises(KeyError, lambda: x[-1]) + + def test_len(self): + x = ldb.MessageElement(["foo", "bar"]) + self.assertEquals(2, len(x)) + + def test_eq(self): + x = ldb.MessageElement(["foo", "bar"]) + self.assertEquals(["foo", "bar"], x) + x = ldb.MessageElement(["foo"]) + self.assertEquals("foo", x) + +class ExampleModule: + name = "example" + +class ModuleTests(unittest.TestCase): + def test_register_module(self): + ldb.register_module(ExampleModule()) + +if __name__ == '__main__': + import unittest + unittest.TestProgram() diff --git a/ldb/tests/python/ldap.py b/ldb/tests/python/ldap.py new file mode 100755 index 00000000..bc6f80e8 --- /dev/null +++ b/ldb/tests/python/ldap.py @@ -0,0 +1,1017 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# This is a port of the original in testprogs/ejs/ldap.js + +import getopt +import optparse +import sys +import time + +sys.path.append("bin/python") + +import samba.getopt as options + +from samba.auth import system_session +from ldb import (SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, + LDB_ERR_NO_SUCH_OBJECT, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS, + LDB_ERR_ENTRY_ALREADY_EXISTS, LDB_ERR_UNWILLING_TO_PERFORM, + LDB_ERR_NOT_ALLOWED_ON_NON_LEAF, LDB_ERR_OTHER, LDB_ERR_INVALID_DN_SYNTAX) +from samba import Ldb +from subunit import SubunitTestRunner +from samba import param +import unittest + +parser = optparse.OptionParser("ldap [options] <host>") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +# use command line creds if available +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +opts, args = parser.parse_args() + +if len(args) < 1: + parser.print_usage() + sys.exit(1) + +host = args[0] + +lp = sambaopts.get_loadparm() +creds = credopts.get_credentials(lp) + +class BasicTests(unittest.TestCase): + def delete_force(self, ldb, dn): + try: + ldb.delete(dn) + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + def find_basedn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, + attrs=["defaultNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["defaultNamingContext"][0] + + def find_configurationdn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["configurationNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["configurationNamingContext"][0] + + def find_schemadn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["schemaNamingContext"][0] + + def setUp(self): + self.ldb = ldb + self.gc_ldb = gc_ldb + self.base_dn = self.find_basedn(ldb) + self.configuration_dn = self.find_configurationdn(ldb) + self.schema_dn = self.find_schemadn(ldb) + + print "baseDN: %s\n" % self.base_dn + + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + + def test_group_add_invalid_member(self): + """Testing group add with invalid member""" + try: + self.ldb.add({ + "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn, + "objectclass": "group", + "member": "cn=ldaptestuser,cn=useRs," + self.base_dn}) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + def test_all(self): + """Basic tests""" + + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + + print "Testing user add" + ldb.add({ + "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn, + "objectclass": ["user", "person"], + "cN": "LDAPtestUSER", + "givenname": "ldap", + "sn": "testy"}) + + ldb.add({ + "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn, + "objectclass": "group", + "member": "cn=ldaptestuser,cn=useRs," + self.base_dn}) + + self.delete_force(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) + ldb.add({ + "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, + "objectclass": "computer", + "cN": "LDAPtestCOMPUTER"}) + + self.delete_force(self.ldb, "cn=ldaptest2computer,cn=computers," + self.base_dn) + ldb.add({"dn": "cn=ldaptest2computer,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "LDAPtest2COMPUTER", + "userAccountControl": "4096", + "displayname": "ldap testy"}) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "LDAPtest2COMPUTER" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_INVALID_DN_SYNTAX) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "ldaptestcomputer3", + "sAMAccountType": "805306368" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "ldaptestcomputer3", + "userAccountControl": "0" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, + "objectClass": "user", + "cn": "LDAPtestuser7", + "userAccountControl": "0" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + + ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, + "objectClass": "user", + "cn": "LDAPtestuser7", + "userAccountControl": "2" + }) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "LDAPtestCOMPUTER3" + }) + + print "Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))"; + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))"); + self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res)) + + self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn)); + self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3"); + self.assertEquals(res[0]["name"][0], "ldaptestcomputer3"); + self.assertEquals(res[0]["objectClass"][0], "top"); + self.assertEquals(res[0]["objectClass"][1], "person"); + self.assertEquals(res[0]["objectClass"][2], "organizationalPerson"); + self.assertEquals(res[0]["objectClass"][3], "user"); + self.assertEquals(res[0]["objectClass"][4], "computer"); + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)); + self.assertEquals(int(res[0]["primaryGroupID"][0]), 513); + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368); + self.assertEquals(int(res[0]["userAccountControl"][0]), 546); + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + + print "Testing attribute or value exists behaviour" + try: + ldb.modify_ldif(""" +dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ +changetype: modify +replace: servicePrincipalName +servicePrincipalName: host/ldaptest2computer +servicePrincipalName: host/ldaptest2computer +servicePrincipalName: cifs/ldaptest2computer +""") + self.fail() + except LdbError, (num, msg): + self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + ldb.modify_ldif(""" +dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ +changetype: modify +replace: servicePrincipalName +servicePrincipalName: host/ldaptest2computer +servicePrincipalName: cifs/ldaptest2computer +""") + try: + ldb.modify_ldif(""" +dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ +changetype: modify +add: servicePrincipalName +servicePrincipalName: host/ldaptest2computer +""") + self.fail() + except LdbError, (num, msg): + self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + print "Testing ranged results" + ldb.modify_ldif(""" +dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ +changetype: modify +replace: servicePrincipalName +""") + + ldb.modify_ldif(""" +dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ +changetype: modify +add: servicePrincipalName +servicePrincipalName: host/ldaptest2computer0 +servicePrincipalName: host/ldaptest2computer1 +servicePrincipalName: host/ldaptest2computer2 +servicePrincipalName: host/ldaptest2computer3 +servicePrincipalName: host/ldaptest2computer4 +servicePrincipalName: host/ldaptest2computer5 +servicePrincipalName: host/ldaptest2computer6 +servicePrincipalName: host/ldaptest2computer7 +servicePrincipalName: host/ldaptest2computer8 +servicePrincipalName: host/ldaptest2computer9 +servicePrincipalName: host/ldaptest2computer10 +servicePrincipalName: host/ldaptest2computer11 +servicePrincipalName: host/ldaptest2computer12 +servicePrincipalName: host/ldaptest2computer13 +servicePrincipalName: host/ldaptest2computer14 +servicePrincipalName: host/ldaptest2computer15 +servicePrincipalName: host/ldaptest2computer16 +servicePrincipalName: host/ldaptest2computer17 +servicePrincipalName: host/ldaptest2computer18 +servicePrincipalName: host/ldaptest2computer19 +servicePrincipalName: host/ldaptest2computer20 +servicePrincipalName: host/ldaptest2computer21 +servicePrincipalName: host/ldaptest2computer22 +servicePrincipalName: host/ldaptest2computer23 +servicePrincipalName: host/ldaptest2computer24 +servicePrincipalName: host/ldaptest2computer25 +servicePrincipalName: host/ldaptest2computer26 +servicePrincipalName: host/ldaptest2computer27 +servicePrincipalName: host/ldaptest2computer28 +servicePrincipalName: host/ldaptest2computer29 +""") + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, + attrs=["servicePrincipalName;range=0-*"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + #print len(res[0]["servicePrincipalName;range=0-*"]) + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + # print res[0]["servicePrincipalName;range=0-19"].length + self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20) + + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0) + + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20) + # pos_11 = res[0]["servicePrincipalName;range=10-*"][18] + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19) + # print res[0]["servicePrincipalName;range=11-*"][18] + # print pos_11 + # self.assertEquals((res[0]["servicePrincipalName;range=11-*"][18]), pos_11) + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5) + # self.assertEquals(res[0]["servicePrincipalName;range=11-15"][4], pos_11) + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + # print res[0]["servicePrincipalName"][18] + # print pos_11 + self.assertEquals(len(res[0]["servicePrincipalName"]), 30) + # self.assertEquals(res[0]["servicePrincipalName"][18], pos_11) + + self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn) + ldb.add({ + "dn": "cn=ldaptestuser2,cn=useRs," + self.base_dn, + "objectClass": ["person", "user"], + "cn": "LDAPtestUSER2", + "givenname": "testy", + "sn": "ldap user2"}) + + print "Testing Ambigious Name Resolution" + # Testing ldb.search for (&(anr=ldap testy)(objectClass=user)) + res = ldb.search(expression="(&(anr=ldap testy)(objectClass=user))") + self.assertEquals(len(res), 3, "Found only %d of 3 for (&(anr=ldap testy)(objectClass=user))" % len(res)) + + # Testing ldb.search for (&(anr=testy ldap)(objectClass=user)) + res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))") + self.assertEquals(len(res), 2, "Found only %d of 2 for (&(anr=testy ldap)(objectClass=user))" % len(res)) + + # Testing ldb.search for (&(anr=ldap)(objectClass=user)) + res = ldb.search(expression="(&(anr=ldap)(objectClass=user))") + self.assertEquals(len(res), 4, "Found only %d of 4 for (&(anr=ldap)(objectClass=user))" % len(res)) + + # Testing ldb.search for (&(anr==ldap)(objectClass=user)) + res = ldb.search(expression="(&(anr==ldap)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(anr==ldap)(objectClass=user)). Found only %d for (&(anr=ldap)(objectClass=user))" % len(res)) + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"][0], "ldaptestuser") + self.assertEquals(res[0]["name"], "ldaptestuser") + + # Testing ldb.search for (&(anr=testy)(objectClass=user)) + res = ldb.search(expression="(&(anr=testy)(objectClass=user))") + self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy)(objectClass=user))" % len(res)) + + # Testing ldb.search for (&(anr=testy ldap)(objectClass=user)) + res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))") + self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy ldap)(objectClass=user))" % len(res)) + + # Testing ldb.search for (&(anr==testy ldap)(objectClass=user)) +# this test disabled for the moment, as anr with == tests are not understood +# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))") +# self.assertEquals(len(res), 1, "Found only %d for (&(anr==testy ldap)(objectClass=user))" % len(res)) + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"][0], "ldaptestuser") + self.assertEquals(res[0]["name"][0], "ldaptestuser") + + # Testing ldb.search for (&(anr==testy ldap)(objectClass=user)) +# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))") +# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"][0], "ldaptestuser") + self.assertEquals(res[0]["name"][0], "ldaptestuser") + + # Testing ldb.search for (&(anr=testy ldap user)(objectClass=user)) + res = ldb.search(expression="(&(anr=testy ldap user)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(anr=testy ldap user)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestuser2") + self.assertEquals(res[0]["name"], "ldaptestuser2") + + # Testing ldb.search for (&(anr==testy ldap user2)(objectClass=user)) +# res = ldb.search(expression="(&(anr==testy ldap user2)(objectClass=user))") +# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap user2)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestuser2") + self.assertEquals(res[0]["name"], "ldaptestuser2") + + # Testing ldb.search for (&(anr==ldap user2)(objectClass=user)) +# res = ldb.search(expression="(&(anr==ldap user2)(objectClass=user))") +# self.assertEquals(len(res), 1, "Could not find (&(anr==ldap user2)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestuser2") + self.assertEquals(res[0]["name"], "ldaptestuser2") + + # Testing ldb.search for (&(anr==not ldap user2)(objectClass=user)) +# res = ldb.search(expression="(&(anr==not ldap user2)(objectClass=user))") +# self.assertEquals(len(res), 0, "Must not find (&(anr==not ldap user2)(objectClass=user))") + + # Testing ldb.search for (&(anr=not ldap user2)(objectClass=user)) + res = ldb.search(expression="(&(anr=not ldap user2)(objectClass=user))") + self.assertEquals(len(res), 0, "Must not find (&(anr=not ldap user2)(objectClass=user))") + + # Testing ldb.search for (&(anr="testy ldap")(objectClass=user)) (ie, with quotes) +# res = ldb.search(expression="(&(anr==\"testy ldap\")(objectClass=user))") +# self.assertEquals(len(res), 0, "Found (&(anr==\"testy ldap\")(objectClass=user))") + + print "Testing Group Modifies" + ldb.modify_ldif(""" +dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """ +changetype: modify +add: member +member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """ +member: cn=ldaptestcomputer,cn=computers,""" + self.base_dn + """ +""") + + self.delete_force(ldb, "cn=ldaptestuser3,cn=users," + self.base_dn) + + print "Testing adding non-existent user to a group" + try: + ldb.modify_ldif(""" +dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """ +changetype: modify +add: member +member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """ +""") + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + print "Testing Renames" + + ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn) + + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn) + + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestUSER3,cn=users," + self.base_dn) + + print "Testing ldb.search for (&(cn=ldaptestuser3)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestuser3)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser3)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))") + self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))") + self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))") + self.assertEquals(len(res), 0, "(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))") + + # This is a Samba special, and does not exist in real AD + # print "Testing ldb.search for (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")" + # res = ldb.search("(dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")") + # if (res.error != 0 || len(res) != 1) { + # print "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")" + # self.assertEquals(len(res), 1) + # } + # self.assertEquals(res[0].dn, ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + # self.assertEquals(res[0].cn, "ldaptestUSER3") + # self.assertEquals(res[0].name, "ldaptestUSER3") + + print "Testing ldb.search for (distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")" + res = ldb.search(expression="(distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")") + self.assertEquals(len(res), 1, "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")") + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + # ensure we cannot add it again + try: + ldb.add({"dn": "cn=ldaptestuser3,cn=userS," + self.base_dn, + "objectClass": ["person", "user"], + "cn": "LDAPtestUSER3"}) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_ENTRY_ALREADY_EXISTS) + + # rename back + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn) + + # ensure we cannnot rename it twice + try: + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, + "cn=ldaptestuser2,cn=users," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + # ensure can now use that name + ldb.add({"dn": "cn=ldaptestuser3,cn=users," + self.base_dn, + "objectClass": ["person", "user"], + "cn": "LDAPtestUSER3"}) + + # ensure we now cannnot rename + try: + ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_ENTRY_ALREADY_EXISTS) + try: + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=configuration," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertTrue(num in (71, 64)) + + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser5,cn=users," + self.base_dn) + + ldb.delete("cn=ldaptestuser5,cn=users," + self.base_dn) + + self.delete_force(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn) + + ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn, "cn=ldaptestgroup2,cn=users," + self.base_dn) + + print "Testing subtree Renames" + + ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn, + "objectClass": "container"}) + + self.delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn) + ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn, + "objectClass": ["person", "user"], + "cn": "LDAPtestUSER4"}) + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +add: member +member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ +""") + + print "Testing ldb.rename of cn=ldaptestcontainer," + self.base_dn + " to cn=ldaptestcontainer2," + self.base_dn + ldb.rename("CN=ldaptestcontainer," + self.base_dn, "CN=ldaptestcontainer2," + self.base_dn) + + print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user))") + + print "Testing subtree ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn + try: + ldb.search("cn=ldaptestcontainer," + self.base_dn, + expression="(&(cn=ldaptestuser4)(objectClass=user))", + scope=SCOPE_SUBTREE) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn + try: + res = ldb.search("cn=ldaptestcontainer," + self.base_dn, + expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_ONELEVEL) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NO_SUCH_OBJECT) + + print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in renamed container" + res = ldb.search("cn=ldaptestcontainer2," + self.base_dn, expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_SUBTREE) + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user)) under cn=ldaptestcontainer2," + self.base_dn) + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)) + self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) + + time.sleep(4) + + print "Testing ldb.search for (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)) to check subtree renames and linked attributes" + res = ldb.search(self.base_dn, expression="(&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group))", scope=SCOPE_SUBTREE) + self.assertEquals(len(res), 1, "Could not find (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)), perhaps linked attributes are not conistant with subtree renames?") + + print "Testing ldb.rename (into itself) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn + try: + ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + print "Testing ldb.rename (into non-existent container) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn + try: + ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertTrue(num in (LDB_ERR_UNWILLING_TO_PERFORM, LDB_ERR_OTHER)) + + print "Testing delete (should fail, not a leaf node) of renamed cn=ldaptestcontainer2," + self.base_dn + try: + ldb.delete("cn=ldaptestcontainer2," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_NOT_ALLOWED_ON_NON_LEAF) + + print "Testing base ldb.search for CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + res = ldb.search(expression="(objectclass=*)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE) + self.assertEquals(len(res), 1) + res = ldb.search(expression="(cn=ldaptestuser40)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE) + self.assertEquals(len(res), 0) + + print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn + res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_ONELEVEL) + # FIXME: self.assertEquals(len(res), 0) + + print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn + res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_SUBTREE) + # FIXME: self.assertEquals(len(res), 0) + + print "Testing delete of subtree renamed "+("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn) + ldb.delete(("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)) + print "Testing delete of renamed cn=ldaptestcontainer2," + self.base_dn + ldb.delete("cn=ldaptestcontainer2," + self.base_dn) + + self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn) + ldb.add({"dn": "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"}) + + self.delete_force(self.ldb, "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn) + ldb.add({"dn": "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"}) + + print "Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestuser)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestuser") + self.assertEquals(res[0]["name"], "ldaptestuser") + self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"]) + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertEquals(res[0]["objectCategory"], ("CN=Person,CN=Schema,CN=Configuration," + self.base_dn)) + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368) + self.assertEquals(int(res[0]["userAccountControl"][0]), 546) + self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) + self.assertEquals(len(res[0]["memberOf"]), 1) + + print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))" + res2 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))") + self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))") + + self.assertEquals(res[0].dn, res2[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon))" + res3 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))") + self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)): matched %d" % len(res3)) + + self.assertEquals(res[0].dn, res3[0].dn) + + if gc_ldb is not None: + print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog" + res3gc = gc_ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))") + self.assertEquals(len(res3gc), 1) + + self.assertEquals(res[0].dn, res3gc[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in with 'phantom root' control" + + res3control = gc_ldb.search(self.base_dn, expression="(&(cn=ldaptestuser)(objectCategory=PerSon))", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"]) + self.assertEquals(len(res3control), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog") + + self.assertEquals(res[0].dn, res3control[0].dn) + + ldb.delete(res[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestcomputer)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer,CN=Computers," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestcomputer") + self.assertEquals(res[0]["name"], "ldaptestcomputer") + self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user", "computer"]) + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertEquals(res[0]["objectCategory"], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)) + self.assertEquals(int(res[0]["primaryGroupID"][0]), 513) + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368) + self.assertEquals(int(res[0]["userAccountControl"][0]), 546) + self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) + self.assertEquals(len(res[0]["memberOf"]), 1) + + print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))" + res2 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))") + self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))") + + self.assertEquals(res[0].dn, res2[0].dn) + + if gc_ldb is not None: + print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog" + res2gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))") + self.assertEquals(len(res2gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog") + + self.assertEquals(res[0].dn, res2gc[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER))" + res3 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))") + self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER))") + + self.assertEquals(res[0].dn, res3[0].dn) + + if gc_ldb is not None: + print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog" + res3gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))") + self.assertEquals(len(res3gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog") + + self.assertEquals(res[0].dn, res3gc[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestcomp*r)(objectCategory=compuTER))" + res4 = ldb.search(expression="(&(cn=ldaptestcomp*r)(objectCategory=compuTER))") + self.assertEquals(len(res4), 1, "Could not find (&(cn=ldaptestcomp*r)(objectCategory=compuTER))") + + self.assertEquals(res[0].dn, res4[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestcomput*)(objectCategory=compuTER))" + res5 = ldb.search(expression="(&(cn=ldaptestcomput*)(objectCategory=compuTER))") + self.assertEquals(len(res5), 1, "Could not find (&(cn=ldaptestcomput*)(objectCategory=compuTER))") + + self.assertEquals(res[0].dn, res5[0].dn) + + print "Testing ldb.search for (&(cn=*daptestcomputer)(objectCategory=compuTER))" + res6 = ldb.search(expression="(&(cn=*daptestcomputer)(objectCategory=compuTER))") + self.assertEquals(len(res6), 1, "Could not find (&(cn=*daptestcomputer)(objectCategory=compuTER))") + + self.assertEquals(res[0].dn, res6[0].dn) + + ldb.delete(res[0].dn) + + print "Testing ldb.search for (&(cn=ldaptest2computer)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptest2computer)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptest2computer)(objectClass=user))") + + self.assertEquals(res[0].dn, ("CN=ldaptest2computer,CN=Computers," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptest2computer") + self.assertEquals(res[0]["name"], "ldaptest2computer") + self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user", "computer"]) + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertEquals(res[0]["objectCategory"][0], "CN=Computer,CN=Schema,CN=Configuration," + self.base_dn) + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306369) + self.assertEquals(int(res[0]["userAccountControl"][0]), 4096) + + ldb.delete(res[0].dn) + + attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "memberOf"] + print "Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))" + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs) + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))") + + self.assertEquals(res[0].dn, ("CN=ldaptestuser2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestuser2") + self.assertEquals(res[0]["name"], "ldaptestuser2") + self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"]) + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertTrue("nTSecurityDescriptor" in res[0]) + self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) + + attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"] + print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group))" + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs) + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))") + + self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestgroup2") + self.assertEquals(res[0]["name"], "ldaptestgroup2") + self.assertEquals(res[0]["objectClass"], ["top", "group"]) + self.assertTrue("objectGuid" not in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertTrue("nTSecurityDescriptor" in res[0]) + self.assertEquals(res[0]["member"][0].upper(), ("CN=ldaptestuser2,CN=Users," + self.base_dn).upper()) + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +replace: member +member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """ +member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ +""") + + print "Testing Linked attribute behaviours" + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +delete: member +""") + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +add: member +member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """ +member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ +""") + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +replace: member +""") + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +add: member +member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """ +member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ +""") + + ldb.modify_ldif(""" +dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ +changetype: modify +delete: member +member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ +""") + + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs) + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))") + + self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["member"][0], ("CN=ldaptestuser2,CN=Users," + self.base_dn)) + self.assertEquals(len(res[0]["member"]), 1) + + ldb.delete(("CN=ldaptestuser2,CN=Users," + self.base_dn)) + + time.sleep(4) + + attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"] + print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete" + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs) + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete") + + self.assertEquals(res[0].dn, ("CN=ldaptestgroup2,CN=Users," + self.base_dn)) + self.assertTrue("member" not in res[0]) + + print "Testing ldb.search for (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))") + + self.assertEquals(res[0].dn, ("CN=ldaptestutf8user èùéìòà ,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestutf8user èùéìòà ") + self.assertEquals(res[0]["name"], "ldaptestutf8user èùéìòà ") + self.assertEquals(res[0]["objectClass"], ["top", "person", "organizationalPerson", "user"]) + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + + ldb.delete(res[0].dn) + + print "Testing ldb.search for (&(cn=ldaptestutf8user2*)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestutf8user2*)(objectClass=user))") + self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user2*)(objectClass=user))") + + ldb.delete(res[0].dn) + + ldb.delete(("CN=ldaptestgroup2,CN=Users," + self.base_dn)) + + print "Testing ldb.search for (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))" + res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))") + + #FIXME: self.assert len(res) == 1, "Could not find (expect space collapse, win2k3 fails) (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))" + + print "Testing that we can't get at the configuration DN from the main search base" + res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertEquals(len(res), 0) + + print "Testing that we can get at the configuration DN from the main search base on the LDAP port with the 'phantom root' search_options control" + res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"]) + self.assertTrue(len(res) > 0) + + if gc_ldb is not None: + print "Testing that we can get at the configuration DN from the main search base on the GC port with the search_options control == 0" + + res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:0"]) + self.assertTrue(len(res) > 0) + + print "Testing that we do find configuration elements in the global catlog" + res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing that we do find configuration elements and user elements at the same time" + res = gc_ldb.search(self.base_dn, expression="(|(objectClass=crossRef)(objectClass=person))", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing that we do find configuration elements in the global catlog, with the configuration basedn" + res = gc_ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing that we can get at the configuration DN on the main LDAP port" + res = ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing objectCategory canonacolisation" + res = ldb.search(self.configuration_dn, expression="objectCategory=ntDsDSA", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=ntDsDSA") + self.assertTrue(len(res) != 0) + + res = ldb.search(self.configuration_dn, expression="objectCategory=CN=ntDs-DSA," + self.schema_dn, scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=CN=ntDs-DSA," + self.schema_dn) + self.assertTrue(len(res) != 0) + + print "Testing objectClass attribute order on "+ self.base_dn + res = ldb.search(expression="objectClass=domain", base=self.base_dn, + scope=SCOPE_BASE, attrs=["objectClass"]) + self.assertEquals(len(res), 1) + + self.assertEquals(res[0]["objectClass"], ["top", "domain", "domainDNS"]) + + # check enumeration + + print "Testing ldb.search for objectCategory=person" + res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing ldb.search for objectCategory=person with domain scope control" + res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) + self.assertTrue(len(res) > 0) + + print "Testing ldb.search for objectCategory=user" + res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing ldb.search for objectCategory=user with domain scope control" + res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) + self.assertTrue(len(res) > 0) + + print "Testing ldb.search for objectCategory=group" + res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"]) + self.assertTrue(len(res) > 0) + + print "Testing ldb.search for objectCategory=group with domain scope control" + res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) + self.assertTrue(len(res) > 0) + + +class BaseDnTests(unittest.TestCase): + def setUp(self): + self.ldb = ldb + + def test_rootdse_attrs(self): + """Testing for all rootDSE attributes""" + res = self.ldb.search(scope=SCOPE_BASE, attrs=[]) + self.assertEquals(len(res), 1) + + def test_highestcommittedusn(self): + """Testing for highestCommittedUSN""" + res = self.ldb.search("", scope=SCOPE_BASE, attrs=["highestCommittedUSN"]) + self.assertEquals(len(res), 1) + self.assertTrue(int(res[0]["highestCommittedUSN"][0]) != 0) + + def test_netlogon(self): + """Testing for netlogon via LDAP""" + res = self.ldb.search("", scope=SCOPE_BASE, attrs=["netlogon"]) + self.assertEquals(len(res), 0) + + def test_netlogon_highestcommitted_usn(self): + """Testing for netlogon and highestCommittedUSN via LDAP""" + res = self.ldb.search("", scope=SCOPE_BASE, + attrs=["netlogon", "highestCommittedUSN"]) + self.assertEquals(len(res), 0) + +class SchemaTests(unittest.TestCase): + def find_schemadn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["schemaNamingContext"][0] + + def setUp(self): + self.ldb = ldb + self.schema_dn = self.find_schemadn(ldb) + + def test_generated_schema(self): + """Testing we can read the generated schema via LDAP""" + res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, + attrs=["objectClasses", "attributeTypes", "dITContentRules"]) + self.assertEquals(len(res), 1) + self.assertTrue("dITContentRules" in res[0]) + self.assertTrue("objectClasses" in res[0]) + self.assertTrue("attributeTypes" in res[0]) + + def test_generated_schema_is_operational(self): + """Testing we don't get the generated schema via LDAP by default""" + res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, + attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertFalse("dITContentRules" in res[0]) + self.assertFalse("objectClasses" in res[0]) + self.assertFalse("attributeTypes" in res[0]) + +if not "://" in host: + host = "ldap://%s" % host + +ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp) +gc_ldb = Ldb("%s:3268" % host, credentials=creds, + session_info=system_session(), lp=lp) + +runner = SubunitTestRunner() +rc = 0 +if not runner.run(unittest.makeSuite(BaseDnTests)).wasSuccessful(): + rc = 1 +if not runner.run(unittest.makeSuite(BasicTests)).wasSuccessful(): + rc = 1 +if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful(): + rc = 1 +sys.exit(rc) diff --git a/ldb/tests/samba4.png b/ldb/tests/samba4.png Binary files differnew file mode 100644 index 00000000..c8096889 --- /dev/null +++ b/ldb/tests/samba4.png diff --git a/ldb/tests/sample_module.c b/ldb/tests/sample_module.c new file mode 100644 index 00000000..1a9e72c9 --- /dev/null +++ b/ldb/tests/sample_module.c @@ -0,0 +1,38 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 + + ** 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb_includes.h" +#include "ldb.h" +#include "ldb_errors.h" + +int sample_add(struct ldb_module *mod, struct ldb_request *req) +{ + ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample"); + + return ldb_next_request(mod, req); +} + +const struct ldb_module_ops ldb_sample_module_ops = { + .name = "sample", + .add = sample_add, +}; diff --git a/ldb/tests/schema-tests/schema-add-test.ldif b/ldb/tests/schema-tests/schema-add-test.ldif new file mode 100644 index 00000000..472ab48f --- /dev/null +++ b/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: 9223372036854775807 +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/ldb/tests/schema-tests/schema-mod-test-1.ldif b/ldb/tests/schema-tests/schema-mod-test-1.ldif new file mode 100644 index 00000000..b9767244 --- /dev/null +++ b/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/ldb/tests/schema-tests/schema-mod-test-2.ldif b/ldb/tests/schema-tests/schema-mod-test-2.ldif new file mode 100644 index 00000000..fa193af6 --- /dev/null +++ b/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/ldb/tests/schema-tests/schema-mod-test-3.ldif b/ldb/tests/schema-tests/schema-mod-test-3.ldif new file mode 100644 index 00000000..8ab7798f --- /dev/null +++ b/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/ldb/tests/schema-tests/schema-mod-test-4.ldif b/ldb/tests/schema-tests/schema-mod-test-4.ldif new file mode 100644 index 00000000..cbf0e60b --- /dev/null +++ b/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/ldb/tests/schema-tests/schema-mod-test-5.ldif b/ldb/tests/schema-tests/schema-mod-test-5.ldif new file mode 100644 index 00000000..bc64e9ed --- /dev/null +++ b/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/ldb/tests/schema-tests/schema.ldif b/ldb/tests/schema-tests/schema.ldif new file mode 100644 index 00000000..4ab19328 --- /dev/null +++ b/ldb/tests/schema-tests/schema.ldif @@ -0,0 +1,100 @@ +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: @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/ldb/tests/slapd.conf b/ldb/tests/slapd.conf new file mode 100644 index 00000000..fa2789d8 --- /dev/null +++ b/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/ldb/tests/start_slapd.sh b/ldb/tests/start_slapd.sh new file mode 100755 index 00000000..11679d47 --- /dev/null +++ b/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 in the background (with &) 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/ldb/tests/test-attribs.ldif b/ldb/tests/test-attribs.ldif new file mode 100644 index 00000000..79508c4b --- /dev/null +++ b/ldb/tests/test-attribs.ldif @@ -0,0 +1,6 @@ +dn: @ATTRIBUTES +uid: CASE_INSENSITIVE +cn: CASE_INSENSITIVE +ou: CASE_INSENSITIVE +dn: CASE_INSENSITIVE + diff --git a/ldb/tests/test-config.ldif b/ldb/tests/test-config.ldif new file mode 100644 index 00000000..7926a9e3 --- /dev/null +++ b/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/ldb/tests/test-default-config.ldif b/ldb/tests/test-default-config.ldif new file mode 100644 index 00000000..87b7bcd3 --- /dev/null +++ b/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/ldb/tests/test-extended.sh b/ldb/tests/test-extended.sh new file mode 100755 index 00000000..a84e3b78 --- /dev/null +++ b/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/ldb/tests/test-generic.sh b/ldb/tests/test-generic.sh new file mode 100755 index 00000000..14337cc1 --- /dev/null +++ b/ldb/tests/test-generic.sh @@ -0,0 +1,128 @@ +#!/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 "Adding again - should fail" +ldbadd $LDBDIR/tests/test.ldif 2> /dev/null && { + echo "Should have failed to add again - gave $?" + 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 bad attributes - should fail" +$VALGRIND ldbadd $LDBDIR/tests/test-wrong_attributes.ldif && { + echo "Should fhave failed - gave $?" + 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=Hampster Ursula)' 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 openLdap ..." +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/ldb/tests/test-index.ldif b/ldb/tests/test-index.ldif new file mode 100644 index 00000000..26817364 --- /dev/null +++ b/ldb/tests/test-index.ldif @@ -0,0 +1,7 @@ +dn: @INDEXLIST +@IDXATTR: uid +@IDXATTR: objectclass + +dn: @ATTRIBUTES +uid: CASE_INSENSITIVE + diff --git a/ldb/tests/test-ldap.sh b/ldb/tests/test-ldap.sh new file mode 100755 index 00000000..14cfb5f9 --- /dev/null +++ b/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/ldb/tests/test-modify.ldif b/ldb/tests/test-modify.ldif new file mode 100644 index 00000000..e5b9ca40 --- /dev/null +++ b/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/ldb/tests/test-schema.sh b/ldb/tests/test-schema.sh new file mode 100755 index 00000000..2f10fb45 --- /dev/null +++ b/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/ldb/tests/test-soloading.sh b/ldb/tests/test-soloading.sh new file mode 100755 index 00000000..da6d5754 --- /dev/null +++ b/ldb/tests/test-soloading.sh @@ -0,0 +1,32 @@ +#!/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 + +cat <<EOF | $VALGRIND ldbadd || exit 1 +dn: @MODULES +@LIST: sample +EOF + +cat <<EOF | $VALGRIND ldbadd || exit 1 +dn: dc=bar +dc: bar +someThing: someThingElse +EOF + +$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1 + diff --git a/ldb/tests/test-sqlite3.sh b/ldb/tests/test-sqlite3.sh new file mode 100755 index 00000000..0cef318d --- /dev/null +++ b/ldb/tests/test-sqlite3.sh @@ -0,0 +1,25 @@ +#!/bin/sh + + +LDB_URL="sqlite3://sqltest.ldb" +export LDB_URL + +rm -f sqltest.ldb + +if [ -z "$LDBDIR" ]; then + LDBDIR=`dirname $0`/.. + export LDBDIR +fi + +PATH=bin:$PATH +export PATH + +LDB_SPECIALS=0 +export LDB_SPECIALS + +$LDBDIR/tests/test-generic.sh + +#. $LDBDIR/tests/test-extended.sh + +#. $LDBDIR/tests/test-tdb-features.sh + diff --git a/ldb/tests/test-tdb-features.sh b/ldb/tests/test-tdb-features.sh new file mode 100644 index 00000000..fc0959c5 --- /dev/null +++ b/ldb/tests/test-tdb-features.sh @@ -0,0 +1,160 @@ +#!/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 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*)' + +checkone() { + count=$1 + base="$2" + expression="$3" + n=`bin/ldbsearch -s one -b "$base" "$expression" | grep '^dn' | wc -l` + if [ $n != $count ]; then + echo "Got $n but expected $count for $expression" + $VALGRIND bin/ldbsearch -s one -b "$base" "$expression" + exit 1 + fi + echo "OK: $count $expression" +} + +echo "Removing wildcard attribute" +cat <<EOF | $VALGRIND bin/ldbmodify || exit 1 +dn: @ATTRIBUTES +changetype: modify +delete: * +*: INTEGER +EOF + +echo "Adding one level indexes" +cat <<EOF | $VALGRIND bin/ldbmodify || exit 1 +dn: @INDEXLIST +changetype: modify +add: @IDXONE +@IDXONE: 1 +EOF + +echo "Testing one level indexed search" +cat <<EOF | $VALGRIND bin/ldbadd || exit 1 +dn: cn=one,cn=t1,cn=TEST +objectClass: oneclass +cn: one +test: one +EOF +checkone 1 "cn=t1,cn=TEST" '(test=one)' +cat <<EOF | $VALGRIND bin/ldbadd || exit 1 +dn: cn=two,cn=t1,cn=TEST +objectClass: oneclass +cn: two +test: one + +dn: cn=three,cn=t1,cn=TEST +objectClass: oneclass +cn: three +test: one +EOF +checkone 3 "cn=t1,cn=TEST" '(test=one)' +checkone 1 "cn=t1,cn=TEST" '(cn=two)' + diff --git a/ldb/tests/test-tdb.sh b/ldb/tests/test-tdb.sh new file mode 100755 index 00000000..7c4f5205 --- /dev/null +++ b/ldb/tests/test-tdb.sh @@ -0,0 +1,31 @@ +#!/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 + +cat <<EOF | $VALGRIND ldbadd || exit 1 +dn: @MODULES +@LIST: rdn_name +EOF + +$VALGRIND ldbadd $LDBDIR/tests/init.ldif || exit 1 + +. $LDBDIR/tests/test-generic.sh + +. $LDBDIR/tests/test-extended.sh + +. $LDBDIR/tests/test-tdb-features.sh diff --git a/ldb/tests/test-wildcard.ldif b/ldb/tests/test-wildcard.ldif new file mode 100644 index 00000000..222512ee --- /dev/null +++ b/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/ldb/tests/test-wrong_attributes.ldif b/ldb/tests/test-wrong_attributes.ldif new file mode 100644 index 00000000..27f45f0e --- /dev/null +++ b/ldb/tests/test-wrong_attributes.ldif @@ -0,0 +1,3 @@ +dn: @ATTRIBUTES +uid: CASE_INTENSIVE + diff --git a/ldb/tests/test.ldif b/ldb/tests/test.ldif new file mode 100644 index 00000000..e53fadc7 --- /dev/null +++ b/ldb/tests/test.ldif @@ -0,0 +1,411 @@ +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 diff --git a/ldb/tests/testdata.txt b/ldb/tests/testdata.txt new file mode 100644 index 00000000..dadb9f0f --- /dev/null +++ b/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/ldb/tests/testsearch.txt b/ldb/tests/testsearch.txt new file mode 100644 index 00000000..c5738639 --- /dev/null +++ b/ldb/tests/testsearch.txt @@ -0,0 +1,5 @@ +(blah=foo) +(objectclass=person) +(dn=*) +(&(objectclass=person)(objectclass=person)) +(&(objectclass=person)(objectclass=personx)) diff --git a/ldb/tools/cmdline.c b/ldb/tools/cmdline.c new file mode 100644 index 00000000..765d8b9e --- /dev/null +++ b/ldb/tools/cmdline.c @@ -0,0 +1,397 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "ldb_includes.h" +#include "tools/cmdline.h" + +#if (_SAMBA_BUILD_ >= 4) +#include "includes.h" +#include "lib/cmdline/popt_common.h" +#include "lib/ldb-samba/ldif_handlers.h" +#include "auth/gensec/gensec.h" +#include "auth/auth.h" +#include "ldb_wrap.h" +#include "param/param.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 }, + { "modules-path", 0, POPT_ARG_STRING, &options.modules_path, 0, "modules path", "PATH" }, + { "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 }, + { "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_CONNECTION + POPT_COMMON_VERSION +#endif + { NULL } + }; + +#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(cmdline_lp_ctx); + + if (ldb_set_opaque(ldb, "sessionInfo", system_session(ldb, cmdline_lp_ctx))) { + goto failed; + } + if (ldb_set_opaque(ldb, "credentials", cmdline_credentials)) { + goto failed; + } + if (ldb_set_opaque(ldb, "loadparm", cmdline_lp_ctx)) { + goto failed; + } + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); +#endif + + if (options.modules_path != NULL) { + ldb_set_modules_dir(ldb, options.modules_path); + } else if (getenv("LDB_MODULES_PATH") != NULL) { + ldb_set_modules_dir(ldb, getenv("LDB_MODULES_PATH")); + } + + /* 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; +} + +/* 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/ldb/tools/cmdline.h b/ldb/tools/cmdline.h new file mode 100644 index 00000000..3473d62a --- /dev/null +++ b/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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include <popt.h> + +struct ldb_cmdline { + const char *url; + enum ldb_scope scope; + const char *basedn; + const char *modules_path; + 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/ldb/tools/config.mk b/ldb/tools/config.mk new file mode 100644 index 00000000..6b57929d --- /dev/null +++ b/ldb/tools/config.mk @@ -0,0 +1,90 @@ +################################################ +# Start SUBSYSTEM LIBLDB_CMDLINE +[SUBSYSTEM::LIBLDB_CMDLINE] +CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include +PUBLIC_DEPENDENCIES = LIBLDB LIBPOPT +PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL POPT_SAMBA POPT_CREDENTIALS gensec +# End SUBSYSTEM LIBLDB_CMDLINE +################################################ + +LIBLDB_CMDLINE_OBJ_FILES = $(ldbsrcdir)/tools/cmdline.o + +################################################ +# Start BINARY ldbadd +[BINARY::ldbadd] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE LIBCLI_RESOLVE +# End BINARY ldbadd +################################################ + + +ldbadd_OBJ_FILES = $(ldbsrcdir)/tools/ldbadd.o + +MANPAGES += $(ldbsrcdir)/man/ldbadd.1 + +################################################ +# Start BINARY ldbdel +[BINARY::ldbdel] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbdel +################################################ + +ldbdel_OBJ_FILES = $(ldbsrcdir)/tools/ldbdel.o + +MANPAGES += $(ldbsrcdir)/man/ldbdel.1 + +################################################ +# Start BINARY ldbmodify +[BINARY::ldbmodify] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbmodify +################################################ + +ldbmodify_OBJ_FILES = $(ldbsrcdir)/tools/ldbmodify.o +MANPAGES += $(ldbsrcdir)/man/ldbmodify.1 + +################################################ +# Start BINARY ldbsearch +[BINARY::ldbsearch] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbsearch +################################################ + +ldbsearch_OBJ_FILES = $(ldbsrcdir)/tools/ldbsearch.o + +MANPAGES += $(ldbsrcdir)/man/ldbsearch.1 + +################################################ +# Start BINARY ldbedit +[BINARY::ldbedit] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbedit +################################################ + +ldbedit_OBJ_FILES = $(ldbsrcdir)/tools/ldbedit.o + +MANPAGES += $(ldbsrcdir)/man/ldbedit.1 + +################################################ +# Start BINARY ldbrename +[BINARY::ldbrename] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBLDB_CMDLINE +# End BINARY ldbrename +################################################ + +ldbrename_OBJ_FILES = $(ldbsrcdir)/tools/ldbrename.o + +MANPAGES += $(ldbsrcdir)/man/ldbrename.1 + + diff --git a/ldb/tools/ldbadd.c b/ldb/tools/ldbadd.c new file mode 100644 index 00000000..15376e73 --- /dev/null +++ b/ldb/tools/ldbadd.c @@ -0,0 +1,116 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbadd + * + * Description: utility to add records - modelled on ldapadd + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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, int *count) +{ + struct ldb_ldif *ldif; + int ret = LDB_SUCCESS; + + 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_get_linearized(ldif->msg->dn)); + failures++; + } else { + (*count)++; + } + ldb_ldif_read_free(ldb, ldif); + } + + return ret; +} + + + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + int i, ret=0, count=0; + struct ldb_cmdline *options; + + ldb = ldb_init(NULL, NULL); + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc == 0) { + ret = process_file(ldb, stdin, &count); + } 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); + } + ret = process_file(ldb, f, &count); + fclose(f); + } + } + + talloc_free(ldb); + + printf("Added %d records with %d failures\n", count, failures); + + return ret; +} diff --git a/ldb/tools/ldbdel.c b/ldb/tools/ldbdel.c new file mode 100644 index 00000000..e66d4fb9 --- /dev/null +++ b/ldb/tools/ldbdel.c @@ -0,0 +1,115 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbdel + * + * Description: utility to delete records - modelled on ldapdelete + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "tools/cmdline.h" + +static int ldb_delete_recursive(struct ldb_context *ldb, 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 = 0, i; + struct ldb_cmdline *options; + + ldb = ldb_init(NULL, NULL); + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc < 1) { + usage(); + exit(1); + } + + for (i=0;i<options->argc;i++) { + struct ldb_dn *dn; + + dn = ldb_dn_new(ldb, ldb, options->argv[i]); + if ( ! ldb_dn_validate(dn)) { + 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_get_linearized(dn), + ldb_errstring(ldb)); + } + } + + talloc_free(ldb); + + return ret; +} diff --git a/ldb/tools/ldbedit.c b/ldb/tools/ldbedit.c new file mode 100644 index 00000000..e58a5a27 --- /dev/null +++ b/ldb/tools/ldbedit.c @@ -0,0 +1,329 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbedit + * + * Description: utility for ldb database editing + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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_get_linearized(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, + struct ldb_dn *dn) +{ + int i; + for (i=0;i<count;i++) { + if (ldb_dn_compare(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_get_linearized(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_get_linearized(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 = ldb_init(NULL, 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_new(ldb, ldb, options->basedn); + if ( ! ldb_dn_validate(basedn)) { + 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/ldb/tools/ldbmodify.c b/ldb/tools/ldbmodify.c new file mode 100644 index 00000000..6e355a10 --- /dev/null +++ b/ldb/tools/ldbmodify.c @@ -0,0 +1,116 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbmodify + * + * Description: utility to modify records - modelled on ldapmodify + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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, int *count) +{ + struct ldb_ldif *ldif; + int ret = LDB_SUCCESS; + + 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 != LDB_SUCCESS) { + fprintf(stderr, "ERR: \"%s\" on DN %s\n", + ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn)); + failures++; + } else { + (*count)++; + } + ldb_ldif_read_free(ldb, ldif); + } + + return ret; +} + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + int count=0; + int i, ret=LDB_SUCCESS; + struct ldb_cmdline *options; + + ldb = ldb_init(NULL, NULL); + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc == 0) { + ret = process_file(ldb, stdin, &count); + } 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); + } + ret = process_file(ldb, f, &count); + } + } + + talloc_free(ldb); + + printf("Modified %d records with %d failures\n", count, failures); + + return ret; +} diff --git a/ldb/tools/ldbrename.c b/ldb/tools/ldbrename.c new file mode 100644 index 00000000..a5feb7a0 --- /dev/null +++ b/ldb/tools/ldbrename.c @@ -0,0 +1,90 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbrename + * + * Description: utility to rename records - modelled on ldapmodrdn + * + * Author: Andrew Tridgell + * Author: Stefan Metzmacher + */ + +#include "ldb_includes.h" +#include "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; + struct ldb_dn *dn1, *dn2; + + ldb = ldb_init(NULL, NULL); + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc < 2) { + usage(); + } + + dn1 = ldb_dn_new(ldb, ldb, options->argv[0]); + dn2 = ldb_dn_new(ldb, ldb, options->argv[1]); + + if ( ! ldb_dn_validate(dn1)) { + printf("Invalid DN1: %s\n", options->argv[0]); + return -1; + } + if ( ! ldb_dn_validate(dn2)) { + printf("Invalid DN2: %s\n", options->argv[1]); + return -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/ldb/tools/ldbsearch.c b/ldb/tools/ldbsearch.c new file mode 100644 index 00000000..b3d1f934 --- /dev/null +++ b/ldb/tools/ldbsearch.c @@ -0,0 +1,320 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbsearch + * + * Description: utility for ldb search - modelled on ldapsearch + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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) +{ + return ldb_dn_compare((*el1)->dn, (*el2)->dn); +} + +struct search_context { + struct ldb_control **req_ctrls; + + int sort; + int num_stored; + struct ldb_message **store; + int refs_stored; + 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_stored + 2); + if (!sctx->refs_store) { + fprintf(stderr, "talloc_realloc failed while storing referrals\n"); + return -1; + } + + sctx->refs_store[sctx->refs_stored] = talloc_move(sctx->refs_store, &referral); + sctx->refs_stored++; + sctx->refs_store[sctx->refs_stored] = 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, + 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->refs_stored = 0; + sctx->store = NULL; + sctx->req_ctrls = ldb_parse_control_strings(ldb, sctx, (const char **)options->controls); + if (options->controls != NULL && sctx->req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + 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(req, 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 || sctx->refs != 0)) { + int i; + + if (sctx->num_stored) { + ldb_qsort(sctx->store, sctx->num_stored, sizeof(struct ldb_message *), + ldb, (ldb_qsort_cmp_fn_t)do_compare_msg); + } + for (i = 0; i < sctx->num_stored; i++) { + display_message(ldb, sctx->store[i], sctx); + } + + for (i = 0; i < sctx->refs_stored; 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 = ldb_init(NULL, NULL); + if (ldb == NULL) { + return -1; + } + + 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_new(ldb, ldb, options->basedn); + if ( ! ldb_dn_validate(basedn)) { + 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/ldb/tools/ldbtest.c b/ldb/tools/ldbtest.c new file mode 100644 index 00000000..169ff02d --- /dev/null +++ b/ldb/tools/ldbtest.c @@ -0,0 +1,418 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbtest + * + * Description: utility to test ldb + * + * Author: Andrew Tridgell + */ + +#include "ldb_includes.h" +#include "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, + 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_copy(tmp_ctx, basedn); + ldb_dn_add_child_fmt(msg.dn, "cn=%s", name); + 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, strlen(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, + 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_copy(tmp_ctx, basedn); + ldb_dn_add_child_fmt(msg.dn, "cn=%s", name); + + 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, + 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_copy(name, basedn); + ldb_dn_add_child_fmt(dn, "cn=%s", name); + + printf("Deleting uid Test%d\r", i); + fflush(stdout); + + if (ldb_delete(ldb, dn) != 0) { + printf("Delete of %s failed - %s\n", ldb_dn_get_linearized(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_new(ldb, ldb, options->basedn); + if ( ! ldb_dn_validate(basedn)) { + printf("Invalid base DN\n"); + exit(1); + } + + 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_new(*ldb, *ldb, "@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_get_linearized(msg->dn), ldb_errstring(*ldb)); + exit(1); + } + + basedn = ldb_dn_new(*ldb, *ldb, options->basedn); + + memset(msg, 0, sizeof(*msg)); + msg->dn = ldb_dn_copy(msg, basedn); + ldb_dn_add_child_fmt(msg->dn, "cn=test"); + 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_get_linearized(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, NULL); + + ret = ldb_connect(*ldb, options->url, flags, NULL); + if (ret != 0) { + printf("failed to connect to %s\n", options->url); + exit(1); + } + + basedn = ldb_dn_new(*ldb, *ldb, options->basedn); + + 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); + } + + indexlist = ldb_dn_new(*ldb, *ldb, "@INDEXLIST"); + + 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 = ldb_init(mem_ctx, NULL); + + 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/ldb/web/index.html b/ldb/web/index.html new file mode 100644 index 00000000..26dd3445 --- /dev/null +++ b/ldb/web/index.html @@ -0,0 +1,86 @@ +<!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 +<a href="http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol">LDAP</a> +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><a href="http://en.wikipedia.org/wiki/LDAP_Application_Program_Interface">LDAP-like API</a> +<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><a href="http://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format">LDIF</a> for import/export +<li>ldbedit tool for database (via LDIF) editing (reminiscent of 'vipw') +</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/ldb/include/ldb.h">ldb.h</a> +and the example code in the <a +href="http://samba.org/ftp/unpacked/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 or the <a href="https://lists.samba.org/mailman/listinfo/ldb">ldb</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 thtough git.<br> +<br> +To fetch via git see the following guide:<br> +<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br> +Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/ldb directory.<br> +<br> +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> |