added the rest of the ldb_modify() code, which required a fairly large
authorAndrew Tridgell <tridge@samba.org>
Sat, 3 Apr 2004 12:29:21 +0000 (12:29 +0000)
committerAndrew Tridgell <tridge@samba.org>
Sat, 3 Apr 2004 12:29:21 +0000 (12:29 +0000)
change in the ldb API. The API is now much closer to LDAP.
(This used to be commit e9e85c464411c561c5073d262a2e3533fec175ca)

19 files changed:
source4/lib/ldb/Makefile.ldb
source4/lib/ldb/common/ldb_ldif.c
source4/lib/ldb/include/ldb.h
source4/lib/ldb/ldb_ldap/ldb_ldap.c
source4/lib/ldb/ldb_tdb/ldb_index.c
source4/lib/ldb/ldb_tdb/ldb_match.c
source4/lib/ldb/ldb_tdb/ldb_pack.c
source4/lib/ldb/ldb_tdb/ldb_search.c
source4/lib/ldb/ldb_tdb/ldb_tdb.c
source4/lib/ldb/ldb_tdb/ldbadd.c [deleted file]
source4/lib/ldb/ldb_tdb/ldbdel.c [deleted file]
source4/lib/ldb/ldb_tdb/ldbsearch.c [deleted file]
source4/lib/ldb/tests/test-generic.sh [new file with mode: 0644]
source4/lib/ldb/tests/test-ldap.sh [new file with mode: 0755]
source4/lib/ldb/tests/test-modify.ldif [new file with mode: 0644]
source4/lib/ldb/tests/test-tdb.sh [new file with mode: 0755]
source4/lib/ldb/tools/ldbadd.c
source4/lib/ldb/tools/ldbmodify.c [new file with mode: 0644]
source4/lib/ldb/tools/ldbsearch.c

index b983ca2c49959c8099f070662a84c1ce94e32a9d..0610ccf19ba59f11cea2c61f127efcc792d859d2 100644 (file)
@@ -18,7 +18,7 @@ OBJS =  $(COMMON_OBJ) $(LDB_TDB_OBJ) $(TDB_OBJ) $(LDB_LDAP_OBJ)
 
 LDB_LIB = lib/libldb.a
 
-BINS = bin/ldbadd bin/ldbsearch bin/ldbdel
+BINS = bin/ldbadd bin/ldbsearch bin/ldbdel bin/ldbmodify
 
 LIBS = $(LDB_LIB)($(OBJS))
 
@@ -43,6 +43,9 @@ bin/ldbsearch: tools/ldbsearch.o $(LIBS)
 bin/ldbdel: tools/ldbdel.o $(LIBS)
        $(CC) -o bin/ldbdel tools/ldbdel.o $(LIB_FLAGS)
 
+bin/ldbmodify: tools/ldbmodify.o $(LIBS)
+       $(CC) -o bin/ldbmodify tools/ldbmodify.o $(LIB_FLAGS)
+
 clean:
        rm -f */*.o *~ */*~ $(BINS) $(LDB_LIB)
 
index 198c984823368071b2434ae4a205d56e5086fc7e..b4c27c3369e19666a56bc47f530e32431a352a3c 100644 (file)
  *  Author: Andrew Tridgell
  */
 
+/*
+  see RFC2849 for the LDIF format definition
+*/
+
 #include "includes.h"
 
 
@@ -176,40 +180,71 @@ static int base64_encode_f(int (*fprintf_fn)(void *, const char *, ...), void *p
        return ret;
 }
 
