evolution-3.6.4/modules/book-config-ldap/evolution-book-config-ldap.c

No issues found

   1 /*
   2  * evolution-book-config-ldap.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  */
  18 
  19 #include <config.h>
  20 #include <stdlib.h>
  21 #include <glib/gi18n-lib.h>
  22 
  23 #include <libebackend/libebackend.h>
  24 
  25 #include <libevolution-utils/e-alert-dialog.h>
  26 #include <misc/e-book-source-config.h>
  27 #include <misc/e-source-config-backend.h>
  28 
  29 #include "e-source-ldap.h"
  30 
  31 #ifndef G_OS_WIN32
  32 #include <ldap.h>
  33 #ifndef SUNLDAP
  34 #include <ldap_schema.h>
  35 #endif
  36 #else
  37 #include <winldap.h>
  38 #include "openldap-extract.h"
  39 #endif
  40 
  41 /* Combo box ordering */
  42 #define LDAP_PORT  389
  43 #define LDAPS_PORT 636
  44 #define MSGC_PORT  3268
  45 #define MSGCS_PORT 3269
  46 
  47 typedef ESourceConfigBackend EBookConfigLDAP;
  48 typedef ESourceConfigBackendClass EBookConfigLDAPClass;
  49 
  50 typedef struct _Closure Closure;
  51 typedef struct _Context Context;
  52 
  53 struct _Closure {
  54 	ESourceConfigBackend *backend;
  55 	ESource *scratch_source;
  56 };
  57 
  58 struct _Context {
  59 	GtkWidget *auth_combo;
  60 	GtkWidget *auth_entry;
  61 	GtkWidget *host_entry;
  62 	GtkWidget *port_combo;
  63 	GtkWidget *security_combo;
  64 	GtkWidget *search_base_combo;
  65 	GtkWidget *search_base_button;
  66 	GtkWidget *search_scope_combo;
  67 	GtkWidget *search_filter_entry;
  68 	GtkWidget *limit_spinbutton;
  69 	GtkWidget *can_browse_toggle;
  70 };
  71 
  72 /* Module Entry Points */
  73 void e_module_load (GTypeModule *type_module);
  74 void e_module_unload (GTypeModule *type_module);
  75 
  76 /* Forward Declarations */
  77 GType e_book_config_ldap_get_type (void);
  78 
  79 G_DEFINE_DYNAMIC_TYPE (
  80 	EBookConfigLDAP,
  81 	e_book_config_ldap,
  82 	E_TYPE_SOURCE_CONFIG_BACKEND)
  83 
  84 static Closure *
  85 book_config_ldap_closure_new (ESourceConfigBackend *backend,
  86                               ESource *scratch_source)
  87 {
  88 	Closure *closure;
  89 
  90 	closure = g_slice_new (Closure);
  91 	closure->backend = g_object_ref (backend);
  92 	closure->scratch_source = g_object_ref (scratch_source);
  93 
  94 	return closure;
  95 }
  96 
  97 static void
  98 book_config_ldap_closure_free (Closure *closure)
  99 {
 100 	g_object_unref (closure->backend);
 101 	g_object_unref (closure->scratch_source);
 102 
 103 	g_slice_free (Closure, closure);
 104 }
 105 
 106 static void
 107 book_config_ldap_context_free (Context *context)
 108 {
 109 	g_object_unref (context->auth_combo);
 110 	g_object_unref (context->auth_entry);
 111 	g_object_unref (context->host_entry);
 112 	g_object_unref (context->port_combo);
 113 	g_object_unref (context->security_combo);
 114 	g_object_unref (context->search_base_combo);
 115 	g_object_unref (context->search_base_button);
 116 	g_object_unref (context->search_scope_combo);
 117 	g_object_unref (context->search_filter_entry);
 118 	g_object_unref (context->limit_spinbutton);
 119 	g_object_unref (context->can_browse_toggle);
 120 
 121 	g_slice_free (Context, context);
 122 }
 123 
 124 static GtkTreeModel *
 125 book_config_ldap_root_dse_query (ESourceConfigBackend *backend,
 126                                  ESource *scratch_source)
 127 {
 128 	LDAP *ldap;
 129 	LDAPMessage *result = NULL;
 130 	GtkListStore *store = NULL;
 131 	ESourceAuthentication *extension;
 132 	struct timeval timeout;
 133 	const gchar *alert_id = NULL;
 134 	const gchar *extension_name;
 135 	const gchar *host;
 136 	gchar **values = NULL;
 137 	gint ldap_error;
 138 	gint option;
 139 	gint version;
 140 	guint16 port;
 141 	gint ii;
 142 
 143 	const gchar *attrs[] = { "namingContexts", NULL };
 144 
 145 	/* FIXME This all runs synchronously in the main loop.
 146 	 *       We should do this in a separate thread behind
 147 	 *       async/finish functions.  May need to define
 148 	 *       some custom GError codes, or maybe just an
 149 	 *       LDAP GError domain that reuses LDAP result
 150 	 *       codes from <ldap.h>. */
 151 
 152 	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
 153 	extension = e_source_get_extension (scratch_source, extension_name);
 154 
 155 	host = e_source_authentication_get_host (extension);
 156 	port = e_source_authentication_get_port (extension);
 157 
 158 	timeout.tv_sec = 60;
 159 	timeout.tv_usec = 0;
 160 
 161 	ldap = ldap_init (host, port);
 162 	if (ldap == NULL) {
 163 		alert_id = "addressbook:ldap-init";
 164 		goto exit;
 165 	}
 166 
 167 	version = LDAP_VERSION3;
 168 	option = LDAP_OPT_PROTOCOL_VERSION;
 169 	if (ldap_set_option (ldap, option, &version) != LDAP_SUCCESS) {
 170 		/* XXX Define an alert for this. */
 171 		g_warning ("Failed to set protocol version to LDAPv3");
 172 		goto exit;
 173 	}
 174 
 175 	/* FIXME Use the user's actual authentication settings. */
 176 	if (ldap_simple_bind_s (ldap, NULL, NULL) != LDAP_SUCCESS) {
 177 		alert_id = "addressbook:ldap-auth";
 178 		goto exit;
 179 	}
 180 
 181 	ldap_error = ldap_search_ext_s (
 182 		ldap, LDAP_ROOT_DSE, LDAP_SCOPE_BASE,
 183 		"(objectclass=*)", (gchar **) attrs, 0,
 184 		NULL, NULL, &timeout, LDAP_NO_LIMIT, &result);
 185 	if (ldap_error != LDAP_SUCCESS) {
 186 		alert_id = "addressbook:ldap-search-base";
 187 		goto exit;
 188 	}
 189 
 190 	values = ldap_get_values (ldap, result, "namingContexts");
 191 	if (values == NULL || values[0] == NULL || *values[0] == '\0') {
 192 		alert_id = "addressbook:ldap-search-base";
 193 		goto exit;
 194 	}
 195 
 196 	store = gtk_list_store_new (1, G_TYPE_STRING);
 197 
 198 	for (ii = 0; values[ii] != NULL; ii++) {
 199 		GtkTreeIter iter;
 200 
 201 		gtk_list_store_append (store, &iter);
 202 		gtk_list_store_set (store, &iter, 0, values[ii], -1);
 203 	}
 204 
 205 exit:
 206 	if (alert_id != NULL) {
 207 		ESourceConfig *config;
 208 		gpointer parent;
 209 
 210 		config = e_source_config_backend_get_config (backend);
 211 
 212 		parent = gtk_widget_get_toplevel (GTK_WIDGET (config));
 213 		parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
 214 
 215 		e_alert_run_dialog_for_args (parent, alert_id, NULL);
 216 	}
 217 
 218 	if (values != NULL)
 219 		ldap_value_free (values);
 220 
 221 	if (result != NULL)
 222 		ldap_msgfree (result);
 223 
 224 	if (ldap != NULL)
 225 		ldap_unbind_s (ldap);
 226 
 227 	/* This may be NULL, so don't use a cast macro. */
 228 	return (GtkTreeModel *) store;
 229 }
 230 
 231 static gboolean
 232 book_config_ldap_port_to_active (GBinding *binding,
 233                                  const GValue *source_value,
 234                                  GValue *target_value,
 235                                  gpointer unused)
 236 {
 237 	guint port;
 238 	gint active;
 239 
 240 	port = g_value_get_uint (source_value);
 241 
 242 	switch (port) {
 243 		case 0:  /* initialize to LDAP_PORT */
 244 		case LDAP_PORT:
 245 			active = 0;
 246 			break;
 247 
 248 		case LDAPS_PORT:
 249 			active = 1;
 250 			break;
 251 
 252 		case MSGC_PORT:
 253 			active = 2;
 254 			break;
 255 
 256 		case MSGCS_PORT:
 257 			active = 3;
 258 			break;
 259 
 260 		default:
 261 			active = -1;
 262 			break;
 263 	}
 264 
 265 	g_value_set_int (target_value, active);
 266 
 267 	if (active == -1) {
 268 		GObject *target;
 269 		GtkWidget *entry;
 270 		gchar *text;
 271 
 272 		target = g_binding_get_target (binding);
 273 		entry = gtk_bin_get_child (GTK_BIN (target));
 274 
 275 		text = g_strdup_printf ("%u", port);
 276 		gtk_entry_set_text (GTK_ENTRY (entry), text);
 277 		g_free (text);
 278 	}
 279 
 280 	return TRUE;
 281 }
 282 
 283 static gboolean
 284 book_config_ldap_active_to_port (GBinding *binding,
 285                                  const GValue *source_value,
 286                                  GValue *target_value,
 287                                  gpointer unused)
 288 {
 289 	guint port = LDAP_PORT;
 290 	gint active;
 291 
 292 	active = g_value_get_int (source_value);
 293 
 294 	switch (active) {
 295 		case 0:
 296 			port = LDAP_PORT;
 297 			break;
 298 
 299 		case 1:
 300 			port = LDAPS_PORT;
 301 			break;
 302 
 303 		case 2:
 304 			port = MSGC_PORT;
 305 			break;
 306 
 307 		case 3:
 308 			port = MSGCS_PORT;
 309 			break;
 310 
 311 		default:
 312 			active = -1;
 313 			break;
 314 	}
 315 
 316 	if (active == -1) {
 317 		GObject *target;
 318 		GtkWidget *entry;
 319 		const gchar *text;
 320 		glong v_long;
 321 
 322 		target = g_binding_get_target (binding);
 323 		entry = gtk_bin_get_child (GTK_BIN (target));
 324 		text = gtk_entry_get_text (GTK_ENTRY (entry));
 325 
 326 		v_long = text ? strtol (text, NULL, 10) : 0;
 327 		if (v_long != 0 && v_long == CLAMP (v_long, 0, G_MAXUINT16))
 328 			port = (guint) v_long;
 329 	}
 330 
 331 	g_value_set_uint (target_value, port);
 332 
 333 	return TRUE;
 334 }
 335 
 336 static void
 337 book_config_ldap_port_combo_changed (GtkComboBox *combo_box)
 338 {
 339 	if (gtk_combo_box_get_active (combo_box) == -1)
 340 		g_object_notify (G_OBJECT (combo_box), "active");
 341 }
 342 
 343 static gboolean
 344 book_config_ldap_port_to_security (GBinding *binding,
 345                                    const GValue *source_value,
 346                                    GValue *target_value,
 347                                    gpointer unused)
 348 {
 349 	switch (g_value_get_int (source_value)) {
 350 		case 0:  /* LDAP_PORT -> StartTLS */
 351 			g_value_set_int (
 352 				target_value,
 353 				E_SOURCE_LDAP_SECURITY_STARTTLS);
 354 			return TRUE;
 355 
 356 		case 1:  /* LDAPS_PORT -> LDAP over SSL */
 357 			g_value_set_int (
 358 				target_value,
 359 				E_SOURCE_LDAP_SECURITY_LDAPS);
 360 			return TRUE;
 361 
 362 		case 2:  /* MSGC_PORT -> StartTLS */
 363 			g_value_set_int (
 364 				target_value,
 365 				E_SOURCE_LDAP_SECURITY_STARTTLS);
 366 			return TRUE;
 367 
 368 		case 3:  /* MSGCS_PORT -> LDAP over SSL */
 369 			g_value_set_int (
 370 				target_value,
 371 				E_SOURCE_LDAP_SECURITY_LDAPS);
 372 			return TRUE;
 373 
 374 		default:
 375 			break;
 376 	}
 377 
 378 	return FALSE;
 379 }
 380 
 381 static void
 382 book_config_ldap_search_base_button_clicked_cb (GtkButton *button,
 383                                                 Closure *closure)
 384 {
 385 	Context *context;
 386 	GtkComboBox *combo_box;
 387 	GtkTreeModel *model;
 388 	const gchar *uid;
 389 
 390 	uid = e_source_get_uid (closure->scratch_source);
 391 	context = g_object_get_data (G_OBJECT (closure->backend), uid);
 392 	g_return_if_fail (context != NULL);
 393 
 394 	model = book_config_ldap_root_dse_query (
 395 		closure->backend, closure->scratch_source);
 396 
 397 	combo_box = GTK_COMBO_BOX (context->search_base_combo);
 398 	gtk_combo_box_set_model (combo_box, model);
 399 	gtk_combo_box_set_active (combo_box, 0);
 400 
 401 	if (model != NULL)
 402 		g_object_unref (model);
 403 }
 404 
 405 static gboolean
 406 book_config_ldap_query_port_tooltip_cb (GtkComboBox *combo_box,
 407                                         gint x,
 408                                         gint y,
 409                                         gboolean keyboard_mode,
 410                                         GtkTooltip *tooltip)
 411 {
 412 	GtkTreeModel *model;
 413 	GtkTreeIter iter;
 414 	gchar *text;
 415 
 416 	/* XXX This only works if the port number was selected from
 417 	 *     the drop down menu.  No tooltip is shown if the user
 418 	 *     types the port number, even if the same port number
 419 	 *     is listed in the drop down menu.  That's fixable but
 420 	 *     the code would be a lot messier, and is arguably a
 421 	 *     job for GtkComboBox. */
 422 
 423 	if (!gtk_combo_box_get_active_iter (combo_box, &iter))
 424 		return FALSE;
 425 
 426 	model = gtk_combo_box_get_model (combo_box);
 427 	gtk_tree_model_get (model, &iter, 1, &text, -1);
 428 	gtk_tooltip_set_text (tooltip, text);
 429 	g_free (text);
 430 
 431 	return TRUE;
 432 }
 433 
 434 static GtkWidget *
 435 book_config_build_port_combo (void)
 436 {
 437 	GtkWidget *widget;
 438 	GtkComboBox *combo_box;
 439 	GtkCellRenderer *renderer;
 440 	GtkListStore *store;
 441 	GtkTreeIter iter;
 442 
 443 	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
 444 
 445 	gtk_list_store_append (store, &iter);
 446 	gtk_list_store_set (
 447 		store, &iter,
 448 		0, G_STRINGIFY (LDAP_PORT),
 449 		1, _("Standard LDAP Port"), -1);
 450 
 451 	gtk_list_store_append (store, &iter);
 452 	gtk_list_store_set (
 453 		store, &iter,
 454 		0, G_STRINGIFY (LDAPS_PORT),
 455 		1, _("LDAP over SSL (deprecated)"), -1);
 456 
 457 	gtk_list_store_append (store, &iter);
 458 	gtk_list_store_set (
 459 		store, &iter,
 460 		0, G_STRINGIFY (MSGC_PORT),
 461 		1, _("Microsoft Global Catalog"), -1);
 462 
 463 	gtk_list_store_append (store, &iter);
 464 	gtk_list_store_set (
 465 		store, &iter,
 466 		0, G_STRINGIFY (MSGCS_PORT),
 467 		1, _("Microsoft Global Catalog over SSL"), -1);
 468 
 469 	widget = gtk_combo_box_new_with_entry ();
 470 
 471 	combo_box = GTK_COMBO_BOX (widget);
 472 	gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
 473 	gtk_combo_box_set_entry_text_column (combo_box, 0);
 474 
 475 	renderer = gtk_cell_renderer_text_new ();
 476 	g_object_set (renderer, "sensitive", FALSE, NULL);
 477 	gtk_cell_layout_pack_start (
 478 		GTK_CELL_LAYOUT (widget), renderer, FALSE);
 479 	gtk_cell_layout_add_attribute (
 480 		GTK_CELL_LAYOUT (widget), renderer, "text", 1);
 481 
 482 	gtk_widget_set_has_tooltip (widget, TRUE);
 483 
 484 	g_signal_connect (
 485 		widget, "query-tooltip",
 486 		G_CALLBACK (book_config_ldap_query_port_tooltip_cb), NULL);
 487 
 488 	g_object_unref (store);
 489 
 490 	return widget;
 491 }
 492 
 493 static void
 494 book_config_ldap_insert_notebook_widget (GtkWidget *vbox,
 495                                          GtkSizeGroup *size_group,
 496                                          const gchar *caption,
 497                                          GtkWidget *widget)
 498 {
 499 	GtkWidget *hbox;
 500 	GtkWidget *label;
 501 
 502 	/* This is similar to e_source_config_insert_widget(),
 503 	 * but instead adds the widget to the LDAP notebook. */
 504 
 505 	hbox = gtk_hbox_new (FALSE, 12);
 506 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
 507 	gtk_widget_show (hbox);
 508 
 509 	label = gtk_label_new (caption);
 510 	gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
 511 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
 512 	gtk_size_group_add_widget (size_group, label);
 513 	gtk_widget_show (label);
 514 
 515 	gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
 516 }
 517 
 518 static void
 519 book_config_ldap_insert_widgets (ESourceConfigBackend *backend,
 520                                  ESource *scratch_source)
 521 {
 522 	ESourceConfig *config;
 523 	ESourceExtension *extension;
 524 	GtkSizeGroup *size_group;
 525 	GtkNotebook *notebook;
 526 	GtkWidget *container;
 527 	GtkWidget *widget;
 528 	GtkWidget *page;
 529 	GtkWidget *hbox;
 530 	Context *context;
 531 	PangoAttribute *attr;
 532 	PangoAttrList *attr_list;
 533 	const gchar *extension_name;
 534 	const gchar *tab_label;
 535 	const gchar *uid;
 536 	gboolean is_new_source;
 537 
 538 	context = g_slice_new (Context);
 539 	uid = e_source_get_uid (scratch_source);
 540 	config = e_source_config_backend_get_config (backend);
 541 
 542 	g_object_set_data_full (
 543 		G_OBJECT (backend), uid, context,
 544 		(GDestroyNotify) book_config_ldap_context_free);
 545 
 546 	e_book_source_config_add_offline_toggle (
 547 		E_BOOK_SOURCE_CONFIG (config), scratch_source);
 548 
 549 	container = e_source_config_get_page (config, scratch_source);
 550 
 551 	/* Extra padding between the notebook and the options above. */
 552 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 553 	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 6, 0, 0, 0);
 554 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 555 	gtk_widget_show (widget);
 556 
 557 	container = widget;
 558 
 559 	widget = gtk_notebook_new ();
 560 	gtk_container_add (GTK_CONTAINER (container), widget);
 561 	gtk_widget_show (widget);
 562 
 563 	notebook = GTK_NOTEBOOK (widget);
 564 
 565 	/* For bold section headers. */
 566 	attr_list = pango_attr_list_new ();
 567 	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
 568 	pango_attr_list_insert (attr_list, attr);
 569 
 570 	/* Page 1 */
 571 
 572 	tab_label = _("Connecting to LDAP");
 573 	page = gtk_vbox_new (FALSE, 12);
 574 	gtk_container_set_border_width (GTK_CONTAINER (page), 12);
 575 	gtk_notebook_append_page (notebook, page, NULL);
 576 	gtk_notebook_set_tab_label_text (notebook, page, tab_label);
 577 	gtk_widget_show (page);
 578 
 579 	size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 580 
 581 	/* Page 1 : Server Information */
 582 
 583 	widget = gtk_vbox_new (FALSE, 6);
 584 	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
 585 	gtk_widget_show (widget);
 586 
 587 	container = widget;
 588 
 589 	widget = gtk_label_new (_("Server Information"));
 590 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
 591 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
 592 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 593 	gtk_widget_show (widget);
 594 
 595 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 596 	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
 597 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 598 	gtk_widget_show (widget);
 599 
 600 	container = widget;
 601 
 602 	widget = gtk_vbox_new (FALSE, 6);
 603 	gtk_container_add (GTK_CONTAINER (container), widget);
 604 	gtk_widget_show (widget);
 605 
 606 	container = widget;
 607 
 608 	widget = gtk_entry_new ();
 609 	book_config_ldap_insert_notebook_widget (
 610 		container, size_group, _("Server:"), widget);
 611 	context->host_entry = g_object_ref (widget);
 612 	gtk_widget_show (widget);
 613 
 614 	widget = book_config_build_port_combo ();
 615 	book_config_ldap_insert_notebook_widget (
 616 		container, size_group, _("Port:"), widget);
 617 	context->port_combo = g_object_ref (widget);
 618 	gtk_widget_show (widget);
 619 
 620 	/* This must follow the order of ESourceLDAPSecurity. */
 621 	widget = gtk_combo_box_text_new ();
 622 	gtk_combo_box_text_append_text (
 623 		GTK_COMBO_BOX_TEXT (widget),
 624 		_("None"));
 625 	gtk_combo_box_text_append_text (
 626 		GTK_COMBO_BOX_TEXT (widget),
 627 		_("LDAP over SSL (deprecated)"));
 628 	gtk_combo_box_text_append_text (
 629 		GTK_COMBO_BOX_TEXT (widget),
 630 		_("StartTLS (recommended)"));
 631 	book_config_ldap_insert_notebook_widget (
 632 		container, size_group, _("Encryption:"), widget);
 633 	context->security_combo = g_object_ref (widget);
 634 	gtk_widget_show (widget);
 635 
 636 	g_object_bind_property_full (
 637 		context->port_combo, "active",
 638 		context->security_combo, "active",
 639 		G_BINDING_DEFAULT,
 640 		book_config_ldap_port_to_security,
 641 		NULL,  /* binding is one-way */
 642 		NULL, (GDestroyNotify) NULL);
 643 
 644 	/* If this is a new source, initialize security to StartTLS. */
 645 	if (e_source_config_get_original_source (config) == NULL)
 646 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
 647 
 648 	/* Page 1 : Authentication */
 649 
 650 	widget = gtk_vbox_new (FALSE, 6);
 651 	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
 652 	gtk_widget_show (widget);
 653 
 654 	container = widget;
 655 
 656 	widget = gtk_label_new (_("Authentication"));
 657 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
 658 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
 659 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 660 	gtk_widget_show (widget);
 661 
 662 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 663 	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
 664 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 665 	gtk_widget_show (widget);
 666 
 667 	container = widget;
 668 
 669 	widget = gtk_vbox_new (FALSE, 6);
 670 	gtk_container_add (GTK_CONTAINER (container), widget);
 671 	gtk_widget_show (widget);
 672 
 673 	container = widget;
 674 
 675 	/* This must follow the order of ESourceLDAPAuthentication. */
 676 	widget = gtk_combo_box_text_new ();
 677 	gtk_combo_box_text_append_text (
 678 		GTK_COMBO_BOX_TEXT (widget),
 679 		_("Anonymous"));
 680 	gtk_combo_box_text_append_text (
 681 		GTK_COMBO_BOX_TEXT (widget),
 682 		_("Using email address"));
 683 	gtk_combo_box_text_append_text (
 684 		GTK_COMBO_BOX_TEXT (widget),
 685 		_("Using distinguished name (DN)"));
 686 	book_config_ldap_insert_notebook_widget (
 687 		container, size_group, _("Method:"), widget);
 688 	context->auth_combo = g_object_ref (widget);
 689 	gtk_widget_show (widget);
 690 
 691 	gtk_widget_set_tooltip_text (
 692 		widget, _("This is the method Evolution will use to "
 693 		"authenticate you.  Note that setting this to \"Using "
 694 		"email address\" requires anonymous access to your LDAP "
 695 		"server."));
 696 
 697 	widget = gtk_entry_new ();
 698 	book_config_ldap_insert_notebook_widget (
 699 		container, size_group, _("Username:"), widget);
 700 	context->auth_entry = g_object_ref (widget);
 701 	gtk_widget_show (widget);
 702 
 703 	g_object_unref (size_group);
 704 
 705 	/* Page 2 */
 706 
 707 	tab_label = _("Using LDAP");
 708 	page = gtk_vbox_new (FALSE, 12);
 709 	gtk_container_set_border_width (GTK_CONTAINER (page), 12);
 710 	gtk_notebook_append_page (notebook, page, NULL);
 711 	gtk_notebook_set_tab_label_text (notebook, page, tab_label);
 712 	gtk_widget_show (page);
 713 
 714 	size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 715 
 716 	/* Page 2 : Searching */
 717 
 718 	widget = gtk_vbox_new (FALSE, 6);
 719 	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
 720 	gtk_widget_show (widget);
 721 
 722 	container = widget;
 723 
 724 	widget = gtk_label_new (_("Searching"));
 725 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
 726 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
 727 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 728 	gtk_widget_show (widget);
 729 
 730 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 731 	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
 732 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 733 	gtk_widget_show (widget);
 734 
 735 	container = widget;
 736 
 737 	widget = gtk_vbox_new (FALSE, 6);
 738 	gtk_container_add (GTK_CONTAINER (container), widget);
 739 	gtk_widget_show (widget);
 740 
 741 	container = widget;
 742 
 743 	widget = gtk_combo_box_new_with_entry ();
 744 	gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (widget), 0);
 745 	book_config_ldap_insert_notebook_widget (
 746 		container, size_group, _("Search Base:"), widget);
 747 	context->search_base_combo = g_object_ref (widget);
 748 	gtk_widget_show (widget);
 749 
 750 	widget = gtk_button_new_with_label (
 751 		_("Find Possible Search Bases"));
 752 	gtk_button_set_image (
 753 		GTK_BUTTON (widget), gtk_image_new_from_stock (
 754 		GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON));
 755 	book_config_ldap_insert_notebook_widget (
 756 		container, size_group, NULL, widget);
 757 	context->search_base_button = g_object_ref (widget);
 758 	gtk_widget_show (widget);
 759 
 760 	/* Only sensitive when we have complete
 761 	 * server and authentication details. */
 762 	g_object_bind_property (
 763 		config, "complete",
 764 		context->search_base_button, "sensitive",
 765 		G_BINDING_DEFAULT);
 766 
 767 	g_signal_connect_data (
 768 		widget, "clicked",
 769 		G_CALLBACK (book_config_ldap_search_base_button_clicked_cb),
 770 		book_config_ldap_closure_new (backend, scratch_source),
 771 		(GClosureNotify) book_config_ldap_closure_free, 0);
 772 
 773 	/* This must follow the order of ESourceLDAPScope. */
 774 	widget = gtk_combo_box_text_new ();
 775 	gtk_combo_box_text_append_text (
 776 		GTK_COMBO_BOX_TEXT (widget), _("One Level"));
 777 	gtk_combo_box_text_append_text (
 778 		GTK_COMBO_BOX_TEXT (widget), _("Subtree"));
 779 	book_config_ldap_insert_notebook_widget (
 780 		container, size_group, _("Search Scope:"), widget);
 781 	context->search_scope_combo = g_object_ref (widget);
 782 	gtk_widget_show (widget);
 783 
 784 	gtk_widget_set_tooltip_text (
 785 		widget, _("The search scope defines how deep you would "
 786 		"like the search to extend down the directory tree.  A "
 787 		"search scope of \"Subtree\" will include all entries "
 788 		"below your search base.  A search scope of \"One Level\" "
 789 		"will only include the entries one level beneath your "
 790 		"search base."));
 791 
 792 	widget = gtk_entry_new ();
 793 	book_config_ldap_insert_notebook_widget (
 794 		container, size_group, _("Search Filter:"), widget);
 795 	context->search_filter_entry = g_object_ref (widget);
 796 	gtk_widget_show (widget);
 797 
 798 	/* Page 2 : Downloading */
 799 
 800 	widget = gtk_vbox_new (FALSE, 6);
 801 	gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0);
 802 	gtk_widget_show (widget);
 803 
 804 	container = widget;
 805 
 806 	widget = gtk_label_new (_("Downloading"));
 807 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
 808 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
 809 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 810 	gtk_widget_show (widget);
 811 
 812 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 813 	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
 814 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
 815 	gtk_widget_show (widget);
 816 
 817 	container = widget;
 818 
 819 	widget = gtk_vbox_new (FALSE, 6);
 820 	gtk_container_add (GTK_CONTAINER (container), widget);
 821 	gtk_widget_show (widget);
 822 
 823 	container = widget;
 824 
 825 	widget = gtk_hbox_new (FALSE, 6);
 826 	book_config_ldap_insert_notebook_widget (
 827 		container, size_group, _("Limit:"), widget);
 828 	gtk_widget_show (widget);
 829 
 830 	hbox = widget;
 831 
 832 	widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
 833 	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE);
 834 	gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
 835 	context->limit_spinbutton = g_object_ref (widget);
 836 	gtk_widget_show (widget);
 837 
 838 	widget = gtk_label_new (_("contacts"));
 839 	gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
 840 	gtk_widget_show (widget);
 841 
 842 	widget = gtk_check_button_new_with_label (
 843 		_("Browse until limit is reached"));
 844 	book_config_ldap_insert_notebook_widget (
 845 		container, size_group, NULL, widget);
 846 	context->can_browse_toggle = g_object_ref (widget);
 847 	gtk_widget_show (widget);
 848 
 849 	g_object_unref (size_group);
 850 
 851 	pango_attr_list_unref (attr_list);
 852 
 853 	/* Bind widgets to extension properties. */
 854 
 855 	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
 856 	is_new_source = !e_source_has_extension (scratch_source, extension_name);
 857 	extension = e_source_get_extension (scratch_source, extension_name);
 858 
 859 	g_object_bind_property (
 860 		extension, "host",
 861 		context->host_entry, "text",
 862 		G_BINDING_BIDIRECTIONAL |
 863 		G_BINDING_SYNC_CREATE);
 864 
 865 	g_object_bind_property_full (
 866 		extension, "port",
 867 		context->port_combo, "active",
 868 		G_BINDING_BIDIRECTIONAL |
 869 		G_BINDING_SYNC_CREATE,
 870 		book_config_ldap_port_to_active,
 871 		book_config_ldap_active_to_port,
 872 		NULL, (GDestroyNotify) NULL);
 873 
 874 	/* "active" doesn't change when setting custom port
 875 	 * in entry, so check also on the "changed" signal. */
 876 	g_signal_connect (
 877 		context->port_combo, "changed",
 878 		G_CALLBACK (book_config_ldap_port_combo_changed), NULL);
 879 
 880 	g_object_bind_property (
 881 		extension, "user",
 882 		context->auth_entry, "text",
 883 		G_BINDING_BIDIRECTIONAL |
 884 		G_BINDING_SYNC_CREATE);
 885 
 886 	extension_name = E_SOURCE_EXTENSION_LDAP_BACKEND;
 887 	extension = e_source_get_extension (scratch_source, extension_name);
 888 
 889 	g_object_bind_property (
 890 		extension, "authentication",
 891 		context->auth_combo, "active",
 892 		G_BINDING_BIDIRECTIONAL |
 893 		G_BINDING_SYNC_CREATE);
 894 
 895 	g_object_bind_property (
 896 		extension, "can-browse",
 897 		context->can_browse_toggle, "active",
 898 		G_BINDING_BIDIRECTIONAL |
 899 		G_BINDING_SYNC_CREATE);
 900 
 901 	g_object_bind_property (
 902 		extension, "limit",
 903 		context->limit_spinbutton, "value",
 904 		G_BINDING_BIDIRECTIONAL |
 905 		G_BINDING_SYNC_CREATE);
 906 
 907 	widget = gtk_bin_get_child (GTK_BIN (context->search_base_combo));
 908 
 909 	g_object_bind_property (
 910 		extension, "root-dn",
 911 		widget, "text",
 912 		G_BINDING_BIDIRECTIONAL |
 913 		G_BINDING_SYNC_CREATE);
 914 
 915 	g_object_bind_property (
 916 		extension, "scope",
 917 		context->search_scope_combo, "active",
 918 		G_BINDING_BIDIRECTIONAL |
 919 		G_BINDING_SYNC_CREATE);
 920 
 921 	g_object_bind_property (
 922 		extension, "filter",
 923 		context->search_filter_entry, "text",
 924 		G_BINDING_BIDIRECTIONAL |
 925 		G_BINDING_SYNC_CREATE);
 926 
 927 	g_object_bind_property (
 928 		extension, "security",
 929 		context->security_combo, "active",
 930 		G_BINDING_BIDIRECTIONAL |
 931 		G_BINDING_SYNC_CREATE);
 932 
 933 	/* Initialize values from UI into extension, if the source
 934 	 * is a fresh new source; bindings will take care of proper
 935 	 * values setting into extension properties. */
 936 	if (is_new_source) {
 937 		g_object_notify (G_OBJECT (context->host_entry), "text");
 938 		g_object_notify (G_OBJECT (context->port_combo), "active");
 939 		g_object_notify (G_OBJECT (context->auth_entry), "text");
 940 		g_object_notify (G_OBJECT (context->auth_combo), "active");
 941 	}
 942 }
 943 
 944 static gboolean
 945 book_config_ldap_check_complete (ESourceConfigBackend *backend,
 946                                  ESource *scratch_source)
 947 {
 948 	ESourceLDAPAuthentication auth;
 949 	ESourceExtension *extension;
 950 	const gchar *extension_name;
 951 	const gchar *host;
 952 	const gchar *user;
 953 	guint16 port;
 954 
 955 	extension_name = E_SOURCE_EXTENSION_LDAP_BACKEND;
 956 	extension = e_source_get_extension (scratch_source, extension_name);
 957 
 958 	auth = e_source_ldap_get_authentication (E_SOURCE_LDAP (extension));
 959 
 960 	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
 961 	extension = e_source_get_extension (scratch_source, extension_name);
 962 
 963 	host = e_source_authentication_get_host (
 964 		E_SOURCE_AUTHENTICATION (extension));
 965 	port = e_source_authentication_get_port (
 966 		E_SOURCE_AUTHENTICATION (extension));
 967 	user = e_source_authentication_get_user (
 968 		E_SOURCE_AUTHENTICATION (extension));
 969 
 970 	if (host == NULL || *host == '\0' || port == 0)
 971 		return FALSE;
 972 
 973 	if (auth != E_SOURCE_LDAP_AUTHENTICATION_NONE)
 974 		if (user == NULL || *user == '\0')
 975 			return FALSE;
 976 
 977 	return TRUE;
 978 }
 979 
 980 static void
 981 e_book_config_ldap_class_init (ESourceConfigBackendClass *class)
 982 {
 983 	EExtensionClass *extension_class;
 984 
 985 	extension_class = E_EXTENSION_CLASS (class);
 986 	extension_class->extensible_type = E_TYPE_BOOK_SOURCE_CONFIG;
 987 
 988 	class->parent_uid = "ldap-stub";
 989 	class->backend_name = "ldap";
 990 	class->insert_widgets = book_config_ldap_insert_widgets;
 991 	class->check_complete = book_config_ldap_check_complete;
 992 }
 993 
 994 static void
 995 e_book_config_ldap_class_finalize (ESourceConfigBackendClass *class)
 996 {
 997 }
 998 
 999 static void
1000 e_book_config_ldap_init (ESourceConfigBackend *backend)
1001 {
1002 }
1003 
1004 G_MODULE_EXPORT void
1005 e_module_load (GTypeModule *type_module)
1006 {
1007 	e_source_ldap_type_register (type_module);
1008 	e_book_config_ldap_register_type (type_module);
1009 }
1010 
1011 G_MODULE_EXPORT void
1012 e_module_unload (GTypeModule *type_module)
1013 {
1014 }