summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/libcli/ldap/config.mk3
-rw-r--r--source4/libcli/ldap/ldap.c411
-rw-r--r--source4/libcli/ldap/ldap.h29
-rw-r--r--source4/libcli/ldap/ldap_ldif.c409
4 files changed, 440 insertions, 412 deletions
diff --git a/source4/libcli/ldap/config.mk b/source4/libcli/ldap/config.mk
index 397cfe0b72..ac047214ca 100644
--- a/source4/libcli/ldap/config.mk
+++ b/source4/libcli/ldap/config.mk
@@ -1,6 +1,7 @@
#################################
# Start SUBSYSTEM LIBCLI_LDAP
[SUBSYSTEM::LIBCLI_LDAP]
-ADD_OBJ_FILES = libcli/ldap/ldap.o
+ADD_OBJ_FILES = libcli/ldap/ldap.o \
+ libcli/ldap/ldap_ldif.o
# End SUBSYSTEM LIBCLI_LDAP
#################################
diff --git a/source4/libcli/ldap/ldap.c b/source4/libcli/ldap/ldap.c
index b319299b1e..123def9416 100644
--- a/source4/libcli/ldap/ldap.c
+++ b/source4/libcli/ldap/ldap.c
@@ -33,35 +33,6 @@
*
***************************************************************************/
-/* Hmm. A blob might be more appropriate here :-) */
-
-struct ldap_val {
- unsigned int length;
- void *data;
-};
-
-enum ldap_parse_op {LDAP_OP_SIMPLE, LDAP_OP_AND, LDAP_OP_OR, LDAP_OP_NOT};
-
-struct ldap_parse_tree {
- enum ldap_parse_op operation;
- union {
- struct {
- char *attr;
- struct ldap_val value;
- } simple;
- struct {
- unsigned int num_elements;
- struct ldap_parse_tree **elements;
- } list;
- struct {
- struct ldap_parse_tree *child;
- } not;
- } u;
-};
-
-#define LDAP_ALL_SEP "()&|=!"
-#define LDAP_CONNECTION_TIMEOUT 10000
-
/*
return next token element. Caller frees
*/
@@ -379,372 +350,6 @@ static BOOL ldap_push_filter(ASN1_DATA *data, struct ldap_parse_tree *tree)
return !data->has_error;
}
-/****************************************************************************
- *
- * LDIF parser
- *
- * Shamelessly stolen and adapted from Samba 4.
- *
- ***************************************************************************/
-
-/*
- pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
- this routine removes any RFC2849 continuations and comments
-
- caller frees
-*/
-static char *next_chunk(TALLOC_CTX *mem_ctx,
- int (*fgetc_fn)(void *), void *private_data)
-{
- size_t alloc_size=0, chunk_size = 0;
- char *chunk = NULL;
- int c;
- int in_comment = 0;
-
- while ((c = fgetc_fn(private_data)) != EOF) {
- if (chunk_size+1 >= alloc_size) {
- char *c2;
- alloc_size += 1024;
- c2 = talloc_realloc(mem_ctx, chunk, alloc_size);
- if (!c2) {
- errno = ENOMEM;
- return NULL;
- }
- chunk = c2;
- }
-
- if (in_comment) {
- if (c == '\n') {
- in_comment = 0;
- }
- continue;
- }
-
- /* handle continuation lines - see RFC2849 */
- if (c == ' ' && chunk_size > 1 &&
- chunk[chunk_size-1] == '\n') {
- chunk_size--;
- continue;
- }
-
- /* chunks are terminated by a double line-feed */
- if (c == '\n' && chunk_size > 0 &&
- chunk[chunk_size-1] == '\n') {
- chunk[chunk_size-1] = 0;
- return chunk;
- }
-
- if (c == '#' &&
- (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
- in_comment = 1;
- continue;
- }
-
- /* ignore leading blank lines */
- if (chunk_size == 0 && c == '\n') {
- continue;
- }
-
- chunk[chunk_size++] = c;
- }
-
- if (chunk) {
- chunk[chunk_size] = 0;
- }
-
- return chunk;
-}
-
-/* simple ldif attribute parser */
-static int next_attr(char **s, const char **attr, struct ldap_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;
- }
-
- *p++ = 0;
-
- if (*p == ':') {
- base64_encoded = 1;
- p++;
- }
-
- *attr = *s;
-
- while (isspace(*p)) {
- p++;
- }
-
- value->data = p;
-
- p = strchr(p, '\n');
-
- if (!p) {
- value->length = strlen((char *)value->data);
- *s = ((char *)value->data) + value->length;
- } else {
- value->length = p - (char *)value->data;
- *s = p+1;
- *p = 0;
- }
-
- if (base64_encoded) {
- DATA_BLOB blob = base64_decode_data_blob(value->data);
- memcpy(value->data, blob.data, blob.length);
- value->length = blob.length;
- ((char *)value->data)[value->length] = '\0';
- }
-
- return 0;
-}
-
-static BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldap_val *value,
- struct ldap_attribute *attrib)
-{
- attrib->values = talloc_realloc(mem_ctx, attrib->values,
- sizeof(*attrib->values) *
- (attrib->num_values+1));
- if (attrib->values == NULL)
- return False;
-
- attrib->values[attrib->num_values] =
- data_blob_talloc(mem_ctx, value->data, value->length);
- attrib->num_values += 1;
- return True;
-}
-
-static BOOL fill_add_attributes(struct ldap_message *msg, char **chunk)
-{
- struct ldap_AddRequest *r = &msg->r.AddRequest;
- const char *attr_name;
- struct ldap_val value;
-
- r->num_attributes = 0;
- r->attributes = NULL;
-
- while (next_attr(chunk, &attr_name, &value) == 0) {
- int i;
- struct ldap_attribute *attrib = NULL;
-
- for (i=0; i<r->num_attributes; i++) {
- if (strequal(r->attributes[i].name, attr_name)) {
- attrib = &r->attributes[i];
- break;
- }
- }
-
- if (attrib == NULL) {
- r->attributes = talloc_realloc(msg->mem_ctx,
- r->attributes,
- sizeof(*r->attributes) *
- (r->num_attributes+1));
- if (r->attributes == NULL)
- return False;
-
- attrib = &(r->attributes[r->num_attributes]);
- r->num_attributes += 1;
- ZERO_STRUCTP(attrib);
- attrib->name = talloc_strdup(msg->mem_ctx,
- attr_name);
- }
-
- if (!add_value_to_attrib(msg->mem_ctx, &value, attrib))
- return False;
- }
- return True;
-}
-
-static BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
- struct ldap_mod *mod,
- struct ldap_mod **mods,
- int *num_mods)
-{
- *mods = talloc_realloc(mem_ctx, *mods,
- sizeof(**mods) * ((*num_mods)+1));
-
- if (*mods == NULL)
- return False;
-
- (*mods)[*num_mods] = *mod;
- *num_mods += 1;
- return True;
-}
-
-static BOOL fill_mods(struct ldap_message *msg, char **chunk)
-{
- struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
- const char *attr_name;
- struct ldap_val value;
-
- r->num_mods = 0;
- r->mods = NULL;
-
- while (next_attr(chunk, &attr_name, &value) == 0) {
-
- struct ldap_mod mod;
- mod.type = LDAP_MODIFY_NONE;
-
- mod.attrib.name = talloc_strdup(msg->mem_ctx, value.data);
-
- if (strequal(attr_name, "add"))
- mod.type = LDAP_MODIFY_ADD;
-
- if (strequal(attr_name, "delete"))
- mod.type = LDAP_MODIFY_DELETE;
-
- if (strequal(attr_name, "replace"))
- mod.type = LDAP_MODIFY_REPLACE;
-
- if (mod.type == LDAP_MODIFY_NONE) {
- DEBUG(2, ("ldif modification type %s unsupported\n",
- attr_name));
- return False;
- }
-
- mod.attrib.num_values = 0;
- mod.attrib.values = NULL;
-
- while (next_attr(chunk, &attr_name, &value) == 0) {
- if (strequal(attr_name, "-"))
- break;
- if (!strequal(attr_name, mod.attrib.name)) {
- DEBUG(3, ("attrib name %s does not "
- "match %s\n", attr_name,
- mod.attrib.name));
- return False;
- }
- if (!add_value_to_attrib(msg->mem_ctx, &value,
- &mod.attrib)) {
- DEBUG(3, ("Could not add value\n"));
- return False;
- }
- }
-
- if (!add_mod_to_array_talloc(msg->mem_ctx, &mod, &r->mods,
- &r->num_mods))
- return False;
- }
-
- return True;
-}
-
-/*
- read from a LDIF source, creating a ldap_message
-*/
-static struct ldap_message *ldif_read(int (*fgetc_fn)(void *),
- void *private_data)
-{
- struct ldap_message *msg;
- const char *attr=NULL;
- const char *dn;
- char *chunk=NULL, *s;
- struct ldap_val value;
-
- value.data = NULL;
-
- msg = new_ldap_message();
- if (msg == NULL)
- return NULL;
-
- chunk = next_chunk(msg->mem_ctx, fgetc_fn, private_data);
- if (!chunk) {
- goto failed;
- }
-
- s = chunk;
-
- if (next_attr(&s, &attr, &value) != 0) {
- goto failed;
- }
-
- /* first line must be a dn */
- if (!strequal(attr, "dn")) {
- DEBUG(5, ("Error: First line of ldif must be a dn not '%s'\n",
- attr));
- goto failed;
- }
-
- dn = talloc_strdup(msg->mem_ctx, value.data);
-
- if (next_attr(&s, &attr, &value) != 0) {
- goto failed;
- }
-
- if (!strequal(attr, "changetype")) {
- DEBUG(5, ("Error: Second line of ldif must be a changetype "
- "not '%s'\n", attr));
- goto failed;
- }
-
- if (strequal(value.data, "delete")) {
- msg->type = LDAP_TAG_DelRequest;
- msg->r.DelRequest.dn = dn;
- return msg;
- }
-
- if (strequal(value.data, "add")) {
- msg->type = LDAP_TAG_AddRequest;
-
- msg->r.AddRequest.dn = dn;
-
- if (!fill_add_attributes(msg, &s))
- goto failed;
-
- return msg;
- }
-
- if (strequal(value.data, "modify")) {
- msg->type = LDAP_TAG_ModifyRequest;
-
- msg->r.ModifyRequest.dn = dn;
-
- if (!fill_mods(msg, &s))
- goto failed;
-
- return msg;
- }
-
- DEBUG(3, ("changetype %s not supported\n", (char *)value.data));
-
-failed:
- destroy_ldap_message(msg);
- return NULL;
-}
-
-/*
- a wrapper around ldif_read() for reading from const char*
-*/
-struct ldif_read_string_state {
- const char *s;
-};
-
-static int fgetc_string(void *private_data)
-{
- struct ldif_read_string_state *state = private_data;
- if (state->s[0] != 0) {
- return *state->s++;
- }
- return EOF;
-}
-
-struct ldap_message *ldap_ldif2msg(const char *s)
-{
- struct ldif_read_string_state state;
- state.s = s;
- return ldif_read(fgetc_string, &state);
-}
-
static void ldap_encode_response(enum ldap_request_tag tag,
struct ldap_Result *result,
ASN1_DATA *data)
@@ -1072,22 +677,6 @@ static void ldap_decode_response(TALLOC_CTX *mem_ctx,
asn1_end_tag(data);
}
-static BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
- const struct ldap_attribute *attrib,
- struct ldap_attribute **attribs,
- int *num_attribs)
-{
- *attribs = talloc_realloc(mem_ctx, *attribs,
- sizeof(**attribs) * (*num_attribs+1));
-
- if (*attribs == NULL)
- return False;
-
- (*attribs)[*num_attribs] = *attrib;
- *num_attribs += 1;
- return True;
-}
-
static BOOL ldap_decode_filter(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
char **filter)
{
diff --git a/source4/libcli/ldap/ldap.h b/source4/libcli/ldap/ldap.h
index da844afa3e..449be9e015 100644
--- a/source4/libcli/ldap/ldap.h
+++ b/source4/libcli/ldap/ldap.h
@@ -254,4 +254,33 @@ struct ldap_connection {
struct gensec_security *gensec;
};
+/* Hmm. A blob might be more appropriate here :-) */
+
+struct ldap_val {
+ unsigned int length;
+ void *data;
+};
+
+enum ldap_parse_op {LDAP_OP_SIMPLE, LDAP_OP_AND, LDAP_OP_OR, LDAP_OP_NOT};
+
+struct ldap_parse_tree {
+ enum ldap_parse_op operation;
+ union {
+ struct {
+ char *attr;
+ struct ldap_val value;
+ } simple;
+ struct {
+ unsigned int num_elements;
+ struct ldap_parse_tree **elements;
+ } list;
+ struct {
+ struct ldap_parse_tree *child;
+ } not;
+ } u;
+};
+
+#define LDAP_ALL_SEP "()&|=!"
+#define LDAP_CONNECTION_TIMEOUT 10000
+
#endif
diff --git a/source4/libcli/ldap/ldap_ldif.c b/source4/libcli/ldap/ldap_ldif.c
new file mode 100644
index 0000000000..2f23dd16a6
--- /dev/null
+++ b/source4/libcli/ldap/ldap_ldif.c
@@ -0,0 +1,409 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 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"
+
+/****************************************************************************
+ *
+ * LDIF parser
+ *
+ * Shamelessly stolen and adapted from ldb.
+ *
+ ***************************************************************************/
+
+/*
+ pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
+ this routine removes any RFC2849 continuations and comments
+
+ caller frees
+*/
+static char *next_chunk(TALLOC_CTX *mem_ctx,
+ int (*fgetc_fn)(void *), void *private_data)
+{
+ size_t alloc_size=0, chunk_size = 0;
+ char *chunk = NULL;
+ int c;
+ int in_comment = 0;
+
+ while ((c = fgetc_fn(private_data)) != EOF) {
+ if (chunk_size+1 >= alloc_size) {
+ char *c2;
+ alloc_size += 1024;
+ c2 = talloc_realloc(mem_ctx, chunk, alloc_size);
+ if (!c2) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ chunk = c2;
+ }
+
+ if (in_comment) {
+ if (c == '\n') {
+ in_comment = 0;
+ }
+ continue;
+ }
+
+ /* handle continuation lines - see RFC2849 */
+ if (c == ' ' && chunk_size > 1 &&
+ chunk[chunk_size-1] == '\n') {
+ chunk_size--;
+ continue;
+ }
+
+ /* chunks are terminated by a double line-feed */
+ if (c == '\n' && chunk_size > 0 &&
+ chunk[chunk_size-1] == '\n') {
+ chunk[chunk_size-1] = 0;
+ return chunk;
+ }
+
+ if (c == '#' &&
+ (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
+ in_comment = 1;
+ continue;
+ }
+
+ /* ignore leading blank lines */
+ if (chunk_size == 0 && c == '\n') {
+ continue;
+ }
+
+ chunk[chunk_size++] = c;
+ }
+
+ if (chunk) {
+ chunk[chunk_size] = 0;
+ }
+
+ return chunk;
+}
+
+/* simple ldif attribute parser */
+static int next_attr(char **s, const char **attr, struct ldap_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;
+ }
+
+ *p++ = 0;
+
+ if (*p == ':') {
+ base64_encoded = 1;
+ p++;
+ }
+
+ *attr = *s;
+
+ while (isspace(*p)) {
+ p++;
+ }
+
+ value->data = p;
+
+ p = strchr(p, '\n');
+
+ if (!p) {
+ value->length = strlen((char *)value->data);
+ *s = ((char *)value->data) + value->length;
+ } else {
+ value->length = p - (char *)value->data;
+ *s = p+1;
+ *p = 0;
+ }
+
+ if (base64_encoded) {
+ DATA_BLOB blob = base64_decode_data_blob(value->data);
+ memcpy(value->data, blob.data, blob.length);
+ value->length = blob.length;
+ ((char *)value->data)[value->length] = '\0';
+ }
+
+ return 0;
+}
+
+BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldap_val *value,
+ struct ldap_attribute *attrib)
+{
+ attrib->values = talloc_realloc(mem_ctx, attrib->values,
+ sizeof(*attrib->values) *
+ (attrib->num_values+1));
+ if (attrib->values == NULL)
+ return False;
+
+ attrib->values[attrib->num_values] =
+ data_blob_talloc(mem_ctx, value->data, value->length);
+ attrib->num_values += 1;
+ return True;
+}
+
+BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
+ const struct ldap_attribute *attrib,
+ struct ldap_attribute **attribs,
+ int *num_attribs)
+{
+ *attribs = talloc_realloc(mem_ctx, *attribs,
+ sizeof(**attribs) * (*num_attribs+1));
+
+ if (*attribs == NULL)
+ return False;
+
+ (*attribs)[*num_attribs] = *attrib;
+ *num_attribs += 1;
+ return True;
+}
+
+static BOOL fill_add_attributes(struct ldap_message *msg, char **chunk)
+{
+ struct ldap_AddRequest *r = &msg->r.AddRequest;
+ const char *attr_name;
+ struct ldap_val value;
+
+ r->num_attributes = 0;
+ r->attributes = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+ int i;
+ struct ldap_attribute *attrib = NULL;
+
+ for (i=0; i<r->num_attributes; i++) {
+ if (strequal(r->attributes[i].name, attr_name)) {
+ attrib = &r->attributes[i];
+ break;
+ }
+ }
+
+ if (attrib == NULL) {
+ r->attributes = talloc_realloc(msg->mem_ctx,
+ r->attributes,
+ sizeof(*r->attributes) *
+ (r->num_attributes+1));
+ if (r->attributes == NULL)
+ return False;
+
+ attrib = &(r->attributes[r->num_attributes]);
+ r->num_attributes += 1;
+ ZERO_STRUCTP(attrib);
+ attrib->name = talloc_strdup(msg->mem_ctx,
+ attr_name);
+ }
+
+ if (!add_value_to_attrib(msg->mem_ctx, &value, attrib))
+ return False;
+ }
+ return True;
+}
+
+BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
+ struct ldap_mod *mod,
+ struct ldap_mod **mods,
+ int *num_mods)
+{
+ *mods = talloc_realloc(mem_ctx, *mods,
+ sizeof(**mods) * ((*num_mods)+1));
+
+ if (*mods == NULL)
+ return False;
+
+ (*mods)[*num_mods] = *mod;
+ *num_mods += 1;
+ return True;
+}
+
+static BOOL fill_mods(struct ldap_message *msg, char **chunk)
+{
+ struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
+ const char *attr_name;
+ struct ldap_val value;
+
+ r->num_mods = 0;
+ r->mods = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+
+ struct ldap_mod mod;
+ mod.type = LDAP_MODIFY_NONE;
+
+ mod.attrib.name = talloc_strdup(msg->mem_ctx, value.data);
+
+ if (strequal(attr_name, "add"))
+ mod.type = LDAP_MODIFY_ADD;
+
+ if (strequal(attr_name, "delete"))
+ mod.type = LDAP_MODIFY_DELETE;
+
+ if (strequal(attr_name, "replace"))
+ mod.type = LDAP_MODIFY_REPLACE;
+
+ if (mod.type == LDAP_MODIFY_NONE) {
+ DEBUG(2, ("ldif modification type %s unsupported\n",
+ attr_name));
+ return False;
+ }
+
+ mod.attrib.num_values = 0;
+ mod.attrib.values = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+ if (strequal(attr_name, "-"))
+ break;
+ if (!strequal(attr_name, mod.attrib.name)) {
+ DEBUG(3, ("attrib name %s does not "
+ "match %s\n", attr_name,
+ mod.attrib.name));
+ return False;
+ }
+ if (!add_value_to_attrib(msg->mem_ctx, &value,
+ &mod.attrib)) {
+ DEBUG(3, ("Could not add value\n"));
+ return False;
+ }
+ }
+
+ if (!add_mod_to_array_talloc(msg->mem_ctx, &mod, &r->mods,
+ &r->num_mods))
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ read from a LDIF source, creating a ldap_message
+*/
+static struct ldap_message *ldif_read(int (*fgetc_fn)(void *),
+ void *private_data)
+{
+ struct ldap_message *msg;
+ const char *attr=NULL;
+ const char *dn;
+ char *chunk=NULL, *s;
+ struct ldap_val value;
+
+ value.data = NULL;
+
+ msg = new_ldap_message();
+ if (msg == NULL)
+ return NULL;
+
+ chunk = next_chunk(msg->mem_ctx, fgetc_fn, private_data);
+ if (!chunk) {
+ goto failed;
+ }
+
+ s = chunk;
+
+ if (next_attr(&s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ /* first line must be a dn */
+ if (!strequal(attr, "dn")) {
+ DEBUG(5, ("Error: First line of ldif must be a dn not '%s'\n",
+ attr));
+ goto failed;
+ }
+
+ dn = talloc_strdup(msg->mem_ctx, value.data);
+
+ if (next_attr(&s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ if (!strequal(attr, "changetype")) {
+ DEBUG(5, ("Error: Second line of ldif must be a changetype "
+ "not '%s'\n", attr));
+ goto failed;
+ }
+
+ if (strequal(value.data, "delete")) {
+ msg->type = LDAP_TAG_DelRequest;
+ msg->r.DelRequest.dn = dn;
+ return msg;
+ }
+
+ if (strequal(value.data, "add")) {
+ msg->type = LDAP_TAG_AddRequest;
+
+ msg->r.AddRequest.dn = dn;
+
+ if (!fill_add_attributes(msg, &s))
+ goto failed;
+
+ return msg;
+ }
+
+ if (strequal(value.data, "modify")) {
+ msg->type = LDAP_TAG_ModifyRequest;
+
+ msg->r.ModifyRequest.dn = dn;
+
+ if (!fill_mods(msg, &s))
+ goto failed;
+
+ return msg;
+ }
+
+ DEBUG(3, ("changetype %s not supported\n", (char *)value.data));
+
+failed:
+ destroy_ldap_message(msg);
+ return NULL;
+}
+
+/*
+ a wrapper around ldif_read() for reading from const char*
+*/
+struct ldif_read_string_state {
+ const char *s;
+};
+
+static int fgetc_string(void *private_data)
+{
+ struct ldif_read_string_state *state = private_data;
+ if (state->s[0] != 0) {
+ return *state->s++;
+ }
+ return EOF;
+}
+
+struct ldap_message *ldap_ldif2msg(const char *s)
+{
+ struct ldif_read_string_state state;
+ state.s = s;
+ return ldif_read(fgetc_string, &state);
+}
+