[libgda] Initial LDAP write support, with dedicated API



commit 6fd47674f41cfe0a3e6f78c510ef9261d5b785fe
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sat Apr 28 18:18:47 2012 +0200

    Initial LDAP write support, with dedicated API

 doc/C/libgda-sections.txt                   |    9 +
 libgda/gda-data-model-ldap.c                |   50 ++++++
 libgda/gda-data-model-ldap.h                |    2 +-
 libgda/libgda.symbols                       |    6 +
 libgda/sqlite/virtual/gda-ldap-connection.c |  202 +++++++++++++++++++++++
 libgda/sqlite/virtual/gda-ldap-connection.h |   35 ++++-
 providers/ldap/gda-ldap-util.c              |   86 ++++++++++-
 providers/ldap/gda-ldap-util.h              |    5 +
 providers/ldap/gdaprov-data-model-ldap.c    |  237 ++++++++++++++++++++++++++-
 9 files changed, 627 insertions(+), 5 deletions(-)
---
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 6bbce14..74351f1 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -258,10 +258,19 @@ gda_ldap_dn_split
 gda_ldap_describe_entry
 gda_ldap_entry_free
 gda_ldap_get_entry_children
+<SUBSECTION>
+gda_ldap_entry_add_attribute
+gda_ldap_entry_new
 GdaLdapAttributeDefinition
 gda_ldap_entry_get_attributes_list
 gda_ldap_attributes_list_free
 <SUBSECTION>
+GdaLdapModificationType
+gda_ldap_add_entry
+gda_ldap_remove_entry
+gda_ldap_rename_entry
+gda_ldap_modify_entry
+<SUBSECTION>
 GdaLdapClassKind
 GdaLdapClass
 gda_ldap_get_class_info
diff --git a/libgda/gda-data-model-ldap.c b/libgda/gda-data-model-ldap.c
index cc22a60..b87c954 100644
--- a/libgda/gda-data-model-ldap.c
+++ b/libgda/gda-data-model-ldap.c
@@ -486,3 +486,53 @@ _gda_ldap_entry_get_attributes_list (GdaLdapConnection *cnc, GdaLdapEntry *entry
 
 	return func (cnc, object_class_attr);
 }
+
+/*
+ * _gda_ldap_modify:
+ * proxy to gda_ldap_add_entry() and others
+ */
+gboolean
+_gda_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
+		  GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+
+	typedef gboolean (*Func) (GdaLdapConnection *,  GdaLdapModificationType, GdaLdapEntry*, GdaLdapEntry*,
+				  GError **);
+	static Func func = NULL;
+
+	if (!func) {
+		load_ldap_module ();
+		if (!ldap_prov_module)
+			return FALSE;
+
+		if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_modify", (void **) &func))
+			return FALSE;
+	}
+
+	return func (cnc, modtype, entry, ref_entry, error);
+}
+
+/*
+ * _gda_ldap_rename_entry:
+ * proxy to gda_ldap_rename_entry()
+ */
+gboolean
+_gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+
+	typedef gboolean (*Func) (GdaLdapConnection *,  const gchar*, const gchar*, GError **);
+	static Func func = NULL;
+
+	if (!func) {
+		load_ldap_module ();
+		if (!ldap_prov_module)
+			return FALSE;
+
+		if (!g_module_symbol (ldap_prov_module, "gdaprov_ldap_rename_entry", (void **) &func))
+			return FALSE;
+	}
+
+	return func (cnc, current_dn, new_dn, error);
+}
diff --git a/libgda/gda-data-model-ldap.h b/libgda/gda-data-model-ldap.h
index 702a9ef..6020cfa 100644
--- a/libgda/gda-data-model-ldap.h
+++ b/libgda/gda-data-model-ldap.h
@@ -85,7 +85,7 @@ GdaDataModelLdap *gda_data_model_ldap_new_with_config  (GdaConnection *cnc,
 							const gchar *base_dn, const gchar *filter,
 							const gchar *attributes, GdaLdapSearchScope scope);
 
