s4-ldb: use TYPESAFE_QSORT() in the rest of the ldb code
[ira/wip.git] / source4 / lib / ldb / common / ldb_dn.c
index e937a2b7fc40cbe10f8c4cfccc800e3203a025a7..c395be29007813ad365114422f0152f94cc3f5fd 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    ldb database library
 
    Copyright (C) Simo Sorce 2005
@@ -6,11 +6,11 @@
      ** NOTE! The following LGPL license applies to the ldb
      ** library. This does NOT imply that all of Samba is released
      ** under the LGPL
-   
+
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   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
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
  *  Name: ldb
  *
- *  Component: ldb dn explode and utility functions
+ *  Component: ldb dn creation and manipulation utility functions
  *
  *  Description: - explode a dn into it's own basic elements
- *                 and put them in a structure
+ *                 and put them in a structure (only if necessary)
  *               - manipulate ldb_dn structures
  *
  *  Author: Simo Sorce
  */
 
-#include "includes.h"
-#include "ldb/include/includes.h"
+#include "ldb_private.h"
+#include <ctype.h>
 
 #define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
 
-#define LDB_SPECIAL "@SPECIAL"
-
-int ldb_dn_is_special(const struct ldb_dn *dn)
-{
-       if (dn == NULL || dn->comp_num != 1) return 0;
-
-       return ! strcmp(dn->components[0].name, LDB_SPECIAL);
-}
-
-int ldb_dn_check_special(const struct ldb_dn *dn, const char *check)
-{
-       if (dn == NULL || dn->comp_num != 1) return 0;
+#define LDB_FREE(x) do { talloc_free(x); x = NULL; } while(0)
 
-       return ! strcmp((char *)dn->components[0].value.data, check);
-}
+/**
+   internal ldb exploded dn structures
+*/
+struct ldb_dn_component {
 
-char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
-{
-       const char *p, *s, *src;
-       char *d, *dst;
-       int len;
+       char *name;
+       struct ldb_val value;
 
-       if (!value.length)
-               return NULL;
+       char *cf_name;
+       struct ldb_val cf_value;
+};
 
-       p = s = src = (const char *)value.data;
-       len = value.length;
+struct ldb_dn_ext_component {
 
-       /* allocate destination string, it will be at most 3 times the source */
-       dst = d = talloc_array(mem_ctx, char, len * 3 + 1);
-       LDB_DN_NULL_FAILED(dst);
+       char *name;
+       struct ldb_val value;
+};
 
-       while (p - src < len) {
+struct ldb_dn {
 
-               p += strcspn(p, ",=\n+<>#;\\\"");
+       struct ldb_context *ldb;
 
-               if (p - src == len) /* found no escapable chars */
-                       break;
+       /* Special DNs are always linearized */
+       bool special;
+       bool invalid;
 
-               memcpy(d, s, p - s); /* copy the part of the string before the stop */
-               d += (p - s); /* move to current position */
+       bool valid_case;
 
-               if (*p) { /* it is a normal escapable character */
-                       *d++ = '\\';
-                       *d++ = *p++;
-               } else { /* we have a zero byte in the string */
-                       strncpy(d, "\00", 3); /* escape the zero */
-                       d = d + 3;
-                       p++; /* skip the zero */
-               }
-               s = p; /* move forward */
-       }
+       char *linearized;
+       char *ext_linearized;
+       char *casefold;
 
-       /* copy the last part (with zero) and return */
-       memcpy(d, s, &src[len] - s + 1);
+       unsigned int comp_num;
+       struct ldb_dn_component *components;
 
-       return dst;
+       unsigned int ext_comp_num;
+       struct ldb_dn_ext_component *ext_components;
+};
 
-failed:
-       talloc_free(dst);
-       return NULL;
+/* it is helpful to be able to break on this in gdb */
+static void ldb_dn_mark_invalid(struct ldb_dn *dn)
+{
+       dn->invalid = true;
 }
 
-static struct ldb_val ldb_dn_unescape_value(void *mem_ctx, const char *src)
+/* 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_val value;
-       unsigned x;
-       char *p, *dst = NULL, *end;
-
-       memset(&value, 0, sizeof(value));
+       struct ldb_dn *dn;
 
-       LDB_DN_NULL_FAILED(src);
+       if (! ldb) return NULL;
 
-       dst = p = (char *)talloc_memdup(mem_ctx, src, strlen(src) + 1);
-       LDB_DN_NULL_FAILED(dst);
+       if (strdn && strdn->data
+           && (strnlen((const char*)strdn->data, strdn->length) != strdn->length)) {
+               /* The RDN must not contain a character with value 0x0 */
+               return NULL;
+       }
 
-       end = &dst[strlen(dst)];
+       dn = talloc_zero(mem_ctx, struct ldb_dn);
+       LDB_DN_NULL_FAILED(dn);
 
-       while (*p) {
-               p += strcspn(p, ",=\n+<>#;\\\"");
+       dn->ldb = talloc_get_type(ldb, struct ldb_context);
+       if (dn->ldb == NULL) {
+               /* the caller probably got the arguments to
+                  ldb_dn_new() mixed up */
+               talloc_free(dn);
+               return NULL;
+       }
 
-               if (*p == '\\') {
-                       if (strchr(",=\n+<>#;\\\"", p[1])) {
-                               memmove(p, p + 1, end - (p + 1) + 1);
-                               end--;
-                               p++;
-                               continue;
-                       }
+       if (strdn->data && strdn->length) {
+               const char *data = (const char *)strdn->data;
+               size_t length = strdn->length;
 
-                       if (sscanf(p + 1, "%02x", &x) == 1) {
-                               *p = (unsigned char)x;
-                               memmove(p + 1, p + 3, end - (p + 3) + 1);
-                               end -= 2;
-                               p++;
-                               continue;
-                       }
+               if (data[0] == '@') {
+                       dn->special = true;
                }
-
-               /* a string with not escaped specials is invalid (tested) */
-               if (*p != '\0') {
-                       goto failed;
+               dn->ext_linearized = talloc_strndup(dn, data, length);
+               LDB_DN_NULL_FAILED(dn->ext_linearized);
+
+               if (data[0] == '<') {
+                       const char *p_save, *p = dn->ext_linearized;
+                       do {
+                               p_save = p;
+                               p = strstr(p, ">;");
+                               if (p) {
+                                       p = p + 2;
+                               }
+                       } while (p);
+
+                       if (p_save == dn->ext_linearized) {
+                               dn->linearized = talloc_strdup(dn, "");
+                       } else {
+                               dn->linearized = talloc_strdup(dn, p_save);
+                       }
+                       LDB_DN_NULL_FAILED(dn->linearized);
+               } else {
+                       dn->linearized = dn->ext_linearized;
+                       dn->ext_linearized = NULL;
                }
+       } else {
+               dn->linearized = talloc_strdup(dn, "");
+               LDB_DN_NULL_FAILED(dn->linearized);
        }
 
-       value.length = end - dst;
-       value.data = (uint8_t *)dst;
-       return value;
+       return dn;
 
 failed:
-       talloc_free(dst);
-       return value;
+       talloc_free(dn);
+       return NULL;
 }
 
-/* check if the string contains quotes
- * skips leading and trailing spaces
- * - returns 0 if no quotes found
- * - returns 1 if quotes are found and put their position
- *   in *quote_start and *quote_end parameters
- * - return -1 if there are open quotes
- */
-
-static int get_quotes_position(const char *source, int *quote_start, int *quote_end)
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_new(void *mem_ctx,
+                         struct ldb_context *ldb,
+                         const char *strdn)
 {
-       const char *p;
-
-       if (source == NULL || quote_start == NULL || quote_end == NULL) return -1;
-
-       p = source;
-
-       /* check if there are quotes surrounding the value */
-       p += strspn(p, " \n"); /* skip white spaces */
+       struct ldb_val blob;
+       blob.data = discard_const_p(uint8_t, strdn);
+       blob.length = strdn ? strlen(strdn) : 0;
+       return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob);
+}
 
-       if (*p == '\"') {
-               *quote_start = p - source;
+struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx,
+                             struct ldb_context *ldb,
+                             const char *new_fmt, ...)
+{
+       char *strdn;
+       va_list ap;
 
-               p++;
-               while (*p != '\"') {
-                       p = strchr(p, '\"');
-                       LDB_DN_NULL_FAILED(p);
+       if ( (! mem_ctx) || (! ldb)) return NULL;
 
-                       if (*(p - 1) == '\\')
-                               p++;
-               }
+       va_start(ap, new_fmt);
+       strdn = talloc_vasprintf(mem_ctx, new_fmt, ap);
+       va_end(ap);
 
-               *quote_end = p - source;
-               return 1;
+       if (strdn) {
+               struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn);
+               talloc_free(strdn);
+               return dn;
        }
 
-       return 0;
-
-failed:
-       return -1;
+       return NULL;
 }
 
