[evolution/account-mgmt: 30/51] Add 'book-config-ldap' module.



commit 0a930709ebf911ff0c5310ea0067c3d54aa20aa0
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Dec 31 10:49:54 2010 -0500

    Add 'book-config-ldap' module.
    
    Register the "On LDAP Servers" backend in EBookSourceConfig widgets.

 configure.ac                                       |    1 +
 modules/Makefile.am                                |    5 +
 modules/book-config-ldap/Makefile.am               |   31 +
 modules/book-config-ldap/e-source-ldap.c           |  657 +++++++++++++
 modules/book-config-ldap/e-source-ldap.h           |  123 +++
 .../book-config-ldap/evolution-book-config-ldap.c  |  990 ++++++++++++++++++++
 6 files changed, 1807 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e4ab9cf..a98e526 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1663,6 +1663,7 @@ modules/bogofilter/Makefile
 modules/calendar/Makefile
 modules/mail/Makefile
 modules/book-config-google/Makefile
+modules/book-config-ldap/Makefile
 modules/book-config-local/Makefile
 modules/composer-autosave/Makefile
 modules/connman/Makefile
diff --git a/modules/Makefile.am b/modules/Makefile.am
index a27311d..31e5f40 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -1,3 +1,7 @@
+if ENABLE_LDAP
+CONFIG_LDAP_DIR = book-config-ldap
+endif
+
 if ENABLE_MONO
 MONO_DIR = plugin-mono
 endif
@@ -28,6 +32,7 @@ SUBDIRS = \
 	calendar \
 	mail \
 	book-config-google \
+	$(CONFIG_LDAP_DIR) \
 	book-config-local \
 	composer-autosave \
 	mailto-handler \