-GList        *gda_data_model_ldap_compute_columns (GdaConnection *cnc, const gchar *attributes);
+GList            *gda_data_model_ldap_compute_columns  (GdaConnection *cnc, const gchar *attributes);
 
 G_END_DECLS
 
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index e83074b..a10f481 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -417,6 +417,7 @@
 	gda_init
 	gda_lang_locale
 #ifdef HAVE_LDAP
+	gda_ldap_add_entry
 	gda_ldap_attributes_list_free
 	gda_ldap_connection_get_type
 	gda_ldap_get_class_info
@@ -427,10 +428,15 @@
 	gda_ldap_connection_undeclare_table
 	gda_ldap_describe_entry
 	gda_ldap_dn_split
+	gda_ldap_entry_add_attribute
+	gda_ldap_entry_new
 	gda_ldap_entry_free
 	gda_ldap_entry_get_attributes_list
 	gda_ldap_get_entry_children
 	gda_ldap_is_dn
+	gda_ldap_modify_entry
+	gda_ldap_remove_entry
+	gda_ldap_rename_entry
 #endif
 	gda_locale_changed
 	gda_lockable_get_type
diff --git a/libgda/sqlite/virtual/gda-ldap-connection.c b/libgda/sqlite/virtual/gda-ldap-connection.c
index 314b98e..84b4e1c 100644
--- a/libgda/sqlite/virtual/gda-ldap-connection.c
+++ b/libgda/sqlite/virtual/gda-ldap-connection.c
@@ -878,6 +878,104 @@ gda_ldap_entry_free (GdaLdapEntry *entry)
 	}
 }
 
+/**
+ * gda_ldap_entry_new:
+ * @dn: (allow-none): a Distinguished name, or %NULL
+ *
+ * Creates a new #GdaLdapEntry. This function is useful when using gda_ldap_modify_entry()
+ *
+ * Returns: a new #GdaLdapEntry
+ *
+ * Since: 5.2.0
+ */
+GdaLdapEntry *
+gda_ldap_entry_new (const gchar *dn)
+{
+	GdaLdapEntry *entry;
+	entry = g_new0 (GdaLdapEntry, 1);
+	if (dn)
+		entry->dn = g_strdup (dn);
+	entry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+	entry->nb_attributes = 0;
+	entry->attributes = g_new0 (GdaLdapAttribute, 1);
+	return entry;
+}
+
+/**
+ * gda_ldap_entry_add_attribute:
+ * @entry: a #GdaLdapEntry pointer
+ * @merge: set to %TRUE to merge the values in case of an existing attribute in @entry, and %FALSE to replace any existing attribute's values in @entry
+ * @attr_name: the name of the attribute to add
+ * @nb_values: number of values in @values
+ * @values: (array length=nb_values): an array of #GValue (as much values as specified by @nb_values)
+ *
+ * Add an attribute (ans its values) to @entry. If the attribute is already present in @entry,
+ * then the attribute's values are merged or replaced depending on the @merge argument.
+ *
+ * Since: 5.2.0
+ */
+void
+gda_ldap_entry_add_attribute (GdaLdapEntry *entry, gboolean merge, const gchar *attr_name,
+			      guint nb_values, GValue **values)
+{
+	guint i;
+	g_return_if_fail (entry);
+	g_return_if_fail (nb_values > 0);
+	g_return_if_fail (values);
+	g_return_if_fail (attr_name && *attr_name);
+
+	GdaLdapAttribute *att = NULL;
+	GdaLdapAttribute *natt = NULL;
+	guint replace_pos = G_MAXUINT;
+
+	if (entry->attributes_hash)
+		att = g_hash_table_lookup (entry->attributes_hash, attr_name);
+	else
+		entry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+	if (att) {
+		if (merge) {
+			TO_IMPLEMENT;
+			return;
+		}
+		else {
+			/* get rid of @attr */
+			g_hash_table_remove (entry->attributes_hash, att->attr_name);
+			for (i = 0; i < entry->nb_attributes; i++) {
+				if (entry->attributes [i] == att) {
+					replace_pos = i;
+					entry->attributes [i] = NULL;
+					break;
+				}
+			}
+			gda_ldap_attribute_free (att);
+		}
+	}
+
+	natt = g_new0 (GdaLdapAttribute, 1);
+	natt->attr_name = g_strdup (attr_name);
+	natt->nb_values = nb_values;
+	if (natt->nb_values > 0) {
+		natt->values = g_new0 (GValue *, natt->nb_values + 1);
+		for (i = 0; i < natt->nb_values; i++) {
+			if (values [i])
+				natt->values [i] = gda_value_copy (values [i]);
+			else
+				natt->values [i] = NULL;
+		}
+	}
+	g_hash_table_insert (entry->attributes_hash, natt->attr_name, natt);
+	if (replace_pos != G_MAXUINT)
+		entry->attributes [replace_pos] = natt;
+	else {
+		entry->nb_attributes++;
+		entry->attributes = g_renew (GdaLdapAttribute*, entry->attributes, entry->nb_attributes + 1);
+		entry->attributes [entry->nb_attributes - 1] = natt;
+		entry->attributes [entry->nb_attributes] = NULL;
+	}
+}
+
+
 /* proxy declaration */
 GdaLdapEntry *_gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error);
 GdaLdapEntry **_gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error);
