LATER... ldb: Add ldb_dn_compare_base_one for checking if a dn is just bellow the...
authorMatthieu Patou <mat@matws.net>
Sun, 30 Dec 2012 05:47:29 +0000 (21:47 -0800)
committerStefan Metzmacher <metze@samba.org>
Tue, 29 Jan 2013 21:03:13 +0000 (22:03 +0100)
lib/ldb/common/ldb_dn.c
lib/ldb/include/ldb.h

index b910489a8e8f1bcf4b9bef9cf57530381f8003d8..d63a27cbd4153ca98da08a857fcae5d477e8135f 100644 (file)
@@ -1020,12 +1020,137 @@ char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
        return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
 }
 
-/* Determine if dn is below base, in the ldap tree.  Used for
- * evaluating a subtree search.
- * 0 if they match, otherwise non-zero
+/**
+ * @brief Just compare the linerarized part of DNs.
+ *
+ * This function check if dn is bellow base, it assumes that both DN have
+ * a linearized version. There is no good reason to use this function directly
+ * it has been abstracted to a function in order to make the
+ * function ldb_dn_compare_base_internal more readable, you should use this one
+ * instead.
+ *
+ * @param[in]  base            The base DN that is checked against
+ *
+ * @param[in]  dn              The DN to test
+ *
+ * @param[in]  onelevel        A boolean to indicate if we validate only
+ *                             DN that are just 1 level bellow the DN
+ *
+ * @return                     0 if the DN is bellow (as influenced by the
+ *                             onelevel parameter), > 0 if it's not, and < 0
+ *                             if a check on each componenent is needed
  */
+static int ldb_dn_linearized_compare_base(struct ldb_dn *base, struct ldb_dn *dn,
+                                         bool onelevel)
+{
+       int dif;
+       int i;
+       bool comma_found = false;
+       dif = strlen(dn->linearized) - strlen(base->linearized);
+       if (dif < 0) {
+               return 1;
+       }
+       if (!base->linearized[0]) {
+               if (onelevel) {
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
 
-int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
+       if (onelevel && dif == 0) {
+               /*
+                * dn has the same length as base, and we were
+                * asked for comparing only onelevel so this
+                * can't be ok whatever the comparison of the string returns
+                */
+               return 1;
+       }
+
+       if (strcmp(base->linearized, &dn->linearized[dif]) == 0) {
+               if (dif == 0) {
+                       /*
+                        * Same length, same content, not onelevel search
+                        * that's ok, matched.
+                        */
+                       return 0;
+               }
+
+               /*
+                * So far we know that len(dn) > len(base) and that
+                * (char*)(dn->linearized+dif) match base->linearized
+                * We can have 0 or more comma ',' if it's 0 it's not a match
+                * (it's for instance dn: adc=sambacorp and base dc=sambacorp)
+                *
+                * As we already matched the string in base->linearized, we
+                * know that dn is a child of base only if chars between the
+                * last comma in the non matched part (from 0 to dif) and the
+                * end of the matched part are spaces.
+                * If there is comma it's also not a match.
+                *
+                * If there is more than 1 comma and if onelevel is true then
+                * it might be ok but in order to be sure and avoid costly
+                * checks for the vast majority of onelevel, we return -1,
+                * leaving it for more complete checks.
+                */
+               i = dif - 1;
+               while (i > 0 && !comma_found) {
+                       /* ',' or ' ' => ok, else fail */
+                       if (dn->linearized[i] == ',') {
+                               comma_found = true;
+                               break;
+                       }
+                       if (dn->linearized[i] == ' ') {
+                               i--;
+                               continue;
+                       }
+                       break;
+               }
+               if (!comma_found) {
+                       return 1;
+               }
+
+               if (!onelevel) {
+                       return 0;
+               }
+               comma_found = false;
+               while (i > 0 && !comma_found) {
+                       if (dn->linearized[i] == ',' &&
+                           dn->linearized[i-1] != '\\') {
+                               comma_found = true;
+                               break;
+                       }
+                       i--;
+               }
+               if (comma_found) {
+                       /* Maybe 1 */
+                       return -1;
+               } else {
+                       return 0;
+               }
+       }
+
+       /* dn can differ in the case and still be the same */
+       return -1;
+}
+/**
+ * @brief compare if a DN is bellow the base
+ *
+ * A DN is said to be bellow another if the last n components of it are the same
+ * as the n component of the base DN
+ *
+ * @param[in]  base            The base DN that is checked against
+ *
+ * @param[in]  dn              The DN to test
+ *
+ * @param[in]  onelevel        A boolean to indicate if we validate only
+ *                             DN that are just 1 level bellow the DN
+ *
+ * @return                     0 if the DN is bellow (as influenced by the
+ *                             onelevel parameter), non zero otherwise
+ */
+static int ldb_dn_compare_base_internal(struct ldb_dn *base, struct ldb_dn *dn,
+                                       bool onelevel)
 {
        int ret;
        unsigned int n_base, n_dn;
@@ -1037,14 +1162,9 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
                if (base->linearized && dn->linearized && dn->special == base->special) {
                        /* 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;
+                       ret = ldb_dn_linearized_compare_base(base, dn, onelevel);
+                       if (ret >=0) {
+                               return ret;
                        }
                }
 
@@ -1064,6 +1184,13 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
                return (dn->comp_num - base->comp_num);
        }
 
+       if (onelevel && dn->comp_num != (base->comp_num + 1)) {
+               int v = dn->comp_num - base->comp_num;
+
+               return v == 0 ? 1: v;
+       }
+
+
        if ((dn->comp_num == 0) || (base->comp_num == 0)) {
                if (dn->special && base->special) {
                        return strcmp(base->linearized, dn->linearized);
@@ -1107,6 +1234,36 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
        return 0;
 }
 
+/**
+ * @brief Determine if DN is bellow base DN
+ *
+ * @param[in]  base        The base DN of the comparison
+ *
+ * @param[in]  dn          The DN to test
+ *
+ * @return                 0 if the DN is bellow base in the ldap tree,
+ *                         otherwise non-zero
+ */
+int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
+{
+       return ldb_dn_compare_base_internal(base, dn, false);
+}
+
+/**
+ * @brief Determine if DN is one level bellow base DN
+ *
+ * @param[in]  base        The base DN of the comparison
+ *
+ * @param[in]  dn          The DN to test
+ *
+ * @return                 0 if the DN is one level bellow base in the ldap tree,
+ *                         otherwise non-zero
+ */
+int ldb_dn_compare_base_one(struct ldb_dn *base, struct ldb_dn *dn)
+{
+       return ldb_dn_compare_base_internal(base, dn, true);
+}
+
 /* compare DNs using casefolding compare functions.
 
    If they match, then return 0
index 748def9435f39e9194b3313d434a6ef6152796ed..4b30e6ffcfbf9b189cb3e36c2b5bca907541e1a4 100644 (file)
@@ -1807,6 +1807,7 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn);
 char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
 
 int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn);
+int ldb_dn_compare_base_one(struct ldb_dn *base, struct ldb_dn *dn);
 int ldb_dn_compare(struct ldb_dn *edn0, struct ldb_dn *edn1);
 
 bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base);