diff --git a/modules/book-config-ldap/Makefile.am b/modules/book-config-ldap/Makefile.am
new file mode 100644
index 0000000..1aabfab
--- /dev/null
+++ b/modules/book-config-ldap/Makefile.am
@@ -0,0 +1,31 @@
+module_LTLIBRARIES = libevolution-module-book-config-ldap.la
+
+libevolution_module_book_config_ldap_la_CPPFLAGS =		\
+	$(AM_CPPFLAGS)						\
+	-I$(top_srcdir)						\
+	-I$(top_srcdir)/widgets					\
+	-DG_LOG_DOMAIN=\"evolution-book-config-ldap\"		\
+	$(EVOLUTION_DATA_SERVER_CFLAGS)				\
+	$(GNOME_PLATFORM_CFLAGS)				\
+	$(LDAP_CFLAGS)
+
+libevolution_module_book_config_ldap_la_SOURCES =		\
+	evolution-book-config-ldap.c				\
+	e-source-ldap.c						\
+	e-source-ldap.h
+
+libevolution_module_book_config_ldap_la_LIBADD =		\
+	$(top_builddir)/e-util/libeutil.la			\
+	$(top_builddir)/widgets/misc/libemiscwidgets.la		\
+	$(top_builddir)/addressbook/printing/libecontactprint.la \
+	$(top_builddir)/addressbook/gui/merging/libeabbookmerging.la \
+	$(top_builddir)/addressbook/gui/widgets/libeabwidgets.la \
+	$(top_builddir)/addressbook/util/libeabutil.la		\
+	$(EVOLUTION_DATA_SERVER_LIBS)				\
+	$(GNOME_PLATFORM_LIBS)					\
+	$(LDAP_LIBS)
+
+libevolution_module_book_config_ldap_la_LDFLAGS =		\
+	-module -avoid-version $(NO_UNDEFINED)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/book-config-ldap/e-source-ldap.c b/modules/book-config-ldap/e-source-ldap.c
new file mode 100644
index 0000000..e0b02d1
--- /dev/null
+++ b/modules/book-config-ldap/e-source-ldap.c
@@ -0,0 +1,657 @@
+/*
+ * e-source-ldap.c
+ *
+ * This program 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) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-source-ldap.h"
+
+#include <ldap.h>
+#include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-security.h>
+
+#define E_SOURCE_LDAP_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SOURCE_LDAP, ESourceLDAPPrivate))
+
+struct _ESourceLDAPPrivate {
+	GMutex *property_lock;
+	gboolean can_browse;
+	gchar *filter;
+	guint limit;
+	gchar *root_dn;
+	ESourceLDAPScope scope;
+
+	/* These are bound to other extensions. */
+	ESourceLDAPAuthentication authentication;
+	ESourceLDAPSecurity security;
+};
+
+enum {
+	PROP_0,
+	PROP_AUTHENTICATION,
+	PROP_CAN_BROWSE,
+	PROP_FILTER,
+	PROP_LIMIT,
+	PROP_ROOT_DN,
+	PROP_SCOPE,
+	PROP_SECURITY
+};
+
+static GType e_source_ldap_authentication_type = G_TYPE_INVALID;
+static GType e_source_ldap_scope_type = G_TYPE_INVALID;
+static GType e_source_ldap_security_type = G_TYPE_INVALID;
+
+G_DEFINE_DYNAMIC_TYPE (
+	ESourceLDAP,
+	e_source_ldap,
+	E_TYPE_SOURCE_EXTENSION)
+
+static gboolean
+source_ldap_transform_enum_nick_to_value (GBinding *binding,
+                                          const GValue *source_value,
+                                          GValue *target_value,
+                                          gpointer not_used)
+{
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+	const gchar *string;
+	gboolean success = FALSE;
+
+	enum_class = g_type_class_peek (G_VALUE_TYPE (target_value));
+	g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);
+
+	string = g_value_get_string (source_value);
+	enum_value = g_enum_get_value_by_nick (enum_class, string);
+	if (enum_value != NULL) {
+		g_value_set_enum (target_value, enum_value->value);
+		success = TRUE;
+	}
+
+	return success;
+}
+
+static gboolean
+source_ldap_transform_enum_value_to_nick (GBinding *binding,
+                                          const GValue *source_value,
+                                          GValue *target_value,
+                                          gpointer not_used)
+{
+	GEnumClass *enum_class;
+	GEnumValue *enum_value;
+	gint value;
+	gboolean success = FALSE;
+
+	enum_class = g_type_class_peek (G_VALUE_TYPE (source_value));
+	g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);
+
+	value = g_value_get_enum (source_value);
+	enum_value = g_enum_get_value (enum_class, value);
+	if (enum_value != NULL) {
+		g_value_set_string (target_value, enum_value->value_nick);
+		success = TRUE;
+	}
+
+	return success;
+}
+
+static void
+source_ldap_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTHENTICATION:
+			e_source_ldap_set_authentication (
+				E_SOURCE_LDAP (object),
+				g_value_get_enum (value));
+			return;
+
+		case PROP_CAN_BROWSE:
+			e_source_ldap_set_can_browse (
+				E_SOURCE_LDAP (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_FILTER:
+			e_source_ldap_set_filter (
+				E_SOURCE_LDAP (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_LIMIT:
+			e_source_ldap_set_limit (
+				E_SOURCE_LDAP (object),
+				g_value_get_uint (value));
+			return;
+
+		case PROP_ROOT_DN:
+			e_source_ldap_set_root_dn (
+				E_SOURCE_LDAP (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_SCOPE:
+			e_source_ldap_set_scope (
+				E_SOURCE_LDAP (object),
+				g_value_get_enum (value));
+			return;
+
+		case PROP_SECURITY:
+			e_source_ldap_set_security (
+				E_SOURCE_LDAP (object),
+				g_value_get_enum (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_ldap_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_AUTHENTICATION:
+			g_value_set_enum (
+				value,
+				e_source_ldap_get_authentication (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_CAN_BROWSE:
+			g_value_set_boolean (
+				value,
+				e_source_ldap_get_can_browse (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_FILTER:
+			g_value_take_string (
+				value,
+				e_source_ldap_dup_filter (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_LIMIT:
+			g_value_set_uint (
+				value,
+				e_source_ldap_get_limit (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_ROOT_DN:
+			g_value_take_string (
+				value,
+				e_source_ldap_dup_root_dn (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_SCOPE:
+			g_value_set_enum (
+				value,
+				e_source_ldap_get_scope (
+				E_SOURCE_LDAP (object)));
+			return;
+
+		case PROP_SECURITY:
+			g_value_set_enum (
+				value,
+				e_source_ldap_get_security (
+				E_SOURCE_LDAP (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_ldap_finalize (GObject *object)
+{
+	ESourceLDAPPrivate *priv;
+
+	priv = E_SOURCE_LDAP_GET_PRIVATE (object);
+
+	g_mutex_free (priv->property_lock);
+
+	g_free (priv->filter);
+	g_free (priv->root_dn);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_source_ldap_parent_class)->finalize (object);
+}
+
+static void
+source_ldap_constructed (GObject *object)
+{
+	ESource *source;
+	ESourceExtension *this_extension;
+	ESourceExtension *other_extension;
+	const gchar *extension_name;
+
+	this_extension = E_SOURCE_EXTENSION (object);
+	source = e_source_extension_get_source (this_extension);
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	other_extension = e_source_get_extension (source, extension_name);
+
+	g_object_bind_property_full (
+		other_extension, "method",
+		this_extension, "authentication",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_ldap_transform_enum_nick_to_value,
+		source_ldap_transform_enum_value_to_nick,
+		NULL, (GDestroyNotify) NULL);
+
+	extension_name = E_SOURCE_EXTENSION_SECURITY;
+	other_extension = e_source_get_extension (source, extension_name);
+
+	g_object_bind_property_full (
+		other_extension, "method",
+		this_extension, "security",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		source_ldap_transform_enum_nick_to_value,
+		source_ldap_transform_enum_value_to_nick,
+		NULL, (GDestroyNotify) NULL);
+}
+
+static void
+e_source_ldap_class_init (ESourceLDAPClass *class)
+{
+	GObjectClass *object_class;
+	ESourceExtensionClass *extension_class;
+
+	g_type_class_add_private (class, sizeof (ESourceLDAPPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = source_ldap_set_property;
+	object_class->get_property = source_ldap_get_property;
+	object_class->finalize = source_ldap_finalize;
+	object_class->constructed = source_ldap_constructed;
+
+	extension_class = E_SOURCE_EXTENSION_CLASS (class);
+	extension_class->name = E_SOURCE_EXTENSION_LDAP_BACKEND;
+
+	/* This is bound to the authentication extension.
+	 * Do not use E_SOURCE_PARAM_SETTING here. */
+	g_object_class_install_property (
+		object_class,
+		PROP_AUTHENTICATION,
+		g_param_spec_enum (
+			"authentication",
+			"Authentication",
+			"LDAP authentication method",
+			E_TYPE_SOURCE_LDAP_AUTHENTICATION,
+			E_SOURCE_LDAP_AUTHENTICATION_NONE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CAN_BROWSE,
+		g_param_spec_boolean (
+			"can-browse",
+			"Can Browse",
+			"Allow browsing contacts",
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FILTER,
+		g_param_spec_string (
+			"filter",
+			"Filter",
+			"LDAP search filter",
+			"",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_LIMIT,
+		g_param_spec_uint (
+			"limit",
+			"Limit",
+			"Download limit",
+			0, G_MAXUINT, 100,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ROOT_DN,
+		g_param_spec_string (
+			"root-dn",
+			"Root DN",
+			"LDAP search base",
+			"",
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SCOPE,
+		g_param_spec_enum (
+			"scope",
+			"Scope",
+			"LDAP search scope",
+			E_TYPE_SOURCE_LDAP_SCOPE,
+			E_SOURCE_LDAP_SCOPE_ONELEVEL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	/* This is bound to the security extension.
+	 * Do not use E_SOURCE_PARAM_SETTING here. */
+	g_object_class_install_property (
+		object_class,
+		PROP_SECURITY,
+		g_param_spec_enum (
+			"security",
+			"Security",
+			"LDAP security method",
+			E_TYPE_SOURCE_LDAP_SECURITY,
+			E_SOURCE_LDAP_SECURITY_NONE,
+			G_PARAM_READWRITE));
+}
+
+static void
+e_source_ldap_class_finalize (ESourceLDAPClass *class)
+{
+}
+
+static void
+e_source_ldap_init (ESourceLDAP *extension)
+{
+	extension->priv = E_SOURCE_LDAP_GET_PRIVATE (extension);
+	extension->priv->property_lock = g_mutex_new ();
+}
+
+void
+e_source_ldap_type_register (GTypeModule *type_module)
+{
+	static const GEnumValue e_source_ldap_authentication_values[] = {
+		{ E_SOURCE_LDAP_AUTHENTICATION_NONE,
+		  "E_SOURCE_LDAP_AUTHENTICATION_NONE",
+		  "none" },
+		{ E_SOURCE_LDAP_AUTHENTICATION_EMAIL,
+		  "E_SOURCE_LDAP_AUTHENTICATION_EMAIL",
+		  "ldap/simple-email" },
+		{ E_SOURCE_LDAP_AUTHENTICATION_BINDDN,
+		  "E_SOURCE_LDAP_AUTHENTICATION_BINDDN",
+		  "ldap/simple-binddn" },
+		{ 0, NULL, NULL }
+	};
+
+	static const GEnumValue e_source_ldap_scope_values[] = {
+		{ E_SOURCE_LDAP_SCOPE_ONELEVEL,
+		  "E_SOURCE_LDAP_SCOPE_ONELEVEL",
+		  "onelevel" },
+		{ E_SOURCE_LDAP_SCOPE_SUBTREE,
+		  "E_SOURCE_LDAP_SCOPE_SUBTREE",
+		  "subtree" },
+		{ 0, NULL, NULL }
+	};
+
+	static const GEnumValue e_source_ldap_security_values[] = {
+		{ E_SOURCE_LDAP_SECURITY_NONE,
+		  "E_SOURCE_LDAP_SECURITY_NONE",
+		  "none" },
+		{ E_SOURCE_LDAP_SECURITY_LDAPS,
+		  "E_SOURCE_LDAP_SECURITY_LDAPS",
+		  "ldaps" },
+		{ E_SOURCE_LDAP_SECURITY_STARTTLS,
+		  "E_SOURCE_LDAP_SECURITY_STARTTLS",
+		  "starttls" },
+		{ 0, NULL, NULL }
+	};
+
+	e_source_ldap_authentication_type =
+		g_type_module_register_enum (
+		type_module, "ESourceLDAPAuthentication",
+		e_source_ldap_authentication_values);
+
+	e_source_ldap_scope_type =
+		g_type_module_register_enum (
+		type_module, "ESourceLDAPScope",
+		e_source_ldap_scope_values);
+
+	e_source_ldap_security_type =
+		g_type_module_register_enum (
+		type_module, "ESourceLDAPSecurity",
+		e_source_ldap_security_values);
+
+	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+	 *     function, so we have to wrap it with a public function in
+	 *     order to register types from a separate compilation unit. */
+	e_source_ldap_register_type (type_module);
+}
+
+ESourceLDAPAuthentication
+e_source_ldap_get_authentication (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), 0);
+
+	return extension->priv->authentication;
+}
+
+void
+e_source_ldap_set_authentication (ESourceLDAP *extension,
+                                  ESourceLDAPAuthentication authentication)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	extension->priv->authentication = authentication;
+
+	g_object_notify (G_OBJECT (extension), "authentication");
+}
+
+gboolean
+e_source_ldap_get_can_browse (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), FALSE);
+
+	return extension->priv->can_browse;
+}
+
+void
+e_source_ldap_set_can_browse (ESourceLDAP *extension,
+                              gboolean can_browse)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	extension->priv->can_browse = can_browse;
+
+	g_object_notify (G_OBJECT (extension), "can-browse");
+}
+
+const gchar *
+e_source_ldap_get_filter (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), NULL);
+
+	return extension->priv->filter;
+}
+
+gchar *
+e_source_ldap_dup_filter (ESourceLDAP *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_ldap_get_filter (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+void
+e_source_ldap_set_filter (ESourceLDAP *extension,
+                          const gchar *filter)
+{
+	gboolean needs_parens;
+
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	needs_parens =
+		(filter != NULL) && (*filter != '\0') &&
+		!g_str_has_prefix (filter, "(") &&
+		!g_str_has_suffix (filter, ")");
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->filter);
+	if (needs_parens)
+		extension->priv->filter = g_strdup_printf ("(%s)", filter);
+	else
+		extension->priv->filter = g_strdup (filter);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "filter");
+}
+
+guint
+e_source_ldap_get_limit (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), 0);
+
+	return extension->priv->limit;
+}
+
+void
+e_source_ldap_set_limit (ESourceLDAP *extension,
+                         guint limit)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	extension->priv->limit = limit;
+
+	g_object_notify (G_OBJECT (extension), "limit");
+}
+
+const gchar *
+e_source_ldap_get_root_dn (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), NULL);
+
+	return extension->priv->root_dn;
+}
+
+gchar *
+e_source_ldap_dup_root_dn (ESourceLDAP *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), NULL);
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	protected = e_source_ldap_get_root_dn (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	return duplicate;
+}
+
+void
+e_source_ldap_set_root_dn (ESourceLDAP *extension,
+                           const gchar *root_dn)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	g_mutex_lock (extension->priv->property_lock);
+
+	g_free (extension->priv->root_dn);
+	extension->priv->root_dn = g_strdup (root_dn);
+
+	g_mutex_unlock (extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "root-dn");
+}
+
+ESourceLDAPScope
+e_source_ldap_get_scope (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), 0);
+
+	return extension->priv->scope;
+}
+
+void
+e_source_ldap_set_scope (ESourceLDAP *extension,
+                         ESourceLDAPScope scope)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	extension->priv->scope = scope;
+
+	g_object_notify (G_OBJECT (extension), "scope");
+}
+
+ESourceLDAPSecurity
+e_source_ldap_get_security (ESourceLDAP *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_LDAP (extension), 0);
+
+	return extension->priv->security;
+}
+
+void
+e_source_ldap_set_security (ESourceLDAP *extension,
+                            ESourceLDAPSecurity security)
+{
+	g_return_if_fail (E_IS_SOURCE_LDAP (extension));
+
+	extension->priv->security = security;
+
+	g_object_notify (G_OBJECT (extension), "security");
+}
+
+GType
+e_source_ldap_authentication_get_type (void)
+{
+	return e_source_ldap_authentication_type;
+}
+
+GType
+e_source_ldap_scope_get_type (void)
+{
+	return e_source_ldap_scope_type;
+}
+
+GType
+e_source_ldap_security_get_type (void)
+{
+	return e_source_ldap_security_type;
+}
diff --git a/modules/book-config-ldap/e-source-ldap.h b/modules/book-config-ldap/e-source-ldap.h
new file mode 100644
index 0000000..ea9cb9f
--- /dev/null
+++ b/modules/book-config-ldap/e-source-ldap.h
@@ -0,0 +1,123 @@
+/*
+ * e-source-ldap.h
+ *
+ * This program 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) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_SOURCE_LDAP_H
+#define E_SOURCE_LDAP_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_LDAP \
+	(e_source_ldap_get_type ())
+#define E_SOURCE_LDAP(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SOURCE_LDAP, ESourceLDAP))
+#define E_SOURCE_LDAP_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SOURCE_LDAP, ESourceLDAPClass))
+#define E_IS_SOURCE_LDAP(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SOURCE_LDAP))
+#define E_IS_SOURCE_LDAP_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SOURCE_LDAP))
+#define E_SOURCE_LDAP_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SOURCE_LDAP, ESourceLDAPClass))
+
+#define E_TYPE_SOURCE_LDAP_AUTHENTICATION \
+	(e_source_ldap_authentication_get_type ())
+
+#define E_TYPE_SOURCE_LDAP_SCOPE \
+	(e_source_ldap_scope_get_type ())
+
+#define E_TYPE_SOURCE_LDAP_SECURITY \
+	(e_source_ldap_security_get_type ())
+
+#define E_SOURCE_EXTENSION_LDAP_BACKEND "LDAP Backend"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceLDAP ESourceLDAP;
+typedef struct _ESourceLDAPClass ESourceLDAPClass;
+typedef struct _ESourceLDAPPrivate ESourceLDAPPrivate;
+
+struct _ESourceLDAP {
+	ESourceExtension parent;
+	ESourceLDAPPrivate *priv;
+};
+
+struct _ESourceLDAPClass {
+	ESourceExtensionClass parent_class;
+};
+
+typedef enum {
+	E_SOURCE_LDAP_AUTHENTICATION_NONE,
+	E_SOURCE_LDAP_AUTHENTICATION_EMAIL,
+	E_SOURCE_LDAP_AUTHENTICATION_BINDDN
+} ESourceLDAPAuthentication;
+
+typedef enum {
+	E_SOURCE_LDAP_SCOPE_ONELEVEL,
+	E_SOURCE_LDAP_SCOPE_SUBTREE
+} ESourceLDAPScope;
+
+typedef enum {
+	E_SOURCE_LDAP_SECURITY_NONE,
+	E_SOURCE_LDAP_SECURITY_LDAPS,
+	E_SOURCE_LDAP_SECURITY_STARTTLS
+} ESourceLDAPSecurity;
+
+GType		e_source_ldap_get_type		(void);
+void		e_source_ldap_type_register	(GTypeModule *type_module);
+ESourceLDAPAuthentication
+		e_source_ldap_get_authentication
+						(ESourceLDAP *extension);
+void		e_source_ldap_set_authentication
+						(ESourceLDAP *extension,
+						 ESourceLDAPAuthentication authentication);
+gboolean	e_source_ldap_get_can_browse	(ESourceLDAP *extension);
+void		e_source_ldap_set_can_browse	(ESourceLDAP *extension,
+						 gboolean can_browse);
+const gchar *	e_source_ldap_get_filter	(ESourceLDAP *extension);
+gchar *		e_source_ldap_dup_filter	(ESourceLDAP *extension);
+void		e_source_ldap_set_filter	(ESourceLDAP *extension,
+						 const gchar *filter);
+guint		e_source_ldap_get_limit		(ESourceLDAP *extension);
+void		e_source_ldap_set_limit		(ESourceLDAP *extension,
+						 guint limit);
+const gchar *	e_source_ldap_get_root_dn	(ESourceLDAP *extension);
+gchar *		e_source_ldap_dup_root_dn	(ESourceLDAP *extension);
+void		e_source_ldap_set_root_dn	(ESourceLDAP *extension,
+						 const gchar *root_dn);
+ESourceLDAPScope
+		e_source_ldap_get_scope		(ESourceLDAP *extension);
+void		e_source_ldap_set_scope		(ESourceLDAP *extension,
+						 ESourceLDAPScope scope);
+ESourceLDAPSecurity
+		e_source_ldap_get_security	(ESourceLDAP *extension);
+void		e_source_ldap_set_security	(ESourceLDAP *extension,
+						 ESourceLDAPSecurity security);
+
+GType		e_source_ldap_authentication_get_type	(void);
+GType		e_source_ldap_scope_get_type		(void);
+GType		e_source_ldap_security_get_type		(void);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_LDAP_H */
diff --git a/modules/book-config-ldap/evolution-book-config-ldap.c b/modules/book-config-ldap/evolution-book-config-ldap.c
new file mode 100644
index 0000000..a957e4c
--- /dev/null
+++ b/modules/book-config-ldap/evolution-book-config-ldap.c
@@ -0,0 +1,990 @@
+/*
+ * evolution-book-config-ldap.c
+ *
+ * This program 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) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+
+#include <libebackend/e-extension.h>
+#include <libedataserver/e-source-authentication.h>
+
+#include <libevolution-utils/e-alert-dialog.h>
+#include <misc/e-source-config-backend.h>
+#include <addressbook/gui/widgets/e-book-source-config.h>
+
+#include "e-source-ldap.h"
+
+#ifndef G_OS_WIN32
+#include <ldap.h>
+#ifndef SUNLDAP
+#include <ldap_schema.h>
+#endif
+#else
+#include <winldap.h>
+#include "openldap-extract.h"
+#endif
+
+/* Combo box ordering */
+#define LDAP_PORT  389
+#define LDAPS_PORT 636
+#define MSGC_PORT  3268
+#define MSGCS_PORT 3269
+
+typedef ESourceConfigBackend EBookConfigLDAP;
+typedef ESourceConfigBackendClass EBookConfigLDAPClass;
+
+typedef struct _Closure Closure;
+typedef struct _Context Context;
+
+struct _Closure {
+	ESourceConfigBackend *backend;
+	ESource *scratch_source;
+};
+
+struct _Context {
+	GtkWidget *auth_combo;
+	GtkWidget *auth_entry;
+	GtkWidget *host_entry;
+	GtkWidget *port_combo;
+	GtkWidget *security_combo;
+	GtkWidget *search_base_combo;
+	GtkWidget *search_base_button;
+	GtkWidget *search_scope_combo;
+	GtkWidget *search_filter_entry;
+	GtkWidget *limit_spinbutton;
+	GtkWidget *can_browse_toggle;
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_book_config_ldap_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (
+	EBookConfigLDAP,
+	e_book_config_ldap,
+	E_TYPE_SOURCE_CONFIG_BACKEND)
+
+static Closure *
+book_config_ldap_closure_new (ESourceConfigBackend *backend,
+                              ESource *scratch_source)
+{
+	Closure *closure;
+
+	closure = g_slice_new (Closure);
+	closure->backend = g_object_ref (backend);
+	closure->scratch_source = g_object_ref (scratch_source);
+
+	return closure;
+}
+
+static void
+book_config_ldap_closure_free (Closure *closure)
+{
+	g_object_unref (closure->backend);
+	g_object_unref (closure->scratch_source);
+
+	g_slice_free (Closure, closure);
+}
+
+static void
+book_config_ldap_context_free (Context *context)
+{
+	g_object_unref (context->auth_combo);
+	g_object_unref (context->auth_entry);
+	g_object_unref (context->host_entry);
+	g_object_unref (context->port_combo);
+	g_object_unref (context->security_combo);
+	g_object_unref (context->search_base_combo);
+	g_object_unref (context->search_base_button);
+	g_object_unref (context->search_scope_combo);
+	g_object_unref (context->search_filter_entry);
+	g_object_unref (context->limit_spinbutton);
+	g_object_unref (context->can_browse_toggle);
+
+	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,
+                                 GValue *target_value,
+                                 gpointer unused)
+{
+	guint port;
+	gint active;
+
+	port = g_value_get_uint (source_value);
+
+	switch (port) {
+		case 0:  /* initialize to LDAP_PORT */
+		case LDAP_PORT:
+			active = 0;
+			break;
+
+		case LDAPS_PORT:
+			active = 1;
+			break;
+
+		case MSGC_PORT:
+			active = 2;
+			break;
+
+		case MSGCS_PORT:
+			active = 3;
+			break;
+
+		default:
+			active = -1;
+			break;
+	}
+
+	g_value_set_int (target_value, active);
+
+	if (active == -1) {
+		GObject *target;
+		GtkWidget *entry;
+		gchar *text;
+
+		target = g_binding_get_target (binding);
+		entry = gtk_bin_get_child (GTK_BIN (target));
+
+		text = g_strdup_printf ("%u", port);
+		gtk_entry_set_text (GTK_ENTRY (entry), text);
+		g_free (text);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+book_config_ldap_active_to_port (GBinding *binding,
+                                 const GValue *source_value,
+                                 GValue *target_value,
+                                 gpointer unused)
+{
+	guint port = LDAP_PORT;
+	gint active;
+
+	active = g_value_get_int (source_value);
+
+	switch (active) {
+		case 0:
+			port = LDAP_PORT;
+			break;
+
+		case 1:
+			port = LDAPS_PORT;
+			break;
+
+		case 2:
+			port = MSGC_PORT;
+			break;
+
+		case 3:
+			port = MSGCS_PORT;
+			break;
+
+		default:
+			active = -1;
+			break;
+	}
+
+	if (active == -1) {
+		GObject *source;
+		GtkWidget *entry;
+		const gchar *text;
+		glong v_long;
+
+		source = g_binding_get_source (binding);
+		entry = gtk_bin_get_child (GTK_BIN (source));
+		text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+		v_long = strtol (text, NULL, 10);
+		if (v_long != 0 && v_long == CLAMP (v_long, 0, G_MAXUINT16))
+			port = (guint) v_long;
+	}
+
+	g_value_set_uint (target_value, port);
+
+	return TRUE;
+}
+
+static gboolean
+book_config_ldap_port_to_security (GBinding *binding,
+                                   const GValue *source_value,
+                                   GValue *target_value,
+                                   gpointer unused)
+{
+	switch (g_value_get_int (source_value)) {
+		case 0:  /* LDAP_PORT -> StartTLS */
+			g_value_set_int (
+				target_value,
+				E_SOURCE_LDAP_SECURITY_STARTTLS);
+			return TRUE;
+
+		case 1:  /* LDAPS_PORT -> LDAP over SSL */
+			g_value_set_int (
+				target_value,
+				E_SOURCE_LDAP_SECURITY_LDAPS);
+			return TRUE;
+
+		case 2:  /* MSGC_PORT -> StartTLS */
+			g_value_set_int (
+				target_value,
+				E_SOURCE_LDAP_SECURITY_STARTTLS);
+			return TRUE;
+
+		case 3:  /* MSGCS_PORT -> LDAP over SSL */
+			g_value_set_int (
+				target_value,
+				E_SOURCE_LDAP_SECURITY_LDAPS);
+			return TRUE;
+
+		default:
+			break;
+	}
+
+	return FALSE;
+}
+
+static void
+book_config_ldap_search_base_button_clicked_cb (GtkButton *button,
+                                                Closure *closure)
+{
+	Context *context;
+	GtkComboBox *combo_box;
+	GtkTreeModel *model;
+	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);
+
+	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);
+
+	if (model != NULL)
+		g_object_unref (model);
+}
+
+static gboolean
+book_config_ldap_query_port_tooltip_cb (GtkComboBox *combo_box,
+                                        gint x,
+                                        gint y,
+                                        gboolean keyboard_mode,
+                                        GtkTooltip *tooltip)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gchar *text;
+
+	/* XXX This only works if the port number was selected from
+	 *     the drop down menu.  No tooltip is shown if the user
+	 *     types the port number, even if the same port number
+	 *     is listed in the drop down menu.  That's fixable but
+	 *     the code would be a lot messier, and is arguably a
+	 *     job for GtkComboBox. */
+
+	if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+		return FALSE;
+
+	model = gtk_combo_box_get_model (combo_box);
+	gtk_tree_model_get (model, &iter, 1, &text, -1);
+	gtk_tooltip_set_text (tooltip, text);
+	g_free (text);
+
+	return TRUE;
+}
+
+static GtkWidget *
+book_config_build_port_combo (void)
+{
+	GtkWidget *widget;
+	GtkComboBox *combo_box;
+	GtkCellRenderer *renderer;
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (
+		store, &iter,
+		0, G_STRINGIFY (LDAP_PORT),
+		1, _("Standard LDAP Port"), -1);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (
+		store, &iter,
+		0, G_STRINGIFY (LDAPS_PORT),
+		1, _("LDAP over SSL (deprecated)"), -1);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (
+		store, &iter,
+		0, G_STRINGIFY (MSGC_PORT),
+		1, _("Microsoft Global Catalog"), -1);
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (
+		store, &iter,
+		0, G_STRINGIFY (MSGCS_PORT),
+		1, _("Microsoft Global Catalog over SSL"), -1);
+
+	widget = gtk_combo_box_new_with_entry ();
+
+	combo_box = GTK_COMBO_BOX (widget);
+	gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+	gtk_combo_box_set_entry_text_column (combo_box, 0);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set (renderer, "sensitive", FALSE, NULL);
+	gtk_cell_layout_pack_start (
+		GTK_CELL_LAYOUT (widget), renderer, FALSE);
+	gtk_cell_layout_add_attribute (
+		GTK_CELL_LAYOUT (widget), renderer, "text", 1);
+
+	gtk_widget_set_has_tooltip (widget, TRUE);
+
+	g_signal_connect (
+		widget, "query-tooltip",
+		G_CALLBACK (book_config_ldap_query_port_tooltip_cb), NULL);
+
+	g_object_unref (store);
+
+	return widget;
+}
+
+static void
+book_config_ldap_insert_notebook_widget (GtkWidget *vbox,
+                                         GtkSizeGroup *size_group,
+                                         const gchar *caption,
+                                         GtkWidget *widget)
+{
+	GtkWidget *hbox;
+	GtkWidget *label;
+
+	/* This is similar to e_source_config_insert_widget(),
+	 * but instead adds the widget to the LDAP notebook. */
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+	gtk_widget_show (hbox);
+
+	label = gtk_label_new (caption);
+	gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+	gtk_size_group_add_widget (size_group, label);
+	gtk_widget_show (label);
+
+	gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+}
+
+static void
+book_config_ldap_insert_widgets (ESourceConfigBackend *backend,
+                                 ESource *scratch_source)
+{
+	ESourceConfig *config;
+	ESourceExtension *extension;
+	GtkSizeGroup *size_group;
+	GtkNotebook *notebook;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkWidget *page;
+	GtkWidget *hbox;
+	Context *context;
+	PangoAttribute *attr;
+	PangoAttrList *attr_list;
+	const gchar *extension_name;
+	const gchar *tab_label;
+	const gchar *uid;
+
+	context = g_slice_new (Context);
+	uid = e_source_get_uid (scratch_source);
+	config = e_source_config_backend_get_config (backend);
+
+	g_object_set_data_full (
+		G_OBJECT (backend), uid, context,
+		(GDestroyNotify) book_config_ldap_context_free);
+
+	e_book_source_config_add_offline_toggle (
+		E_BOOK_SOURCE_CONFIG (config), scratch_source);
+
+	container = e_source_config_get_page (config, scratch_source);
+
+	/* Extra padding between the notebook and the options above. */
+	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 6, 0, 0, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_notebook_new ();
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	notebook = GTK_NOTEBOOK (widget);
+
+	/* For bold section headers. */
+	attr_list = pango_attr_list_new ();
+	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+	pango_attr_list_insert (attr_list, attr);
+
+	/* Page 1 */
+
+	tab_label = _("Connecting to LDAP");
+	page = gtk_vbox_new (FALSE, 12);
+	gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+	gtk_notebook_append_page (notebook, page, NULL);
+	gtk_notebook_set_tab_label_text (notebook, page, tab_label);
+	gtk_widget_show (page);
+
+	size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+	/* Page 1 : Server Information */
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_label_new (_("Server Information"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_entry_new ();
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Server:"), widget);
+	context->host_entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = book_config_build_port_combo ();
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Port:"), widget);
+	context->port_combo = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* This must follow the order of ESourceLDAPSecurity. */
+	widget = gtk_combo_box_text_new ();
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("None"));
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("LDAP over SSL (deprecated)"));
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("StartTLS (recommended)"));
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Encryption:"), widget);
+	context->security_combo = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_object_bind_property_full (
+		context->port_combo, "active",
+		context->security_combo, "active",
+		G_BINDING_DEFAULT,
+		book_config_ldap_port_to_security,
+		NULL,  /* binding is one-way */
+		NULL, (GDestroyNotify) NULL);
+
+	/* If this is a new source, initialize security to StartTLS. */
+	if (e_source_config_get_original_source (config) == NULL)
+		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
+
+	/* Page 1 : Authentication */
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_label_new (_("Authentication"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	/* This must follow the order of ESourceLDAPAuthentication. */
+	widget = gtk_combo_box_text_new ();
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("Anonymous"));
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("Using email address"));
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget),
+		_("Using distinguished name (DN)"));
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Method:"), widget);
+	context->auth_combo = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	gtk_widget_set_tooltip_text (
+		widget, _("This is the method Evolution will use to "
+		"authenticate you.  Note that setting this to \"Using "
+		"email address\" requires anonymous access to your LDAP "
+		"server."));
+
+	widget = gtk_entry_new ();
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Username:"), widget);
+	context->auth_entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_object_unref (size_group);
+
+	/* Page 2 */
+
+	tab_label = _("Using LDAP");
+	page = gtk_vbox_new (FALSE, 12);
+	gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+	gtk_notebook_append_page (notebook, page, NULL);
+	gtk_notebook_set_tab_label_text (notebook, page, tab_label);
+	gtk_widget_show (page);
+
+	size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+	/* Page 2 : Searching */
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_label_new (_("Searching"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_combo_box_new_with_entry ();
+	gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (widget), 0);
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Search Base:"), widget);
+	context->search_base_combo = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_button_new_with_label (
+		_("Find Possible Search Bases"));
+	gtk_button_set_image (
+		GTK_BUTTON (widget), gtk_image_new_from_stock (
+		GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON));
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, NULL, widget);
+	context->search_base_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Only sensitive when we have complete
+	 * server and authentication details. */
+	g_object_bind_property (
+		config, "complete",
+		context->search_base_button, "sensitive",
+		G_BINDING_DEFAULT);
+
+	g_signal_connect_data (
+		widget, "clicked",
+		G_CALLBACK (book_config_ldap_search_base_button_clicked_cb),
+		book_config_ldap_closure_new (backend, scratch_source),
+		(GClosureNotify) book_config_ldap_closure_free, 0);
+
+	/* This must follow the order of ESourceLDAPScope. */
+	widget = gtk_combo_box_text_new ();
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget), _("One Level"));
+	gtk_combo_box_text_append_text (
+		GTK_COMBO_BOX_TEXT (widget), _("Subtree"));
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Search Scope:"), widget);
+	context->search_scope_combo = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	gtk_widget_set_tooltip_text (
+		widget, _("The search scope defines how deep you would "
+		"like the search to extend down the directory tree.  A "
+		"search scope of \"Subtree\" will include all entries "
+		"below your search base.  A search scope of \"One Level\" "
+		"will only include the entries one level beneath your "
+		"search base."));
+
+	widget = gtk_entry_new ();
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Search Filter:"), widget);
+	context->search_filter_entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Page 2 : Downloading */
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_label_new (_("Downloading"));
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_vbox_new (FALSE, 6);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_hbox_new (FALSE, 6);
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, _("Limit:"), widget);
+	gtk_widget_show (widget);
+
+	hbox = widget;
+
+	widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE);
+	gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+	context->limit_spinbutton = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new (_("contacts"));
+	gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_check_button_new_with_label (
+		_("Browse until limit is reached"));
+	book_config_ldap_insert_notebook_widget (
+		container, size_group, NULL, widget);
+	context->can_browse_toggle = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_object_unref (size_group);
+
+	pango_attr_list_unref (attr_list);
+
+	/* Bind widgets to extension properties. */
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	extension = e_source_get_extension (scratch_source, extension_name);
+
+	g_object_bind_property (
+		extension, "host",
+		context->host_entry, "text",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property_full (
+		extension, "port",
+		context->port_combo, "active",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE,
+		book_config_ldap_port_to_active,
+		book_config_ldap_active_to_port,
+		NULL, (GDestroyNotify) NULL);
+
+	g_object_bind_property (
+		extension, "user",
+		context->auth_entry, "text",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	extension_name = E_SOURCE_EXTENSION_LDAP_BACKEND;
+	extension = e_source_get_extension (scratch_source, extension_name);
+
+	g_object_bind_property (
+		extension, "authentication",
+		context->auth_combo, "active",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property (
+		extension, "can-browse",
+		context->can_browse_toggle, "active",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property (
+		extension, "limit",
+		context->limit_spinbutton, "value",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	widget = gtk_bin_get_child (GTK_BIN (context->search_base_combo));
+
+	g_object_bind_property (
+		extension, "root-dn",
+		widget, "text",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property (
+		extension, "scope",
+		context->search_scope_combo, "active",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property (
+		extension, "filter",
+		context->search_filter_entry, "text",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+
+	g_object_bind_property (
+		extension, "security",
+		context->security_combo, "active",
+		G_BINDING_BIDIRECTIONAL |
+		G_BINDING_SYNC_CREATE);
+}
+
+static gboolean
+book_config_ldap_check_complete (ESourceConfigBackend *backend,
+                                 ESource *scratch_source)
+{
+	ESourceLDAPAuthentication auth;
+	ESourceExtension *extension;
+	const gchar *extension_name;
+	const gchar *host;
+	const gchar *user;
+	guint16 port;
+
+	extension_name = E_SOURCE_EXTENSION_LDAP_BACKEND;
+	extension = e_source_get_extension (scratch_source, extension_name);
+
+	auth = e_source_ldap_get_authentication (E_SOURCE_LDAP (extension));
+
+	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+	extension = e_source_get_extension (scratch_source, extension_name);
+
+	host = e_source_authentication_get_host (
+		E_SOURCE_AUTHENTICATION (extension));
+	port = e_source_authentication_get_port (
+		E_SOURCE_AUTHENTICATION (extension));
+	user = e_source_authentication_get_user (
+		E_SOURCE_AUTHENTICATION (extension));
+
+	if (host == NULL || *host == '\0' || port == 0)
+		return FALSE;
+
+	if (auth != E_SOURCE_LDAP_AUTHENTICATION_NONE)
+		if (user == NULL || *user == '\0')
+			return FALSE;
+
+	return TRUE;
+}
+
+static void
+e_book_config_ldap_class_init (ESourceConfigBackendClass *class)
+{
+	EExtensionClass *extension_class;
+
+	extension_class = E_EXTENSION_CLASS (class);
+	extension_class->extensible_type = E_TYPE_BOOK_SOURCE_CONFIG;
+
+	class->parent_uid = "ldap-stub";
+	class->backend_name = "ldap";
+	class->insert_widgets = book_config_ldap_insert_widgets;
+	class->check_complete = book_config_ldap_check_complete;
+}
+
+static void
+e_book_config_ldap_class_finalize (ESourceConfigBackendClass *class)
+{
+}
+
+static void
+e_book_config_ldap_init (ESourceConfigBackend *backend)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+	e_source_ldap_type_register (type_module);
+	e_book_config_ldap_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}



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