-static char *seek_to_separator(char *string, const char *separators)
+/* see RFC2253 section 2.4 */
+static int ldb_dn_escape_internal(char *dst, const char *src, int len)
 {
-       char *p, *q;
-       int ret, qs, qe, escaped;
+       const char *p, *s;
+       char *d;
+       int l;
 
-       if (string == NULL || separators == NULL) return NULL;
+       p = s = src;
+       d = dst;
 
-       p = strchr(string, '=');
-       LDB_DN_NULL_FAILED(p);
+       while (p - src < len) {
+               p += strcspn(p, ",=\n\r+<>#;\\\" ");
 
-       p++;
+               if (p - src == len) /* found no escapable chars */
+                       break;
 
-       /* check if there are quotes surrounding the value */
+               /* copy the part of the string before the stop */
+               memcpy(d, s, p - s);
+               d += (p - s); /* move to current position */
+               
+               switch (*p) {
+               case ' ':
+                       if (p == src || (p-src)==(len-1)) {
+                               /* if at the beginning or end
+                                * of the string then escape */
+                               *d++ = '\\';
+                               *d++ = *p++;                                     
+                       } else {
+                               /* otherwise don't escape */
+                               *d++ = *p++;
+                       }
+                       break;
 
-       ret = get_quotes_position(p, &qs, &qe);
-       if (ret == -1)
-               return NULL;
+               case '#':
+                       /* despite the RFC, windows escapes a #
+                          anywhere in the string */
+               case ',':
+               case '+':
+               case '"':
+               case '\\':
+               case '<':
+               case '>':
+               case '?':
+                       /* these must be escaped using \c form */
+                       *d++ = '\\';
+                       *d++ = *p++;
+                       break;
 
-       if (ret == 1) { /* quotes found */
+               default: {
+                       /* any others get \XX form */
+                       unsigned char v;
+                       const char *hexbytes = "0123456789ABCDEF";
+                       v = *(const unsigned char *)p;
+                       *d++ = '\\';
+                       *d++ = hexbytes[v>>4];
+                       *d++ = hexbytes[v&0xF];
+                       p++;
+                       break;
+               }
+               }
+               s = p; /* move forward */
+       }
 
-               p += qe; /* positioning after quotes */
-               p += strspn(p, " \n"); /* skip white spaces after the quote */
+       /* copy the last part (with zero) and return */
+       l = len - (s - src);
+       memcpy(d, s, l + 1);
 
-               if (strcspn(p, separators) != 0) /* if there are characters between quotes */
-                       return NULL;        /* and separators, the dn is invalid */
+       /* return the length of the resulting string */
+       return (l + (d - dst));
+}
 
-               return p; /* return on the separator */
-       }
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
+{
+       char *dst;
 
-       /* no quotes found seek to separators */
-       q = p;
-       do {
-               escaped = 0;
-               ret = strcspn(q, separators);
-               
-               if (q[ret - 1] == '\\') {
-                       escaped = 1;
-                       q = q + ret + 1;
-               }
-       } while (escaped);
+       if (!value.length)
+               return NULL;
 
-       if (ret == 0 && p == q) /* no separators ?! bail out */
+       /* 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;
+       }
 
-       return q + ret;
+       ldb_dn_escape_internal(dst, (const char *)value.data, value.length);
 
-failed:
-       return NULL;
+       dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1);
+
+       return dst;
 }
 
-static char *ldb_dn_trim_string(char *string, const char *edge)
+/*
+  explode a DN string into a ldb_dn structure
+  based on RFC4514 except that we don't support multiple valued RDNs
+
+  TODO: according to MS-ADTS:3.1.1.5.2 Naming Constraints
+  DN must be compliant with RFC2253
+*/
+static bool ldb_dn_explode(struct ldb_dn *dn)
 {
-       char *s, *p;
+       char *p, *ex_name, *ex_value, *data, *d, *dt, *t;
+       bool trim = false;
+       bool in_extended = false;
+       bool in_ex_name = false;
+       bool in_ex_value = false;
+       bool in_attr = false;
+       bool in_value = false;
+       bool in_quote = false;
+       bool is_oid = false;
+       bool escape = false;
+       unsigned x;
+       int l, ret;
+       char *parse_dn;
+       bool is_index;
 
-       /* seek out edge from start of string */
-       s = string + strspn(string, edge);
+       if ( ! dn || dn->invalid) return false;
 
-       /* backwards skip from end of string */
-       p = &s[strlen(s) - 1];
-       while (p > s && strchr(edge, *p)) {
-               *p = '\0';
-               p--;
+       if (dn->components) {
+               return true;
        }
 
-       return s;
-}
+       if (dn->ext_linearized) {
+               parse_dn = dn->ext_linearized;
+       } else {
+               parse_dn = dn->linearized;
+       }
 
-/* we choosed to not support multpile valued components */
-static struct ldb_dn_component ldb_dn_explode_component(void *mem_ctx, char *raw_component)
-{
-       struct ldb_dn_component dc;
-       char *p;
-       int ret, qs, qe;
+       if ( ! parse_dn ) {
+               return false;
+       }
 
-       memset(&dc, 0, sizeof(dc));
+       is_index = (strncmp(parse_dn, "DN=@INDEX:", 10) == 0);
 
-       if (raw_component == NULL) {
-               return dc;
+       /* Empty DNs */
+       if (parse_dn[0] == '\0') {
+               return true;
        }
 
-       /* find attribute type/value separator */
-       p = strchr(raw_component, '=');
-       LDB_DN_NULL_FAILED(p);
+       /* Special DNs case */
+       if (dn->special) {
+               return true;
+       }
 
-       *p++ = '\0'; /* terminate name and point to value */
+       /* make sure we free this if alloced previously before replacing */
+       talloc_free(dn->components);
 
-       /* copy and trim name in the component */
-       dc.name = talloc_strdup(mem_ctx, ldb_dn_trim_string(raw_component, " \n"));
-       if (!dc.name)
-               return dc;
+       talloc_free(dn->ext_components);
+       dn->ext_components = NULL;
 
-       if (! ldb_valid_attr_name(dc.name)) {
-               goto failed;
+       /* in the common case we have 3 or more components */
+       /* make sure all components are zeroed, other functions depend on it */
+       dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3);
+       if ( ! dn->components) {
+               return false;
        }
+       dn->comp_num = 0;
 
-       ret = get_quotes_position(p, &qs, &qe);
+       /* Components data space is allocated here once */
+       data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
+       if (!data) {
+               return false;
+       }
 
-       switch (ret) {
-       case 0: /* no quotes trim the string */
-               p = ldb_dn_trim_string(p, " \n");
-               dc.value = ldb_dn_unescape_value(mem_ctx, p);
-               break;
+       p = parse_dn;
+       in_extended = true;
+       in_ex_name = false;
+       in_ex_value = false;
+       trim = true;
+       t = NULL;
+       d = dt = data;
 
-       case 1: /* quotes found get the unquoted string */
-               p[qe] = '\0';
-               p = p + qs + 1;
-               dc.value.length = strlen(p);
-               dc.value.data = (uint8_t *)talloc_memdup(mem_ctx, p,
-                                                        dc.value.length + 1);
-               break;
+       while (*p) {
+               if (in_extended) {
+
+                       if (!in_ex_name && !in_ex_value) {
+
+                               if (p[0] == '<') {
+                                       p++;
+                                       ex_name = d;
+                                       in_ex_name = true;
+                                       continue;
+                               } else if (p[0] == '\0') {
+                                       p++;
+                                       continue;
+                               } else {
+                                       in_extended = false;
+                                       in_attr = true;
+                                       dt = d;
+
+                                       continue;
+                               }
+                       }
 
-       default: /* mismatched quotes ot other error, bail out */
-               goto failed;
-       }
+                       if (in_ex_name && *p == '=') {
+                               *d++ = '\0';
+                               p++;
+                               ex_value = d;
+                               in_ex_name = false;
+                               in_ex_value = true;
+                               continue;
+                       }
 
-       if (dc.value.length == 0) {
-               goto failed;
-       }
+                       if (in_ex_value && *p == '>') {
+                               const struct ldb_dn_extended_syntax *ext_syntax;
+                               struct ldb_val ex_val = {
+                                       .data = (uint8_t *)ex_value,
+                                       .length = d - ex_value
+                               };
 
-       return dc;
+                               *d++ = '\0';
+                               p++;
+                               in_ex_value = false;
+
+                               /* Process name and ex_value */
+
+                               dn->ext_components = talloc_realloc(dn,
+                                                                   dn->ext_components,
+                                                                   struct ldb_dn_ext_component,
+                                                                   dn->ext_comp_num + 1);
+                               if ( ! dn->ext_components) {
+                                       /* ouch ! */
+                                       goto failed;
+                               }
+
+                               ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name);
+                               if (!ext_syntax) {
+                                       /* We don't know about this type of extended DN */
+                                       goto failed;
+                               }
+
+                               dn->ext_components[dn->ext_comp_num].name = talloc_strdup(dn->ext_components, ex_name);
+                               if (!dn->ext_components[dn->ext_comp_num].name) {
+                                       /* ouch */
+                                       goto failed;
+                               }
+                               ret = ext_syntax->read_fn(dn->ldb, dn->ext_components,
+                                                         &ex_val, &dn->ext_components[dn->ext_comp_num].value);
+                               if (ret != LDB_SUCCESS) {
+                                       ldb_dn_mark_invalid(dn);
+                                       goto failed;
+                               }
+
+                               dn->ext_comp_num++;
+
+                               if (*p == '\0') {
+                                       /* We have reached the end (extended component only)! */
+                                       talloc_free(data);
+                                       return true;
+
+                               } else if (*p == ';') {
+                                       p++;
+                                       continue;
+                               } else {
+                                       ldb_dn_mark_invalid(dn);
+                                       goto failed;
+                               }
+                       }
 
-failed:
-       talloc_free(dc.name);
-       dc.name = NULL;
-       return dc;
-}
+                       *d++ = *p++;
+                       continue;
+               }
+               if (in_attr) {
+                       if (trim) {
+                               if (*p == ' ') {
+                                       p++;
+                                       continue;
+                               }
+
+                               /* first char */
+                               trim = false;
+
+                               if (!isascii(*p)) {
+                                       /* attr names must be ascii only */
+                                       ldb_dn_mark_invalid(dn);
+                                       goto failed;
+                               }
+
+                               if (isdigit(*p)) {
+                                       is_oid = true;
+                               } else
+                               if ( ! isalpha(*p)) {
+                                       /* not a digit nor an alpha,
+                                        * invalid attribute name */
+                                       ldb_dn_mark_invalid(dn);
+                                       goto failed;
+                               }
+
+                               /* Copy this character across from parse_dn,
+                                * now we have trimmed out spaces */
+                               *d++ = *p++;
+                               continue;
+                       }
 
-struct ldb_dn *ldb_dn_new(void *mem_ctx)
-{
-       struct ldb_dn *edn;
+                       if (*p == ' ') {
+                               p++;
+                               /* valid only if we are at the end */
+                               trim = true;
+                               continue;
+                       }
 
-       edn = talloc(mem_ctx, struct ldb_dn);
-       LDB_DN_NULL_FAILED(edn);
+                       if (trim && (*p != '=')) {
+                               /* spaces/tabs are not allowed */
+                               ldb_dn_mark_invalid(dn);
+                               goto failed;
+                       }
 
-       /* Initially there are no components */
-       edn->comp_num = 0;
-       edn->components = NULL;
+                       if (*p == '=') {
+                               /* attribute terminated */
+                               in_attr = false;
+                               in_value = true;
+                               trim = true;
+                               l = 0;
+
+                               /* Terminate this string in d
+                                * (which is a copy of parse_dn
+                                *  with spaces trimmed) */
+                               *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;
 
-       return edn;
+                               p++;
+                               continue;
+                       }
 
-failed:
-       return NULL;
-}
+                       if (!isascii(*p)) {
+                               /* attr names must be ascii only */
+                               ldb_dn_mark_invalid(dn);
+                               goto failed;
+                       }
 
-/*
-  explode a DN string into a ldb_dn structure
-*/
-struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn)
-{
-       struct ldb_dn *edn; /* the exploded dn */
-       char *pdn, *p;
+                       if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) {
+                               /* not a digit nor a dot,
+                                * invalid attribute oid */
+                               ldb_dn_mark_invalid(dn);
+                               goto failed;
+                       } else
+                       if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) {
+                               /* not ALPHA, DIGIT or HYPHEN */
+                               ldb_dn_mark_invalid(dn);
+                               goto failed;
+                       }
 
-       if (dn == NULL) return NULL;
+                       *d++ = *p++;
+                       continue;
+               }
 
-       /* Allocate a structure to hold the exploded DN */
-       edn = ldb_dn_new(mem_ctx);
-       if (edn == NULL) {
-               return NULL;
-       }
+               if (in_value) {
+                       if (in_quote) {
+                               if (*p == '\"') {
+                                       if (p[-1] != '\\') {
+                                               p++;
+                                               in_quote = false;
+                                               continue;
+                                       }
+                               }
+                               *d++ = *p++;
+                               l++;
+                               continue;
+                       }
 
-       pdn = NULL;
+                       if (trim) {
+                               if (*p == ' ') {
+                                       p++;
+                                       continue;
+                               }
 
-       /* Empty DNs */
-       if (dn[0] == '\0') {
-               return edn;
-       }
+                               /* first char */
+                               trim = false;
 
-       /* Special DNs case */
-       if (dn[0] == '@') {
-               edn->comp_num = 1;
-               edn->components = talloc(edn, struct ldb_dn_component);
-               if (edn->components == NULL) goto failed;
-               edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
-               if (edn->components[0].name == NULL) goto failed;
-               edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
-               if (edn->components[0].value.data== NULL) goto failed;
-               edn->components[0].value.length = strlen(dn);
-               return edn;
-       }
-
-       pdn = p = talloc_strdup(edn, dn);
-       LDB_DN_NULL_FAILED(pdn);
-
-       /* get the components */
-       do {
-               char *t;
-
-               /* terminate the current component and return pointer to the next one */
-               t = seek_to_separator(p, ",;");
-               LDB_DN_NULL_FAILED(t);
-
-               if (*t) { /* here there is a separator */
-                       *t = '\0'; /*terminate */
-                       t++; /* a separtor means another component follows */
-               }
-
-               /* allocate space to hold the dn component */
-               edn->components = talloc_realloc(edn, edn->components,
-                                                struct ldb_dn_component,
-                                                edn->comp_num + 1);
-               if (edn->components == NULL)
-                       goto failed;
+                               if (*p == '\"') {
+                                       in_quote = true;
+                                       p++;
+                                       continue;
+                               }
+                       }
 
-               /* store the exploded component in the main structure */
-               edn->components[edn->comp_num] = ldb_dn_explode_component(edn, p);
-               LDB_DN_NULL_FAILED(edn->components[edn->comp_num].name);
+                       switch (*p) {
 
-               edn->comp_num++;
+                       /* TODO: support ber encoded values
+                       case '#':
+                       */
 
-               /* jump to the next component if any */
-               p = t;
+                       case ',':
+                               if (escape) {
+                                       *d++ = *p++;
+                                       l++;
+                                       escape = false;
+                                       continue;
+                               }
+                               /* ok found value terminator */
 
-       } while(*p);
+                               if ( t ) {
+                                       /* trim back */
+                                       d -= (p - t);
+                                       l -= (p - t);
+                               }
 
-       talloc_free(pdn);
-       return edn;
+                               in_attr = true;
+                               in_value = false;
+                               trim = true;
 
-failed:
-       talloc_free(pdn);
-       talloc_free(edn);
-       return NULL;
-}
+                               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));
+                               }
 
