I#27 - [IMAPx] Ignore DavMail's CR/LF in BODYSTRUCTURE response
[evolution-data-server.git] / src / libedataserverui / e-webdav-discover-widget.c
blob73c2a6f494ca11e5402cb064ea2b2a55b02a713a
1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
4 * This library 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.
8 * This library 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 Lesser General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-data-server-config.h"
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
23 #include <gtk/gtk.h>
25 #include <libedataserver/libedataserver.h>
27 #include "e-cell-renderer-color.h"
28 #include "e-trust-prompt.h"
29 #include "e-webdav-discover-widget.h"
31 #define WEBDAV_DISCOVER_CONTENT_KEY "e-webdav-discover-content-widget-key"
32 #define WEBDAV_DISCOVER_CONTENT_ENTRY_KEY "e-webdav-discover-content-widget-entry-key"
33 #define WEBDAV_DISCOVER_CONTENT_DATA_KEY "e-webdav-discover-content-widget-data-key"
35 enum {
36 COL_HREF_STRING = 0,
37 COL_SUPPORTS_UINT,
38 COL_DISPLAY_NAME_STRING,
39 COL_COLOR_STRING,
40 COL_DESCRIPTION_STRING,
41 COL_SUPPORTS_STRING,
42 COL_COLOR_GDKRGBA,
43 COL_SHOW_COLOR_BOOLEAN,
44 N_COLUMNS
47 typedef struct _EWebDAVDiscoverContentData {
48 ECredentialsPrompter *credentials_prompter;
49 ESource *source;
50 gchar *base_url;
51 guint supports_filter;
53 GtkTreeView *sources_tree_view; /* not referenced */
54 GtkComboBox *email_addresses_combo; /* not referenced */
55 GtkInfoBar *info_bar; /* not referenced */
56 } EWebDAVDiscoverContentData;
58 static void
59 e_webdav_discover_content_data_free (gpointer ptr)
61 EWebDAVDiscoverContentData *data = ptr;
63 if (data) {
64 g_clear_object (&data->credentials_prompter);
65 g_clear_object (&data->source);
66 g_free (data->base_url);
67 g_free (data);
71 /**
72 * e_webdav_discover_content_new:
73 * @credentials_prompter: an #ECredentialsPrompter to use to ask for credentials
74 * @source: (allow-none): optional #ESource to use for authentication, or %NULL
75 * @base_url: (allow-none): optional base URL to use for discovery, or %NULL
76 * @supports_filter: a bit-or of #EWebDAVDiscoverSupports, a filter to limit what source
77 * types will be shown in the dialog content; use %E_WEBDAV_DISCOVER_SUPPORTS_NONE
78 * to show all
80 * Creates a new WebDAV discovery content, which is a #GtkGrid containing necessary
81 * widgets to provide a UI interface for a user to search and select for available
82 * WebDAV (CalDAV or CardDAV) sources provided by the given server. Do not pack
83 * anything into this content, its content can be changed dynamically.
85 * Returns: (transfer full): a new WebDAV discovery content widget.
87 * Since: 3.18
88 **/
89 GtkWidget *
90 e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
91 ESource *source,
92 const gchar *base_url,
93 guint supports_filter)
95 EWebDAVDiscoverContentData *data;
96 GtkWidget *content, *scrolled_window, *tree_view;
97 GtkTreeViewColumn *column;
98 GtkCellRenderer *renderer;
99 GtkListStore *list_store;
100 GtkGrid *grid;
102 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (credentials_prompter), NULL);
103 g_return_val_if_fail (base_url != NULL, NULL);
105 data = g_new0 (EWebDAVDiscoverContentData, 1);
106 data->credentials_prompter = g_object_ref (credentials_prompter);
107 data->source = source ? g_object_ref (source) : NULL;
108 data->base_url = g_strdup (base_url);
109 data->supports_filter = supports_filter;
111 content = gtk_grid_new ();
112 grid = GTK_GRID (content);
113 gtk_container_set_border_width (GTK_CONTAINER (grid), 4);
114 gtk_grid_set_row_spacing (grid, 4);
115 gtk_grid_set_column_spacing (grid, 4);
117 g_object_set_data_full (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY, data, e_webdav_discover_content_data_free);
119 list_store = gtk_list_store_new (N_COLUMNS,
120 G_TYPE_STRING, /* COL_HREF_STRING */
121 G_TYPE_UINT, /* COL_SUPPORTS_UINT */
122 G_TYPE_STRING, /* COL_DISPLAY_NAME_STRING */
123 G_TYPE_STRING, /* COL_COLOR_STRING */
124 G_TYPE_STRING, /* COL_DESCRIPTION_STRING */
125 G_TYPE_STRING, /* COL_SUPPORTS_STRING */
126 GDK_TYPE_RGBA, /* COL_COLOR_GDKRGBA */
127 G_TYPE_BOOLEAN); /* COL_SHOW_COLOR_BOOLEAN */
129 tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
130 g_object_unref (list_store);
132 g_object_set (G_OBJECT (tree_view),
133 "hexpand", TRUE,
134 "vexpand", TRUE,
135 "halign", GTK_ALIGN_FILL,
136 "valign", GTK_ALIGN_FILL,
137 NULL);
139 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
140 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
141 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
143 gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
144 gtk_grid_attach (grid, scrolled_window, 0, 0, 1, 1);
146 data->sources_tree_view = GTK_TREE_VIEW (tree_view);
148 renderer = e_cell_renderer_color_new ();
149 g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
151 column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, "rgba", COL_COLOR_GDKRGBA, "visible", COL_SHOW_COLOR_BOOLEAN, NULL);
152 gtk_tree_view_append_column (data->sources_tree_view, column);
154 renderer = gtk_cell_renderer_text_new ();
155 gtk_tree_view_column_pack_start (column, renderer, FALSE);
156 gtk_tree_view_column_add_attribute (column, renderer, "markup", COL_DESCRIPTION_STRING);
157 g_object_set (G_OBJECT (renderer),
158 "max-width-chars", 60,
159 "wrap-mode", PANGO_WRAP_WORD_CHAR,
160 "wrap-width", 640,
161 NULL);
163 renderer = gtk_cell_renderer_text_new ();
164 column = gtk_tree_view_column_new_with_attributes (_("Supports"), renderer, "text", COL_SUPPORTS_STRING, NULL);
165 gtk_tree_view_append_column (data->sources_tree_view, column);
167 if (!supports_filter || (supports_filter & (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS |
168 E_WEBDAV_DISCOVER_SUPPORTS_MEMOS | E_WEBDAV_DISCOVER_SUPPORTS_TASKS)) != 0) {
169 GtkWidget *widget, *box;
171 widget = gtk_combo_box_text_new ();
172 data->email_addresses_combo = GTK_COMBO_BOX (widget);
174 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
175 widget = gtk_label_new_with_mnemonic (_("_User mail:"));
176 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (data->email_addresses_combo));
178 gtk_container_add (GTK_CONTAINER (box), widget);
179 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (data->email_addresses_combo));
181 g_object_set (G_OBJECT (widget),
182 "hexpand", FALSE,
183 "vexpand", FALSE,
184 "halign", GTK_ALIGN_START,
185 "valign", GTK_ALIGN_CENTER,
186 NULL);
188 g_object_set (G_OBJECT (data->email_addresses_combo),
189 "hexpand", TRUE,
190 "vexpand", FALSE,
191 "halign", GTK_ALIGN_FILL,
192 "valign", GTK_ALIGN_START,
193 NULL);
195 g_object_set (G_OBJECT (box),
196 "hexpand", TRUE,
197 "vexpand", FALSE,
198 "halign", GTK_ALIGN_FILL,
199 "valign", GTK_ALIGN_START,
200 NULL);
202 gtk_grid_attach (grid, box, 0, 1, 1, 1);
205 gtk_widget_show_all (content);
207 return content;
211 * e_webdav_discover_content_get_tree_selection:
212 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
214 * Returns inner #GtkTreeViewSelection. This is meant to be able to connect
215 * to its "changed" signal and update other parts of the parent widgets accordingly.
217 * Returns: (transfer none): inner #GtkTreeViewSelection
219 * Since: 3.18
221 GtkTreeSelection *
222 e_webdav_discover_content_get_tree_selection (GtkWidget *content)
224 EWebDAVDiscoverContentData *data;
226 g_return_val_if_fail (GTK_IS_GRID (content), NULL);
228 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
229 g_return_val_if_fail (data != NULL, NULL);
231 return gtk_tree_view_get_selection (data->sources_tree_view);
235 * e_webdav_discover_content_set_multiselect:
236 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
237 * @multiselect: whether multiselect is allowed
239 * Sets whether the WebDAV discovery content allows multiselect.
241 * Since: 3.18
243 void
244 e_webdav_discover_content_set_multiselect (GtkWidget *content,
245 gboolean multiselect)
247 EWebDAVDiscoverContentData *data;
248 GtkTreeSelection *selection;
250 g_return_if_fail (GTK_IS_GRID (content));
252 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
253 g_return_if_fail (data != NULL);
255 selection = gtk_tree_view_get_selection (data->sources_tree_view);
256 gtk_tree_selection_set_mode (selection, multiselect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
260 * e_webdav_discover_content_get_multiselect:
261 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
263 * Returns: whether multiselect is allowed for the @content.
265 * Since: 3.18
267 gboolean
268 e_webdav_discover_content_get_multiselect (GtkWidget *content)
270 EWebDAVDiscoverContentData *data;
271 GtkTreeSelection *selection;
273 g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
275 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
276 g_return_val_if_fail (data != NULL, FALSE);
278 selection = gtk_tree_view_get_selection (data->sources_tree_view);
279 return gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE;
283 * e_webdav_discover_content_set_base_url:
284 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
286 * Sets base URL for the @content. This is used to overwrite the one set on
287 * the #ESource from the creation time. The URL can be either a full URL, a path
288 * or even a %NULL.
290 * Since: 3.18
292 void
293 e_webdav_discover_content_set_base_url (GtkWidget *content,
294 const gchar *base_url)
296 EWebDAVDiscoverContentData *data;
298 g_return_if_fail (GTK_IS_GRID (content));
299 g_return_if_fail (base_url != NULL);
301 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
302 g_return_if_fail (data != NULL);
304 if (g_strcmp0 (base_url, data->base_url) != 0) {
305 g_free (data->base_url);
306 data->base_url = g_strdup (base_url);
311 * e_webdav_discover_content_get_base_url:
312 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
314 * Returns currently set base URL for the @content. This is used to overwrite the one
315 * set on the #ESource from the creation time. The URL can be either a full URL, a path
316 * or even a %NULL.
318 * Returns: currently set base URL for the @content.
320 * Since: 3.18
322 const gchar *
323 e_webdav_discover_content_get_base_url (GtkWidget *content)
325 EWebDAVDiscoverContentData *data;
327 g_return_val_if_fail (GTK_IS_GRID (content), NULL);
329 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
330 g_return_val_if_fail (data != NULL, NULL);
332 return data->base_url;
336 * e_webdav_discover_content_get_selected:
337 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
338 * @index: an index of the selected source; counts from 0
339 * @out_href: (out): an output location for the URL of the selected source
340 * @out_supports: (out): an output location of a bit-or of #EWebDAVDiscoverSupports, the set
341 * of source types this server source location supports
342 * @out_display_name: (out): an output location of the sources display name
343 * @out_color: (out): an output location of the string representation of the color
344 * for the source, as set on the server
346 * Returns information about selected source at index @index. The function can be called
347 * multiple times, with the index starting at zero and as long as it doesn't return %FALSE.
348 * If the @content doesn't have allowed multiselection, then the only valid @index is 0.
350 * All the @out_href, @out_display_name and @out_color are newly allocated strings, which should
351 * be freed with g_free(), when no longer needed.
353 * Returns: %TRUE, when a selected source of index @index exists, %FALSE otherwise.
355 * Since: 3.18
357 gboolean
358 e_webdav_discover_content_get_selected (GtkWidget *content,
359 gint index,
360 gchar **out_href,
361 guint *out_supports,
362 gchar **out_display_name,
363 gchar **out_color)
365 EWebDAVDiscoverContentData *data;
366 GtkTreeSelection *selection;
367 GtkTreeModel *model = NULL;
368 GList *selected_rows, *link;
369 gboolean success = FALSE;
371 g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
372 g_return_val_if_fail (index >= 0, FALSE);
373 g_return_val_if_fail (out_href != NULL, FALSE);
374 g_return_val_if_fail (out_supports != NULL, FALSE);
375 g_return_val_if_fail (out_display_name != NULL, FALSE);
376 g_return_val_if_fail (out_color != NULL, FALSE);
378 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
379 g_return_val_if_fail (data != NULL, FALSE);
381 selection = gtk_tree_view_get_selection (data->sources_tree_view);
382 selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
384 for (link = selected_rows; link && index > 0; link = g_list_next (link)) {
385 index--;
388 if (index == 0 && link) {
389 GtkTreePath *path = link->data;
391 if (path) {
392 GtkTreeIter iter;
394 success = gtk_tree_model_get_iter (model, &iter, path);
395 if (success) {
396 gtk_tree_model_get (model, &iter,
397 COL_HREF_STRING, out_href,
398 COL_SUPPORTS_UINT, out_supports,
399 COL_DISPLAY_NAME_STRING, out_display_name,
400 COL_COLOR_STRING, out_color,
401 -1);
406 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
408 return success;
412 * e_webdav_discover_content_get_user_address:
413 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
415 * Get currently selected user address in the @content, if the server returned any.
416 * This value has meaning only with calendar sources.
418 * Returns: (transfer full): currently selected user address. The returned string
419 * is newly allocated and should be freed with g_free() when no longer needed.
420 * If there are none addresses provided by the server, or no calendar sources
421 * were found, then %NULL is returned instead.
423 * Since: 3.18
425 gchar *
426 e_webdav_discover_content_get_user_address (GtkWidget *content)
428 EWebDAVDiscoverContentData *data;
429 gchar *active_text;
431 g_return_val_if_fail (GTK_IS_GRID (content), NULL);
433 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
434 g_return_val_if_fail (data != NULL, NULL);
436 if (!data->email_addresses_combo)
437 return NULL;
439 active_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (data->email_addresses_combo));
440 if (active_text && !*active_text) {
441 g_free (active_text);
442 active_text = NULL;
445 return active_text;
448 static void
449 e_webdav_discover_content_fill_discovered_sources (GtkTreeView *tree_view,
450 guint supports_filter,
451 GSList *discovered_sources)
453 GtkListStore *list_store;
454 GtkTreeModel *model;
455 GtkTreeIter iter;
456 GSList *link;
458 /* It's okay to pass NULL here */
459 if (!tree_view)
460 return;
462 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
464 model = gtk_tree_view_get_model (tree_view);
465 list_store = GTK_LIST_STORE (model);
466 gtk_list_store_clear (list_store);
468 for (link = discovered_sources; link; link = g_slist_next (link)) {
469 const EWebDAVDiscoveredSource *source = link->data;
470 guint supports_bits;
471 GString *supports;
472 gchar *description_markup, *colorstr = NULL;
473 gboolean show_color = FALSE;
474 GdkRGBA rgba;
476 if (!source || (supports_filter && (source->supports & supports_filter) == 0) || !source->display_name)
477 continue;
479 if (source->color && *source->color) {
480 gint rr, gg, bb;
482 if (gdk_rgba_parse (&rgba, source->color)) {
483 show_color = TRUE;
484 } else if (sscanf (source->color, "#%02x%02x%02x", &rr, &gg, &bb) == 3) {
485 rgba.red = ((gdouble) rr) / 255.0;
486 rgba.green = ((gdouble) gg) / 255.0;
487 rgba.blue = ((gdouble) bb) / 255.0;
488 rgba.alpha = 1.0;
490 show_color = TRUE;
493 if (show_color) {
494 rr = 0xFF * rgba.red;
495 gg = 0xFF * rgba.green;
496 bb = 0xFF * rgba.blue;
498 colorstr = g_strdup_printf ("#%02x%02x%02x", rr & 0xFF, gg & 0xFF, bb & 0xFF);
502 if (source->description && *source->description) {
503 description_markup = g_markup_printf_escaped ("<b>%s</b>\n<small>%s</small>",
504 source->display_name, source->description);
505 } else {
506 description_markup = g_markup_printf_escaped ("<b>%s</b>",
507 source->display_name);
510 supports_bits = source->supports;
511 supports = g_string_new ("");
513 #define addbit(flg, cpt) { \
514 if (((flg) & supports_bits) != 0) { \
515 if (supports->len) \
516 g_string_append (supports, ", "); \
517 g_string_append (supports, cpt); \
521 addbit (E_WEBDAV_DISCOVER_SUPPORTS_CONTACTS, C_("WebDAVDiscover", "Contacts"));
522 addbit (E_WEBDAV_DISCOVER_SUPPORTS_EVENTS, C_("WebDAVDiscover", "Events"));
523 addbit (E_WEBDAV_DISCOVER_SUPPORTS_MEMOS, C_("WebDAVDiscover", "Memos"));
524 addbit (E_WEBDAV_DISCOVER_SUPPORTS_TASKS, C_("WebDAVDiscover", "Tasks"));
526 #undef addbit
528 gtk_list_store_append (list_store, &iter);
529 gtk_list_store_set (list_store, &iter,
530 COL_HREF_STRING, source->href,
531 COL_SUPPORTS_UINT, source->supports,
532 COL_DISPLAY_NAME_STRING, source->display_name,
533 COL_COLOR_STRING, colorstr,
534 COL_DESCRIPTION_STRING, description_markup,
535 COL_SUPPORTS_STRING, supports->str,
536 COL_COLOR_GDKRGBA, show_color ? &rgba : NULL,
537 COL_SHOW_COLOR_BOOLEAN, show_color,
538 -1);
540 g_free (description_markup);
541 g_free (colorstr);
542 g_string_free (supports, TRUE);
546 static void
547 e_webdav_discover_content_fill_calendar_emails (GtkComboBox *combo_box,
548 GSList *calendar_user_addresses)
550 GtkComboBoxText *text_combo;
551 gboolean any_added = FALSE;
552 GSList *link;
554 /* It's okay to pass NULL here */
555 if (!combo_box)
556 return;
558 g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
560 text_combo = GTK_COMBO_BOX_TEXT (combo_box);
562 gtk_combo_box_text_remove_all (text_combo);
564 for (link = calendar_user_addresses; link; link = g_slist_next (link)) {
565 const gchar *address = link->data;
567 if (address && *address) {
568 gtk_combo_box_text_append_text (text_combo, address);
569 any_added = TRUE;
573 if (any_added)
574 gtk_combo_box_set_active (combo_box, 0);
577 typedef struct _RefreshData {
578 GtkWidget *content;
579 GCancellable *cancellable;
580 GSimpleAsyncResult *simple;
581 gchar *base_url;
582 ENamedParameters *credentials;
583 ESourceRegistry *registry;
584 } RefreshData;
586 static void
587 refresh_data_free (gpointer ptr)
589 RefreshData *rd = ptr;
591 if (rd) {
592 if (rd->content) {
593 EWebDAVDiscoverContentData *data;
595 data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
597 if (data) {
598 if (data->info_bar && gtk_info_bar_get_message_type (data->info_bar) == GTK_MESSAGE_INFO) {
599 gtk_widget_destroy (GTK_WIDGET (data->info_bar));
600 data->info_bar = NULL;
603 gtk_widget_set_sensitive (GTK_WIDGET (data->sources_tree_view), TRUE);
604 if (data->email_addresses_combo)
605 gtk_widget_set_sensitive (GTK_WIDGET (data->email_addresses_combo), TRUE);
609 g_clear_object (&rd->content);
610 g_clear_object (&rd->cancellable);
611 g_clear_object (&rd->simple);
612 g_clear_object (&rd->registry);
613 g_free (rd->base_url);
614 e_named_parameters_free (rd->credentials);
615 g_free (rd);
619 static void
620 e_webdav_discover_content_refresh_done_cb (GObject *source_object,
621 GAsyncResult *result,
622 gpointer user_data);
624 static void
625 e_webdav_discover_content_trust_prompt_done_cb (GObject *source_object,
626 GAsyncResult *result,
627 gpointer user_data)
629 ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
630 ESource *source;
631 RefreshData *rd = user_data;
632 GError *local_error = NULL;
634 g_return_if_fail (E_IS_SOURCE (source_object));
635 g_return_if_fail (rd != NULL);
637 source = E_SOURCE (source_object);
638 if (!e_trust_prompt_run_for_source_finish (source, result, &response, &local_error)) {
639 g_simple_async_result_take_error (rd->simple, local_error);
640 local_error = NULL;
641 g_simple_async_result_complete (rd->simple);
642 refresh_data_free (rd);
643 } else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT || response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) {
644 /* Use NULL credentials to reuse those from the last time. */
645 e_webdav_discover_sources_full (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, rd->credentials,
646 rd->registry ? (EWebDAVDiscoverRefSourceFunc) e_source_registry_ref_source : NULL, rd->registry,
647 rd->cancellable, e_webdav_discover_content_refresh_done_cb, rd);
648 } else {
649 g_cancellable_cancel (rd->cancellable);
650 g_warn_if_fail (g_cancellable_set_error_if_cancelled (rd->cancellable, &local_error));
651 g_simple_async_result_take_error (rd->simple, local_error);
652 local_error = NULL;
653 g_simple_async_result_complete (rd->simple);
654 refresh_data_free (rd);
657 g_clear_error (&local_error);
660 static void
661 e_webdav_discover_content_credentials_prompt_done_cb (GObject *source_object,
662 GAsyncResult *result,
663 gpointer user_data)
665 RefreshData *rd = user_data;
666 ENamedParameters *credentials = NULL;
667 ESource *source = NULL;
668 GError *local_error = NULL;
670 g_return_if_fail (rd != NULL);
671 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (source_object));
673 if (!e_credentials_prompter_prompt_finish (E_CREDENTIALS_PROMPTER (source_object), result,
674 &source, &credentials, &local_error)) {
675 g_simple_async_result_take_error (rd->simple, local_error);
676 local_error = NULL;
677 g_simple_async_result_complete (rd->simple);
678 refresh_data_free (rd);
679 } else {
680 e_named_parameters_free (rd->credentials);
681 rd->credentials = credentials;
682 credentials = NULL;
684 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION) &&
685 rd->credentials && e_named_parameters_exists (rd->credentials, E_SOURCE_CREDENTIAL_USERNAME)) {
686 ESourceAuthentication *auth_extension;
688 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
689 e_source_authentication_set_user (auth_extension, e_named_parameters_get (rd->credentials, E_SOURCE_CREDENTIAL_USERNAME));
692 e_webdav_discover_sources_full (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, rd->credentials,
693 rd->registry ? (EWebDAVDiscoverRefSourceFunc) e_source_registry_ref_source : NULL, rd->registry,
694 rd->cancellable, e_webdav_discover_content_refresh_done_cb, rd);
697 e_named_parameters_free (credentials);
698 g_clear_object (&source);
699 g_clear_error (&local_error);
702 static void
703 e_webdav_discover_content_refresh_done_cb (GObject *source_object,
704 GAsyncResult *result,
705 gpointer user_data)
707 RefreshData *rd = user_data;
708 ESource *source;
709 gchar *certificate_pem = NULL;
710 GTlsCertificateFlags certificate_errors = 0;
711 GSList *discovered_sources = NULL;
712 GSList *calendar_user_addresses = NULL;
713 GError *local_error = NULL;
715 g_return_if_fail (E_IS_SOURCE (source_object));
716 g_return_if_fail (rd != NULL);
718 source = E_SOURCE (source_object);
720 if (!e_webdav_discover_sources_finish (source, result,
721 &certificate_pem, &certificate_errors, &discovered_sources,
722 &calendar_user_addresses, &local_error)) {
723 if (!g_cancellable_is_cancelled (rd->cancellable) && certificate_pem &&
724 g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
725 GtkWindow *parent;
726 GtkWidget *widget;
728 widget = gtk_widget_get_toplevel (rd->content);
729 parent = widget ? GTK_WINDOW (widget) : NULL;
731 e_trust_prompt_run_for_source (parent, source, certificate_pem, certificate_errors,
732 NULL, FALSE, rd->cancellable, e_webdav_discover_content_trust_prompt_done_cb, rd);
733 rd = NULL;
734 } else if (g_cancellable_is_cancelled (rd->cancellable) ||
735 (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED) &&
736 !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
737 !g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
738 !g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN))) {
739 g_simple_async_result_take_error (rd->simple, local_error);
740 local_error = NULL;
741 g_simple_async_result_complete (rd->simple);
742 } else {
743 EWebDAVDiscoverContentData *data;
745 data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
746 g_return_if_fail (data != NULL);
748 e_credentials_prompter_prompt (data->credentials_prompter, source,
749 local_error ? local_error->message : NULL,
750 rd->credentials ? E_CREDENTIALS_PROMPTER_PROMPT_FLAG_NONE:
751 E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS,
752 e_webdav_discover_content_credentials_prompt_done_cb, rd);
753 rd = NULL;
755 } else {
756 EWebDAVDiscoverContentData *data;
758 data = g_object_get_data (G_OBJECT (rd->content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
759 g_warn_if_fail (data != NULL);
761 if (data) {
762 e_webdav_discover_content_fill_discovered_sources (data->sources_tree_view,
763 data->supports_filter, discovered_sources);
764 e_webdav_discover_content_fill_calendar_emails (data->email_addresses_combo,
765 calendar_user_addresses);
768 g_simple_async_result_set_op_res_gboolean (rd->simple, TRUE);
769 g_simple_async_result_complete (rd->simple);
772 g_free (certificate_pem);
773 e_webdav_discover_free_discovered_sources (discovered_sources);
774 g_slist_free_full (calendar_user_addresses, g_free);
775 refresh_data_free (rd);
776 g_clear_error (&local_error);
779 static void
780 e_webdav_discover_info_bar_response_cb (GtkInfoBar *info_bar,
781 gint response_id,
782 RefreshData *rd)
784 if (response_id == GTK_RESPONSE_CANCEL) {
785 g_return_if_fail (rd != NULL);
786 g_return_if_fail (rd->cancellable != NULL);
788 g_cancellable_cancel (rd->cancellable);
793 * e_webdav_discover_content_refresh:
794 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
795 * @display_name: (allow-none): optional display name to use for scratch sources
796 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
797 * @callback: (scope async): a #GAsyncReadyCallback to call when the request
798 * is satisfied
799 * @user_data: (closure): data to pass to the callback function
801 * Asynchronously starts refresh of the @content. This means to access the server
802 * and search it for available sources. The @content shows a feedback and a Cancel
803 * button during the operation.
805 * The @display_name is used only if the @content wasn't created with an #ESource and
806 * it's shown in the password prompts, if there are required any.
808 * When the operation is finished, @callback will be called. You can then
809 * call e_webdav_discover_content_refresh_finish() to get the result of the operation.
811 * Since: 3.18
813 void
814 e_webdav_discover_content_refresh (GtkWidget *content,
815 const gchar *display_name,
816 GCancellable *cancellable,
817 GAsyncReadyCallback callback,
818 gpointer user_data)
820 EWebDAVDiscoverContentData *data;
821 RefreshData *rd;
822 ESource *source;
823 SoupURI *soup_uri;
824 GtkWidget *label;
826 g_return_if_fail (GTK_IS_GRID (content));
828 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
829 g_return_if_fail (data != NULL);
830 g_return_if_fail (data->base_url != NULL);
832 soup_uri = soup_uri_new (data->base_url);
833 if (!soup_uri) {
834 GSimpleAsyncResult *simple;
836 simple = g_simple_async_result_new (G_OBJECT (content), callback, user_data, e_webdav_discover_content_refresh);
837 g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
838 _("Invalid URL"));
839 g_simple_async_result_complete_in_idle (simple);
840 g_object_unref (simple);
842 return;
845 rd = g_new0 (RefreshData, 1);
846 rd->content = g_object_ref (content);
847 rd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
848 rd->simple = g_simple_async_result_new (G_OBJECT (content), callback, user_data, e_webdav_discover_content_refresh);
849 rd->base_url = g_strdup (data->base_url);
850 rd->credentials = NULL;
851 rd->registry = e_credentials_prompter_get_registry (data->credentials_prompter);
853 if (rd->registry)
854 g_object_ref (rd->registry);
856 if (data->source) {
857 source = g_object_ref (data->source);
858 } else {
859 ESourceWebdav *webdav_extension;
860 ESourceAuthentication *auth_extension;
862 source = e_source_new_with_uid (data->base_url, NULL, NULL);
863 g_return_if_fail (source != NULL);
865 webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
866 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
868 if (display_name && *display_name)
869 e_source_set_display_name (source, display_name);
870 e_source_webdav_set_soup_uri (webdav_extension, soup_uri);
871 e_source_authentication_set_host (auth_extension, soup_uri_get_host (soup_uri));
872 e_source_authentication_set_port (auth_extension, soup_uri_get_port (soup_uri));
873 e_source_authentication_set_user (auth_extension, soup_uri_get_user (soup_uri));
876 gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (data->sources_tree_view)));
877 if (data->email_addresses_combo)
878 gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (data->email_addresses_combo));
880 if (data->info_bar)
881 gtk_widget_destroy (GTK_WIDGET (data->info_bar));
883 data->info_bar = GTK_INFO_BAR (gtk_info_bar_new_with_buttons (_("Cancel"), GTK_RESPONSE_CANCEL, NULL));
884 gtk_info_bar_set_message_type (data->info_bar, GTK_MESSAGE_INFO);
885 gtk_info_bar_set_show_close_button (data->info_bar, FALSE);
886 label = gtk_label_new (_("Searching server sources..."));
887 gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (data->info_bar)), label);
888 gtk_widget_show (label);
889 gtk_widget_show (GTK_WIDGET (data->info_bar));
891 g_signal_connect (data->info_bar, "response", G_CALLBACK (e_webdav_discover_info_bar_response_cb), rd);
893 gtk_widget_set_sensitive (GTK_WIDGET (data->sources_tree_view), FALSE);
894 if (data->email_addresses_combo)
895 gtk_widget_set_sensitive (GTK_WIDGET (data->email_addresses_combo), FALSE);
897 gtk_grid_attach (GTK_GRID (content), GTK_WIDGET (data->info_bar), 0, 2, 1, 1);
899 e_webdav_discover_sources_full (source, rd->base_url, E_WEBDAV_DISCOVER_SUPPORTS_NONE, rd->credentials,
900 rd->registry ? (EWebDAVDiscoverRefSourceFunc) e_source_registry_ref_source : NULL, rd->registry,
901 rd->cancellable, e_webdav_discover_content_refresh_done_cb, rd);
903 g_object_unref (source);
904 soup_uri_free (soup_uri);
908 * e_webdav_discover_content_refresh_finish:
909 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
910 * @result: a #GAsyncResult
911 * @error: (allow-none): return location for a #GError, or %NULL
913 * Finishes the operation started with e_webdav_discover_content_refresh(). If an
914 * error occurred, the function will set @error and return %FALSE. There is
915 * available e_webdav_discover_content_show_error() for convenience, which
916 * shows the error within @content and takes care of it when refreshing
917 * the content.
919 * Returns: %TRUE on success, %FALSE on failure
921 * Since: 3.18
923 gboolean
924 e_webdav_discover_content_refresh_finish (GtkWidget *content,
925 GAsyncResult *result,
926 GError **error)
928 EWebDAVDiscoverContentData *data;
929 GSimpleAsyncResult *simple;
931 g_return_val_if_fail (GTK_IS_GRID (content), FALSE);
933 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
934 g_return_val_if_fail (data != NULL, FALSE);
935 g_return_val_if_fail (g_simple_async_result_is_valid (
936 result, G_OBJECT (content), e_webdav_discover_content_refresh), FALSE);
938 simple = G_SIMPLE_ASYNC_RESULT (result);
940 if (g_simple_async_result_propagate_error (simple, error))
941 return FALSE;
943 return g_simple_async_result_get_op_res_gboolean (simple);
946 static void
947 e_webdav_discover_info_bar_error_response_cb (GtkInfoBar *info_bar,
948 gint response_id,
949 GtkWidget *content)
951 EWebDAVDiscoverContentData *data;
953 g_return_if_fail (GTK_IS_GRID (content));
955 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
956 g_return_if_fail (data != NULL);
958 if (data->info_bar == info_bar) {
959 gtk_widget_destroy (GTK_WIDGET (data->info_bar));
960 data->info_bar = NULL;
965 * e_webdav_discover_content_show_error:
966 * @content: a WebDAV discovery content, created by e_webdav_discover_content_new()
967 * @error: (allow-none): a #GError to show in the UI, or %NULL
969 * Shows the @error within @content, unless it's a #G_IO_ERROR_CANCELLED, or %NULL,
970 * which are safely ignored. The advantage of this function is that the error
971 * message is removed when the refresh operation is started.
973 * Since: 3.18
975 void
976 e_webdav_discover_content_show_error (GtkWidget *content,
977 const GError *error)
979 EWebDAVDiscoverContentData *data;
980 GtkWidget *label;
982 g_return_if_fail (GTK_IS_GRID (content));
984 data = g_object_get_data (G_OBJECT (content), WEBDAV_DISCOVER_CONTENT_DATA_KEY);
985 g_return_if_fail (data != NULL);
987 if (data->info_bar) {
988 gtk_widget_destroy (GTK_WIDGET (data->info_bar));
989 data->info_bar = NULL;
992 if (!error || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
993 return;
995 data->info_bar = GTK_INFO_BAR (gtk_info_bar_new ());
996 gtk_info_bar_set_message_type (data->info_bar, GTK_MESSAGE_ERROR);
997 gtk_info_bar_set_show_close_button (data->info_bar, TRUE);
999 label = gtk_label_new (error->message);
1000 gtk_label_set_max_width_chars (GTK_LABEL (label), 120);
1001 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1002 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
1003 gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (data->info_bar)), label);
1004 gtk_widget_show (label);
1005 gtk_widget_show (GTK_WIDGET (data->info_bar));
1007 g_signal_connect (data->info_bar, "response", G_CALLBACK (e_webdav_discover_info_bar_error_response_cb), content);
1009 gtk_grid_attach (GTK_GRID (content), GTK_WIDGET (data->info_bar), 0, 2, 1, 1);
1012 static void
1013 e_webdav_discover_content_dialog_refresh_done_cb (GObject *source_object,
1014 GAsyncResult *result,
1015 gpointer user_data)
1017 GError *local_error = NULL;
1019 if (!e_webdav_discover_content_refresh_finish (GTK_WIDGET (source_object), result, &local_error)) {
1020 e_webdav_discover_content_show_error (GTK_WIDGET (source_object), local_error);
1023 g_clear_error (&local_error);
1026 static void
1027 e_webdav_discover_content_selection_changed_cb (GtkTreeSelection *selection,
1028 GtkDialog *dialog)
1030 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1031 g_return_if_fail (GTK_IS_DIALOG (dialog));
1033 gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT,
1034 gtk_tree_selection_count_selected_rows (selection) > 0);
1038 * e_webdav_discover_dialog_new:
1039 * @parent: a #GtkWindow parent for the dialog
1040 * @title: title of the window
1041 * @credentials_prompter: an #ECredentialsPrompter to use to ask for credentials
1042 * @source: an #ESource to use for authentication
1043 * @base_url: (allow-none): optional base URL to use for discovery, or %NULL
1044 * @supports_filter: a bit-or of #EWebDAVDiscoverSupports, a filter to limit what source
1045 * types will be shown in the dialog content; use %E_WEBDAV_DISCOVER_SUPPORTS_NONE
1046 * to show all
1048 * Creates a new #GtkDialog which has as its content a WebDAV discovery widget,
1049 * created with e_webdav_discover_content_new(). This dialog can be shown to a user
1050 * and when its final response is %GTK_RESPONSE_ACCEPT, then the inner content
1051 * can be asked for currently selected source(s).
1053 * Returns: (transfer full): a newly created #GtkDialog, which should be freed
1054 * with gtk_widget_destroy(), when no longer needed.
1056 * Since: 3.18
1058 GtkDialog *
1059 e_webdav_discover_dialog_new (GtkWindow *parent,
1060 const gchar *title,
1061 ECredentialsPrompter *credentials_prompter,
1062 ESource *source,
1063 const gchar *base_url,
1064 guint supports_filter)
1066 GtkWidget *dialog, *container, *widget;
1067 GtkTreeSelection *selection;
1069 dialog = gtk_dialog_new_with_buttons (title, parent, GTK_DIALOG_DESTROY_WITH_PARENT,
1070 _("_Cancel"), GTK_RESPONSE_REJECT,
1071 _("_OK"), GTK_RESPONSE_ACCEPT,
1072 NULL);
1074 widget = e_webdav_discover_content_new (credentials_prompter, source, base_url, supports_filter);
1076 g_object_set (G_OBJECT (widget),
1077 "hexpand", TRUE,
1078 "vexpand", TRUE,
1079 "halign", GTK_ALIGN_FILL,
1080 "valign", GTK_ALIGN_FILL,
1081 NULL);
1083 container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1085 gtk_container_add (GTK_CONTAINER (container), widget);
1087 g_object_set_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY, widget);
1089 gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 400);
1091 selection = e_webdav_discover_content_get_tree_selection (widget);
1092 g_signal_connect (selection, "changed", G_CALLBACK (e_webdav_discover_content_selection_changed_cb), dialog);
1093 e_webdav_discover_content_selection_changed_cb (selection, GTK_DIALOG (dialog));
1095 return GTK_DIALOG (dialog);
1099 * e_webdav_discover_dialog_get_content:
1100 * @dialog: a #GtkDialog returned by e_webdav_discover_dialog_new()
1102 * Returns inner WebDAV discovery content, which can be further manipulated.
1104 * Returns: (transfer none): inner WebDAV discovery content
1106 * Since: 3.18
1108 GtkWidget *
1109 e_webdav_discover_dialog_get_content (GtkDialog *dialog)
1111 GtkWidget *content;
1113 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
1115 content = g_object_get_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY);
1116 g_return_val_if_fail (content != NULL, NULL);
1118 return content;
1122 * e_webdav_discover_dialog_refresh:
1123 * @dialog: a #GtkDialog returned by e_webdav_discover_dialog_new()
1125 * Invokes refresh of the inner content of the WebDAV discovery dialog.
1127 * Since: 3.18
1129 void
1130 e_webdav_discover_dialog_refresh (GtkDialog *dialog)
1132 GtkWidget *content;
1134 g_return_if_fail (GTK_IS_DIALOG (dialog));
1136 content = g_object_get_data (G_OBJECT (dialog), WEBDAV_DISCOVER_CONTENT_KEY);
1137 g_return_if_fail (content != NULL);
1139 e_webdav_discover_content_refresh (content, gtk_window_get_title (GTK_WINDOW (dialog)),
1140 NULL, e_webdav_discover_content_dialog_refresh_done_cb, NULL);