[evolution] Bug 596763 - Make LDAP 'Find Possible Search Bases' run in dedicated thread
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 596763 - Make LDAP 'Find Possible Search Bases' run in dedicated thread
- Date: Mon, 9 Oct 2017 15:41:49 +0000 (UTC)
commit 345aa7fdcaf0ddc49714984be1230e30eb04ff0a
Author: Milan Crha <mcrha redhat com>
Date: Mon Oct 9 17:42:15 2017 +0200
Bug 596763 - Make LDAP 'Find Possible Search Bases' run in dedicated thread
src/addressbook/addressbook.error.xml | 10 +-
src/e-util/CMakeLists.txt | 14 +
src/e-util/e-misc-utils.c | 151 ++++++++++
src/e-util/e-misc-utils.h | 5 +
.../book-config-ldap/evolution-book-config-ldap.c | 293 ++++++++++++--------
5 files changed, 347 insertions(+), 126 deletions(-)
---
diff --git a/src/addressbook/addressbook.error.xml b/src/addressbook/addressbook.error.xml
index 34c5282..753072b 100644
--- a/src/addressbook/addressbook.error.xml
+++ b/src/addressbook/addressbook.error.xml
@@ -3,17 +3,17 @@
<error id="ldap-init" type="error">
<_primary>This address book could not be opened.</_primary>
- <_secondary>This address book server might be unreachable or the server name may be misspelled or your
network connection could be down.</_secondary>
+ <secondary>{0}</secondary>
</error>
- <error id="ldap-auth" type="error">
- <_primary>Failed to authenticate with LDAP server.</_primary>
- <_secondary>Check to make sure your password is spelled correctly and that you are using a supported
login method. Remember that many passwords are case sensitive; your caps lock might be on.</_secondary>
+ <error id="ldap-communicate" type="error">
+ <_primary>Failed to communicate with LDAP server.</_primary>
+ <secondary>{0}</secondary>
</error>
<error id="ldap-search-base" type="error">
<_primary>This address book server does not have any suggested search bases.</_primary>
- <_secondary>This LDAP server may use an older version of LDAP, which does not support this functionality
or it may be misconfigured. Ask your administrator for supported search bases.</_secondary>
+ <secondary>{0}</secondary>
</error>
<error id="ldap-v3-schema" type="error">
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 260191b..effd4a6 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -647,6 +647,20 @@ target_link_libraries(evolution-util
${MATH_LDFLAGS}
)
+if(HAVE_LDAP)
+ target_compile_options(evolution-util PUBLIC
+ ${LDAP_CFLAGS}
+ )
+
+ target_include_directories(evolution-util PUBLIC
+ ${LDAP_INCLUDE_DIRS}
+ )
+
+ target_link_libraries(evolution-util
+ ${LDAP_LIBS}
+ )
+endif(HAVE_LDAP)
+
install(TARGETS evolution-util
DESTINATION ${privsolibdir}
)
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index ea27449..8e96911 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -51,6 +51,13 @@
#include <webkit2/webkit2.h>
+#ifdef HAVE_LDAP
+#include <ldap.h>
+#ifndef SUNLDAP
+#include <ldap_schema.h>
+#endif
+#endif /* HAVE_LDAP */
+
#include "e-alert-dialog.h"
#include "e-alert-sink.h"
#include "e-client-cache.h"
@@ -4050,3 +4057,147 @@ e_util_resize_window_for_screen (GtkWindow *window,
gtk_window_set_default_size (GTK_WINDOW (window), width + content_width, height +
content_height);
}
}
+
+/**
+ * e_util_query_ldap_root_dse_sync:
+ * @host: an LDAP server host name
+ * @port: an LDAP server port
+ * @out_root_dse: (out) (transfer full): NULL-terminated array of the server root DSE-s, or %NULL on error
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Queries an LDAP server identified by @host and @port for supported
+ * search bases and returns them as an NULL-terminated array of strings
+ * at @out_root_dse. It sets @out_root_dse to NULL on error.
+ * Free the returned @out_root_dse with g_strfreev() when no longer needed.
+ *
+ * The function fails and sets @error to G_IO_ERROR_NOT_SUPPORTED when
+ * Evolution had been compiled without LDAP support.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.28
+ **/
+gboolean
+e_util_query_ldap_root_dse_sync (const gchar *host,
+ guint16 port,
+ gchar ***out_root_dse,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifdef HAVE_LDAP
+ G_LOCK_DEFINE_STATIC (ldap);
+ LDAP *ldap = NULL;
+ LDAPMessage *result = NULL;
+ struct timeval timeout;
+ gchar **values = NULL, **root_dse;
+ gint ldap_error;
+ gint option;
+ gint version;
+ gint ii;
+ const gchar *attrs[] = { "namingContexts", NULL };
+
+ g_return_val_if_fail (host && *host, FALSE);
+ g_return_val_if_fail (port > 0, FALSE);
+ g_return_val_if_fail (out_root_dse != NULL, FALSE);
+
+ *out_root_dse = NULL;
+
+ timeout.tv_sec = 60;
+ timeout.tv_usec = 0;
+
+ G_LOCK (ldap);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ ldap = ldap_init (host, port);
+ if (!ldap) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("This address book server might be unreachable or the server name may be misspelled
or your network connection could be down."));
+ goto exit;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ version = LDAP_VERSION3;
+ option = LDAP_OPT_PROTOCOL_VERSION;
+ ldap_error = ldap_set_option (ldap, option, &version);
+ if (ldap_error != LDAP_OPT_SUCCESS) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ _("Failed to set protocol version to LDAPv3 (%d): %s"), ldap_error,
+ ldap_err2string (ldap_error) ? ldap_err2string (ldap_error) : _("Unknown error"));
+ goto exit;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ /* FIXME Use the user's actual authentication settings. */
+ ldap_error = ldap_simple_bind_s (ldap, NULL, NULL);
+ if (ldap_error != LDAP_SUCCESS) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Failed to authenticate with LDAP server (%d): %s"), ldap_error,
+ ldap_err2string (ldap_error) ? ldap_err2string (ldap_error) : _("Unknown error"));
+ goto exit;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ ldap_error = ldap_search_ext_s (
+ ldap, LDAP_ROOT_DSE, LDAP_SCOPE_BASE,
+ "(objectclass=*)", (gchar **) attrs, 0,
+ NULL, NULL, &timeout, LDAP_NO_LIMIT, &result);
+ if (ldap_error != LDAP_SUCCESS) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("This LDAP server may use an older version of LDAP, which does not support this
functionality or it may be misconfigured. Ask your administrator for supported search bases.\n\nDetailed
error (%d): %s"),
+ ldap_error, ldap_err2string (ldap_error) ? ldap_err2string (ldap_error) : _("Unknown
error"));
+ goto exit;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ values = ldap_get_values (ldap, result, "namingContexts");
+ if (values == NULL || values[0] == NULL || *values[0] == '\0') {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("This LDAP server may use an older version of LDAP, which does not support this
functionality or it may be misconfigured. Ask your administrator for supported search bases."));
+ goto exit;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto exit;
+
+ root_dse = g_new0 (gchar *, g_strv_length (values) + 1);
+
+ for (ii = 0; values[ii]; ii++) {
+ root_dse[ii] = g_strdup (values[ii]);
+ }
+
+ root_dse[ii] = NULL;
+
+ *out_root_dse = root_dse;
+
+ exit:
+ if (values)
+ ldap_value_free (values);
+
+ if (result)
+ ldap_msgfree (result);
+
+ if (ldap)
+ ldap_unbind_s (ldap);
+
+ G_UNLOCK (ldap);
+
+ return *out_root_dse != NULL;
+
+#else /* HAVE_LDAP */
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Evolution had not been compiled with LDAP support"));
+
+ return FALSE;
+#endif
+}
diff --git a/src/e-util/e-misc-utils.h b/src/e-util/e-misc-utils.h
index 270af86..d6ce3ba 100644
--- a/src/e-util/e-misc-utils.h
+++ b/src/e-util/e-misc-utils.h
@@ -339,6 +339,11 @@ void e_util_resize_window_for_screen (GtkWindow *window,
gint window_width,
gint window_height,
const GSList *children); /* GtkWidget * */
+gboolean e_util_query_ldap_root_dse_sync (const gchar *host,
+ guint16 port,
+ gchar ***out_root_dse,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/modules/book-config-ldap/evolution-book-config-ldap.c
b/src/modules/book-config-ldap/evolution-book-config-ldap.c
index 79ea315..f54658f 100644
--- a/src/modules/book-config-ldap/evolution-book-config-ldap.c
+++ b/src/modules/book-config-ldap/evolution-book-config-ldap.c
@@ -25,11 +25,6 @@
#include <e-util/e-util.h>
-#include <ldap.h>
-#ifndef SUNLDAP
-#include <ldap_schema.h>
-#endif
-
/* Combo box ordering */
#define LDAP_PORT 389
#define LDAPS_PORT 636
@@ -115,113 +110,6 @@ book_config_ldap_context_free (Context *context)
g_slice_free (Context, context);
}
-static GtkTreeModel *
-book_config_ldap_root_dse_query (ESourceConfigBackend *backend,
- ESource *scratch_source)
-{
- LDAP *ldap;
- LDAPMessage *result = NULL;
- GtkListStore *store = NULL;
- ESourceAuthentication *extension;
- struct timeval timeout;
- const gchar *alert_id = NULL;
- const gchar *extension_name;
- const gchar *host;
- gchar **values = NULL;
- gint ldap_error;
- gint option;
- gint version;
- guint16 port;
- gint ii;
-
- const gchar *attrs[] = { "namingContexts", NULL };
-
- /* FIXME This all runs synchronously in the main loop.
- * We should do this in a separate thread behind
- * async/finish functions. May need to define
- * some custom GError codes, or maybe just an
- * LDAP GError domain that reuses LDAP result
- * codes from <ldap.h>. */
-
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- extension = e_source_get_extension (scratch_source, extension_name);
-
- host = e_source_authentication_get_host (extension);
- port = e_source_authentication_get_port (extension);
-
- timeout.tv_sec = 60;
- timeout.tv_usec = 0;
-
- ldap = ldap_init (host, port);
- if (ldap == NULL) {
- alert_id = "addressbook:ldap-init";
- goto exit;
- }
-
- version = LDAP_VERSION3;
- option = LDAP_OPT_PROTOCOL_VERSION;
- if (ldap_set_option (ldap, option, &version) != LDAP_SUCCESS) {
- /* XXX Define an alert for this. */
- g_warning ("Failed to set protocol version to LDAPv3");
- goto exit;
- }
-
- /* FIXME Use the user's actual authentication settings. */
- if (ldap_simple_bind_s (ldap, NULL, NULL) != LDAP_SUCCESS) {
- alert_id = "addressbook:ldap-auth";
- goto exit;
- }
-
- ldap_error = ldap_search_ext_s (
- ldap, LDAP_ROOT_DSE, LDAP_SCOPE_BASE,
- "(objectclass=*)", (gchar **) attrs, 0,
- NULL, NULL, &timeout, LDAP_NO_LIMIT, &result);
- if (ldap_error != LDAP_SUCCESS) {
- alert_id = "addressbook:ldap-search-base";
- goto exit;
- }
-
- values = ldap_get_values (ldap, result, "namingContexts");
- if (values == NULL || values[0] == NULL || *values[0] == '\0') {
- alert_id = "addressbook:ldap-search-base";
- goto exit;
- }
-
- store = gtk_list_store_new (1, G_TYPE_STRING);
-
- for (ii = 0; values[ii] != NULL; ii++) {
- GtkTreeIter iter;
-
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter, 0, values[ii], -1);
- }
-
-exit:
- if (alert_id != NULL) {
- ESourceConfig *config;
- gpointer parent;
-
- config = e_source_config_backend_get_config (backend);
-
- parent = gtk_widget_get_toplevel (GTK_WIDGET (config));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- e_alert_run_dialog_for_args (parent, alert_id, NULL);
- }
-
- if (values != NULL)
- ldap_value_free (values);
-
- if (result != NULL)
- ldap_msgfree (result);
-
- if (ldap != NULL)
- ldap_unbind_s (ldap);
-
- /* This may be NULL, so don't use a cast macro. */
- return (GtkTreeModel *) store;
-}
-
static gboolean
book_config_ldap_port_to_active (GBinding *binding,
const GValue *source_value,
@@ -372,28 +260,191 @@ book_config_ldap_port_to_security (GBinding *binding,
return FALSE;
}
+typedef struct _SearchBaseData {
+ GtkWindow *parent; /* not referenced */
+ GtkWidget *search_base_combo;
+ GtkWidget *dialog;
+ GCancellable *cancellable;
+ ESource *source;
+ gchar **root_dse;
+ GError *error;
+} SearchBaseData;
+
+static void
+search_base_data_free (gpointer ptr)
+{
+ SearchBaseData *sbd = ptr;
+
+ if (sbd) {
+ if (sbd->dialog)
+ gtk_widget_destroy (sbd->dialog);
+ g_clear_object (&sbd->search_base_combo);
+ g_clear_object (&sbd->cancellable);
+ g_clear_object (&sbd->source);
+ g_clear_error (&sbd->error);
+ g_strfreev (sbd->root_dse);
+ g_free (sbd);
+ }
+}
+
+static void
+book_config_ldap_search_base_thread (ESimpleAsyncResult *result,
+ gpointer source_object,
+ GCancellable *cancellable)
+{
+ ESourceAuthentication *extension;
+ SearchBaseData *sbd;
+
+ g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+
+ sbd = e_simple_async_result_get_user_data (result);
+
+ g_return_if_fail (sbd != NULL);
+
+ extension = e_source_get_extension (sbd->source, E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ if (!e_util_query_ldap_root_dse_sync (
+ e_source_authentication_get_host (extension),
+ e_source_authentication_get_port (extension),
+ &sbd->root_dse, cancellable, &sbd->error)) {
+ sbd->root_dse = NULL;
+ }
+}
+
+static void
+book_config_ldap_search_base_done (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SearchBaseData *sbd = user_data;
+ gboolean was_cancelled = FALSE;
+
+ g_return_if_fail (E_IS_SOURCE_CONFIG_BACKEND (source_object));
+ g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+
+ sbd = e_simple_async_result_get_user_data (E_SIMPLE_ASYNC_RESULT (result));
+ g_return_if_fail (sbd != NULL);
+
+ if (!g_cancellable_is_cancelled (sbd->cancellable)) {
+ if (sbd->dialog) {
+ gtk_widget_destroy (sbd->dialog);
+ sbd->dialog = NULL;
+ }
+ } else {
+ was_cancelled = TRUE;
+ }
+
+ if (!was_cancelled) {
+ if (sbd->error) {
+ const gchar *alert_id;
+
+ if (g_error_matches (sbd->error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+ alert_id = "addressbook:ldap-init";
+ else if (g_error_matches (sbd->error, G_IO_ERROR, G_IO_ERROR_FAILED))
+ alert_id = "addressbook:ldap-search-base";
+ else
+ alert_id = "addressbook:ldap-communicate";
+
+ e_alert_run_dialog_for_args (sbd->parent, alert_id, sbd->error->message, NULL);
+ } else if (sbd->root_dse) {
+ GtkComboBox *combo_box;
+ GtkListStore *store;
+ gint ii;
+
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+
+ for (ii = 0; sbd->root_dse[ii]; ii++) {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, sbd->root_dse[ii], -1);
+ }
+
+ combo_box = GTK_COMBO_BOX (sbd->search_base_combo);
+ gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+ gtk_combo_box_set_active (combo_box, 0);
+
+ g_clear_object (&store);
+ }
+ }
+}
+
+static void
+search_base_data_response_cb (GtkWidget *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ SearchBaseData *sbd = user_data;
+
+ g_return_if_fail (sbd != NULL);
+ g_return_if_fail (sbd->dialog == dialog);
+
+ sbd->dialog = NULL;
+
+ g_cancellable_cancel (sbd->cancellable);
+ gtk_widget_destroy (dialog);
+}
+
static void
book_config_ldap_search_base_button_clicked_cb (GtkButton *button,
Closure *closure)
{
+ GtkWidget *dialog, *label, *content, *spinner, *box;
+ GtkWindow *parent;
+ ESimpleAsyncResult *result;
Context *context;
- GtkComboBox *combo_box;
- GtkTreeModel *model;
+ SearchBaseData *sbd;
const gchar *uid;
uid = e_source_get_uid (closure->scratch_source);
context = g_object_get_data (G_OBJECT (closure->backend), uid);
g_return_if_fail (context != NULL);
- model = book_config_ldap_root_dse_query (
- closure->backend, closure->scratch_source);
+ dialog = gtk_widget_get_toplevel (context->search_base_combo);
+ parent = GTK_IS_WINDOW (dialog) ? GTK_WINDOW (dialog) : NULL;
+
+ dialog = gtk_dialog_new_with_buttons ("",
+ parent,
+ GTK_DIALOG_MODAL,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ spinner = e_spinner_new ();
+ e_spinner_start (E_SPINNER (spinner));
+ gtk_box_pack_start (GTK_BOX (box), spinner, FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("Looking up server search bases, please wait…"));
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (box);
+
+ content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ gtk_container_add (GTK_CONTAINER (content), box);
+ gtk_container_set_border_width (GTK_CONTAINER (content), 12);
+
+ sbd = g_new0 (SearchBaseData, 1);
+ sbd->parent = parent;
+ sbd->search_base_combo = g_object_ref (context->search_base_combo);
+ sbd->dialog = dialog;
+ sbd->cancellable = g_cancellable_new ();
+ sbd->source = g_object_ref (closure->scratch_source);
+
+ result = e_simple_async_result_new (G_OBJECT (closure->backend),
+ book_config_ldap_search_base_done, NULL, book_config_ldap_search_base_done);
+
+ e_simple_async_result_set_user_data (result, sbd, search_base_data_free);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (search_base_data_response_cb), sbd);
+
+ e_simple_async_result_run_in_thread (result, G_PRIORITY_DEFAULT,
+ book_config_ldap_search_base_thread, sbd->cancellable);
- combo_box = GTK_COMBO_BOX (context->search_base_combo);
- gtk_combo_box_set_model (combo_box, model);
- gtk_combo_box_set_active (combo_box, 0);
+ g_object_unref (result);
- if (model != NULL)
- g_object_unref (model);
+ gtk_dialog_run (GTK_DIALOG (dialog));
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]