@@ -1027,6 +1125,7 @@ gda_ldap_get_top_classes (GdaLdapConnection *cnc)
 GSList *_gda_ldap_entry_get_attributes_list (GdaLdapConnection *cnc, GdaLdapEntry *entry, GdaLdapAttribute *object_class_attr);
 /**
  * gda_ldap_entry_get_attributes_list:
+ * @cnc: a #GdaLdapConnection
  * @entry: a #GdaLdapEntry
  *
  * Get a list of all the possible attributes which @entry can have. Each possible attribute is represented
@@ -1068,3 +1167,106 @@ gda_ldap_attributes_list_free (GSList *list)
 	}
 	g_slist_free (list);
 }
+
+
+gboolean
+_gda_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
+		  GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error);
+
+/**
+ * gda_ldap_add_entry:
+ * @cnc: a #GdaLdapConnection
+ * @entry: a #GdaLDapEntry describing the LDAP entry to add
+ * @error: (allow-none): a place to store an error, or %NULL
+ *
+ * Creates a new LDAP entry.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 5.2.0
+ */
+gboolean
+gda_ldap_add_entry (GdaLdapConnection *cnc, GdaLdapEntry *entry, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (entry, FALSE);
+	g_return_val_if_fail (entry->dn && *(entry->dn), FALSE);
+
+	return _gda_ldap_modify (cnc, GDA_LDAP_MODIFICATION_INSERT, entry, NULL, error);
+}
+
+/**
+ * gda_ldap_remove_entry:
+ * @cnc: a #GdaLdapConnection
+ * @entry: a #GdaLDapEntry describing the LDAP entry to remove
+ * @error: (allow-none): a place to store an error, or %NULL
+ *
+ * Delete an LDAP entry.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 5.2.0
+ */
+gboolean
+gda_ldap_remove_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (dn && *dn, FALSE);
+
+	GdaLdapEntry entry;
+	memset (&entry, 0, sizeof (GdaLdapEntry));
+	entry.dn = (gchar*) dn;
+
+	return _gda_ldap_modify (cnc, GDA_LDAP_MODIFICATION_DELETE, &entry, NULL, error);
+}
+
+gboolean
+_gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error);
+
+/**
+ * gda_ldap_rename_entry:
+ * @cnc: a #GdaLdapConnection
+ * @current_dn: the current DN of the entry
+ * @new_dn: the new DN of the entry
+ * @error: (allow-none): a place to store an error, or %NULL
+ *
+ * Renames an LDAP entry.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 5.2.0
+ */
+gboolean
+gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (current_dn && *current_dn, FALSE);
+	g_return_val_if_fail (new_dn && *new_dn, FALSE);
+
+	return _gda_ldap_rename_entry (cnc, current_dn, new_dn, error);
+}
+
+/**
+ * gda_ldap_modify_entry:
+ * @cnc: a #GdaLdapConnection
+ * @modtype: the type of modification to perform
+ * @entry: a #GdaLDapEntry describing the LDAP entry to apply modifications, along with the attributes which will be modified
+ * @ref_entry: (allow-none): a #GdaLDapEntry describing the reference LDAP entry, if @modtype is %GDA_LDAP_MODIFICATION_ATTR_DIFF
+ * @error: (allow-none): a place to store an error, or %NULL
+ *
+ * Modifies an LDAP entry.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 5.2.0
+ */
+gboolean
+gda_ldap_modify_entry (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
+		       GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (entry, FALSE);
+	g_return_val_if_fail (entry->dn && *(entry->dn), FALSE);
+
+	return _gda_ldap_modify (cnc, modtype, entry, ref_entry, error);
+}
diff --git a/libgda/sqlite/virtual/gda-ldap-connection.h b/libgda/sqlite/virtual/gda-ldap-connection.h
index 4269163..1816252 100644
--- a/libgda/sqlite/virtual/gda-ldap-connection.h
+++ b/libgda/sqlite/virtual/gda-ldap-connection.h
@@ -162,7 +162,11 @@ typedef struct {
 	GHashTable        *attributes_hash;
 } GdaLdapEntry;
 