+
+static const struct {
+       const char *name;
+       enum ldb_changetype changetype;
+} ldb_changetypes[] = {
+       {"add",    LDB_CHANGETYPE_ADD},
+       {"delete", LDB_CHANGETYPE_DELETE},
+       {"modify", LDB_CHANGETYPE_MODIFY},
+       {NULL, 0}
+};
+
 /*
   write to ldif, using a caller supplied write method
 */
 int ldif_write(int (*fprintf_fn)(void *, const char *, ...), 
               void *private,
-              const struct ldb_message *msg)
+              const struct ldb_ldif *ldif)
 {
-       int i;
+       int i, j;
        int total=0, ret;
+       const struct ldb_message *msg;
+
+       msg = &ldif->msg;
 
        ret = fprintf_fn(private, "dn: %s\n", msg->dn);
        CHECK_RET;
 
+       if (ldif->changetype != LDB_CHANGETYPE_NONE) {
+               for (i=0;ldb_changetypes[i].name;i++) {
+                       if (ldb_changetypes[i].changetype == ldif->changetype) {
+                               break;
+                       }
+               }
+               if (!ldb_changetypes[i].name) {
+                       fprintf(stderr,"Invalid changetype\n");
+                       return -1;
+               }
+               ret = fprintf_fn(private, "changetype: %s\n", ldb_changetypes[i].name);
+               CHECK_RET;
+       }
+
        for (i=0;i<msg->num_elements;i++) {
-               if (ldb_should_b64_encode(&msg->elements[i].value)) {
-                       ret = fprintf_fn(private, "%s:: ", msg->elements[i].name);
-                       CHECK_RET;
-                       ret = base64_encode_f(fprintf_fn, private, 
-                                             msg->elements[i].value.data, 
-                                             msg->elements[i].value.length,
-                                             strlen(msg->elements[i].name)+3);
-                       CHECK_RET;
-                       ret = fprintf_fn(private, "\n");
-                       CHECK_RET;
-               } else {
-                       ret = fprintf_fn(private, "%s: ", msg->elements[i].name);
-                       CHECK_RET;
-                       ret = fold_string(fprintf_fn, private,
-                                         msg->elements[i].value.data,
-                                         msg->elements[i].value.length,
-                                         strlen(msg->elements[i].name)+2);
-                       CHECK_RET;
-                       ret = fprintf_fn(private, "\n");
-                       CHECK_RET;
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       if (ldb_should_b64_encode(&msg->elements[i].values[j])) {
+                               ret = fprintf_fn(private, "%s:: ", 
+                                                msg->elements[i].name);
+                               CHECK_RET;
+                               ret = base64_encode_f(fprintf_fn, private, 
+                                                     msg->elements[i].values[j].data, 
+                                                     msg->elements[i].values[j].length,
+                                                     strlen(msg->elements[i].name)+3);
+                               CHECK_RET;
+                               ret = fprintf_fn(private, "\n");
+                               CHECK_RET;
+                       } else {
+                               ret = fprintf_fn(private, "%s: ", msg->elements[i].name);
+                               CHECK_RET;
+                               ret = fold_string(fprintf_fn, private,
+                                                 msg->elements[i].values[j].data,
+                                                 msg->elements[i].values[j].length,
+                                                 strlen(msg->elements[i].name)+2);
+                               CHECK_RET;
+                               ret = fprintf_fn(private, "\n");
+                               CHECK_RET;
+                       }
                }
        }
        ret = fprintf_fn(private,"\n");
@@ -235,7 +270,7 @@ static char *next_chunk(int (*fgetc_fn)(void *), void *private)
        int in_comment = 0;
 
        while ((c = fgetc_fn(private)) != EOF) {
-               if (chunk_size == alloc_size) {
+               if (chunk_size+1 >= alloc_size) {
                        char *c2;
                        alloc_size += 1024;
                        c2 = realloc_p(chunk, char, alloc_size);
@@ -279,6 +314,10 @@ static char *next_chunk(int (*fgetc_fn)(void *), void *private)
                chunk[chunk_size++] = c;
        }
 
+       if (chunk) {
+               chunk[chunk_size] = 0;
+       }
+
        return chunk;
 }
 
@@ -289,6 +328,13 @@ static int next_attr(char **s, char **attr, struct ldb_val *value)
        char *p;
        int base64_encoded = 0;
 
+       if (strncmp(*s, "-\n", 2) == 0) {
+               value->length = 0;
+               *attr = "-";
+               *s += 2;
+               return 0;
+       }
+
        p = strchr(*s, ':');
        if (!p) {
                return -1;
@@ -336,26 +382,63 @@ static int next_attr(char **s, char **attr, struct ldb_val *value)
 /*
   free a message from a ldif_read
 */
-void ldif_read_free(struct ldb_message *msg)
+void ldif_read_free(struct ldb_ldif *ldif)
 {
+       struct ldb_message *msg = &ldif->msg;
+       int i;
+       for (i=0;i<msg->num_elements;i++) {
+               if (msg->elements[i].values) free(msg->elements[i].values);
+       }
        if (msg->elements) free(msg->elements);
        if (msg->private) free(msg->private);
-       free(msg);
+       free(ldif);
+}
+
+/*
+  add an empty element
+*/
+static int msg_add_empty(struct ldb_message *msg, const char *name, unsigned flags)
+{
+       struct ldb_message_element *el2, *el;
+
+       el2 = realloc_p(msg->elements, struct ldb_message_element, msg->num_elements+1);
+       if (!el2) {
+               errno = ENOMEM;
+               return -1;
+       }
+       
+       msg->elements = el2;
+
+       el = &msg->elements[msg->num_elements];
+       
+       el->name = name;
+       el->num_values = 0;
+       el->values = NULL;
+       el->flags = flags;
+
+       msg->num_elements++;
+
+       return 0;
 }
 
 /*
  read from a LDIF source, creating a ldb_message
 */
-struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private)
+struct ldb_ldif *ldif_read(int (*fgetc_fn)(void *), void *private)
 {
+       struct ldb_ldif *ldif;
        struct ldb_message *msg;
        char *attr=NULL, *chunk=NULL, *s;
        struct ldb_val value;
+       unsigned flags = 0;
 
        value.data = NULL;
 
-       msg = malloc_p(struct ldb_message);
-       if (!msg) return NULL;
+       ldif = malloc_p(struct ldb_ldif);
+       if (!ldif) return NULL;
+
+       ldif->changetype = LDB_CHANGETYPE_NONE;
+       msg = &ldif->msg;
 
        msg->dn = NULL;
        msg->elements = NULL;
@@ -383,22 +466,86 @@ struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private)
        msg->dn = value.data;
 
        while (next_attr(&s, &attr, &value) == 0) {
-               msg->elements = realloc_p(msg->elements, 
-                                         struct ldb_message_element, 
-                                         msg->num_elements+1);
-               if (!msg->elements) {
-                       goto failed;
+               struct ldb_message_element *el;
+               int empty = 0;
+
+               if (strcmp(attr, "changetype") == 0) {
+                       int i;
+                       for (i=0;ldb_changetypes[i].name;i++) {
+                               if (strcmp((char *)value.data, ldb_changetypes[i].name) == 0) {
+                                       ldif->changetype = ldb_changetypes[i].changetype;
+                                       break;
+                               }
+                       }
+                       if (!ldb_changetypes[i].name) {
+                               fprintf(stderr,"Bad changetype '%s'\n",
+                                       (char *)value.data);
+                       }
+                       flags = 0;
+                       continue;
+               }
+
+               if (strcmp(attr, "add") == 0) {
+                       flags = LDB_FLAG_MOD_ADD;
+                       empty = 1;
+               }
+               if (strcmp(attr, "delete") == 0) {
+                       flags = LDB_FLAG_MOD_DELETE;
+                       empty = 1;
+               }
+               if (strcmp(attr, "replace") == 0) {
+                       flags = LDB_FLAG_MOD_REPLACE;
+                       empty = 1;
+               }
+               if (strcmp(attr, "-") == 0) {
+                       flags = 0;
+                       continue;
+               }
+
+               if (empty) {
+                       if (msg_add_empty(msg, (char *)value.data, flags) != 0) {
+                               goto failed;
+                       }
+                       continue;
+               }
+               
+               el = &msg->elements[msg->num_elements-1];
+
+               if (msg->num_elements > 0 && strcmp(attr, el->name) == 0 &&
+                   flags == el->flags) {
+                       /* its a continuation */
+                       el->values = 
+                               realloc_p(el->values, struct ldb_val, el->num_values+1);
+                       if (!el->values) {
+                               goto failed;
+                       }
+                       el->values[el->num_values] = value;
+                       el->num_values++;
+               } else {
+                       /* its a new attribute */
+                       msg->elements = realloc_p(msg->elements, 
+                                                 struct ldb_message_element, 
+                                                 msg->num_elements+1);
+                       if (!msg->elements) {
+                               goto failed;
+                       }
+                       msg->elements[msg->num_elements].flags = flags;
+                       msg->elements[msg->num_elements].name = attr;
+                       el = &msg->elements[msg->num_elements];
+                       el->values = malloc_p(struct ldb_val);
+                       if (!el->values) {
+                               goto failed;
+                       }
+                       el->num_values = 1;
+                       el->values[0] = value;
+                       msg->num_elements++;
                }
-               msg->elements[msg->num_elements].flags = 0;
-               msg->elements[msg->num_elements].name = attr;
-               msg->elements[msg->num_elements].value = value;
-               msg->num_elements++;
        }
 
-       return msg;
+       return ldif;
 
 failed:
-       if (msg) ldif_read_free(msg);
+       if (ldif) ldif_read_free(ldif);
        return NULL;
 }
 
@@ -417,7 +564,7 @@ static int fgetc_file(void *private)
        return fgetc(state->f);
 }
 
-struct ldb_message *ldif_read_file(FILE *f)
+struct ldb_ldif *ldif_read_file(FILE *f)
 {
        struct ldif_read_file_state state;
        state.f = f;
@@ -441,7 +588,7 @@ static int fgetc_string(void *private)
        return EOF;
 }
 
-struct ldb_message *ldif_read_string(const char *s)
+struct ldb_ldif *ldif_read_string(const char *s)
 {
        struct ldif_read_string_state state;
        state.s = s;
@@ -468,9 +615,9 @@ static int fprintf_file(void *private, const char *fmt, ...)
        return ret;
 }
 
-int ldif_write_file(FILE *f, const struct ldb_message *msg)
+int ldif_write_file(FILE *f, const struct ldb_ldif *ldif)
 {
        struct ldif_write_file_state state;
        state.f = f;
-       return ldif_write(fprintf_file, &state, msg);
+       return ldif_write(fprintf_file, &state, ldif);
 }
index 35474d23c5149606bca41696033965325ce38181..12064bbf754656dd4c58bd5cb3c556e83879b84a 100644 (file)
@@ -73,7 +73,8 @@ struct ldb_val {
 struct ldb_message_element {
        unsigned int flags;
        char *name;
-       struct ldb_val value;
+       unsigned int num_values;
+       struct ldb_val *values;
 };
 
 
@@ -88,6 +89,20 @@ struct ldb_message {
        void *private; /* private to the backend */
 };
 
+enum ldb_changetype {
+       LDB_CHANGETYPE_NONE=0,
+       LDB_CHANGETYPE_ADD,
+       LDB_CHANGETYPE_DELETE,
+       LDB_CHANGETYPE_MODIFY
+};
+
+/*
+  a ldif record - from ldif_read
+*/
+struct ldb_ldif {
+       enum ldb_changetype changetype;
+       struct ldb_message msg;
+};
 
 enum ldb_scope {LDB_SCOPE_DEFAULT=-1, 
                LDB_SCOPE_BASE=0, 
@@ -196,9 +211,9 @@ const char *ldb_errstring(struct ldb_context *ldb);
 */
 int ldif_write(int (*fprintf_fn)(void *, const char *, ...), 
               void *private,
-              const struct ldb_message *msg);
-void ldif_read_free(struct ldb_message *msg);
-struct ldb_message *ldif_read(int (*fgetc_fn)(void *), void *private);
-struct ldb_message *ldif_read_file(FILE *f);
-struct ldb_message *ldif_read_string(const char *s);
-int ldif_write_file(FILE *f, const struct ldb_message *msg);
+              const struct ldb_ldif *ldif);
+void ldif_read_free(struct ldb_ldif *);
+struct ldb_ldif *ldif_read(int (*fgetc_fn)(void *), void *private);
+struct ldb_ldif *ldif_read_file(FILE *f);
+struct ldb_ldif *ldif_read_string(const char *s);
+int ldif_write_file(FILE *f, const struct ldb_ldif *msg);
index b2f768849775c10be4cd942d24d2ae288e6522db..e6cbb52cad8724440c0ab310b18b59c1e48a45dc 100644 (file)
@@ -37,7 +37,7 @@
 
 #if 0
 /*
-  we don't need this right now, but will once we add more backend 
+  we don't need this right now, but will once we add some backend 
   options
 */
 
@@ -110,13 +110,16 @@ static int lldb_delete(struct ldb_context *ldb, const char *dn)
 */
 static int lldb_msg_free(struct ldb_context *ldb, struct ldb_message *msg)
 {
-       int i;
+       int i, j;
        free(msg->dn);
        for (i=0;i<msg->num_elements;i++) {
                free(msg->elements[i].name);
-               if (msg->elements[i].value.data) {
-                       free(msg->elements[i].value.data);
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       if (msg->elements[i].values[j].data) {
+                               free(msg->elements[i].values[j].data);
+                       }
                }
+               free(msg->elements[i].values);
        }
        if (msg->elements) free(msg->elements);
        free(msg);
@@ -155,7 +158,7 @@ static int lldb_add_msg_attr(struct ldb_message *msg,
        }
 
        el = realloc_p(msg->elements, struct ldb_message_element, 
-                      msg->num_elements + count);
+                      msg->num_elements + 1);
        if (!el) {
                errno = ENOMEM;
                return -1;
@@ -163,22 +166,34 @@ static int lldb_add_msg_attr(struct ldb_message *msg,
 
        msg->elements = el;
 
+       el = &msg->elements[msg->num_elements];
+
+       el->name = strdup(attr);
+       if (!el->name) {
+               errno = ENOMEM;
+               return -1;
+       }
+       el->flags = 0;
+
+       el->num_values = 0;
+       el->values = malloc_array_p(struct ldb_val, count);
+       if (!el->values) {
+               errno = ENOMEM;
+               return -1;
+       }
+
        for (i=0;i<count;i++) {
-               msg->elements[msg->num_elements].name = strdup(attr);
-               if (!msg->elements[msg->num_elements].name) {
+               el->values[i].data = malloc(bval[i]->bv_len);
+               if (!el->values[i].data) {
                        return -1;
                }
-               msg->elements[msg->num_elements].value.data = malloc(bval[i]->bv_len);
-               if (!msg->elements[msg->num_elements].value.data) {
-                       free(msg->elements[msg->num_elements].name);
-                       return -1;
-               }
-               memcpy(msg->elements[msg->num_elements].value.data, 
-                      bval[i]->bv_val, bval[i]->bv_len);
-               msg->elements[msg->num_elements].value.length = bval[i]->bv_len;
-               msg->num_elements++;
+               memcpy(el->values[i].data, bval[i]->bv_val, bval[i]->bv_len);
+               el->values[i].length = bval[i]->bv_len;
+               el->num_values++;
        }
 
+       msg->num_elements++;
+
        return 0;
 }
 
@@ -309,7 +324,7 @@ static void lldb_mods_free(LDAPMod **mods)
 static LDAPMod **lldb_msg_to_mods(const struct ldb_message *msg, int use_flags)
 {
        LDAPMod **mods;
-       int i, num_vals, num_mods = 0;
+       int i, j, num_mods = 0;
 
        /* allocate maximum number of elements needed */
        mods = malloc_array_p(LDAPMod *, msg->num_elements+1);
@@ -320,30 +335,7 @@ static LDAPMod **lldb_msg_to_mods(const struct ldb_message *msg, int use_flags)
        mods[0] = NULL;
 
        for (i=0;i<msg->num_elements;i++) {
-
-               if (i > 0 && 
-                   (!use_flags || 
-                    (msg->elements[i].flags == msg->elements[i-1].flags)) &&
-                   strcmp(msg->elements[i].name, msg->elements[i-1].name) == 0) {
-                       struct berval **b;
-                       /* when attributes are repeated we need to extend the
-                          existing bvals array */
-                       b = realloc_p(mods[num_mods-1]->mod_vals.modv_bvals, 
-                                     struct berval *, num_vals+2);
-                       if (!b) {
-                               goto failed;
-                       }
-                       mods[num_mods-1]->mod_vals.modv_bvals = b;
-                       b[num_vals+1] = NULL;
-                       b[num_vals] = malloc_p(struct berval);
-                       if (!b[num_vals]) goto failed;
-                       b[num_vals]->bv_val = msg->elements[i].value.data;
-                       b[num_vals]->bv_len = msg->elements[i].value.length;
-                       num_vals++;
-                       continue;
-               }
-
-               num_vals = 1;
+               const struct ldb_message_element *el = &msg->elements[i];
 
                mods[num_mods] = malloc_p(LDAPMod);
                if (!mods[num_mods]) {
@@ -352,7 +344,7 @@ static LDAPMod **lldb_msg_to_mods(const struct ldb_message *msg, int use_flags)
                mods[num_mods+1] = NULL;
                mods[num_mods]->mod_op = LDAP_MOD_BVALUES;
                if (use_flags) {
-                       switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+                       switch (el->flags & LDB_FLAG_MOD_MASK) {
                        case LDB_FLAG_MOD_ADD:
                                mods[num_mods]->mod_op |= LDAP_MOD_ADD;
                                break;
@@ -364,18 +356,22 @@ static LDAPMod **lldb_msg_to_mods(const struct ldb_message *msg, int use_flags)
                                break;
                        }
                }
-               mods[num_mods]->mod_type = msg->elements[i].name;
-               mods[num_mods]->mod_vals.modv_bvals = malloc_array_p(struct berval *, 2);
+               mods[num_mods]->mod_type = el->name;
+               mods[num_mods]->mod_vals.modv_bvals = malloc_array_p(struct berval *, 
+                                                                    1+el->num_values);
                if (!mods[num_mods]->mod_vals.modv_bvals) {
                        goto failed;
                }
-               mods[num_mods]->mod_vals.modv_bvals[0] = malloc_p(struct berval);
-               if (!mods[num_mods]->mod_vals.modv_bvals[0]) {
-                       goto failed;
+
+               for (j=0;j<el->num_values;j++) {
+                       mods[num_mods]->mod_vals.modv_bvals[j] = malloc_p(struct berval);
+                       if (!mods[num_mods]->mod_vals.modv_bvals[j]) {
+                               goto failed;
+                       }
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_val = el->values[j].data;
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_len = el->values[j].length;
                }
-               mods[num_mods]->mod_vals.modv_bvals[0]->bv_val = msg->elements[i].value.data;
-               mods[num_mods]->mod_vals.modv_bvals[0]->bv_len = msg->elements[i].value.length;
-               mods[num_mods]->mod_vals.modv_bvals[1] = NULL;
+               mods[num_mods]->mod_vals.modv_bvals[j] = NULL;
                num_mods++;
        }
 
index dda80a6b2a9817e66e12ab0cf22e44b3b7632475..8cda8abff89d41f3b7dc40b5aed1fff6858a71f6 100644 (file)
@@ -74,13 +74,22 @@ static char *ldb_dn_key(const char *attr, const struct ldb_val *value)
 /*
   see if a attribute value is in the list of indexed attributes
 */
-static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr)
+static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr,
+                           int *v_idx)
 {
-       int i;
+       int i, j;
        for (i=0;i<msg->num_elements;i++) {
-               if (strcmp(msg->elements[i].name, "@IDXATTR") == 0 &&
-                   strcmp((char *)msg->elements[i].value.data, attr) == 0) {
-                       return i;
+               if (strcmp(msg->elements[i].name, "@IDXATTR") == 0) {
+                       const struct ldb_message_element *el = 
+                               &msg->elements[i];
+                       for (j=0;j<el->num_values;j++) {
+                               if (strcmp((char *)el->values[j].data, attr) == 0) {
+                                       if (v_idx) {
+                                               *v_idx = j;
+                                       }
+                                       return i;
+                               }
+                       }
                }
        }
        return -1;
@@ -95,7 +104,7 @@ static int ltdb_index_dn_simple(struct ldb_context *ldb,
                                struct dn_list *list)
 {
        char *dn = NULL;
-       int ret, i;
+       int ret, i, j;
        struct ldb_message msg;
 
        list->count = 0;
@@ -110,7 +119,7 @@ static int ltdb_index_dn_simple(struct ldb_context *ldb,
 
        /* if the attribute isn't in the list of indexed attributes then
           this node needs a full search */
-       if (ldb_msg_find_idx(index_list, tree->u.simple.attr) == -1) {
+       if (ldb_msg_find_idx(index_list, tree->u.simple.attr, NULL) == -1) {
                return -1;
        }
 
@@ -125,23 +134,30 @@ static int ltdb_index_dn_simple(struct ldb_context *ldb,
                return ret;
        }
 
-       list->dn = malloc_array_p(char *, msg.num_elements);
-       if (!list->dn) {
-               ltdb_search_dn1_free(ldb, &msg);
-       }
-
        for (i=0;i<msg.num_elements;i++) {
+               struct ldb_message_element *el;
+
                if (strcmp(msg.elements[i].name, "@IDX") != 0) {
                        continue;
                }
-               list->dn[list->count] = 
-                       strdup((char *)msg.elements[i].value.data);
-               if (!list->dn[list->count]) {
-                       dn_list_free(list);
-                       ltdb_search_dn1_free(ldb, &msg);
-                       return -1;
+
+               el = &msg.elements[i];
+
+               list->dn = malloc_array_p(char *, el->num_values);
+               if (!list->dn) {
+                       break;          
+               }
+
+               for (j=0;j<el->num_values;j++) {
+                       list->dn[list->count] = 
+                               strdup((char *)el->values[j].data);
+                       if (!list->dn[list->count]) {
+                               dn_list_free(list);
+                               ltdb_search_dn1_free(ldb, &msg);
+                               return -1;
+                       }
+                       list->count++;
                }
-               list->count++;
        }
 
        ltdb_search_dn1_free(ldb, &msg);
@@ -470,18 +486,76 @@ int ltdb_search_indexed(struct ldb_context *ldb,
        return ret;
 }
 
+/*
+  add a index element where this is the first indexed DN for this value
+*/
+static int ltdb_index_add1_new(struct ldb_context *ldb, 
+                              struct ldb_message *msg,
+                              struct ldb_message_element *el,
+                              const char *dn)
+{
+       struct ldb_message_element *el2;
+
+       /* add another entry */
+       el2 = realloc_p(msg->elements, struct ldb_message_element, msg->num_elements+1);
+       if (!el2) {
+               return -1;
+       }
+
+       msg->elements = el2;
+       msg->elements[msg->num_elements].name = "@IDX";
+       msg->elements[msg->num_elements].num_values = 0;
+       msg->elements[msg->num_elements].values = malloc_p(struct ldb_val);
+       if (!msg->elements[msg->num_elements].values) {
+               return -1;
+       }
+       msg->elements[msg->num_elements].values[0].length = strlen(dn);
+       msg->elements[msg->num_elements].values[0].data = dn;
+       msg->elements[msg->num_elements].num_values = 1;
+       msg->num_elements++;
+
+       return 0;
+}
+
+
+/*
+  add a index element where this is not the first indexed DN for this
+  value
+*/
+static int ltdb_index_add1_add(struct ldb_context *ldb, 
+                              struct ldb_message *msg,
+                              struct ldb_message_element *el,
+                              int idx,
+                              const char *dn)
+{
+       struct ldb_val *v2;
+
+       v2 = realloc_p(msg->elements[idx].values,
+                      struct ldb_val, 
+                      msg->elements[idx].num_values+1);
+       if (!v2) {
+               return -1;
+       }
+       msg->elements[idx].values = v2;
+
+       msg->elements[idx].values[msg->elements[idx].num_values].length = strlen(dn);
+       msg->elements[idx].values[msg->elements[idx].num_values].data = dn;
+       msg->elements[idx].num_values++;
+
+       return 0;
+}
+
 /*
   add an index entry for one message element
 */
 static int ltdb_index_add1(struct ldb_context *ldb, const char *dn, 
-                          struct ldb_message_element *el)
+                          struct ldb_message_element *el, int v_idx)
 {
        struct ldb_message msg;
        char *dn_key;
-       int ret;
-       struct ldb_message_element *el2;
+       int ret, i;
 
-       dn_key = ldb_dn_key(el->name, &el->value);
+       dn_key = ldb_dn_key(el->name, &el->values[v_idx]);
        if (!dn_key) {
                return -1;
        }
@@ -493,36 +567,37 @@ static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
        }
 
        if (ret == 0) {
-               msg.dn = dn_key;
+               msg.dn = strdup(dn_key);
+               if (!msg.dn) {
+                       free(dn_key);
+                       errno = ENOMEM;
+                       return -1;
+               }
                msg.num_elements = 0;
                msg.elements = NULL;
                msg.private = NULL;
        }
 
-       /* add another entry */
-       el2 = realloc_p(msg.elements, struct ldb_message_element, msg.num_elements+1);
-       if (!el2) {
-               if (ret == 1) {
-                       ltdb_search_dn1_free(ldb, &msg);
+       free(dn_key);
+
+       for (i=0;i<msg.num_elements;i++) {
+               if (strcmp("@IDX", msg.elements[i].name) == 0) {
+                       break;
                }
-               free(dn_key);
-               return -1;
        }
 
-       msg.elements = el2;
-       msg.elements[msg.num_elements].name = "@IDX";
-       msg.elements[msg.num_elements].value.length = strlen(dn);
-       msg.elements[msg.num_elements].value.data = dn;
-       msg.num_elements++;
-
-       ret = ltdb_store(ldb, &msg, TDB_REPLACE);
-
-       if (msg.num_elements == 1) {
-               free(msg.elements);
+       if (i == msg.num_elements) {
+               ret = ltdb_index_add1_new(ldb, &msg, el, dn);
        } else {
-               ltdb_search_dn1_free(ldb, &msg);
+               ret = ltdb_index_add1_add(ldb, &msg, el, i, dn);
+       }
+
+       if (ret == 0) {
+               ret = ltdb_store(ldb, &msg, TDB_REPLACE);
        }
 
+       ltdb_search_dn1_free(ldb, &msg);
+
        return ret;
 }
 
@@ -532,7 +607,7 @@ static int ltdb_index_add1(struct ldb_context *ldb, const char *dn,
 */
 int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
 {
-       int ret, i;
+       int ret, i, j;
        struct ldb_message index_list;
 
        /* find the list of indexed fields */   
@@ -543,17 +618,21 @@ int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
        }
 
        for (i=0;i<msg->num_elements;i++) {
-               ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
+               ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
                if (ret == -1) {
                        continue;
                }
-               ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i]);
-               if (ret == -1) {
-                       ltdb_search_dn1_free(ldb, &index_list);
-                       return -1;
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       ret = ltdb_index_add1(ldb, msg->dn, &msg->elements[i], j);
+                       if (ret == -1) {
+                               ltdb_search_dn1_free(ldb, &index_list);
+                               return -1;
+                       }
                }
        }
 
+       ltdb_search_dn1_free(ldb, &index_list);
+
        return 0;
 }
 
@@ -562,13 +641,13 @@ int ltdb_index_add(struct ldb_context *ldb, const struct ldb_message *msg)
   delete an index entry for one message element
 */
 static int ltdb_index_del1(struct ldb_context *ldb, const char *dn, 
-                          struct ldb_message_element *el)
+                          struct ldb_message_element *el, int v_idx)
 {
        struct ldb_message msg;
        char *dn_key;
-       int ret, i;
+       int ret, i, j;
 
-       dn_key = ldb_dn_key(el->name, &el->value);
+       dn_key = ldb_dn_key(el->name, &el->values[v_idx]);
        if (!dn_key) {
                return -1;
        }
@@ -586,19 +665,22 @@ static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
                return 0;
        }
 
-       i = ldb_msg_find_idx(&msg, dn);
+       i = ldb_msg_find_idx(&msg, dn, &j);
        if (i == -1) {
                /* it ain't there. hmmm */
                ltdb_search_dn1_free(ldb, &msg);
                return 0;
        }
 
-       if (i != msg.num_elements - 1) {
-               memmove(&msg.elements[i], &msg.elements[i+1], sizeof(msg.elements[i]));
+       if (j != msg.elements[i].num_values - 1) {
+               memmove(&msg.elements[i].values[j], 
+                       &msg.elements[i].values[j+1], 
+                       (msg.elements[i].num_values-1) * 
+                       sizeof(msg.elements[i].values[0]));
        }
-       msg.num_elements--;
+       msg.elements[i].num_values--;
 
-       if (msg.num_elements == 0) {
+       if (msg.elements[i].num_values == 0) {
                ret = ltdb_delete_noindex(ldb, dn_key);
        } else {
                ret = ltdb_store(ldb, &msg, TDB_REPLACE);
@@ -615,7 +697,7 @@ static int ltdb_index_del1(struct ldb_context *ldb, const char *dn,
 */
 int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
 {
-       int ret, i;
+       int ret, i, j;
        struct ldb_message index_list;
 
        /* find the list of indexed fields */   
@@ -626,14 +708,16 @@ int ltdb_index_del(struct ldb_context *ldb, const struct ldb_message *msg)
        }
 
        for (i=0;i<msg->num_elements;i++) {
-               ret = ldb_msg_find_idx(&index_list, msg->elements[i].name);
+               ret = ldb_msg_find_idx(&index_list, msg->elements[i].name, NULL);
                if (ret == -1) {
                        continue;
                }
-               ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i]);
-               if (ret == -1) {
-                       ltdb_search_dn1_free(ldb, &index_list);
-                       return -1;
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       ret = ltdb_index_del1(ldb, msg->dn, &msg->elements[i], j);
+                       if (ret == -1) {
+                               ltdb_search_dn1_free(ldb, &index_list);
+                               return -1;
+                       }
                }
        }
 
index 89d204f56a62c3fda644c9d1753f7f95deb9c19a..6f29726ee7cf67c0576332c6faf8f80c5051c43c 100644 (file)
@@ -39,7 +39,7 @@
   see if two ldb_val structures contain the same data
   return 1 for a match, 0 for a mis-match
 */
-static int ldb_val_equal(struct ldb_val *v1, struct ldb_val *v2)
+int ldb_val_equal(const struct ldb_val *v1, const struct ldb_val *v2)
 {
        if (v1->length != v2->length) return 0;
 
@@ -108,7 +108,7 @@ static int match_leaf(struct ldb_context *ldb,
                      const char *base,
                      enum ldb_scope scope)
 {
-       int i;
+       int i, j;
 
        if (!scope_match(msg->dn, base, scope)) {
                return 0;
@@ -122,10 +122,16 @@ static int match_leaf(struct ldb_context *ldb,
        }
 
        for (i=0;i<msg->num_elements;i++) {
-               if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0 &&
-                   (strcmp(tree->u.simple.value.data, "*") == 0 ||
-                    ldb_val_equal(&msg->elements[i].value, &tree->u.simple.value))) {
-                       return 1;
+               if (strcmp(msg->elements[i].name, tree->u.simple.attr) == 0) {
+                       if (strcmp(tree->u.simple.value.data, "*") == 0) {
+                               return 1;
+                       }
+                       for (j=0;j<msg->elements[i].num_values;j++) {
+                               if (ldb_val_equal(&msg->elements[i].values[j], 
+                                                 &tree->u.simple.value)) {
+                                       return 1;
+                               }
+                       }
                }
        }
 
index b0c825e8e212c548c915a9ff149e8a3b6c9f8ee1..1b0c8a18577b4d1dd3b3627ea9275c4555553202 100644 (file)
 /*
   pack a ldb message into a linear buffer in a TDB_DATA
 
+  note that this routine avoids saving elements with zero values,
+  as these are equivalent to having no element
+
   caller frees the data buffer after use
 */
 int ltdb_pack_data(struct ldb_context *ctx,
                   const struct ldb_message *message,
                   struct TDB_DATA *data)
 {
-       int i;
+       int i, j;
        size_t size;
        char *p;
 
@@ -55,8 +58,13 @@ int ltdb_pack_data(struct ldb_context *ctx,
        size = 8;
 
        for (i=0;i<message->num_elements;i++) {
-               size += 1 + strlen(message->elements[i].name);
-               size += 4 + message->elements[i].value.length + 1;
+               if (message->elements[i].num_values == 0) {
+                       continue;
+               }
+               size += 1 + strlen(message->elements[i].name) + 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       size += 4 + message->elements[i].values[j].length + 1;
+               }
        }
 
        /* allocate it */
@@ -73,28 +81,50 @@ int ltdb_pack_data(struct ldb_context *ctx,
        p += 8;
        
        for (i=0;i<message->num_elements;i++) {
-               size_t len = strlen(message->elements[i].name);
+               size_t len;
+               if (message->elements[i].num_values == 0) {
+                       continue;
+               }
+               len = strlen(message->elements[i].name);
                memcpy(p, message->elements[i].name, len+1);
                p += len + 1;
-               SIVAL(p, 0, message->elements[i].value.length);
-               memcpy(p+4, message->elements[i].value.data, 
-                      message->elements[i].value.length);
-               p[4+message->elements[i].value.length] = 0;
-               p += 4 + message->elements[i].value.length + 1;
+               SIVAL(p, 0, message->elements[i].num_values);
+               p += 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       SIVAL(p, 0, message->elements[i].values[j].length);
+                       memcpy(p+4, message->elements[i].values[j].data, 
+                              message->elements[i].values[j].length);
+                       p[4+message->elements[i].values[j].length] = 0;
+                       p += 4 + message->elements[i].values[j].length + 1;
+               }
        }
 
        return 0;
 }
 
+/*
+  free the memory allocated from a ltdb_unpack_data()
+*/
+void ltdb_unpack_data_free(struct ldb_message *message)
+{
+       int i;
+
+       for (i=0;i<message->num_elements;i++) {
+               if (message->elements[i].values) free(message->elements[i].values);
+       }
+       if (message->elements) free(message->elements);
+}
+
 
 /*
   unpack a ldb message from a linear buffer in TDB_DATA
 
   note that this does not fill in the class and key elements
 
-  caller frees. Memory for the elements[] array is malloced, 
-  but the memory for the elements is re-used from the TDB_DATA
-  data. This means the caller only has to free the elements array
+  caller frees. Memory for the elements[] and values[] arrays are
+  malloced, but the memory for the elements is re-used from the
+  TDB_DATA data. This means the caller only has to free the elements
+  and values arrays. This can be done with ltdb_unpack_data_free()
 */
 int ltdb_unpack_data(struct ldb_context *ctx,
                     const struct TDB_DATA *data,
@@ -102,7 +132,7 @@ int ltdb_unpack_data(struct ldb_context *ctx,
 {
        char *p;
        unsigned int remaining;
-       int i;
+       int i, j;
 
        message->elements = NULL;
 
@@ -145,30 +175,44 @@ int ltdb_unpack_data(struct ldb_context *ctx,
 
        for (i=0;i<message->num_elements;i++) {
                size_t len;
-               if (remaining < 6) {
+               if (remaining < 10) {
                        errno = EIO;
                        goto failed;
                }
                len = strnlen(p, remaining-6);
+               message->elements[i].flags = 0;
                message->elements[i].name = p;
                remaining -= len + 1;
                p += len + 1;
-               len = IVAL(p, 0);
-               if (len > remaining-5) {
-                       errno = EIO;
-                       goto failed;
+               message->elements[i].num_values = IVAL(p, 0);
+               message->elements[i].values = NULL;
+               if (message->elements[i].num_values != 0) {
+                       message->elements[i].values = malloc_array_p(struct ldb_val, 
+                                                                    message->elements[i].num_values);
+                       if (!message->elements[i].values) {
+                               errno = ENOMEM;
+                               goto failed;
+                       }
+               }
+               p += 4;
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       len = IVAL(p, 0);
+                       if (len > remaining-5) {
+                               errno = EIO;
+                               goto failed;
+                       }
+
+                       message->elements[i].values[j].length = len;
+                       message->elements[i].values[j].data = p+4;
+                       remaining -= len+4+1;
+                       p += len+4+1;
                }
-               message->elements[i].value.length = len;
-               message->elements[i].value.data = p+4;
-               remaining -= len+4+1;
-               p += len+4+1;
        }
 
        return 0;
 
 failed:
-       if (message->elements) {
-               free(message->elements);
-       }
+       ltdb_unpack_data_free(message);
+
        return -1;
 }
index 952053c920ffd5871d19fc04bc6d26a9e8e7e47e..d7a7b7ffbd53da8066eae3c30d8d7c202ea54119 100644 (file)
 */
 static void msg_free_all_parts(struct ldb_message *msg)
 {
-       int i;
+       int i, j;
        if (msg->dn) free(msg->dn);
        for (i=0;i<msg->num_elements;i++) {
                if (msg->elements[i].name) free(msg->elements[i].name);
-               if (msg->elements[i].value.data) free(msg->elements[i].value.data);
+               for (j=0;j<msg->elements[i].num_values;j++) {
+                       if (msg->elements[i].values[j].data) 
+                               free(msg->elements[i].values[j].data);
+               }
+               if (msg->elements[i].values) free(msg->elements[i].values);
        }
        free(msg->elements);
        free(msg);
@@ -53,6 +57,7 @@ static void msg_free_all_parts(struct ldb_message *msg)
 
 /*
   TODO: this should take advantage of the sorted nature of the message
+
   return index of the attribute, or -1 if not found
 */
 int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr)
@@ -69,7 +74,7 @@ int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr)
 /*
   duplicate a ldb_val structure
 */
-static struct ldb_val ldb_val_dup(const struct ldb_val *v)
+struct ldb_val ldb_val_dup(const struct ldb_val *v)
 {
        struct ldb_val v2;
        v2.length = v->length;
@@ -98,7 +103,8 @@ static struct ldb_val ldb_val_dup(const struct ldb_val *v)
 */
 static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el)
 {
-       struct ldb_message_element *e2;
+       int i;
+       struct ldb_message_element *e2, *elnew;
 
        e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1);
        if (!e2) {
@@ -106,15 +112,31 @@ static int msg_add_element(struct ldb_message *ret, const struct ldb_message_ele
        }
        ret->elements = e2;
        
-       e2[ret->num_elements].name = strdup(el->name);
-       if (!e2[ret->num_elements].name) {
+       elnew = &e2[ret->num_elements];
+
+       elnew->name = strdup(el->name);
+       if (!elnew->name) {
                return -1;
        }
-       e2[ret->num_elements].value = ldb_val_dup(&el->value);
-       if (e2[ret->num_elements].value.length != el->value.length) {
-               return -1;
+
+       if (el->num_values) {
+               elnew->values = malloc_array_p(struct ldb_val, el->num_values);
+               if (!elnew->values) {
+                       return -1;
+               }
+       } else {
+               elnew->values = NULL;
        }
 
+       for (i=0;i<el->num_values;i++) {
+               elnew->values[i] = ldb_val_dup(&el->values[i]);
+               if (elnew->values[i].length != el->values[i].length) {
+                       return -1;
+               }
+       }
+
+       elnew->num_values = el->num_values;
+
        ret->num_elements++;
 
        return 0;
@@ -215,8 +237,12 @@ int ltdb_has_wildcard(const struct ldb_val *val)
 */
 void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
 {
-       free(msg->dn);
-       free(msg->private);
+       int i;
+       if (msg->dn) free(msg->dn);
+       if (msg->private) free(msg->private);
+       for (i=0;i<msg->num_elements;i++) {
+               if (msg->elements[i].values) free(msg->elements[i].values);
+       }
        if (msg->elements) free(msg->elements);
 }
 
@@ -375,7 +401,7 @@ static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, voi
        /* see if it matches the given expression */
        if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree, 
                               sinfo->base, sinfo->scope)) {
-               if (msg.elements) free(msg.elements);
+               ltdb_unpack_data_free(&msg);
                return 0;
        }
 
@@ -385,7 +411,7 @@ static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, voi
                sinfo->failures++;
        }
 
-       if (msg.elements) free(msg.elements);
+       ltdb_unpack_data_free(&msg);
 
        return ret;
 }
index 17931352f7569211207af86fbb527d19d02e3d79..95dce498f12ff47720e5a1699a9f90033ca5ab33 100644 (file)
@@ -58,6 +58,44 @@ struct TDB_DATA ltdb_key(const char *dn)
        return key;
 }
 
+/*
+  lock the database for write - currently a single lock is used
+*/
+static int ltdb_lock(struct ldb_context *ldb)
+{
+       struct ltdb_private *ltdb = ldb->private;
+       TDB_DATA key;
+       int ret;
+
+       key = ltdb_key("LDBLOCK");
+       if (!key.dptr) {
+               return -1;
+       }
+
+       ret = tdb_chainlock(ltdb->tdb, key);
+
+       free(key.dptr);
+
+       return ret;
+}
+
+/*
+  unlock the database after a ltdb_lock()
+*/
+static void ltdb_unlock(struct ldb_context *ldb)
+{
+       struct ltdb_private *ltdb = ldb->private;
+       TDB_DATA key;
+
+       key = ltdb_key("LDBLOCK");
+       if (!key.dptr) {
+               return;
+       }
+
+       tdb_chainunlock(ltdb->tdb, key);
+
+       free(key.dptr);
+}
 
 /*
   store a record into the db
@@ -102,7 +140,17 @@ done:
 */
 static int ltdb_add(struct ldb_context *ldb, const struct ldb_message *msg)
 {
-       return ltdb_store(ldb, msg, TDB_INSERT);
+       int ret;
+
+       if (ltdb_lock(ldb) != 0) {
+               return -1;
+       }
+       
+       ret = ltdb_store(ldb, msg, TDB_INSERT);
+
+       ltdb_unlock(ldb);
+
+       return ret;
 }
 
 
@@ -135,18 +183,22 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
        int ret;
        struct ldb_message msg;
 
+       if (ltdb_lock(ldb) != 0) {
+               return -1;
+       }
+
        /* in case any attribute of the message was indexed, we need
           to fetch the old record */
        ret = ltdb_search_dn1(ldb, dn, &msg);
        if (ret != 1) {
                /* not finding the old record is an error */
-               return -1;
+               goto failed;
        }
 
        ret = ltdb_delete_noindex(ldb, dn);
        if (ret == -1) {
                ltdb_search_dn1_free(ldb, &msg);
-               return -1;
+               goto failed;
        }
 
        /* remove any indexed attributes */
@@ -154,57 +206,244 @@ static int ltdb_delete(struct ldb_context *ldb, const char *dn)
 
        ltdb_search_dn1_free(ldb, &msg);
 
+       ltdb_unlock(ldb);
        return ret;
+
+failed:
+       ltdb_unlock(ldb);
+       return -1;
+}
+
+
+/*
+  find an element by attribute name. At the moment this does a linear search, it should
+  be re-coded to use a binary search once all places that modify records guarantee
+  sorted order
+
+  return the index of the first matching element if found, otherwise -1
+*/
+static int find_element(const struct ldb_message *msg, const char *name)
+{
+       int i;
+       for (i=0;i<msg->num_elements;i++) {
+               if (strcmp(msg->elements[i].name, name) == 0) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+
+/*
+  add an element to an existing record. Assumes a elements array that we
+  can call re-alloc on, and assumed that we can re-use the data pointers from the 
+  passed in additional values. Use with care!
+
+  returns 0 on success, -1 on failure (and sets errno)
+*/
+static int msg_add_element(struct ldb_message *msg, struct ldb_message_element *el)
+{
+       struct ldb_message_element *e2;
+       int i;
+
+       e2 = realloc_p(msg->elements, struct ldb_message_element, 
+                      msg->num_elements+1);
+       if (!e2) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       msg->elements = e2;
+
+       e2 = &msg->elements[msg->num_elements];
+
+       e2->name = el->name;
+       e2->flags = el->flags;
+       e2->values = NULL;
+       if (el->num_values != 0) {
+               e2->values = malloc_array_p(struct ldb_val, el->num_values);
+               if (!e2->values) {
+                       free(e2->name);
+                       errno = ENOMEM;
+                       return -1;
+               }
+       }
+       for (i=0;i<el->num_values;i++) {
+               e2->values[i] = el->values[i];
+       }
+       e2->num_values = el->num_values;
+
+       msg->num_elements++;
+
+       return 0;
+}
+
+/*
+  delete all elements having a specified attribute name
+*/
+static int msg_delete_attribute(struct ldb_message *msg, const char *name)
+{
+       int i, count=0;
+       struct ldb_message_element *el2;
+
+       el2 = malloc_array_p(struct ldb_message_element, msg->num_elements);
+       if (!el2) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       for (i=0;i<msg->num_elements;i++) {
+               if (strcmp(msg->elements[i].name, name) != 0) {
+                       el2[count++] = msg->elements[i];
+               } else {
+                       if (msg->elements[i].values) free(msg->elements[i].values);
+               }
+       }
+
+       msg->num_elements = count;
+       if (msg->elements) free(msg->elements);
+       msg->elements = el2;
+
+       return 0;
 }
 
+/*
+  delete all elements matching an attribute name/value 
+
+  return 0 on success, -1 on failure
+*/
+static int msg_delete_element(struct ldb_message *msg, 
+                             const char *name,
+                             const struct ldb_val *val)
+{
+       int i;
+       struct ldb_message_element *el;
+
+       i = find_element(msg, name);
+       if (i == -1) {
+               return -1;
+       }
+
+       el = &msg->elements[i];
+
+       for (i=0;i<el->num_values;i++) {
+               if (ldb_val_equal(&el->values[i], val)) {
+                       if (i<el->num_values-1) {
+                               memmove(&el->values[i], &el->values[i+1],
+                                       sizeof(el->values[i])*el->num_values-(i+1));
+                       }
+                       el->num_values--;
+                       return 0;
+               }
+       }
+       
+       return -1;
+}
 
 /*
   modify a record
+
+  yuck - this is O(n^2). Luckily n is usually small so we probably
+  get away with it, but if we ever have really large attribute lists 
+  then we'll need to look at this again
 */
 static int ltdb_modify(struct ldb_context *ldb, const struct ldb_message *msg)
 {
        struct ltdb_private *ltdb = ldb->private;
        TDB_DATA tdb_key, tdb_data;
        struct ldb_message msg2;
-       int ret;
+       int ret, i, j;
+
+       if (ltdb_lock(ldb) != 0) {
+               return -1;
+       }
 
        tdb_key = ltdb_key(msg->dn);
        if (!tdb_key.dptr) {
-               return -1;
+               goto unlock_fail;
        }
 
        tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
        if (!tdb_data.dptr) {
                free(tdb_key.dptr);
-               return -1;
+               goto unlock_fail;
        }
 
        ret = ltdb_unpack_data(ldb, &tdb_data, &msg2);
        if (ret == -1) {
                free(tdb_key.dptr);
                free(tdb_data.dptr);
-               return -1;
+               goto unlock_fail;
        }
 
-#if 0
+       msg2.dn = msg->dn;
+
        for (i=0;i<msg->num_elements;i++) {
                switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+
                case LDB_FLAG_MOD_ADD:
+                       /* add this element to the message. fail if it
+                          already exists */
                        ret = find_element(&msg2, msg->elements[i].name);
                        if (ret != -1) {
                                errno = EEXIST;
                                goto failed;
                        }
-                       
+                       if (msg_add_element(&msg2, &msg->elements[i]) != 0) {
+                               goto failed;
+                       }
+                       break;
+
+               case LDB_FLAG_MOD_REPLACE:
+                       /* replace all elements of this attribute name with the elements
+                          listed */
+                       if (msg_delete_attribute(&msg2, msg->elements[i].name) != 0) {
+                               goto failed;
+                       }
+                       /* add the replacement element */
+                       if (msg_add_element(&msg2, &msg->elements[i]) != 0) {
+                               goto failed;
+                       }
+                       break;
+
+               case LDB_FLAG_MOD_DELETE:
+                       /* we could be being asked to delete all
+                          values or just some values */
+                       if (msg->elements[i].num_values == 0) {
+                               if (msg_delete_attribute(&msg2, 
+                                                         msg->elements[i].name) != 0) {
+                                       goto failed;
+                               }
+                               break;
+                       }
+                       for (j=0;j<msg->elements[i].num_values;j++) {
+                               if (msg_delete_element(&msg2, 
+                                                       msg->elements[i].name,
+                                                       &msg->elements[i].values[j]) != 0) {
+                                       goto failed;
+                               }
+                       }
+                       break;
                }
        }
 
-failed:
-#endif
+       /* we've made all the mods - save the modified record back into the database */
+       ret = ltdb_store(ldb, &msg2, TDB_MODIFY);
+
+       free(tdb_key.dptr);
+       free(tdb_data.dptr);
+       ltdb_unpack_data_free(&msg2);
+       ltdb_unlock(ldb);
 
+       return ret;
+
+failed:
        free(tdb_key.dptr);
        free(tdb_data.dptr);
-       if (msg2.elements) free(msg2.elements);
+       ltdb_unpack_data_free(&msg2);
+
+unlock_fail:
+       ltdb_unlock(ldb);
        
        return -1;
 }
diff --git a/source4/lib/ldb/ldb_tdb/ldbadd.c b/source4/lib/ldb/ldb_tdb/ldbadd.c
deleted file mode 100644 (file)
index 3959a17..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
- /* 
-   Unix SMB/CIFS implementation.
-
-   a utility to add elements to a ldb
-
-   Copyright (C) Andrew Tridgell 2004
-   
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-   
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "includes.h"
-
- int main(void)
-{
-       static struct ldb_context *ldb;
-       struct ldb_message *msg;
-       int ret;
-       int count=0, failures=0;
-
-       ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
-
-       if (!ldb) {
-               perror("ldb_connect");
-               exit(1);
-       }
-
-       while ((msg = ldif_read(stdin))) {
-               ret = ldb->ops->add(ldb, msg);
-               if (ret != 0) {
-                       fprintf(stderr, "Failed to add record '%s'\n", msg->dn);
-                       failures++;
-               } else {
-                       count++;
-               }
-               ldif_read_free(msg);
-       }
-
-       ldb->ops->close(ldb);
-
-       printf("Added %d records with %d failures\n", count, failures);
-       
-       return 0;
-}
diff --git a/source4/lib/ldb/ldb_tdb/ldbdel.c b/source4/lib/ldb/ldb_tdb/ldbdel.c
deleted file mode 100644 (file)
index 8f8a039..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
- /* 
-   Unix SMB/CIFS implementation.
-
-   a utility to delete elements in a ldb
-
-   Copyright (C) Andrew Tridgell 2004
-   
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-   
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "includes.h"
-
- int main(int argc, const char *argv[])
-{
-       static struct ldb_context *ldb;
-       int ret, i;
-
-       if (argc < 2) {
-               printf("Usage: ldbdel <dn...>\n");
-               exit(1);
-       }
-
-       ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
-       if (!ldb) {
-               perror("ldb_connect");
-               exit(1);
-       }
-
-       for (i=1;i<argc;i++) {
-               ret = ldb->ops->delete(ldb, argv[i]);
-               if (ret != 0) {
-                       printf("delete of '%s' failed\n", argv[i]);
-               }
-       }
-
-       ldb->ops->close(ldb);
-       return 0;
-}
diff --git a/source4/lib/ldb/ldb_tdb/ldbsearch.c b/source4/lib/ldb/ldb_tdb/ldbsearch.c
deleted file mode 100644 (file)
index a6d63e7..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
- /* 
-   Unix SMB/CIFS implementation.
-
-   simple ldb search tool
-
-   Copyright (C) Andrew Tridgell 2004
-   
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-   
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "includes.h"
-
- int main(int argc, const char *argv[])
-{
-       static struct ldb_context *ldb;
-       struct ldb_message **msgs;
-       int ret, i;
-       const char *expression;
-       const char **attrs = NULL;
-
-       if (argc < 2) {
-               printf("Usage: ldbsearch <expression> [attrs...]\n");
-               exit(1);
-       }
-
-       if (argc > 2) {
-               attrs = argv+2;
-       }
-
-       expression = argv[1];
-
-       ldb = ltdb_connect("tdb://test.ldb", 0, NULL);
-
-       if (!ldb) {
-               perror("ldb_connect");
-               exit(1);
-       }
-
-       ret = ldb->ops->search(ldb, expression, attrs, &msgs);
-
-       if (ret == -1) {
-               printf("search failed\n");
-               exit(1);
-       }
-
-       printf("# returned %d records\n", ret);
-
-       for (i=0;i<ret;i++) {
-               printf("# record %d\n", i+1);
-               ldif_write(stdout, msgs[i]);
-       }
-
-       ret = ldb->ops->search_free(ldb, msgs);
-       if (ret == -1) {
-               fprintf(stderr, "search_free failed\n");
-               exit(1);
-       }
-
-       ldb->ops->close(ldb);
-       return 0;
-}
diff --git a/source4/lib/ldb/tests/test-generic.sh b/source4/lib/ldb/tests/test-generic.sh
new file mode 100644 (file)
index 0000000..41f5707
--- /dev/null
@@ -0,0 +1,8 @@
+echo "Adding base elements"
+bin/ldbadd < tests/test.ldif
+
+echo "Modifying elements"
+bin/ldbmodify < tests/test-modify.ldif
+
+echo "Showing modified record"
+bin/ldbsearch '(uid=uham)'
diff --git a/source4/lib/ldb/tests/test-ldap.sh b/source4/lib/ldb/tests/test-ldap.sh
new file mode 100755 (executable)
index 0000000..29b40ff
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+tests/init_slapd.sh
+tests/start_slapd.sh
+
+export LDB_URL=`tests/ldapi_url.sh`
+
+. tests/test-generic.sh
diff --git a/source4/lib/ldb/tests/test-modify.ldif b/source4/lib/ldb/tests/test-modify.ldif
new file mode 100644 (file)
index 0000000..521c6d8
--- /dev/null
@@ -0,0 +1,14 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=US
+changetype: modify
+add: drink
+drink: mango lassi
+-
+delete: pager
+-
+replace: telephonenumber
+telephonenumber: +61 2 6260 6012
+telephonenumber: +61 412 666 929
+-
+delete: telephonenumber
+telephonenumber: +61 2 6260 6012
diff --git a/source4/lib/ldb/tests/test-tdb.sh b/source4/lib/ldb/tests/test-tdb.sh
new file mode 100755 (executable)
index 0000000..1e21acc
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+
+export LDB_URL="tdb://test.ldb"
+
+rm -f test.ldb
+
+. tests/test-generic.sh
index 3eb7cb8de2171680045dc417c8481f9038dc5c9c..92ed29e6b89a9ede146591850a851c21ea1049a7 100644 (file)
@@ -37,7 +37,7 @@
  int main(void)
 {
        static struct ldb_context *ldb;
-       struct ldb_message *msg;
+       struct ldb_ldif *ldif;
        int ret;
        int count=0, failures=0;
        const char *ldb_url;
                exit(1);
        }
 
-       while ((msg = ldif_read_file(stdin))) {
-               ret = ldb_add(ldb, msg);
+       while ((ldif = ldif_read_file(stdin))) {
+
+               if (ldif->changetype != LDB_CHANGETYPE_ADD &&
+                   ldif->changetype != LDB_CHANGETYPE_NONE) {
+                       fprintf(stderr, "Only CHANGETYPE_ADD records allowed\n");
+                       break;
+               }
+
+               ret = ldb_add(ldb, &ldif->msg);
                if (ret != 0) {
                        fprintf(stderr, "ERR: \"%s\" on DN %s\n", 
-                               ldb_errstring(ldb), msg->dn);
+                               ldb_errstring(ldb), ldif->msg.dn);
                        failures++;
                } else {
                        count++;
                }
-               ldif_read_free(msg);
+               ldif_read_free(ldif);
        }
 
        ldb_close(ldb);
diff --git a/source4/lib/ldb/tools/ldbmodify.c b/source4/lib/ldb/tools/ldbmodify.c
new file mode 100644 (file)
index 0000000..e1cff65
--- /dev/null
@@ -0,0 +1,85 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldbmodify
+ *
+ *  Description: utility to modify records - modelled on ldapmodify
+ *
+ *  Author: Andrew Tridgell
+ */
+
+#include "includes.h"
+
+ int main(void)
+{
+       static struct ldb_context *ldb;
+       struct ldb_ldif *ldif;
+       int ret;
+       int count=0, failures=0;
+       const char *ldb_url;
+
+       ldb_url = getenv("LDB_URL");
+       if (!ldb_url) {
+               ldb_url = "tdb://test.ldb";
+       }
+
+       ldb = ldb_connect(ldb_url, 0, NULL);
+
+       if (!ldb) {
+               perror("ldb_connect");
+               exit(1);
+       }
+
+       while ((ldif = ldif_read_file(stdin))) {
+               switch (ldif->changetype) {
+               case LDB_CHANGETYPE_NONE:
+               case LDB_CHANGETYPE_ADD:
+                       ret = ldb_add(ldb, &ldif->msg);
+                       break;
+               case LDB_CHANGETYPE_DELETE:
+                       ret = ldb_delete(ldb, ldif->msg.dn);
+                       break;
+               case LDB_CHANGETYPE_MODIFY:
+                       ret = ldb_modify(ldb, &ldif->msg);
+                       break;
+               }
+               if (ret != 0) {
+                       fprintf(stderr, "ERR: \"%s\" on DN %s\n", 
+                               ldb_errstring(ldb), ldif->msg.dn);
+                       failures++;
+               } else {
+                       count++;
+               }
+               ldif_read_free(ldif);
+       }
+
+       ldb_close(ldb);
+
+       printf("Modified %d records with %d failures\n", count, failures);
+       
+       return 0;
+}
index d7d3c83162cf2f27b5b9e7cb6192fb775c86b68e..f4eb8f00db3c4562b71d98f0d6b52da3f0136db5 100644 (file)
@@ -45,7 +45,7 @@
        const char *ldb_url;
        const char *basedn = NULL;
        int opt;
-       enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+       enum ldb_scope scope = LDB_SCOPE_SUBTREE;
 
        ldb_url = getenv("LDB_URL");
        if (!ldb_url) {
        ret = ldb_search(ldb, basedn, scope, expression, attrs, &msgs);
 
        if (ret == -1) {
-               printf("search failed\n");
+               printf("search failed - %s\n", ldb_errstring(ldb));
                exit(1);
        }
 
        printf("# returned %d records\n", ret);
 
        for (i=0;i<ret;i++) {
+               struct ldb_ldif ldif;
                printf("# record %d\n", i+1);
-               ldif_write_file(stdout, msgs[i]);
+
+               ldif.changetype = LDB_CHANGETYPE_NONE;
+               ldif.msg = *msgs[i];
+
+               ldif_write_file(stdout, &ldif);
        }
 
        if (ret > 0) {