-struct ldb_dn *ldb_dn_explode_or_special(void *mem_ctx, const char *dn)
-{
-       struct ldb_dn *edn; /* the exploded dn */
+                               continue;
 
-       if (dn == NULL) return NULL;
+                       case '+':
+                       case '=':
+                               /* to main compatibility with earlier
+                               versions of ldb indexing, we have to
+                               accept the base64 encoded binary index
+                               values, which contain a '+' or '='
+                               which should normally be escaped */
+                               if (is_index) {
+                                       if ( t ) t = NULL;
+                                       *d++ = *p++;
+                                       l++;
+                                       break;
+                               }
+                               /* fall through */
+                       case '\"':
+                       case '<':
+                       case '>':
+                       case ';':
+                               /* a string with not escaped specials is invalid (tested) */
+                               if ( ! escape) {
+                                       ldb_dn_mark_invalid(dn);
+                                       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 (isxdigit(p[0]) && isxdigit(p[1])) {
+                                               if (sscanf(p, "%02x", &x) != 1) {
+                                                       /* invalid escaping sequence */
+                                                       ldb_dn_mark_invalid(dn);
+                                                       goto failed;
+                                               }
+                                               p += 2;
+                                               *d++ = (unsigned char)x;
+                                       } else {
+                                               *d++ = *p++;
+                                       }
+
+                                       escape = false;
+                                       l++;
+                                       if ( t ) t = NULL;
+                                       break;
+                               }
+
+                               if (*p == ' ') {
+                                       if ( ! t) t = p;
+                               } else {
+                                       if ( t ) t = NULL;
+                               }
+
+                               *d++ = *p++;
+                               l++;
+
+                               break;
+                       }
 