+GdaLdapEntry  *gda_ldap_entry_new                  (const gchar *dn);
+void           gda_ldap_entry_add_attribute        (GdaLdapEntry *entry, gboolean merge, const gchar *attr_name,
+						    guint nb_values, GValue **values);
 void           gda_ldap_entry_free                 (GdaLdapEntry *entry);
+
 GdaLdapEntry  *gda_ldap_describe_entry             (GdaLdapConnection *cnc, const gchar *dn, GError **error);
 GdaLdapEntry **gda_ldap_get_entry_children         (GdaLdapConnection *cnc, const gchar *dn,
 						    gchar **attributes, GError **error);
@@ -225,13 +229,40 @@ typedef struct {
 	guint             nb_opt_attributes;
 	gchar           **opt_attributes;
 
-	GSList           *parents; /* list of #LdapClass */
-	GSList           *children; /* list of #LdapClass */
+	GSList           *parents; /* list of #GdaLdapClass */
+	GSList           *children; /* list of #GdaLdapClass */
 } GdaLdapClass;
 
 GdaLdapClass   *gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname);
 const GSList   *gda_ldap_get_top_classes (GdaLdapConnection *cnc);
 
+
+/**
+ * GdaLdapModificationType:
+ * @GDA_LDAP_MODIFICATION_INSERT: modification corresponds to a new LDAP entry
+ * @GDA_LDAP_MODIFICATION_DELETE: modification corresponds to removing an LDAP entry
+ * @GDA_LDAP_MODIFICATION_ATTR_ADD: modification correspond to adding attributes to an existing LDAP entry
+ * @GDA_LDAP_MODIFICATION_ATTR_DEL: modification correspond to removing attributes from an existing LDAP entry
+ * @GDA_LDAP_MODIFICATION_ATTR_REPL: modification correspond to replacing attributes of an existing LDAP entry
+ * @GDA_LDAP_MODIFICATION_ATTR_DIFF: modification correspond to modifying attributes to an existing LDAP entry
+ *
+ * Speficies the type of operation requested when writing to an LDAP directory.
+ */
+typedef enum {
+	GDA_LDAP_MODIFICATION_INSERT,
+	GDA_LDAP_MODIFICATION_DELETE,
+	GDA_LDAP_MODIFICATION_ATTR_ADD,
+	GDA_LDAP_MODIFICATION_ATTR_DEL,
+	GDA_LDAP_MODIFICATION_ATTR_REPL,
+	GDA_LDAP_MODIFICATION_ATTR_DIFF
+} GdaLdapModificationType;
+
+gboolean        gda_ldap_add_entry    (GdaLdapConnection *cnc, GdaLdapEntry *entry, GError **error);
+gboolean        gda_ldap_remove_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error);
+gboolean        gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error);
+gboolean        gda_ldap_modify_entry (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
+				       GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error);
+
 G_END_DECLS
 
 #endif
