1 /*
2  * evolution-cal-config-caldav.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 <glib/gi18n-lib.h>
21 
22 #include <libebackend/libebackend.h>
23 #include <libedataserverui/libedataserverui.h>
24 
25 #include <e-util/e-util.h>
26 
27 typedef ESourceConfigBackend ECalConfigCalDAV;
28 typedef ESourceConfigBackendClass ECalConfigCalDAVClass;
29 
30 typedef struct _Context Context;
31 
32 struct _Context {
33 	ESourceConfigBackend *backend;		/* not referenced */
34 	ESource *scratch_source;		/* not referenced */
35 
36 	GtkWidget *url_entry;
37 	GtkWidget *email_entry;
38 	GtkWidget *find_button;
39 	GtkWidget *auto_schedule_toggle;
40 };
41 
42 /* Module Entry Points */
43 void e_module_load (GTypeModule *type_module);
44 void e_module_unload (GTypeModule *type_module);
45 
46 /* Forward Declarations */
47 GType e_cal_config_caldav_get_type (void);
48 
G_DEFINE_DYNAMIC_TYPE(ECalConfigCalDAV,e_cal_config_caldav,E_TYPE_SOURCE_CONFIG_BACKEND)49 G_DEFINE_DYNAMIC_TYPE (
50 	ECalConfigCalDAV,
51 	e_cal_config_caldav,
52 	E_TYPE_SOURCE_CONFIG_BACKEND)
53 
54 static Context *
55 cal_config_caldav_context_new (ESourceConfigBackend *backend,
56                                ESource *scratch_source)
57 {
58 	Context *context;
59 
60 	context = g_slice_new0 (Context);
61 	context->backend = backend;
62 	context->scratch_source = scratch_source;
63 
64 	return context;
65 }
66 
67 static void
cal_config_caldav_context_free(Context * context)68 cal_config_caldav_context_free (Context *context)
69 {
70 	g_clear_object (&context->url_entry);
71 	g_clear_object (&context->email_entry);
72 	g_clear_object (&context->find_button);
73 	g_clear_object (&context->auto_schedule_toggle);
74 
75 	g_slice_free (Context, context);
76 }
77 
78 static GtkWindow *
caldav_config_get_dialog_parent_cb(ECredentialsPrompter * prompter,GtkWindow * dialog)79 caldav_config_get_dialog_parent_cb (ECredentialsPrompter *prompter,
80 				    GtkWindow *dialog)
81 {
82 	return dialog;
83 }
84 
85 static void
cal_config_caldav_run_dialog(GtkButton * button,Context * context)86 cal_config_caldav_run_dialog (GtkButton *button,
87                               Context *context)
88 {
89 	ESourceConfig *config;
90 	ESourceRegistry *registry;
91 	ESourceWebdav *webdav_extension;
92 	ECalClientSourceType source_type;
93 	ECredentialsPrompter *prompter;
94 	SoupURI *uri;
95 	gchar *base_url;
96 	GtkDialog *dialog;
97 	gpointer parent;
98 	gulong handler_id;
99 	guint supports_filter = 0;
100 	const gchar *title = NULL;
101 
102 	config = e_source_config_backend_get_config (context->backend);
103 	registry = e_source_config_get_registry (config);
104 
105 	parent = gtk_widget_get_toplevel (GTK_WIDGET (config));
106 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
107 	source_type = e_cal_source_config_get_source_type (E_CAL_SOURCE_CONFIG (config));
108 
109 	switch (source_type) {
110 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
111 		supports_filter = E_WEBDAV_DISCOVER_SUPPORTS_EVENTS;
112 		title = _("Choose a Calendar");
113 		break;
114 	case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
115 		supports_filter = E_WEBDAV_DISCOVER_SUPPORTS_MEMOS;
116 		title = _("Choose a Memo List");
117 		break;
118 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
119 		supports_filter = E_WEBDAV_DISCOVER_SUPPORTS_TASKS;
120 		title = _("Choose a Task List");
121 		break;
122 	default:
123 		g_return_if_reached ();
124 	}
125 
126 	webdav_extension = e_source_get_extension (context->scratch_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
127 
128 	uri = e_source_webdav_dup_soup_uri (webdav_extension);
129 
130 	prompter = e_credentials_prompter_new (registry);
131 	e_credentials_prompter_set_auto_prompt (prompter, FALSE);
132 	base_url = soup_uri_to_string (uri, FALSE);
133 
134 	dialog = e_webdav_discover_dialog_new (parent, title, prompter, context->scratch_source, base_url, supports_filter);
135 
136 	if (parent != NULL)
137 		e_binding_bind_property (
138 			parent, "icon-name",
139 			dialog, "icon-name",
140 			G_BINDING_SYNC_CREATE);
141 
142 	handler_id = g_signal_connect (prompter, "get-dialog-parent",
143 		G_CALLBACK (caldav_config_get_dialog_parent_cb), dialog);
144 
145 	e_webdav_discover_dialog_refresh (dialog);
146 
147 	if (gtk_dialog_run (dialog) == GTK_RESPONSE_ACCEPT) {
148 		gchar *href = NULL, *display_name = NULL, *color = NULL, *email;
149 		guint supports = 0, order = 0;
150 		GtkWidget *content;
151 
152 		content = e_webdav_discover_dialog_get_content (dialog);
153 
154 		if (e_webdav_discover_content_get_selected (content, 0, &href, &supports, &display_name, &color, &order)) {
155 			soup_uri_free (uri);
156 			uri = soup_uri_new (href);
157 
158 			if (uri) {
159 				ESourceSelectable *selectable_extension;
160 				const gchar *extension_name;
161 
162 				switch (source_type) {
163 					case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
164 						extension_name = E_SOURCE_EXTENSION_CALENDAR;
165 						break;
166 					case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
167 						extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
168 						break;
169 					case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
170 						extension_name = E_SOURCE_EXTENSION_TASK_LIST;
171 						break;
172 					default:
173 						g_return_if_reached ();
174 				}
175 
176 				selectable_extension = e_source_get_extension (context->scratch_source, extension_name);
177 
178 				e_source_set_display_name (context->scratch_source, display_name);
179 
180 				e_source_webdav_set_display_name (webdav_extension, display_name);
181 				e_source_webdav_set_soup_uri (webdav_extension, uri);
182 				e_source_webdav_set_order (webdav_extension, order);
183 
184 				if (source_type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
185 					e_source_webdav_set_calendar_auto_schedule (webdav_extension, (supports & E_WEBDAV_DISCOVER_SUPPORTS_CALENDAR_AUTO_SCHEDULE) != 0);
186 
187 				if (color && *color)
188 					e_source_selectable_set_color (selectable_extension, color);
189 
190 				e_source_selectable_set_order (selectable_extension, order);
191 			}
192 
193 			g_free (href);
194 			g_free (display_name);
195 			g_free (color);
196 
197 			href = NULL;
198 			display_name = NULL;
199 			color = NULL;
200 		}
201 
202 		email = e_webdav_discover_content_get_user_address (content);
203 		if (email && *email)
204 			e_source_webdav_set_email_address (webdav_extension, email);
205 		g_free (email);
206 	}
207 
208 	g_signal_handler_disconnect (prompter, handler_id);
209 
210 	gtk_widget_destroy (GTK_WIDGET (dialog));
211 
212 	g_object_unref (prompter);
213 	if (uri)
214 		soup_uri_free (uri);
215 	g_free (base_url);
216 }
217 
218 static gboolean
cal_config_caldav_uri_to_text(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)219 cal_config_caldav_uri_to_text (GBinding *binding,
220                                const GValue *source_value,
221                                GValue *target_value,
222                                gpointer user_data)
223 {
224 	SoupURI *soup_uri;
225 	gchar *text;
226 
227 	soup_uri = g_value_get_boxed (source_value);
228 	soup_uri_set_user (soup_uri, NULL);
229 
230 	if (soup_uri_get_host (soup_uri)) {
231 		text = soup_uri_to_string (soup_uri, FALSE);
232 	} else {
233 		GObject *target;
234 
235 		text = NULL;
236 		target = g_binding_get_target (binding);
237 		g_object_get (target, g_binding_get_target_property (binding), &text, NULL);
238 
239 		if (!text || !*text) {
240 			g_free (text);
241 			text = soup_uri_to_string (soup_uri, FALSE);
242 		}
243 	}
244 
245 	g_value_take_string (target_value, text);
246 
247 	return TRUE;
248 }
249 
250 static gboolean
cal_config_caldav_text_to_uri(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)251 cal_config_caldav_text_to_uri (GBinding *binding,
252                                const GValue *source_value,
253                                GValue *target_value,
254                                gpointer user_data)
255 {
256 	ESource *source;
257 	SoupURI *soup_uri;
258 	ESourceAuthentication *extension;
259 	const gchar *extension_name;
260 	const gchar *text;
261 	const gchar *user;
262 
263 	text = g_value_get_string (source_value);
264 	soup_uri = soup_uri_new (text);
265 
266 	if (!soup_uri)
267 		soup_uri = soup_uri_new ("http://");
268 
269 	source = E_SOURCE (user_data);
270 	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
271 	extension = e_source_get_extension (source, extension_name);
272 	user = e_source_authentication_get_user (extension);
273 
274 	soup_uri_set_user (soup_uri, user);
275 
276 	g_value_take_boxed (target_value, soup_uri);
277 
278 	return TRUE;
279 }
280 
281 static void
cal_config_caldav_insert_widgets(ESourceConfigBackend * backend,ESource * scratch_source)282 cal_config_caldav_insert_widgets (ESourceConfigBackend *backend,
283                                   ESource *scratch_source)
284 {
285 	ESourceConfig *config;
286 	ESource *collection_source;
287 	ESourceExtension *extension;
288 	ECalClientSourceType source_type;
289 	GtkWidget *widget;
290 	Context *context;
291 	const gchar *extension_name;
292 	const gchar *label;
293 	const gchar *uid;
294 
295 	config = e_source_config_backend_get_config (backend);
296 	collection_source = e_source_config_get_collection_source (config);
297 
298 	uid = e_source_get_uid (scratch_source);
299 	context = cal_config_caldav_context_new (backend, scratch_source);
300 
301 	g_object_set_data_full (
302 		G_OBJECT (backend), uid, context,
303 		(GDestroyNotify) cal_config_caldav_context_free);
304 
305 	if (collection_source) {
306 		widget = gtk_label_new ("");
307 		g_object_set (G_OBJECT (widget),
308 			"ellipsize", PANGO_ELLIPSIZE_MIDDLE,
309 			"selectable", TRUE,
310 			"xalign", 0.0f,
311 			NULL);
312 		e_source_config_insert_widget (config, scratch_source, _("URL:"), widget);
313 		gtk_widget_show (widget);
314 
315 		extension = e_source_get_extension (scratch_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
316 
317 		e_binding_bind_property_full (
318 			extension, "soup-uri",
319 			widget, "label",
320 			G_BINDING_SYNC_CREATE,
321 			cal_config_caldav_uri_to_text,
322 			NULL,
323 			g_object_ref (scratch_source),
324 			(GDestroyNotify) g_object_unref);
325 
326 		e_binding_bind_property (
327 			widget, "label",
328 			widget, "tooltip-text",
329 			G_BINDING_SYNC_CREATE);
330 	} else {
331 		widget = gtk_entry_new ();
332 		e_source_config_insert_widget (
333 			config, scratch_source, _("URL:"), widget);
334 		context->url_entry = g_object_ref (widget);
335 		gtk_widget_show (widget);
336 	}
337 
338 	e_source_config_add_secure_connection_for_webdav (
339 		config, scratch_source);
340 
341 	source_type = e_cal_source_config_get_source_type (E_CAL_SOURCE_CONFIG (config));
342 
343 	if (!collection_source) {
344 		e_source_config_add_user_entry (config, scratch_source);
345 
346 		switch (source_type) {
347 			case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
348 				label = _("Find Calendars");
349 				break;
350 			case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
351 				label = _("Find Memo Lists");
352 				break;
353 			case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
354 				label = _("Find Task Lists");
355 				break;
356 			default:
357 				g_return_if_reached ();
358 		}
359 
360 		widget = gtk_button_new_with_label (label);
361 		e_source_config_insert_widget (
362 			config, scratch_source, NULL, widget);
363 		context->find_button = g_object_ref (widget);
364 		gtk_widget_show (widget);
365 
366 		g_signal_connect (
367 			widget, "clicked",
368 			G_CALLBACK (cal_config_caldav_run_dialog), context);
369 	}
370 
371 	widget = gtk_entry_new ();
372 	e_source_config_insert_widget (
373 		config, scratch_source, _("Email:"), widget);
374 	context->email_entry = g_object_ref (widget);
375 	gtk_widget_show (widget);
376 
377 	if (source_type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
378 		widget = gtk_check_button_new_with_label (
379 			_("Server handles meeting invitations"));
380 		e_source_config_insert_widget (
381 			config, scratch_source, NULL, widget);
382 		context->auto_schedule_toggle = g_object_ref (widget);
383 		gtk_widget_show (widget);
384 	}
385 
386 	e_source_config_add_refresh_interval (config, scratch_source);
387 
388 	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
389 	extension = e_source_get_extension (scratch_source, extension_name);
390 
391 	if (context->auto_schedule_toggle) {
392 		e_binding_bind_property (
393 			extension, "calendar-auto-schedule",
394 			context->auto_schedule_toggle, "active",
395 			G_BINDING_BIDIRECTIONAL |
396 			G_BINDING_SYNC_CREATE);
397 	}
398 
399 	e_binding_bind_object_text_property (
400 		extension, "email-address",
401 		context->email_entry, "text",
402 		G_BINDING_BIDIRECTIONAL |
403 		G_BINDING_SYNC_CREATE);
404 
405 	if (context->url_entry) {
406 		e_binding_bind_property_full (
407 			extension, "soup-uri",
408 			context->url_entry, "text",
409 			G_BINDING_BIDIRECTIONAL |
410 			G_BINDING_SYNC_CREATE,
411 			cal_config_caldav_uri_to_text,
412 			cal_config_caldav_text_to_uri,
413 			g_object_ref (scratch_source),
414 			(GDestroyNotify) g_object_unref);
415 	}
416 }
417 
418 static gboolean
cal_config_caldav_check_complete(ESourceConfigBackend * backend,ESource * scratch_source)419 cal_config_caldav_check_complete (ESourceConfigBackend *backend,
420                                   ESource *scratch_source)
421 {
422 	Context *context;
423 	const gchar *uid;
424 	const gchar *uri_string;
425 	SoupURI *soup_uri;
426 	gboolean complete;
427 
428 	uid = e_source_get_uid (scratch_source);
429 	context = g_object_get_data (G_OBJECT (backend), uid);
430 	g_return_val_if_fail (context != NULL, FALSE);
431 
432 	if (!context->url_entry)
433 		return TRUE;
434 
435 	uri_string = gtk_entry_get_text (GTK_ENTRY (context->url_entry));
436 	soup_uri = soup_uri_new (uri_string);
437 
438 	if (soup_uri) {
439 		if (g_strcmp0 (soup_uri_get_scheme (soup_uri), "caldav") == 0)
440 			soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTP);
441 
442 		complete = soup_uri_get_host (soup_uri) && SOUP_URI_VALID_FOR_HTTP (soup_uri);
443 	} else {
444 		complete = FALSE;
445 	}
446 
447 	if (soup_uri != NULL)
448 		soup_uri_free (soup_uri);
449 
450 	gtk_widget_set_sensitive (context->find_button, complete);
451 
452 	e_util_set_entry_issue_hint (context->url_entry, complete ? NULL : _("URL is not a valid http:// nor https:// URL"));
453 
454 	return complete;
455 }
456 
457 static void
e_cal_config_caldav_class_init(ESourceConfigBackendClass * class)458 e_cal_config_caldav_class_init (ESourceConfigBackendClass *class)
459 {
460 	EExtensionClass *extension_class;
461 
462 	extension_class = E_EXTENSION_CLASS (class);
463 	extension_class->extensible_type = E_TYPE_CAL_SOURCE_CONFIG;
464 
465 	class->parent_uid = "caldav-stub";
466 	class->backend_name = "caldav";
467 	class->insert_widgets = cal_config_caldav_insert_widgets;
468 	class->check_complete = cal_config_caldav_check_complete;
469 }
470 
471 static void
e_cal_config_caldav_class_finalize(ESourceConfigBackendClass * class)472 e_cal_config_caldav_class_finalize (ESourceConfigBackendClass *class)
473 {
474 }
475 
476 static void
e_cal_config_caldav_init(ESourceConfigBackend * backend)477 e_cal_config_caldav_init (ESourceConfigBackend *backend)
478 {
479 }
480 
481 G_MODULE_EXPORT void
e_module_load(GTypeModule * type_module)482 e_module_load (GTypeModule *type_module)
483 {
484 	e_cal_config_caldav_register_type (type_module);
485 }
486 
487 G_MODULE_EXPORT void
e_module_unload(GTypeModule * type_module)488 e_module_unload (GTypeModule *type_module)
489 {
490 }
491