r6833: split out the routine that calculates the diff between two ldb messages from...
[kamenim/samba.git] / source4 / lib / ldb / common / ldb_msg.c
index 59d480a33a54c4471084be7877b085eb2f1f69fb..7a6dea049ae15fc1031bb8eba6c0d7d3a82fb084 100644 (file)
  */
 
 #include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_private.h"
 
+/*
+  create a new ldb_message in a given memory context (NULL for top level)
+*/
+struct ldb_message *ldb_msg_new(void *mem_ctx)
+{
+       return talloc_zero(mem_ctx, struct ldb_message);
+}
 
 /*
   find an element in a message by attribute name
@@ -41,7 +50,7 @@
 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
                                                 const char *attr_name)
 {
-       int i;
+       unsigned int i;
        for (i=0;i<msg->num_elements;i++) {
                if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
                        return &msg->elements[i];
@@ -74,7 +83,7 @@ int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
                                 struct ldb_val *val)
 {
-       int i;
+       unsigned int i;
        for (i=0;i<el->num_values;i++) {
                if (ldb_val_equal_exact(val, &el->values[i])) {
                        return &el->values[i];
@@ -83,6 +92,31 @@ struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
        return NULL;
 }
 
+/*
+  duplicate a ldb_val structure
+*/
+struct ldb_val ldb_val_dup(TALLOC_CTX *mem_ctx, 
+                          const struct ldb_val *v)
+{
+       struct ldb_val v2;
+       v2.length = v->length;
+       if (v->length == 0) {
+               v2.data = NULL;
+               return v2;
+       }
+
+       /* the +1 is to cope with buggy C library routines like strndup
+          that look one byte beyond */
+       v2.data = talloc_array(mem_ctx, char, v->length+1);
+       if (!v2.data) {
+               v2.length = 0;
+               return v2;
+       }
+
+       memcpy(v2.data, v->data, v->length);
+       ((char *)v2.data)[v->length] = 0;
+       return v2;
+}
 
 /*
   add an empty element to a message
@@ -92,8 +126,8 @@ int ldb_msg_add_empty(struct ldb_context *ldb,
 {
        struct ldb_message_element *els;
 
-       els = ldb_realloc_p(ldb, msg->elements, 
-                           struct ldb_message_element, msg->num_elements+1);
+       els = talloc_realloc(msg, msg->elements, 
+                              struct ldb_message_element, msg->num_elements+1);
        if (!els) {
                errno = ENOMEM;
                return -1;
@@ -102,7 +136,7 @@ int ldb_msg_add_empty(struct ldb_context *ldb,
        els[msg->num_elements].values = NULL;
        els[msg->num_elements].num_values = 0;
        els[msg->num_elements].flags = flags;
-       els[msg->num_elements].name = ldb_strdup(ldb, attr_name);
+       els[msg->num_elements].name = talloc_strdup(els, attr_name);
        if (!els[msg->num_elements].name) {
                return -1;
        }
@@ -137,7 +171,7 @@ int ldb_msg_add(struct ldb_context *ldb,
 int ldb_msg_add_value(struct ldb_context *ldb,
                      struct ldb_message *msg, 
                      const char *attr_name,
-                     struct ldb_val *val)
+                     const struct ldb_val *val)
 {
        struct ldb_message_element *el;
        struct ldb_val *vals;
@@ -151,7 +185,7 @@ int ldb_msg_add_value(struct ldb_context *ldb,
                return -1;
        }
 
-       vals = ldb_realloc_p(ldb, el->values, struct ldb_val, el->num_values+1);
+       vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
        if (!vals) {
                errno = ENOMEM;
                return -1;
@@ -168,11 +202,33 @@ int ldb_msg_add_value(struct ldb_context *ldb,
   add a string element to a message
 */
 int ldb_msg_add_string(struct ldb_context *ldb, struct ldb_message *msg, 
-                      char *attr_name, char *str)
+                      const char *attr_name, const char *str)
 {
        struct ldb_val val;
 
-       val.data = str;
+       val.data = discard_const_p(char, str);
+       val.length = strlen(str);
+
+       return ldb_msg_add_value(ldb, msg, attr_name, &val);
+}
+
+/*
+  add a printf formatted element to a message
+*/
+int ldb_msg_add_fmt(struct ldb_context *ldb, struct ldb_message *msg, 
+                   const char *attr_name, const char *fmt, ...)
+{
+       struct ldb_val val;
+       va_list ap;
+       char *str;
+
+       va_start(ap, fmt);
+       str = talloc_vasprintf(msg, fmt, ap);
+       va_end(ap);
+
+       if (str == NULL) return -1;
+
+       val.data   = str;
        val.length = strlen(str);
 
        return ldb_msg_add_value(ldb, msg, attr_name, &val);
@@ -185,7 +241,7 @@ int ldb_msg_add_string(struct ldb_context *ldb, struct ldb_message *msg,
 int ldb_msg_element_compare(struct ldb_message_element *el1, 
                            struct ldb_message_element *el2)
 {
-       int i;
+       unsigned int i;
 
        if (el1->num_values != el2->num_values) {
                return el1->num_values - el2->num_values;
@@ -200,6 +256,16 @@ int ldb_msg_element_compare(struct ldb_message_element *el1,
        return 0;
 }
 
+/*
+  compare two ldb_message_element structures
+  comparing by element name
+*/
+int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
+                                struct ldb_message_element *el2)
+{
+       return ldb_attr_cmp(el1->name, el2->name);
+}
+
 /*
   convenience functions to return common types from a message
   these return the first value if the attribute is multi-valued
@@ -226,7 +292,7 @@ int ldb_msg_find_int(const struct ldb_message *msg,
 
 unsigned int ldb_msg_find_uint(const struct ldb_message *msg, 
                               const char *attr_name,
-                              int default_value)
+                              unsigned int default_value)
 {
        const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
        if (!v || !v->data) {
@@ -235,6 +301,28 @@ unsigned int ldb_msg_find_uint(const struct ldb_message *msg,
        return strtoul(v->data, NULL, 0);
 }
 
+int64_t ldb_msg_find_int64(const struct ldb_message *msg, 
+                          const char *attr_name,
+                          int64_t default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtoll(v->data, NULL, 0);
+}
+
+uint64_t ldb_msg_find_uint64(const struct ldb_message *msg, 
+                            const char *attr_name,
+                            uint64_t default_value)
+{
+       const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+       if (!v || !v->data) {
+               return default_value;
+       }
+       return strtoull(v->data, NULL, 0);
+}
+
 double ldb_msg_find_double(const struct ldb_message *msg, 
                           const char *attr_name,
                           double default_value)
@@ -256,3 +344,170 @@ const char *ldb_msg_find_string(const struct ldb_message *msg,
        }
        return v->data;
 }
+
+
+/*
+  sort the elements of a message by name
+*/
+void ldb_msg_sort_elements(struct ldb_message *msg)
+{
+       qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
+             (comparison_fn_t)ldb_msg_element_compare_name);
+}
+
+
+/*
+  free a message created using ldb_msg_copy
+*/
+void ldb_msg_free(struct ldb_context *ldb, struct ldb_message *msg)
+{
+       talloc_free(msg);
+}
+
+/*
+  copy a message, allocating new memory for all parts
+*/
+struct ldb_message *ldb_msg_copy(struct ldb_context *ldb, 
+                                const struct ldb_message *msg)
+{
+       struct ldb_message *msg2;
+       int i, j;
+
+       msg2 = talloc(ldb, struct ldb_message);
+       if (msg2 == NULL) return NULL;
+
+       msg2->elements = NULL;
+       msg2->num_elements = 0;
+       msg2->private_data = NULL;
+
+       msg2->dn = talloc_strdup(msg2, msg->dn);
+       if (msg2->dn == NULL) goto failed;
+
+       msg2->elements = talloc_array(msg2, struct ldb_message_element, msg->num_elements);
+       if (msg2->elements == NULL) goto failed;
+
+       for (i=0;i<msg->num_elements;i++) {
+               struct ldb_message_element *el1 = &msg->elements[i];
+               struct ldb_message_element *el2 = &msg2->elements[i];
+
+               el2->flags = el1->flags;
+               el2->num_values = 0;
+               el2->values = NULL;
+               el2->name = talloc_strdup(msg2->elements, el1->name);
+               if (el2->name == NULL) goto failed;
+               el2->values = talloc_array(msg2->elements, struct ldb_val, el1->num_values);
+               for (j=0;j<el1->num_values;j++) {
+                       el2->values[j] = ldb_val_dup(ldb, &el1->values[j]);
+                       if (el2->values[j].data == NULL &&
+                           el1->values[j].length != 0) {
+                               goto failed;
+                       }
+                       el2->values[j].data = talloc_steal(el2->values, el2->values[j].data);
+                       el2->num_values++;
+               }
+
+               msg2->num_elements++;
+       }
+
+       return msg2;
+
+failed:
+       talloc_free(msg2);
+       return NULL;
+}
+
+
+/*
+  canonicalise a message, merging elements of the same name
+*/
+struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
+                                        const struct ldb_message *msg)
+{
+       int i;
+       struct ldb_message *msg2;
+
+       msg2 = ldb_msg_copy(ldb, msg);
+       if (msg2 == NULL) return NULL;
+
+       ldb_msg_sort_elements(msg2);
+
+       for (i=1;i<msg2->num_elements;i++) {
+               struct ldb_message_element *el1 = &msg2->elements[i-1];
+               struct ldb_message_element *el2 = &msg2->elements[i];
+               if (ldb_msg_element_compare_name(el1, el2) == 0) {
+                       el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
+                                                      el1->num_values + el2->num_values);
+                       if (el1->values == NULL) {
+                               return NULL;
+                       }
+                       memcpy(el1->values + el1->num_values,
+                              el2->values,
+                              sizeof(struct ldb_val) * el2->num_values);
+                       el1->num_values += el2->num_values;
+                       talloc_free(el2->name);
+                       if (i+1<msg2->num_elements) {
+                               memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
+                                       (msg2->num_elements - (i+1)));
+                       }
+                       msg2->num_elements--;
+                       i--;
+               }
+       }
+
+       return msg2;
+}
+
+
+/*
+  return a ldb_message representing the differences between msg1 and msg2. If you
+  then use this in a ldb_modify() call it can be used to save edits to a message
+*/
+struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
+                                struct ldb_message *msg1,
+                                struct ldb_message *msg2)
+{
+       struct ldb_message *mod;
+       struct ldb_message_element *el;
+       unsigned int i;
+
+       mod = ldb_msg_new(ldb);
+
+       mod->dn = msg1->dn;
+       mod->num_elements = 0;
+       mod->elements = NULL;
+
+       msg2 = ldb_msg_canonicalize(ldb, msg2);
+       if (msg2 == NULL) {
+               return NULL;
+       }
+       
+       /* look in msg2 to find elements that need to be added
+          or modified */
+       for (i=0;i<msg2->num_elements;i++) {
+               el = ldb_msg_find_element(msg1, msg2->elements[i].name);
+
+               if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
+                       continue;
+               }
+
+               if (ldb_msg_add(ldb, mod, 
+                               &msg2->elements[i],
+                               el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
+                       return NULL;
+               }
+       }
+
+       /* look in msg1 to find elements that need to be deleted */
+       for (i=0;i<msg1->num_elements;i++) {
+               el = ldb_msg_find_element(msg2, msg1->elements[i].name);
+               if (!el) {
+                       if (ldb_msg_add_empty(ldb, mod, 
+                                             msg1->elements[i].name,
+                                             LDB_FLAG_MOD_DELETE) != 0) {
+                               return NULL;
+                       }
+               }
+       }
+
+       return mod;
+}