diff --git a/providers/ldap/gda-ldap-util.c b/providers/ldap/gda-ldap-util.c
index 489c17d..1fd722d 100644
--- a/providers/ldap/gda-ldap-util.c
+++ b/providers/ldap/gda-ldap-util.c
@@ -918,6 +918,90 @@ gda_ldap_attr_value_to_g_value (LdapConnectionData *cdata, GType type, BerValue
 	return value;
 }
 
+BerValue *
+gda_ldap_attr_g_value_to_value (LdapConnectionData *cdata, const GValue *cvalue)
+{
+	BerValue *bv;
+
+	if (!cvalue)
+		return NULL;
+
+	bv = g_new (struct berval, 1);
+
+	if (G_VALUE_TYPE (cvalue) == G_TYPE_STRING) {
+		const gchar *cstr;
+		cstr = g_value_get_string (cvalue);
+		bv->bv_val = g_strdup (cstr);
+		bv->bv_len = strlen (cstr);
+	}
+	else if (G_VALUE_TYPE (cvalue) == GDA_TYPE_TIMESTAMP) {
+		const GdaTimestamp *ts;
+		gchar *str;
+		ts = gda_value_get_timestamp (cvalue);
+		if (ts->fraction == 0) {
+			if (ts->timezone == GDA_TIMEZONE_INVALID)
+				str = g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d", ts->year, ts->month,
+						       ts->day, ts->hour, ts->minute, ts->second);
+			else {
+				str = g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d", ts->year, ts->month,
+						       ts->day, ts->hour, ts->minute, ts->second);
+				TO_IMPLEMENT;
+			}
+		}
+		else {
+			if (ts->timezone == GDA_TIMEZONE_INVALID)
+				str = g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d,%lu", ts->year, ts->month,
+						       ts->day, ts->hour, ts->minute, ts->second,
+						       ts->fraction);
+			else {
+				str = g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d,%lu", ts->year, ts->month,
+						       ts->day, ts->hour, ts->minute, ts->second,
+						       ts->fraction);
+				TO_IMPLEMENT;
+			}
+		}
+		bv->bv_val = str;
+		bv->bv_len = strlen (str);
+	}
+	else if (G_VALUE_TYPE (cvalue) == G_TYPE_DATE) {
+		GDate *date;
+		gchar *str;
+		date = (GDate*) g_value_get_boxed (cvalue);
+		str = g_strdup_printf ("%04d-%02d-%02d", g_date_get_year (date), g_date_get_month (date),
+				       g_date_get_day (date));
+		bv->bv_val = str;
+		bv->bv_len = strlen (str);
+	}
+	else if (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL) {
+		bv->bv_val = NULL;
+		bv->bv_len = 0;
+	}
+	else if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) {
+		TO_IMPLEMENT;
+	}
+	else if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BLOB) {
+		TO_IMPLEMENT;
+	}
+	else {
+		gchar *str;
+		str = gda_value_stringify (cvalue);
+		bv->bv_val = str;
+		bv->bv_len = strlen (str);
+	}
+	return bv;
+}
+
+/*
+ * Frees @bvalue, which MUST have been created using gda_ldap_attr_g_value_to_value()
+ */
+void
+gda_ldap_attr_value_free (LdapConnectionData *cdata, BerValue *bvalue)
+{
+	g_free (bvalue->bv_val);
+	g_free (bvalue);
+}
+
+
 /*
  * make sure we respect http://www.faqs.org/rfcs/rfc2253.html
  */
