summaryrefslogtreecommitdiffstats
path: root/ldb/common
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2008-09-05 10:13:10 -0400
committerSimo Sorce <idra@samba.org>2008-09-05 10:13:10 -0400
commit23b61265b85f321ae2ceddc6c75771f8881566d8 (patch)
treebb43d44fb09247c8e12d58371e56af911a7c0db9 /ldb/common
downloadsssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.gz
sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.xz
sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.zip
Initial Import
Diffstat (limited to 'ldb/common')
-rw-r--r--ldb/common/attrib_handlers.c378
-rw-r--r--ldb/common/ldb.c1261
-rw-r--r--ldb/common/ldb_attributes.c227
-rw-r--r--ldb/common/ldb_controls.c577
-rw-r--r--ldb/common/ldb_debug.c103
-rw-r--r--ldb/common/ldb_dn.c1463
-rw-r--r--ldb/common/ldb_ldif.c757
-rw-r--r--ldb/common/ldb_match.c428
-rw-r--r--ldb/common/ldb_modules.c625
-rw-r--r--ldb/common/ldb_msg.c899
-rw-r--r--ldb/common/ldb_parse.c819
-rw-r--r--ldb/common/ldb_utf8.c136
-rw-r--r--ldb/common/qsort.c251
13 files changed, 7924 insertions, 0 deletions
diff --git a/ldb/common/attrib_handlers.c b/ldb/common/attrib_handlers.c
new file mode 100644
index 000000000..fb57e2dad
--- /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 000000000..ce4796dee
--- /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 000000000..747f24178
--- /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 000000000..e3f855140
--- /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 000000000..0f78e37c1
--- /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 000000000..c0d36cfbf
--- /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 000000000..fb93e17c6
--- /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 000000000..64d0e5476
--- /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 000000000..4d69dc662
--- /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 000000000..2f5fe1d18
--- /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 000000000..8c970a442
--- /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 000000000..69ee2b696
--- /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 000000000..0fa76d3b4
--- /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;
+ }
+ }
+ }
+ }
+}