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 }