-       if (strncasecmp(dn, "<GUID=", 6) == 0) {
-               /* this is special DN returned when the
-                * exploded_dn control is used
-                */
+               }
+       }
 
-               /* Allocate a structure to hold the exploded DN */
-               edn = ldb_dn_new(mem_ctx);
+       if (in_attr || in_quote) {
+               /* invalid dn */
+               ldb_dn_mark_invalid(dn);
+               goto failed;
+       }
 
-               edn->comp_num = 1;
-               edn->components = talloc(edn, struct ldb_dn_component);
-               if (edn->components == NULL) goto failed;
-               edn->components[0].name = talloc_strdup(edn->components, LDB_SPECIAL);
-               if (edn->components[0].name == NULL) goto failed;
-               edn->components[0].value.data = (uint8_t *)talloc_strdup(edn->components, dn);
-               if (edn->components[0].value.data== NULL) goto failed;
-               edn->components[0].value.length = strlen(dn);
-               return edn;
+       /* save last element */
+       if ( t ) {
+               /* trim back */
+               d -= (p - t);
+               l -= (p - t);
+       }
 
+       *d++ = '\0';
+       dn->components[dn->comp_num].value.length = l;
+       dn->components[dn->comp_num].value.data =
+                               (uint8_t *)talloc_strdup(dn->components, dt);
+       if ( ! dn->components[dn->comp_num].value.data) {
+               /* ouch */
+               goto failed;
        }
-       
-       return ldb_dn_explode(mem_ctx, dn);
+
+       dn->comp_num++;
+
+       talloc_free(data);
+       return true;
 
 failed:
-       talloc_free(edn);
-       return NULL;
+       dn->comp_num = 0;
+       talloc_free(dn->components);
+       return false;
 }
 
-char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *edn)
+bool ldb_dn_validate(struct ldb_dn *dn)
 {
-       char *dn, *value;
-       int i;
+       return ldb_dn_explode(dn);
+}
+
+const char *ldb_dn_get_linearized(struct ldb_dn *dn)
+{
+       int i, len;
+       char *d, *n;
 
-       if (edn == NULL) return NULL;
+       if ( ! dn || ( dn->invalid)) return NULL;
 
-       /* Special DNs */
-       if (ldb_dn_is_special(edn)) {
-               dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data);
-               return dn;
+       if (dn->linearized) return dn->linearized;
+
+       if ( ! dn->components) {
+               ldb_dn_mark_invalid(dn);
+               return NULL;
        }
 
-       dn = talloc_strdup(mem_ctx, "");
-       LDB_DN_NULL_FAILED(dn);
+       if (dn->comp_num == 0) {
+               dn->linearized = talloc_strdup(dn, "");
+               if ( ! dn->linearized) return NULL;
+               return dn->linearized;
+       }
 
-       for (i = 0; i < edn->comp_num; i++) {
-               value = ldb_dn_escape_value(dn, edn->components[i].value);
-               LDB_DN_NULL_FAILED(value);
+       /* calculate maximum possible length of DN */
+       for (len = 0, i = 0; i < dn->comp_num; i++) {
+               /* name len */
+               len += strlen(dn->components[i].name);
+               /* max escaped data len */
+               len += (dn->components[i].value.length * 3);
+               len += 2; /* '=' and ',' */
+       }
+       dn->linearized = talloc_array(dn, char, len);
+       if ( ! dn->linearized) return NULL;
 
-               if (i == 0) {
-                       dn = talloc_asprintf_append(dn, "%s=%s", edn->components[i].name, value);
-               } else {
-                       dn = talloc_asprintf_append(dn, ",%s=%s", edn->components[i].name, value);
-               }
-               LDB_DN_NULL_FAILED(dn);
+       d = dn->linearized;
 
-               talloc_free(value);
+       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++ = ',';
        }
 
-       return dn;
+       *(--d) = '\0';
 
-failed:
-       talloc_free(dn);
-       return NULL;
+       /* don't waste more memory than necessary */
+       dn->linearized = talloc_realloc(dn, dn->linearized,
+                                       char, (d - dn->linearized + 1));
+
+       return dn->linearized;
 }
 
