[libgda] LDAP provider: close connection when not used
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] LDAP provider: close connection when not used
- Date: Sat, 28 Apr 2012 17:28:35 +0000 (UTC)
commit 59ca7a63003acee283db7f2bd192c43f082b2281
Author: Vivien Malerba <malerba gnome-db org>
Date: Sat Apr 28 19:18:22 2012 +0200
LDAP provider: close connection when not used
to avoid keeping an opened connection to the server if not
necessary
providers/ldap/gda-ldap-provider.c | 45 +++++++++++++++--
providers/ldap/gda-ldap-util.c | 35 ++++++++++--
providers/ldap/gda-ldap.h | 6 ++-
providers/ldap/gdaprov-data-model-ldap.c | 83 +++++++++++++++++++++++++----
4 files changed, 146 insertions(+), 23 deletions(-)
---
diff --git a/providers/ldap/gda-ldap-provider.c b/providers/ldap/gda-ldap-provider.c
index 416f8f9..fe8c09c 100644
--- a/providers/ldap/gda-ldap-provider.c
+++ b/providers/ldap/gda-ldap-provider.c
@@ -501,6 +501,7 @@ gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *c
}
cdata = g_new0 (LdapConnectionData, 1);
+ cdata->keep_bound_count = 0;
cdata->handle = ld;
cdata->url = url;
cdata->time_limit = 0;
@@ -590,16 +591,46 @@ gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *c
return FALSE;
}
+ gda_ldap_may_unbind (cdata);
return TRUE;
}
/*
+ * Unbinds the connection if possible (i.e. if cdata->keep_bound_count is 0)
+ * This allows to avoid keeping the connection to the LDAP server if unused
+ */
+void
+gda_ldap_may_unbind (LdapConnectionData *cdata)
+{
+ if (!cdata || (cdata->keep_bound_count > 0))
+ return;
+ if (cdata->handle) {
+ ldap_unbind_ext (cdata->handle, NULL, NULL);
+ cdata->handle = NULL;
+ }
+}
+
+/*
+ * Makes sure the connection is opened
+ */
+gboolean
+gda_ldap_ensure_bound (LdapConnectionData *cdata, GError **error)
+{
+ if (!cdata)
+ return FALSE;
+ else if (cdata->handle)
+ return TRUE;
+
+ return gda_ldap_rebind (cdata, error);
+}
+
+/*
* Reopens a connection after the server has closed it (possibly because of a timeout)
*
* If it fails, then @cdata is left unchanged, otherwise it is modified to be useable again.
*/
gboolean
-gda_ldap_silently_rebind (LdapConnectionData *cdata)
+gda_ldap_rebind (LdapConnectionData *cdata, GError **error)
{
if (!cdata)
return FALSE;
@@ -608,8 +639,11 @@ gda_ldap_silently_rebind (LdapConnectionData *cdata)
LDAP *ld;
int res;
res = ldap_initialize (&ld, cdata->url);
- if (res != LDAP_SUCCESS)
+ if (res != LDAP_SUCCESS) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
+ "%s", ldap_err2string (res));
return FALSE;
+ }
/* set protocol version to 3 by default */
int version = LDAP_VERSION3;
@@ -620,6 +654,8 @@ gda_ldap_silently_rebind (LdapConnectionData *cdata)
res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
}
if (res != LDAP_SUCCESS) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
+ "%s", ldap_err2string (res));
ldap_unbind_ext (ld, NULL, NULL);
return FALSE;
}
@@ -634,6 +670,8 @@ gda_ldap_silently_rebind (LdapConnectionData *cdata)
cred.bv_val = pwd && *pwd ? (char *) pwd : NULL;
res = ldap_sasl_bind_s (ld, cdata->user, NULL, &cred, NULL, NULL, NULL);
if (res != LDAP_SUCCESS) {
+ g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
+ "%s", ldap_err2string (res));
ldap_unbind_ext (ld, NULL, NULL);
return FALSE;
}
@@ -700,8 +738,7 @@ gda_ldap_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
if (!cdata)
return NULL;
- TO_IMPLEMENT;
- return NULL;
+ return cdata->base_dn;
}
/*
diff --git a/providers/ldap/gda-ldap-util.c b/providers/ldap/gda-ldap-util.c
index 1fd722d..229864c 100644
--- a/providers/ldap/gda-ldap-util.c
+++ b/providers/ldap/gda-ldap-util.c
@@ -556,13 +556,17 @@ gdaprov_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
char *schema_attrs[] = {"objectClasses", NULL};
/* look for subschema */
+ if (! gda_ldap_ensure_bound (cdata, NULL))
+ return NULL;
res = ldap_search_ext_s (cdata->handle, "", LDAP_SCOPE_BASE,
"(objectclass=*)",
subschemasubentry, 0,
NULL, NULL, NULL, 0,
&msg);
- if (res != LDAP_SUCCESS)
+ if (res != LDAP_SUCCESS) {
+ gda_ldap_may_unbind (cdata);
return NULL;
+ }
if ((entry = ldap_first_entry (cdata->handle, msg))) {
char *attr;
@@ -580,8 +584,10 @@ gdaprov_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
}
ldap_msgfree (msg);
- if (! subschema)
+ if (! subschema) {
+ gda_ldap_may_unbind (cdata);
return NULL;
+ }
/* look for attributeTypes */
res = ldap_search_ext_s (cdata->handle, subschema, LDAP_SCOPE_BASE,
@@ -590,8 +596,10 @@ gdaprov_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
NULL, NULL, NULL, 0,
&msg);
g_free (subschema);
- if (res != LDAP_SUCCESS)
+ if (res != LDAP_SUCCESS) {
+ gda_ldap_may_unbind (cdata);
return NULL;
+ }
GHashTable *h_refs;
h_refs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_strfreev);
@@ -716,6 +724,7 @@ gdaprov_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
g_hash_table_destroy (h_refs);
retval = g_hash_table_lookup (cdata->classes_hash, classname);
+ gda_ldap_may_unbind (cdata);
return retval;
}
@@ -1199,6 +1208,10 @@ gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **e
if (!cdata)
return NULL;
+
+ if (! gda_ldap_ensure_bound (cdata, error))
+ return NULL;
+
int res;
LDAPMessage *msg = NULL;
const gchar *real_dn;
@@ -1221,6 +1234,7 @@ gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **e
nb_entries = ldap_count_entries (cdata->handle, msg);
if (nb_entries == 0) {
ldap_msgfree (msg);
+ gda_ldap_may_unbind (cdata);
return NULL;
}
else if (nb_entries > 1) {
@@ -1228,6 +1242,7 @@ gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **e
GDA_SERVER_PROVIDER_INTERNAL_ERROR,
_("LDAP server returned more than one entry with DN '%s'"),
real_dn);
+ gda_ldap_may_unbind (cdata);
return NULL;
}
@@ -1278,12 +1293,13 @@ gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **e
lentry->nb_attributes = array->len;
g_array_free (array, FALSE);
}
+ gda_ldap_may_unbind (cdata);
return lentry;
}
case LDAP_SERVER_DOWN: {
gint i;
for (i = 0; i < 5; i++) {
- if (gda_ldap_silently_rebind (cdata))
+ if (gda_ldap_rebind (cdata, NULL))
goto retry;
g_usleep (G_USEC_PER_SEC * 2);
}
@@ -1294,6 +1310,7 @@ gdaprov_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **e
ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
"%s", ldap_err2string(ldap_errno));
+ gda_ldap_may_unbind (cdata);
return NULL;
}
}
@@ -1309,7 +1326,8 @@ entry_array_sort_func (gconstpointer a, gconstpointer b)
}
GdaLdapEntry **
-gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error)
+gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes,
+ GError **error)
{
LdapConnectionData *cdata;
g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
@@ -1319,6 +1337,9 @@ gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar
if (!cdata)
return NULL;
+ if (! gda_ldap_ensure_bound (cdata, error))
+ return NULL;
+
int res;
LDAPMessage *msg = NULL;
retry:
@@ -1414,6 +1435,7 @@ gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar
g_array_append_val (children, lentry);
}
ldap_msgfree (msg);
+ gda_ldap_may_unbind (cdata);
if (children) {
g_array_sort (children, (GCompareFunc) entry_array_sort_func);
return (GdaLdapEntry**) g_array_free (children, FALSE);
@@ -1424,7 +1446,7 @@ gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar
case LDAP_SERVER_DOWN: {
gint i;
for (i = 0; i < 5; i++) {
- if (gda_ldap_silently_rebind (cdata))
+ if (gda_ldap_rebind (cdata, NULL))
goto retry;
g_usleep (G_USEC_PER_SEC * 2);
}
@@ -1435,6 +1457,7 @@ gdaprov_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar
ldap_get_option (cdata->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
"%s", ldap_err2string(ldap_errno));
+ gda_ldap_may_unbind (cdata);
return NULL;
}
}
diff --git a/providers/ldap/gda-ldap.h b/providers/ldap/gda-ldap.h
index bbd4388..baff245 100644
--- a/providers/ldap/gda-ldap.h
+++ b/providers/ldap/gda-ldap.h
@@ -36,7 +36,9 @@
* Provider's specific connection data
*/
typedef struct {
+ guint keep_bound_count; /* set to >0 if connection must remain opened */
LDAP *handle;
+
gchar *base_dn;
gchar *server_version;
gchar *url;
@@ -53,6 +55,8 @@ typedef struct {
GHashTable *classes_hash; /* key = class name, value = a #LdapClass */
} LdapConnectionData;
-gboolean gda_ldap_silently_rebind (LdapConnectionData *cdata);
+void gda_ldap_may_unbind (LdapConnectionData *cdata);
+gboolean gda_ldap_ensure_bound (LdapConnectionData *cdata, GError **error);
+gboolean gda_ldap_rebind (LdapConnectionData *cdata, GError **error);
#endif
diff --git a/providers/ldap/gdaprov-data-model-ldap.c b/providers/ldap/gdaprov-data-model-ldap.c
index acaca76..05c98f4 100644
--- a/providers/ldap/gdaprov-data-model-ldap.c
+++ b/providers/ldap/gdaprov-data-model-ldap.c
@@ -64,7 +64,7 @@ struct _LdapPart {
#define LDAP_PART(x) ((LdapPart*)(x))
static LdapPart *ldap_part_new (LdapPart *parent, const gchar *base_dn, GdaLdapSearchScope scope);
-static void ldap_part_free (LdapPart *part);
+static void ldap_part_free (LdapPart *part, LdapConnectionData *cdata);
static gboolean ldap_part_split (LdapPart *part, GdaDataModelLdap *model, gboolean *out_error);
static LdapPart *ldap_part_next (LdapPart *part, gboolean executed);
#ifdef GDA_DEBUG_SUBSEARCHES
@@ -267,7 +267,7 @@ gda_data_model_ldap_class_init (GdaDataModelLdapClass *klass)
}
static void
-gda_data_model_ldap_dispose (GObject * object)
+gda_data_model_ldap_dispose (GObject *object)
{
GdaDataModelLdap *model = (GdaDataModelLdap *) object;
@@ -295,8 +295,11 @@ gda_data_model_ldap_dispose (GObject * object)
if (model->priv->column_mv_actions)
g_array_free (model->priv->column_mv_actions, TRUE);
- if (model->priv->top_exec)
- ldap_part_free (model->priv->top_exec);
+ if (model->priv->top_exec) {
+ LdapConnectionData *cdata;
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (model->priv->cnc));
+ ldap_part_free (model->priv->top_exec, cdata);
+ }
g_free (model->priv->base_dn);
g_free (model->priv->filter);
@@ -701,6 +704,9 @@ update_iter_from_ldap_row (GdaDataModelLdap *imodel, GdaDataModelIter *iter)
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (imodel->priv->cnc));
g_return_if_fail (cdata);
+ /* LDAP connection must have been kept opened */
+ g_assert (cdata->handle);
+
g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
@@ -933,6 +939,12 @@ execute_ldap_search (GdaDataModelLdap *model)
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (model->priv->cnc));
g_return_if_fail (cdata);
+ GError *e = NULL;
+ if (! gda_ldap_ensure_bound (cdata, &e)) {
+ add_exception (model, e);
+ return;
+ }
+
g_assert (model->priv->current_exec);
g_assert (! model->priv->current_exec->executed);
@@ -997,6 +1009,10 @@ execute_ldap_search (GdaDataModelLdap *model)
/* all Ok */
model->priv->current_exec->ldap_msg = msg;
model->priv->current_exec->nb_entries = ldap_count_entries (cdata->handle, msg);
+
+ /* keep the connection opened for this LdapPart */
+ cdata->keep_bound_count ++;
+
#ifdef GDA_DEBUG_SUBSEARCHES
g_print ("model->priv->current_exec->nb_entries = %d\n",
model->priv->current_exec->nb_entries);
@@ -1038,13 +1054,16 @@ execute_ldap_search (GdaDataModelLdap *model)
model->priv->truncated = TRUE;
model->priv->current_exec->ldap_msg = msg;
model->priv->current_exec->nb_entries = ldap_count_entries (cdata->handle, msg);
+
+ /* keep the connection opened for this LdapPart */
+ cdata->keep_bound_count ++;
}
break;
}
case LDAP_SERVER_DOWN: {
gint i;
for (i = 0; i < 5; i++) {
- if (gda_ldap_silently_rebind (cdata))
+ if (gda_ldap_rebind (cdata, NULL))
goto retry;
g_usleep (G_USEC_PER_SEC * 2);
}
@@ -1057,6 +1076,7 @@ execute_ldap_search (GdaDataModelLdap *model)
g_set_error (&e, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
"%s", ldap_err2string (ldap_errno));
add_exception (model, e);
+ gda_ldap_may_unbind (cdata);
return;
}
}
@@ -1073,6 +1093,7 @@ execute_ldap_search (GdaDataModelLdap *model)
model->priv->n_rows += iter->nb_entries;
}
}
+
#ifdef GDA_DEBUG_NO
gint tmpnb = 0;
if (model->priv->top_exec->ldap_msg)
@@ -1105,7 +1126,7 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
}
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (imodel->priv->cnc));
- if (!cdata) {
+ if (!cdata || ! gda_ldap_ensure_bound (cdata, NULL)) {
/* error */
gda_data_model_iter_invalidate_contents (iter);
return FALSE;
@@ -1129,6 +1150,7 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
if (! cpart->ldap_msg) {
/* error somewhere */
gda_data_model_iter_invalidate_contents (iter);
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
@@ -1152,9 +1174,17 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
update_iter_from_ldap_row (imodel, iter);
break;
}
- else
+ else {
/* nothing more for this part, switch to the next one */
+ ldap_msgfree (imodel->priv->current_exec->ldap_msg);
+ imodel->priv->current_exec->ldap_msg = NULL;
+
+ g_assert (cdata->keep_bound_count > 0);
+ cdata->keep_bound_count --;
+ gda_ldap_may_unbind (cdata);
+
imodel->priv->current_exec = ldap_part_next (cpart, FALSE);
+ }
}
if (!imodel->priv->current_exec) {
@@ -1169,9 +1199,11 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
add_exception (imodel, e);
}
g_signal_emit_by_name (iter, "end-of-data");
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
+ gda_ldap_may_unbind (cdata);
return TRUE;
}
@@ -1235,16 +1267,23 @@ ldap_part_new (LdapPart *parent, const gchar *base_dn, GdaLdapSearchScope scope)
}
static void
-ldap_part_free (LdapPart *part)
+ldap_part_free (LdapPart *part, LdapConnectionData *cdata)
{
g_assert (part);
g_free (part->base_dn);
if (part->children) {
- g_slist_foreach (part->children, (GFunc) ldap_part_free, NULL);
+ g_slist_foreach (part->children, (GFunc) ldap_part_free, cdata);
g_slist_free (part->children);
}
- if (part->ldap_msg)
+ if (part->ldap_msg) {
ldap_msgfree (part->ldap_msg);
+
+ /* Release the connection being opened for this LdapPart */
+ g_assert (cdata);
+ g_assert (cdata->keep_bound_count > 0);
+ cdata->keep_bound_count --;
+ gda_ldap_may_unbind (cdata);
+ }
g_free (part);
}
@@ -1334,7 +1373,9 @@ ldap_part_split (LdapPart *part, GdaDataModelLdap *model, gboolean *out_error)
}
if (!sub) {
/* error */
- g_slist_foreach (part->children, (GFunc) ldap_part_free, NULL);
+ LdapConnectionData *cdata;
+ cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (model->priv->cnc));
+ g_slist_foreach (part->children, (GFunc) ldap_part_free, cdata);
g_slist_free (part->children);
part->children = NULL;
break;
@@ -1529,6 +1570,9 @@ gdaprov_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
g_return_val_if_fail (cdata, FALSE);
+ if (! gda_ldap_ensure_bound (cdata, error))
+ return FALSE;
+
/* checks */
if ((modtype != GDA_LDAP_MODIFICATION_INSERT) &&
(modtype != GDA_LDAP_MODIFICATION_ATTR_ADD) &&
@@ -1536,36 +1580,43 @@ gdaprov_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
(modtype != GDA_LDAP_MODIFICATION_ATTR_REPL) &&
(modtype != GDA_LDAP_MODIFICATION_ATTR_DIFF)) {
g_warning (_("Unknown GdaLdapModificationType %d"), modtype);
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
if (((modtype == GDA_LDAP_MODIFICATION_DELETE) || (modtype == GDA_LDAP_MODIFICATION_INSERT)) &&
!entry) {
g_warning ("%s", _("No GdaLdapEntry specified"));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
if ((modtype == GDA_LDAP_MODIFICATION_ATTR_ADD) && !entry) {
g_warning ("%s", _("No GdaLdapEntry specified to define attributes to add"));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
if ((modtype == GDA_LDAP_MODIFICATION_ATTR_DEL) && !entry) {
g_warning ("%s", _("No GdaLdapEntry specified to define attributes to remove"));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
if ((modtype == GDA_LDAP_MODIFICATION_ATTR_REPL) && !entry) {
g_warning ("%s", _("No GdaLdapEntry specified to define attributes to replace"));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
if ((modtype == GDA_LDAP_MODIFICATION_ATTR_DIFF) && (!entry || !ref_entry)) {
g_warning ("%s", _("No GdaLdapEntry specified to compare attributes"));
+ gda_ldap_may_unbind (cdata);
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"));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
@@ -1575,10 +1626,13 @@ gdaprov_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
if (res != LDAP_SUCCESS) {
g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_OTHER_ERROR,
"%s", ldap_err2string (res));
+ gda_ldap_may_unbind (cdata);
return FALSE;
}
- else
+ else {
+ gda_ldap_may_unbind (cdata);
return TRUE;
+ }
}
/* build array of modifications to perform */
@@ -1679,6 +1733,7 @@ gdaprov_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
}
g_array_free (mods_array, TRUE);
+ gda_ldap_may_unbind (cdata);
return retval;
}
@@ -1711,6 +1766,9 @@ gdaprov_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, cons
cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
g_return_val_if_fail (cdata, FALSE);
+ if (! gda_ldap_ensure_bound (cdata, error))
+ return FALSE;
+
gchar **carray, **narray;
int res;
gboolean retval = TRUE;
@@ -1734,5 +1792,6 @@ gdaprov_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, cons
retval = FALSE;
}
+ gda_ldap_may_unbind (cdata);
return retval;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]