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