-/* Determine if dn is below base, in the ldap tree.  Used for
- * evaluating a subtree search.
- * 0 if they match, otherwise non-zero
- */
+static int ldb_dn_extended_component_compare(const void *p1, const void *p2)
+{
+       const struct ldb_dn_ext_component *ec1 = (const struct ldb_dn_ext_component *)p1;
+       const struct ldb_dn_ext_component *ec2 = (const struct ldb_dn_ext_component *)p2;
+       return strcmp(ec1->name, ec2->name);
+}
 
-int ldb_dn_compare_base(struct ldb_context *ldb,
-                       const struct ldb_dn *base,
-                       const struct ldb_dn *dn)
+char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
 {
-       int ret;
-       int n0, n1;
+       const char *linearized = ldb_dn_get_linearized(dn);
+       char *p;
+       int i;
 
-       if (base == NULL || base->comp_num == 0) return 0;
-       if (dn == NULL || dn->comp_num == 0) return -1;
+       if (!linearized) {
+               return NULL;
+       }
 
-       /* if the base has more componts than the dn, then they differ */
-       if (base->comp_num > dn->comp_num) {
-               return (dn->comp_num - base->comp_num);
+       if (!ldb_dn_has_extended(dn)) {
+               return talloc_strdup(mem_ctx, linearized);
        }
 
-       n0 = base->comp_num - 1;
-       n1 = dn->comp_num - 1;
-       while (n0 >= 0 && n1 >= 0) {
-               const struct ldb_attrib_handler *h;
+       if (!ldb_dn_validate(dn)) {
+               return NULL;
+       }
 
-               /* compare names (attribute names are guaranteed to be ASCII only) */
-               ret = ldb_attr_cmp(base->components[n0].name,
-                                  dn->components[n1].name);
-               if (ret) {
-                       return ret;
+       /* sort the extended components by name. The idea is to make
+        * the resulting DNs consistent, plus to ensure that we put
+        * 'DELETED' first, so it can be very quickly recognised
+        */
+       TYPESAFE_QSORT(dn->ext_components, dn->ext_comp_num,
+                      ldb_dn_extended_component_compare);
+
+       for (i = 0; i < dn->ext_comp_num; i++) {
+               const struct ldb_dn_extended_syntax *ext_syntax;
+               const char *name = dn->ext_components[i].name;
+               struct ldb_val ec_val = dn->ext_components[i].value;
+               struct ldb_val val;
+               int ret;
+
+               ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
+               if (!ext_syntax) {
+                       return NULL;
                }
 
-               /* names match, compare values */
-               h = ldb_attrib_handler(ldb, base->components[n0].name);
-               ret = h->comparison_fn(ldb, ldb, &(base->components[n0].value),
-                                                 &(dn->components[n1].value));
-               if (ret) {
-                       return ret;
+               if (mode == 1) {
+                       ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx,
+                                                       &ec_val, &val);
+               } else if (mode == 0) {
+                       ret = ext_syntax->write_hex_fn(dn->ldb, mem_ctx,
+                                                       &ec_val, &val);
+               } else {
+                       ret = -1;
                }
-               n1--;
-               n0--;
-       }
 
-       return 0;
-}
+               if (ret != LDB_SUCCESS) {
+                       return NULL;
+               }
 
-/* compare DNs using casefolding compare functions.  
+               if (i == 0) {
+                       p = talloc_asprintf(mem_ctx, "<%s=%s>", 
+                                           name, val.data);
+               } else {
+                       p = talloc_asprintf_append_buffer(p, ";<%s=%s>",
+                                                         name, val.data);
+               }
 
-   If they match, then return 0
- */
+               talloc_free(val.data);
 
-int ldb_dn_compare(struct ldb_context *ldb,
-                  const struct ldb_dn *edn0,
-                  const struct ldb_dn *edn1)
-{
-       if (edn0 == NULL || edn1 == NULL) return edn1 - edn0;
+               if (!p) {
+                       return NULL;
+               }
+       }
+
+       if (dn->ext_comp_num && *linearized) {
+               p = talloc_asprintf_append_buffer(p, ";%s", linearized);
+       }
 
-       if (edn0->comp_num != edn1->comp_num)
-               return (edn1->comp_num - edn0->comp_num);
+       if (!p) {
+               return NULL;
+       }
 
-       return ldb_dn_compare_base(ldb, edn0, edn1);
+       return p;
 }
 
-int ldb_dn_cmp(struct ldb_context *ldb, const char *dn0, const char *dn1)
+/*
+  filter out all but an acceptable list of extended DN components
+ */
+void ldb_dn_extended_filter(struct ldb_dn *dn, const char * const *accept)
 {
-       struct ldb_dn *edn0;
-       struct ldb_dn *edn1;
-       int ret;
-
-       if (dn0 == NULL || dn1 == NULL) return dn1 - dn0;
-
-       edn0 = ldb_dn_explode_casefold(ldb, ldb, dn0);
-       if (edn0 == NULL) return 1;
-
-       edn1 = ldb_dn_explode_casefold(ldb, ldb, dn1);
-       if (edn1 == NULL) {
-               talloc_free(edn0);
-               return -1;
+       int i;
+       for (i=0; i<dn->ext_comp_num; i++) {
+               if (!ldb_attr_in_list(accept, dn->ext_components[i].name)) {
+                       memmove(&dn->ext_components[i],
+                               &dn->ext_components[i+1],
+                               (dn->ext_comp_num-(i+1))*sizeof(dn->ext_components[0]));
+                       dn->ext_comp_num--;
+                       i--;
+               }
        }
+}
 
-       ret = ldb_dn_compare(ldb, edn0, edn1);
-
-       talloc_free(edn0);
-       talloc_free(edn1);
 
-       return ret;
+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 
+  casefold a dn. We need to casefold the attribute names, and canonicalize
   attribute values of case insensitive attributes.
 */
-struct ldb_dn *ldb_dn_casefold(struct ldb_context *ldb, void *mem_ctx, const struct ldb_dn *edn)
+
+static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
 {
-       struct ldb_dn *cedn;
        int i, ret;
 
-       if (edn == NULL) return NULL;
+       if ( ! dn || dn->invalid) return false;
 
-       cedn = ldb_dn_new(mem_ctx);
-       if (!cedn) {
-               return NULL;
-       }
+       if (dn->valid_case) return true;
 
-       cedn->comp_num = edn->comp_num;
-       cedn->components = talloc_array(cedn, struct ldb_dn_component, edn->comp_num);
-       if (!cedn->components) {
-               talloc_free(cedn);
-               return NULL;
+       if (( ! dn->components) && ( ! ldb_dn_explode(dn))) {
+               return false;
        }
 
-       for (i = 0; i < edn->comp_num; i++) {
-               struct ldb_dn_component dc;
-               const struct ldb_attrib_handler *h;
+       for (i = 0; i < dn->comp_num; i++) {
+               const struct ldb_schema_attribute *a;
 
-               memset(&dc, 0, sizeof(dc));
-               dc.name = ldb_attr_casefold(cedn->components, edn->components[i].name);
-               if (!dc.name) {
-                       talloc_free(cedn);
-                       return NULL;
+               dn->components[i].cf_name =
+                       ldb_attr_casefold(dn->components,
+                                         dn->components[i].name);
+               if (!dn->components[i].cf_name) {
+                       goto failed;
                }
 
-               h = ldb_attrib_handler(ldb, dc.name);
-               ret = h->canonicalise_fn(ldb, cedn->components,
-                                        &(edn->components[i].value),
-                                        &(dc.value));
+               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) {
-                       talloc_free(cedn);
-                       return NULL;
+                       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;
 
-               cedn->components[i] = dc;
+       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) {
+               dn->casefold = talloc_strdup(dn, "");
+               return dn->casefold;
        }
 
-       return cedn;
+       /* calculate maximum possible length of DN */
+       for (len = 0, i = 0; i < dn->comp_num; i++) {
+               /* name len */
+               len += strlen(dn->components[i].cf_name);
+               /* max escaped data len */
+               len += (dn->components[i].cf_value.length * 3);
+               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;
 }
 
-struct ldb_dn *ldb_dn_explode_casefold(struct ldb_context *ldb, void *mem_ctx, const char *dn)
+char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn)
 {
-       struct ldb_dn *edn, *cdn;
+       return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
+}
 
-       if (dn == NULL) return NULL;
+/* Determine if dn is below base, in the ldap tree.  Used for
+ * evaluating a subtree search.
+ * 0 if they match, otherwise non-zero
+ */
 
-       edn = ldb_dn_explode(ldb, dn);
-       if (edn == NULL) return NULL;
+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);
+       }
 
-       cdn = ldb_dn_casefold(ldb, mem_ctx, edn);
-       
-       talloc_free(edn);
-       return cdn;
+       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) {
+               char *b_name = base->components[n_base].cf_name;
+               char *dn_name = dn->components[n_dn].cf_name;
+
+               char *b_vdata = (char *)base->components[n_base].cf_value.data;
+               char *dn_vdata = (char *)dn->components[n_dn].cf_value.data;
+
+               size_t b_vlen = base->components[n_base].cf_value.length;
+               size_t dn_vlen = dn->components[n_dn].cf_value.length;
+
+               /* compare attr names */
+               ret = strcmp(b_name, dn_name);
+               if (ret != 0) return ret;
+
+               /* compare attr.cf_value. */
+               if (b_vlen != dn_vlen) {
+                       return b_vlen - dn_vlen;
+               }
+               ret = strcmp(b_vdata, dn_vdata);
+               if (ret != 0) return ret;
+
+               n_base--;
+               n_dn--;
+       }
+
+       return 0;
 }
 
-char *ldb_dn_linearize_casefold(struct ldb_context *ldb, void *mem_ctx, const struct ldb_dn *edn)
+/* compare DNs using casefolding compare functions.
+
+   If they match, then return 0
+ */
+
+int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
 {
-       struct ldb_dn *cdn;
-       char *dn;
+       int i, ret;
 
-       if (edn == NULL) return NULL;
+       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;
+               }
 
-       /* Special DNs */
-       if (ldb_dn_is_special(edn)) {
-               dn = talloc_strdup(mem_ctx, (char *)edn->components[0].value.data);
-               return dn;
        }
 
-       cdn = ldb_dn_casefold(ldb, mem_ctx, edn);
-       if (cdn == NULL) return NULL;
+       if (dn0->comp_num != dn1->comp_num) {
+               return (dn1->comp_num - dn0->comp_num);
+       }
 
-       dn = ldb_dn_linearize(ldb, cdn);
-       if (dn == NULL) {
-               talloc_free(cdn);
-               return NULL;
+       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;
+               }
        }
 
-       talloc_free(cdn);
-       return dn;
+       for (i = 0; i < dn0->comp_num; i++) {
+               char *dn0_name = dn0->components[i].cf_name;
+               char *dn1_name = dn1->components[i].cf_name;
+
+               char *dn0_vdata = (char *)dn0->components[i].cf_value.data;
+               char *dn1_vdata = (char *)dn1->components[i].cf_value.data;
+
+               size_t dn0_vlen = dn0->components[i].cf_value.length;
+               size_t dn1_vlen = dn1->components[i].cf_value.length;
+
+               /* compare attr names */
+               ret = strcmp(dn0_name, dn1_name);
+               if (ret != 0) {
+                       return ret;
+               }
+
+               /* compare attr.cf_value. */
+               if (dn0_vlen != dn1_vlen) {
+                       return dn0_vlen - dn1_vlen;
+               }
+               ret = strcmp(dn0_vdata, dn1_vdata);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       return 0;
 }
 
-static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+static struct ldb_dn_component ldb_dn_copy_component(
+                                               void *mem_ctx,
+                                               struct ldb_dn_component *src)
 {
        struct ldb_dn_component dst;
 
@@ -681,217 +1199,502 @@ static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_d
 
        dst.name = talloc_strdup(mem_ctx, src->name);
        if (dst.name == NULL) {
-               talloc_free(dst.value.data);
-               dst.value.data = 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;
 }
 
-/* copy specified number of elements of a dn into a new one
-   element are copied from top level up to the unique rdn
-   num_el may be greater than dn->comp_num (see ldb_dn_make_child)
-*/
-struct ldb_dn *ldb_dn_copy_partial(void *mem_ctx, const struct ldb_dn *dn, int num_el)
+static struct ldb_dn_ext_component ldb_dn_ext_copy_component(
+                                               void *mem_ctx,
+                                               struct ldb_dn_ext_component *src)
 {
-       struct ldb_dn *newdn;
-       int i, n, e;
-
-       if (dn == NULL) return NULL;
-       if (num_el <= 0) return NULL;
-
-       newdn = ldb_dn_new(mem_ctx);
-       LDB_DN_NULL_FAILED(newdn);
+       struct ldb_dn_ext_component dst;
 
-       newdn->comp_num = num_el;
-       n = newdn->comp_num - 1;
-       newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
-       if (newdn->components == NULL) goto failed;
+       memset(&dst, 0, sizeof(dst));
 
-       if (dn->comp_num == 0) return newdn;
-       e = dn->comp_num - 1;
+       if (src == NULL) {
+               return dst;
+       }
 
-       for (i = 0; i < newdn->comp_num; i++) {
-               newdn->components[n - i] = ldb_dn_copy_component(newdn->components,
-                                                               &(dn->components[e - i]));
-               if ((e - i) == 0) {
-                       return newdn;
-               }
+       dst.value = ldb_val_dup(mem_ctx, &(src->value));
+       if (dst.value.data == NULL) {
+               return dst;
        }
 
-       return newdn;
+       dst.name = talloc_strdup(mem_ctx, src->name);
+       if (dst.name == NULL) {
+               LDB_FREE(dst.value.data);
+               return dst;
+       }
 
-failed:
-       talloc_free(newdn);
-       return NULL;
+       return dst;
 }
 
-struct ldb_dn *ldb_dn_copy(void *mem_ctx, const struct ldb_dn *dn)
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
 {
-       if (dn == NULL) return NULL;
-       return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num);
-}
+       struct ldb_dn *new_dn;
 
-struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, const struct ldb_dn *dn)
-{
-       if (dn == NULL) return NULL;
-       return ldb_dn_copy_partial(mem_ctx, dn, dn->comp_num - 1);
-}
+       if (!dn || dn->invalid) {
+               return NULL;
+       }
 
-struct ldb_dn_component *ldb_dn_build_component(void *mem_ctx, const char *attr,
-                                               const char *val)
-{
-       struct ldb_dn_component *dc;
+       new_dn = talloc_zero(mem_ctx, struct ldb_dn);
+       if ( !new_dn) {
+               return NULL;
+       }
 
-       if (attr == NULL || val == NULL) return NULL;
+       *new_dn = *dn;
 
-       dc = talloc(mem_ctx, struct ldb_dn_component);
-       if (dc == NULL) return NULL;
+       if (dn->components) {
+               int i;
 
-       dc->name = talloc_strdup(dc, attr);
-       if (dc->name ==  NULL) {
-               talloc_free(dc);
-               return NULL;
+               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;
+                       }
+               }
        }
 
-       dc->value.data = (uint8_t *)talloc_strdup(dc, val);
-       if (dc->value.data ==  NULL) {
-               talloc_free(dc);
-               return NULL;
+       if (dn->ext_components) {
+               int i;
+
+               new_dn->ext_components =
+                       talloc_zero_array(new_dn,
+                                         struct ldb_dn_ext_component,
+                                         dn->ext_comp_num);
+               if ( ! new_dn->ext_components) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
+
+               for (i = 0; i < dn->ext_comp_num; i++) {
+                       new_dn->ext_components[i] =
+                                ldb_dn_ext_copy_component(
+                                               new_dn->ext_components,
+                                               &dn->ext_components[i]);
+                       if ( ! new_dn->ext_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;
+               }
        }
 
-       dc->value.length = strlen(val);
+       if (dn->linearized) {
+               new_dn->linearized = talloc_strdup(new_dn, dn->linearized);
+               if ( ! new_dn->linearized) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
+       }
 
-       return dc;
+       if (dn->ext_linearized) {
+               new_dn->ext_linearized = talloc_strdup(new_dn,
+                                                       dn->ext_linearized);
+               if ( ! new_dn->ext_linearized) {
+                       talloc_free(new_dn);
+                       return NULL;
+               }
+       }
+
+       return new_dn;
 }
 
-struct ldb_dn *ldb_dn_build_child(void *mem_ctx, const char *attr,
-                                                const char * value,
-                                                const struct ldb_dn *base)
+/* 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)
 {
-       struct ldb_dn *newdn;
-       if (! ldb_valid_attr_name(attr)) return NULL;
-       if (value == NULL || value == '\0') return NULL; 
+       const char *s;
+       char *t;
 
-       if (base != NULL) {
-               newdn = ldb_dn_copy_partial(mem_ctx, base, base->comp_num + 1);
-               LDB_DN_NULL_FAILED(newdn);
-       } else {
-               newdn = ldb_dn_new(mem_ctx);
-               LDB_DN_NULL_FAILED(newdn);
+       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;
+                       }
+               }
 
-               newdn->comp_num = 1;
-               newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
+               dn->components = talloc_realloc(dn,
+                                               dn->components,
+                                               struct ldb_dn_component,
+                                               dn->comp_num + base->comp_num);
+               if ( ! dn->components) {
+                       ldb_dn_mark_invalid(dn);
+                       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) {
+                               ldb_dn_mark_invalid(dn);
+                               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;
+               }
        }
 
-       newdn->components[0].name = talloc_strdup(newdn->components, attr);
-       LDB_DN_NULL_FAILED(newdn->components[0].name);
+       if (dn->linearized) {
 
-       newdn->components[0].value.data = (uint8_t *)talloc_strdup(newdn->components, value);
-       LDB_DN_NULL_FAILED(newdn->components[0].value.data);
-       newdn->components[0].value.length = strlen((char *)newdn->components[0].value.data);
+               s = ldb_dn_get_linearized(base);
+               if ( ! s) {
+                       return false;
+               }
 
-       return newdn;
+               if (*dn->linearized) {
+                       t = talloc_asprintf(dn, "%s,%s",
+                                           dn->linearized, s);
+               } else {
+                       t = talloc_strdup(dn, s);
+               }
+               if ( ! t) {
+                       ldb_dn_mark_invalid(dn);
+                       return false;
+               }
+               LDB_FREE(dn->linearized);
+               dn->linearized = t;
+       }
 
-failed:
-       talloc_free(newdn);
-       return NULL;
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       if (dn->ext_linearized) {
+               LDB_FREE(dn->ext_linearized);
+       }
 
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+       return true;
 }
 
-struct ldb_dn *ldb_dn_make_child(void *mem_ctx, const struct ldb_dn_component *component,
-                                               const struct ldb_dn *base)
+/* 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, ...)
 {
-       if (component == NULL) return NULL;
+       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);
 
-       return ldb_dn_build_child(mem_ctx, component->name, 
-                                 (char *)component->value.data, base);
+       talloc_free(base_str);
+
+       return ret;
 }
 
-struct ldb_dn *ldb_dn_compose(void *mem_ctx, const struct ldb_dn *dn1, const struct ldb_dn *dn2)
+/* 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)
 {
-       int i;
-       struct ldb_dn *newdn;
+       const char *s;
+       char *t;
 
-       if (dn2 == NULL && dn1 == NULL) {
-               return NULL;
+       if ( !child || child->invalid || !dn || dn->invalid) {
+               return false;
        }
 
-       if (dn2 == NULL) {
-               newdn = ldb_dn_new(mem_ctx);
-               LDB_DN_NULL_FAILED(newdn);
+       if (dn->components) {
+               int n, i, j;
 
-               newdn->comp_num = dn1->comp_num;
-               newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
-       } else {
-               int comp_num = dn2->comp_num;
-               if (dn1 != NULL) comp_num += dn1->comp_num;
-               newdn = ldb_dn_copy_partial(mem_ctx, dn2, comp_num);
-               LDB_DN_NULL_FAILED(newdn);
-       }
+               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) {
+                       ldb_dn_mark_invalid(dn);
+                       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) {
+                               ldb_dn_mark_invalid(dn);
+                               return false;
+                       }
+               }
+
+               dn->comp_num = n;
 
-       if (dn1 == NULL) {
-               return newdn;
+               if (dn->casefold && s) {
+                       t = talloc_asprintf(dn, "%s,%s", s, dn->casefold);
+                       LDB_FREE(dn->casefold);
+                       dn->casefold = t;
+               }
        }
 
-       for (i = 0; i < dn1->comp_num; i++) {
-               newdn->components[i] = ldb_dn_copy_component(newdn->components,
-                                                          &(dn1->components[i]));
-               if (newdn->components[i].value.data == NULL) {
-                       goto failed;
+       if (dn->linearized) {
+
+               s = ldb_dn_get_linearized(child);
+               if ( ! s) {
+                       return false;
                }
+
+               t = talloc_asprintf(dn, "%s,%s", s, dn->linearized);
+               if ( ! t) {
+                       ldb_dn_mark_invalid(dn);
+                       return false;
+               }
+               LDB_FREE(dn->linearized);
+               dn->linearized = t;
        }
 
-       return newdn;
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->ext_linearized);
 
-failed:
-       talloc_free(newdn);
-       return NULL;
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+
+       return true;
 }
 
-struct ldb_dn *ldb_dn_string_compose(void *mem_ctx, const struct ldb_dn *base, const char *child_fmt, ...)
+/* 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 *dn, *dn1;
+       struct ldb_dn *child;
        char *child_str;
        va_list ap;
-       
-       if (child_fmt == NULL) return NULL;
+       bool ret;
+
+       if ( !dn || dn->invalid) {
+               return false;
+       }
 
        va_start(ap, child_fmt);
-       child_str = talloc_vasprintf(mem_ctx, child_fmt, ap);
+       child_str = talloc_vasprintf(dn, child_fmt, ap);
        va_end(ap);
 
-       if (child_str == NULL) return NULL;
+       if (child_str == NULL) {
+               return false;
+       }
+
+       child = ldb_dn_new(child_str, dn->ldb, child_str);
 
-       dn1 = ldb_dn_explode(mem_ctx, child_str);
-       dn = ldb_dn_compose(mem_ctx, dn1, base);
+       ret = ldb_dn_add_child(dn, child);
 
        talloc_free(child_str);
-       talloc_free(dn1);
 
-       return dn;
+       return ret;
 }
 
-struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *dn)
+bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
 {
-       struct ldb_dn_component *rdn;
+       int i;
 
-       if (dn == NULL) return NULL;
+       if ( ! ldb_dn_validate(dn)) {
+               return false;
+       }
 
-       if (dn->comp_num < 1) {
-               return NULL;
+       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);
        }
 
-       rdn = talloc(mem_ctx, struct ldb_dn_component);
-       if (rdn == NULL) return NULL;
+       dn->comp_num -= num;
 
-       *rdn = ldb_dn_copy_component(mem_ctx, &dn->components[0]);
-       if (rdn->name == NULL) {
-               talloc_free(rdn);
+       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);
+
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->ext_linearized);
+
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+
+       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);
+
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->ext_linearized);
+
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+       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 rdn;
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->ext_linearized);
+
+       LDB_FREE(dn->ext_components);
+       dn->ext_comp_num = 0;
+       return new_dn;
 }
 
 /* Create a 'canonical name' string from a DN:
@@ -899,12 +1702,21 @@ struct ldb_dn_component *ldb_dn_get_rdn(void *mem_ctx, const struct ldb_dn *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).
+   There are two formats,
+   the EX format has the last '/' replaced with a newline (\n).
 
 */
-static char *ldb_dn_canonical(void *mem_ctx, const struct ldb_dn *dn, int ex_format) {
+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--) {
@@ -912,53 +1724,309 @@ static char *ldb_dn_canonical(void *mem_ctx, const struct ldb_dn *dn, int ex_for
                        break;
                }
                if (cracked) {
-                       cracked = talloc_asprintf(mem_ctx, "%s.%s",
-                                                 ldb_dn_escape_value(mem_ctx, dn->components[i].value),
+                       cracked = talloc_asprintf(tmpctx, "%s.%s",
+                                                 ldb_dn_escape_value(tmpctx,
+                                                       dn->components[i].value),
                                                  cracked);
                } else {
-                       cracked = ldb_dn_escape_value(mem_ctx, dn->components[i].value);
+                       cracked = ldb_dn_escape_value(tmpctx,
+                                                       dn->components[i].value);
                }
                if (!cracked) {
-                       return NULL;
+                       goto done;
                }
        }
 
        /* Only domain components?  Finish here */
        if (i < 0) {
-               if (ex_format) {
-                       cracked = talloc_asprintf(mem_ctx, "%s\n", cracked);
-               } else {
-                       cracked = talloc_asprintf(mem_ctx, "%s/", cracked);
-               }
-               return cracked;
+               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(mem_ctx, "%s/%s", cracked, 
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
+               cracked = talloc_asprintf_append_buffer(cracked, "/%s",
+                                                       ldb_dn_escape_value(tmpctx,
+                                                       dn->components[i].value));
                if (!cracked) {
-                       return NULL;
+                       goto done;
                }
        }
 
        /* Last one, possibly a newline for the 'ex' format */
-       if (ex_format) {
-               cracked = talloc_asprintf(mem_ctx, "%s\n%s", cracked, 
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
-       } else {
-               cracked = talloc_asprintf(mem_ctx, "%s/%s", cracked, 
-                                         ldb_dn_escape_value(mem_ctx, dn->components[i].value));
-       }
+       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, const struct ldb_dn *dn) {
+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, const struct ldb_dn *dn) {
+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);
+
+       /* Wipe the ext_linearized DN,
+        * the GUID and SID are almost certainly no longer valid */
+       LDB_FREE(dn->ext_linearized);
+
+       dn->ext_comp_num = 0;
+       LDB_FREE(dn->ext_components);
+       return LDB_SUCCESS;
+}
+
+const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn,
+                                                   const char *name)
+{
+       int i;
+       if ( ! ldb_dn_validate(dn)) {
+               return NULL;
+       }
+       for (i=0; i < dn->ext_comp_num; i++) {
+               if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
+                       return &dn->ext_components[i].value;
+               }
+       }
+       return NULL;
+}
+
+int ldb_dn_set_extended_component(struct ldb_dn *dn,
+                                 const char *name, const struct ldb_val *val)
+{
+       struct ldb_dn_ext_component *p;
+       int i;
+       struct ldb_val v2;
+
+       if ( ! ldb_dn_validate(dn)) {
+               return LDB_ERR_OTHER;
+       }
+
+       if (!ldb_dn_extended_syntax_by_name(dn->ldb, name)) {
+               /* We don't know how to handle this type of thing */
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       for (i=0; i < dn->ext_comp_num; i++) {
+               if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) {
+                       if (val) {
+                               dn->ext_components[i].value =
+                                       ldb_val_dup(dn->ext_components, val);
+
+                               dn->ext_components[i].name =
+                                       talloc_strdup(dn->ext_components, name);
+                               if (!dn->ext_components[i].name ||
+                                   !dn->ext_components[i].value.data) {
+                                       ldb_dn_mark_invalid(dn);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                               return LDB_SUCCESS;
+                       } else {
+                               if (i != (dn->ext_comp_num - 1)) {
+                                       memmove(&dn->ext_components[i],
+                                               &dn->ext_components[i+1],
+                                               ((dn->ext_comp_num-1) - i) *
+                                                 sizeof(*dn->ext_components));
+                               }
+                               dn->ext_comp_num--;
+
+                               dn->ext_components = talloc_realloc(dn,
+                                                  dn->ext_components,
+                                                  struct ldb_dn_ext_component,
+                                                  dn->ext_comp_num);
+                               if (!dn->ext_components) {
+                                       ldb_dn_mark_invalid(dn);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                               return LDB_SUCCESS;
+                       }
+               }
+       }
+
+       if (val == NULL) {
+               /* removing a value that doesn't exist is not an error */
+               return LDB_SUCCESS;
+       }
+
+       v2 = *val;
+
+       p = dn->ext_components
+               = talloc_realloc(dn,
+                                dn->ext_components,
+                                struct ldb_dn_ext_component,
+                                dn->ext_comp_num + 1);
+       if (!dn->ext_components) {
+               ldb_dn_mark_invalid(dn);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, &v2);
+       p[dn->ext_comp_num].name = talloc_strdup(p, name);
+
+       if (!dn->ext_components[i].name || !dn->ext_components[i].value.data) {
+               ldb_dn_mark_invalid(dn);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       dn->ext_components = p;
+       dn->ext_comp_num++;
+
+       return LDB_SUCCESS;
+}
+
+void ldb_dn_remove_extended_components(struct ldb_dn *dn)
+{
+       dn->ext_comp_num = 0;
+       LDB_FREE(dn->ext_components);
+}
+
+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_has_extended(struct ldb_dn *dn)
+{
+       if ( ! dn || dn->invalid) return false;
+       if (dn->ext_linearized && (dn->ext_linearized[0] == '<')) return true;
+       return dn->ext_comp_num != 0;
+}
+
+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 (ldb_dn_has_extended(dn)) return false;
+       if (dn->linearized && (dn->linearized[0] == '\0')) return true;
+       return false;
+}
+
+/*
+  this updates dn->components, taking the components from ref_dn.
+  This is used by code that wants to update the DN path of a DN
+  while not impacting on the extended DN components
+ */
+int ldb_dn_update_components(struct ldb_dn *dn, const struct ldb_dn *ref_dn)
+{
+       dn->components = talloc_realloc(dn, dn->components,
+                                       struct ldb_dn_component, ref_dn->comp_num);
+       if (!dn->components) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       memcpy(dn->components, ref_dn->components,
+              sizeof(struct ldb_dn_component)*ref_dn->comp_num);
+       dn->comp_num = ref_dn->comp_num;
+
+       talloc_free(dn->linearized);
+       talloc_free(dn->ext_linearized);
+       dn->ext_linearized = NULL;
+       dn->linearized = NULL;
+
+       return LDB_SUCCESS;
+}