@@ -1526,7 +1610,7 @@ gdaprov_ldap_get_attributes_list (GdaLdapConnection *cnc, GdaLdapAttribute *obje
 		GdaLdapClass *kl;
 		kl = gdaprov_ldap_get_class_info (cnc, tmp);
 		if (!kl) {
-			g_warning (_("Can't get information about '%s' class"), tmp);
+			/*g_warning (_("Can't get information about '%s' class"), tmp);*/
 			continue;
 		}
 		retlist = handle_ldap_class (cdata, kl, retlist, hash);
diff --git a/providers/ldap/gda-ldap-util.h b/providers/ldap/gda-ldap-util.h
index f9800e7..0766831 100644
--- a/providers/ldap/gda-ldap-util.h
+++ b/providers/ldap/gda-ldap-util.h
@@ -50,6 +50,11 @@ GType          gda_ldap_get_g_type    (LdapConnectionData *cdata, const gchar *a
  * Misc.
  */
 GValue        *gda_ldap_attr_value_to_g_value (LdapConnectionData *cdata, GType type, BerValue *bv);
+BerValue      *gda_ldap_attr_g_value_to_value (LdapConnectionData *cdata, const GValue *cvalue);
+void           gda_ldap_attr_value_free (LdapConnectionData *cdata, BerValue *bvalue);
+
 gboolean       gda_ldap_parse_dn (const char *attr, gchar **out_userdn);
 
+gboolean       gdaprov_ldap_is_dn (const gchar *dn);
+
 #endif
diff --git a/providers/ldap/gdaprov-data-model-ldap.c b/providers/ldap/gdaprov-data-model-ldap.c
index 8c33aff..acaca76 100644
--- a/providers/ldap/gdaprov-data-model-ldap.c
+++ b/providers/ldap/gdaprov-data-model-ldap.c
@@ -1055,7 +1055,7 @@ execute_ldap_search (GdaDataModelLdap *model)
 		int ldap_errno;
 		ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
 		g_set_error (&e, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
-			     "%s", ldap_err2string(ldap_errno));
+			     "%s", ldap_err2string (ldap_errno));
 		add_exception (model, e);
 		return;
 	}
@@ -1501,3 +1501,238 @@ row_multiplier_index_next (RowMultiplier *rm)
 		}
 	}
 }
+
+/*
+ * Writing support
+ */
+
+typedef struct {
+	LdapConnectionData *cdata;
+	GArray *mods_array;
+} FHData;
+static void removed_attrs_func (const gchar *attr_name, GdaLdapAttribute *attr, FHData *data);
+
+gboolean
+gdaprov_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
+		     GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error)
+{
+	LdapConnectionData *cdata;
+	int res;
+
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (entry, FALSE);
+	if (entry)
+		g_return_val_if_fail (gdaprov_ldap_is_dn (entry->dn), FALSE);
+	if (ref_entry)
+		g_return_val_if_fail (gdaprov_ldap_is_dn (ref_entry->dn), FALSE);
+
+	cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+	g_return_val_if_fail (cdata, FALSE);
+
+	/* checks */
+	if ((modtype != GDA_LDAP_MODIFICATION_INSERT) &&
+	    (modtype != GDA_LDAP_MODIFICATION_ATTR_ADD) &&
+	    (modtype != GDA_LDAP_MODIFICATION_ATTR_DEL) &&
+	    (modtype != GDA_LDAP_MODIFICATION_ATTR_REPL) &&
+	    (modtype != GDA_LDAP_MODIFICATION_ATTR_DIFF)) {
+		g_warning (_("Unknown GdaLdapModificationType %d"), modtype);
+		return FALSE;
+	}
+
+	if (((modtype == GDA_LDAP_MODIFICATION_DELETE) || (modtype == GDA_LDAP_MODIFICATION_INSERT)) &&
+	    !entry) {
+		g_warning ("%s", _("No GdaLdapEntry specified"));
+		return FALSE;
+	}
+
+	if ((modtype == GDA_LDAP_MODIFICATION_ATTR_ADD) && !entry) {
+		g_warning ("%s", _("No GdaLdapEntry specified to define attributes to add"));
+		return FALSE;
+	}
+
+	if ((modtype == GDA_LDAP_MODIFICATION_ATTR_DEL) && !entry) {
+		g_warning ("%s", _("No GdaLdapEntry specified to define attributes to remove"));
+		return FALSE;
+	}
+
+	if ((modtype == GDA_LDAP_MODIFICATION_ATTR_REPL) && !entry) {
+		g_warning ("%s", _("No GdaLdapEntry specified to define attributes to replace"));
+		return FALSE;
+	}
+
+	if ((modtype == GDA_LDAP_MODIFICATION_ATTR_DIFF) && (!entry || !ref_entry)) {
+		g_warning ("%s", _("No GdaLdapEntry specified to compare attributes"));
+		return FALSE;
+	}
+	if ((modtype == GDA_LDAP_MODIFICATION_ATTR_DIFF) && strcmp (entry->dn, ref_entry->dn)) {
+		g_warning ("%s", _("GdaLdapEntry specified to compare have different DN"));
+		return FALSE;
+	}
+
+	/* handle DELETE operation */
+	if (modtype == GDA_LDAP_MODIFICATION_DELETE) {
+		res = ldap_delete_ext_s (cdata->handle, entry->dn, NULL, NULL);
+		if (res != LDAP_SUCCESS) {
+			g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+				     "%s", ldap_err2string (res));
+			return FALSE;
+		}
+		else
+			return TRUE;
+	}
+
+	/* build array of modifications to perform */
+	GArray *mods_array;
+	mods_array = g_array_new (TRUE, FALSE, sizeof (LDAPMod*));
+	if (modtype == GDA_LDAP_MODIFICATION_ATTR_DIFF) {
+		/* index ref_entry's attributes */
+		GHashTable *hash;
+		guint i;
+		hash = g_hash_table_new (g_str_hash, g_str_equal);
+		for (i = 0; i < ref_entry->nb_attributes; i++) {
+			GdaLdapAttribute *attr;
+			attr = ref_entry->attributes [i];
+			g_hash_table_insert (hash, attr->attr_name, attr);
+		}
+		
+		for (i = 0; i < entry->nb_attributes; i++) {
+			LDAPMod *mod;
+			GdaLdapAttribute *attr, *ref_attr;
+			guint j;
+
+			attr = entry->attributes [i];
+			ref_attr = g_hash_table_lookup (hash, attr->attr_name);
+
+			mod = g_new0 (LDAPMod, 1);
+			mod->mod_type = attr->attr_name; /* no duplication */
+			if (ref_attr) {
+				mod->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
+				g_hash_table_remove (hash, attr->attr_name);
+			}
+			else
+				mod->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
+
+			mod->mod_bvalues = g_new0 (struct berval *, attr->nb_values + 1); /* last is NULL */
+			for (j = 0; j < attr->nb_values; j++)
+				mod->mod_bvalues[j] = gda_ldap_attr_g_value_to_value (cdata, attr->values [j]);
+			g_array_append_val (mods_array, mod);
+		}
+
+		FHData fhdata;
+		fhdata.cdata = cdata;
+		fhdata.mods_array = mods_array;
+		g_hash_table_foreach (hash, (GHFunc) removed_attrs_func, &fhdata);
+		g_hash_table_destroy (hash);
+	}
+	else {
+		guint i;
+		for (i = 0; i < entry->nb_attributes; i++) {
+			LDAPMod *mod;
+			GdaLdapAttribute *attr;
+			guint j;
+
+			attr = entry->attributes [i];
+			mod = g_new0 (LDAPMod, 1);
+			mod->mod_op = LDAP_MOD_BVALUES;
+			if ((modtype == GDA_LDAP_MODIFICATION_INSERT) ||
+			    (modtype == GDA_LDAP_MODIFICATION_ATTR_ADD))
+				mod->mod_op |= LDAP_MOD_ADD;
+			else if (modtype == GDA_LDAP_MODIFICATION_ATTR_DEL)
+				mod->mod_op |= LDAP_MOD_DELETE;
+			else
+				mod->mod_op |= LDAP_MOD_REPLACE;
+			mod->mod_type = attr->attr_name; /* no duplication */
+			mod->mod_bvalues = g_new0 (struct berval *, attr->nb_values + 1); /* last is NULL */
+			for (j = 0; j < attr->nb_values; j++)
+				mod->mod_bvalues[j] = gda_ldap_attr_g_value_to_value (cdata, attr->values [j]);
+			g_array_append_val (mods_array, mod);
+		}
+	}
+	
+	gboolean retval = TRUE;
+	if (mods_array->len > 0) {
+		/* apply modifications */
+		if (modtype == GDA_LDAP_MODIFICATION_INSERT)
+			res = ldap_add_ext_s (cdata->handle, entry->dn, (LDAPMod **) mods_array->data, NULL, NULL);
+		else
+			res = ldap_modify_ext_s (cdata->handle, entry->dn, (LDAPMod **) mods_array->data, NULL, NULL);
+
+		if (res != LDAP_SUCCESS) {
+			g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+				     "%s", ldap_err2string (res));
+			retval = FALSE;
+		}
+	}
+
+	/* clear tmp data */
+	guint i;
+	for (i = 0; i < mods_array->len; i++) {
+		LDAPMod *mod;
+		mod = g_array_index (mods_array, LDAPMod*, i);
+		if (mod->mod_values) {
+			guint j;
+			for (j = 0; mod->mod_values [j]; j++)
+				gda_ldap_attr_value_free (cdata, mod->mod_bvalues [j]);
+			g_free (mod->mod_values);
+		}
+		g_free (mod);
+	}
+	g_array_free (mods_array, TRUE);
+
+	return retval;
+}
+
+static void
+removed_attrs_func (const gchar *attr_name, GdaLdapAttribute *attr, FHData *data)
+{
+	LDAPMod *mod;
+	guint j;
+
+	mod = g_new0 (LDAPMod, 1);
+	mod->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_DELETE;
+	mod->mod_type = attr->attr_name; /* no duplication */
+	mod->mod_bvalues = g_new0 (struct berval *, attr->nb_values + 1); /* last is NULL */
+	for (j = 0; j < attr->nb_values; j++)
+		mod->mod_bvalues[j] = gda_ldap_attr_g_value_to_value (data->cdata, attr->values [j]);
+	g_array_append_val (data->mods_array, mod);
+}
+
+gboolean
+gdaprov_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn,
+			   GError **error)
+{
+	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (current_dn && *current_dn, FALSE);
+	g_return_val_if_fail (gdaprov_ldap_is_dn (current_dn), FALSE);
+	g_return_val_if_fail (new_dn && *new_dn, FALSE);
+	g_return_val_if_fail (gdaprov_ldap_is_dn (new_dn), FALSE);
+
+	LdapConnectionData *cdata;
+	cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
+	g_return_val_if_fail (cdata, FALSE);
+
+	gchar **carray, **narray;
+	int res;
+	gboolean retval = TRUE;
+	gchar *parent = NULL;
+
+	carray = gda_ldap_dn_split (current_dn, FALSE);
+	narray = gda_ldap_dn_split (new_dn, FALSE);
+
+	if (carray[1] && narray[1] && strcmp (carray[1], narray[1]))
+		parent = narray [1];
+	else if (! carray[1] && narray[1])
+		parent = narray [1];
+
+	res = ldap_rename_s (cdata->handle, current_dn, narray[0], parent, 1, NULL, NULL);
+	g_strfreev (carray);
+	g_strfreev (narray);
+
+	if (res != LDAP_SUCCESS) {
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
+			     "%s", ldap_err2string (res));
+		retval = FALSE;
+	}
+
+	return retval;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]