1 /*
2  * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
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 Lesser 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  * Authors: Milan Crha <mcrha@redhat.com>
17  */
18 
19 #include "evolution-config.h"
20 
21 #include <string.h>
22 #include <glib/gi18n-lib.h>
23 
24 #include <libedataserver/libedataserver.h>
25 
26 #include "calendar/gui/calendar-config.h"
27 #include <calendar/gui/comp-util.h>
28 #include <calendar/gui/e-comp-editor.h>
29 
30 #include "shell/e-shell-backend.h"
31 #include "shell/e-shell-view.h"
32 #include "shell/e-shell-window.h"
33 
34 #include "e-cal-base-shell-view.h"
35 #include "e-cal-base-shell-backend.h"
36 
37 #define E_CAL_BASE_SHELL_BACKEND_GET_PRIVATE(obj) \
38 	(G_TYPE_INSTANCE_GET_PRIVATE \
39 	((obj), E_TYPE_CAL_BASE_SHELL_BACKEND, ECalBaseShellBackendPrivate))
40 
41 struct _ECalBaseShellBackendPrivate {
42 	gint placeholder;
43 };
44 
G_DEFINE_ABSTRACT_TYPE(ECalBaseShellBackend,e_cal_base_shell_backend,E_TYPE_SHELL_BACKEND)45 G_DEFINE_ABSTRACT_TYPE (ECalBaseShellBackend, e_cal_base_shell_backend, E_TYPE_SHELL_BACKEND)
46 
47 static void
48 cal_base_shell_backend_handle_webcal_uri (EShellBackend *shell_backend,
49 					  const gchar *uri)
50 {
51 	EShell *shell;
52 	ESourceRegistry *registry;
53 	ESourceConfig *source_config;
54 	const gchar *extension_name;
55 	GtkWidget *config;
56 	GtkWidget *dialog;
57 	GtkWindow *window, *active_window;
58 	GSList *candidates, *link;
59 
60 	g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
61 	g_return_if_fail (uri != NULL);
62 
63 	shell = e_shell_backend_get_shell (shell_backend);
64 
65 	active_window = e_shell_get_active_window (shell);
66 	registry = e_shell_get_registry (shell);
67 	config = e_cal_source_config_new (registry, NULL, E_CAL_CLIENT_SOURCE_TYPE_EVENTS);
68 	source_config = E_SOURCE_CONFIG (config);
69 
70 	if (E_IS_SHELL_WINDOW (active_window)) {
71 		EShellWindow *shell_window;
72 		EShellView *shell_view;
73 
74 		shell_window = E_SHELL_WINDOW (active_window);
75 		shell_view = e_shell_window_peek_shell_view (shell_window, e_shell_window_get_active_view (shell_window));
76 
77 		if (shell_view && E_IS_CAL_BASE_SHELL_VIEW (shell_view))
78 			e_cal_base_shell_view_preselect_source_config (shell_view, config);
79 	}
80 
81 	extension_name = e_source_config_get_backend_extension_name (source_config);
82 
83 	dialog = e_source_config_dialog_new (source_config);
84 	window = GTK_WINDOW (dialog);
85 
86 	if (active_window)
87 		gtk_window_set_transient_for (window, active_window);
88 	gtk_window_set_icon_name (window, "x-office-calendar");
89 	gtk_window_set_title (window, _("New Calendar"));
90 
91 	gtk_widget_show (dialog);
92 
93 	/* Can do this only after the dialog is shown, thus the list
94 	   of candidates is populated. */
95 	candidates = e_source_config_list_candidates (source_config);
96 
97 	for (link = candidates; link; link = g_slist_next (link)) {
98 		ESource *candidate = link->data;
99 
100 		if (e_source_has_extension (candidate, extension_name)) {
101 			const gchar *backend_name;
102 
103 			backend_name = e_source_backend_get_backend_name (
104 				e_source_get_extension (candidate, extension_name));
105 			if (g_strcmp0 (backend_name, "webcal") == 0) {
106 				ESourceWebdav *webdav_extension;
107 				SoupURI *soup_uri;
108 
109 				soup_uri = soup_uri_new (uri);
110 				if (!soup_uri) {
111 					/* Just a fallback when the passed-in URI is invalid,
112 					   to have set something in the UI. */
113 					soup_uri = soup_uri_new (NULL);
114 					soup_uri_set_path (soup_uri, uri);
115 				}
116 
117 				/* https everywhere */
118 				soup_uri_set_scheme (soup_uri, "https");
119 
120 				if (soup_uri_get_path (soup_uri)) {
121 					gchar *basename;
122 
123 					basename = g_path_get_basename (soup_uri_get_path (soup_uri));
124 					if (basename && g_utf8_strlen (basename, -1) > 3) {
125 						gchar *dot;
126 
127 						dot = strrchr (basename, '.');
128 						if (dot && strlen (dot) <= 4)
129 							*dot = '\0';
130 
131 						if (*basename)
132 							e_source_set_display_name (candidate, basename);
133 					}
134 
135 					g_free (basename);
136 				}
137 
138 				webdav_extension = e_source_get_extension (candidate, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
139 				e_source_webdav_set_soup_uri (webdav_extension, soup_uri);
140 
141 				e_source_config_select_page (source_config, candidate);
142 
143 				soup_uri_free (soup_uri);
144 				break;
145 			}
146 		}
147 	}
148 
149 	g_slist_free_full (candidates, g_object_unref);
150 }
151 
152 static gboolean
cal_base_shell_backend_handle_uri_cb(EShellBackend * shell_backend,const gchar * uri)153 cal_base_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
154 				      const gchar *uri)
155 {
156 	ECalBaseShellBackendClass *klass;
157 
158 	g_return_val_if_fail (E_IS_CAL_BASE_SHELL_BACKEND (shell_backend), FALSE);
159 	g_return_val_if_fail (uri != NULL, FALSE);
160 
161 	if (g_str_has_prefix (uri, "webcal:")) {
162 		cal_base_shell_backend_handle_webcal_uri (shell_backend, uri);
163 		return TRUE;
164 	}
165 
166 	klass = E_CAL_BASE_SHELL_BACKEND_GET_CLASS (shell_backend);
167 	g_return_val_if_fail (klass != NULL, FALSE);
168 
169 	return klass->handle_uri && klass->handle_uri (shell_backend, uri);
170 }
171 
172 static void
cal_base_shell_backend_window_added_cb(ECalBaseShellBackend * cal_base_shell_backend,GtkWindow * window)173 cal_base_shell_backend_window_added_cb (ECalBaseShellBackend *cal_base_shell_backend,
174 					GtkWindow *window)
175 {
176 	ECalBaseShellBackendClass *cal_base_shell_backend_class;
177 	const gchar *backend_name;
178 
179 	if (!E_IS_SHELL_WINDOW (window))
180 		return;
181 
182 	cal_base_shell_backend_class = E_CAL_BASE_SHELL_BACKEND_GET_CLASS (cal_base_shell_backend);
183 	g_return_if_fail (cal_base_shell_backend_class != NULL);
184 
185 	backend_name = E_SHELL_BACKEND_GET_CLASS (cal_base_shell_backend)->name;
186 
187 	if (cal_base_shell_backend_class->new_item_entries &&
188 	    cal_base_shell_backend_class->new_item_n_entries > 0)
189 		e_shell_window_register_new_item_actions (
190 			E_SHELL_WINDOW (window), backend_name,
191 			cal_base_shell_backend_class->new_item_entries,
192 			cal_base_shell_backend_class->new_item_n_entries);
193 
194 	if (cal_base_shell_backend_class->source_entries &&
195 	    cal_base_shell_backend_class->source_n_entries > 0)
196 		e_shell_window_register_new_source_actions (
197 			E_SHELL_WINDOW (window), backend_name,
198 			cal_base_shell_backend_class->source_entries,
199 			cal_base_shell_backend_class->source_n_entries);
200 }
201 
202 static void
cal_base_shell_backend_constructed(GObject * object)203 cal_base_shell_backend_constructed (GObject *object)
204 {
205 	EShell *shell;
206 	EShellBackend *shell_backend;
207 
208 	/* Chain up to parent's constructed() method. */
209 	G_OBJECT_CLASS (e_cal_base_shell_backend_parent_class)->constructed (object);
210 
211 	shell_backend = E_SHELL_BACKEND (object);
212 	shell = e_shell_backend_get_shell (shell_backend);
213 
214 	g_signal_connect_swapped (
215 		shell, "handle-uri",
216 		G_CALLBACK (cal_base_shell_backend_handle_uri_cb),
217 		shell_backend);
218 
219 	g_signal_connect_swapped (
220 		shell, "window-added",
221 		G_CALLBACK (cal_base_shell_backend_window_added_cb),
222 		shell_backend);
223 }
224 
225 static void
e_cal_base_shell_backend_class_init(ECalBaseShellBackendClass * class)226 e_cal_base_shell_backend_class_init (ECalBaseShellBackendClass *class)
227 {
228 	GObjectClass *object_class;
229 
230 	g_type_class_add_private (class, sizeof (ECalBaseShellBackendPrivate));
231 
232 	object_class = G_OBJECT_CLASS (class);
233 	object_class->constructed = cal_base_shell_backend_constructed;
234 
235 	class->new_item_entries = NULL;
236 	class->new_item_n_entries = 0;
237 	class->source_entries = NULL;
238 	class->source_n_entries = 0;
239 	class->handle_uri = NULL;
240 
241 	/* Register relevant ESource extensions. */
242 	g_type_ensure (E_TYPE_SOURCE_CALENDAR);
243 
244 	/* Force 24 hour format for locales which don't support 12 hour format */
245 	if (!calendar_config_locale_supports_12_hour_format ()) {
246 		GSettings *settings;
247 
248 		settings = e_util_ref_settings ("org.gnome.evolution.calendar");
249 
250 		if (!g_settings_get_boolean (settings, "use-24hour-format"))
251 			g_settings_set_boolean (settings, "use-24hour-format", TRUE);
252 
253 		g_clear_object (&settings);
254 	}
255 }
256 
257 static void
e_cal_base_shell_backend_init(ECalBaseShellBackend * cal_base_shell_backend)258 e_cal_base_shell_backend_init (ECalBaseShellBackend *cal_base_shell_backend)
259 {
260 	cal_base_shell_backend->priv = E_CAL_BASE_SHELL_BACKEND_GET_PRIVATE (cal_base_shell_backend);
261 }
262 
263 void
e_cal_base_shell_backend_util_new_source(EShellWindow * shell_window,ECalClientSourceType source_type)264 e_cal_base_shell_backend_util_new_source (EShellWindow *shell_window,
265 					  ECalClientSourceType source_type)
266 {
267 	EShell *shell;
268 	EShellView *shell_view;
269 	ESourceRegistry *registry;
270 	GtkWidget *config;
271 	GtkWidget *dialog;
272 	GtkWindow *window;
273 	const gchar *icon_name;
274 	const gchar *title;
275 
276 	g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
277 
278 	switch (source_type) {
279 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
280 			title = _("New Calendar");
281 			icon_name = "x-office-calendar";
282 			break;
283 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
284 			title = _("New Memo List");
285 			icon_name = "stock_notes";
286 			break;
287 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
288 			title = _("New Task List");
289 			icon_name = "stock_todo";
290 			break;
291 		default:
292 			g_warn_if_reached ();
293 			return;
294 	}
295 
296 	shell = e_shell_window_get_shell (shell_window);
297 
298 	registry = e_shell_get_registry (shell);
299 	config = e_cal_source_config_new (registry, NULL, source_type);
300 
301 	shell_view = e_shell_window_peek_shell_view (shell_window, e_shell_window_get_active_view (shell_window));
302 
303 	if (shell_view && E_IS_CAL_BASE_SHELL_VIEW (shell_view))
304 		e_cal_base_shell_view_preselect_source_config (shell_view, config);
305 
306 	dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
307 	window = GTK_WINDOW (dialog);
308 
309 	gtk_window_set_transient_for (window, GTK_WINDOW (shell_window));
310 	gtk_window_set_icon_name (window, icon_name);
311 	gtk_window_set_title (window, title);
312 
313 	gtk_widget_show (dialog);
314 }
315 
316 typedef struct {
317 	EShellBackend *shell_backend;
318 	ECalClientSourceType source_type;
319 	gchar *source_uid;
320 	gchar *comp_uid;
321 	gchar *comp_rid;
322 
323 	ECalClient *cal_client;
324 	ICalComponent *existing_icomp;
325 } HandleUriData;
326 
327 static void
handle_uri_data_free(gpointer ptr)328 handle_uri_data_free (gpointer ptr)
329 {
330 	HandleUriData *hud = ptr;
331 
332 	if (!hud)
333 		return;
334 
335 	if (hud->cal_client) {
336 		ECompEditor *comp_editor = NULL;
337 
338 		comp_editor = e_comp_editor_open_for_component (NULL,
339 			e_shell_backend_get_shell (hud->shell_backend),
340 			e_client_get_source (E_CLIENT (hud->cal_client)),
341 			hud->existing_icomp, 0);
342 
343 		if (comp_editor)
344 			gtk_window_present (GTK_WINDOW (comp_editor));
345 	}
346 
347 	g_clear_object (&hud->existing_icomp);
348 	g_clear_object (&hud->cal_client);
349 	g_clear_object (&hud->shell_backend);
350 	g_free (hud->source_uid);
351 	g_free (hud->comp_uid);
352 	g_free (hud->comp_rid);
353 	g_slice_free (HandleUriData, hud);
354 }
355 
356 static void
cal_base_shell_backend_handle_uri_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)357 cal_base_shell_backend_handle_uri_thread (EAlertSinkThreadJobData *job_data,
358 					  gpointer user_data,
359 					  GCancellable *cancellable,
360 					  GError **error)
361 {
362 	HandleUriData *hud = user_data;
363 	EShell *shell;
364 	ESourceRegistry *registry;
365 	ESource *source;
366 	const gchar *extension_name;
367 	GError *local_error = NULL;
368 
369 	g_return_if_fail (hud != NULL);
370 
371 	switch (hud->source_type) {
372 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
373 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
374 			break;
375 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
376 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
377 			break;
378 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
379 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
380 			break;
381 		default:
382 			g_warn_if_reached ();
383 			return;
384 	}
385 
386 	shell = e_shell_backend_get_shell (hud->shell_backend);
387 	registry = e_shell_get_registry (shell);
388 	source = e_source_registry_ref_source (registry, hud->source_uid);
389 	if (!source) {
390 		g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
391 			_("Source with UID “%s” not found"), hud->source_uid);
392 	} else {
393 		EClientCache *client_cache;
394 		EClient *client;
395 
396 		client_cache = e_shell_get_client_cache (shell);
397 
398 		client = e_client_cache_get_client_sync (client_cache, source, extension_name, 30, cancellable, &local_error);
399 		if (client) {
400 			hud->cal_client = E_CAL_CLIENT (client);
401 
402 			if (!e_cal_client_get_object_sync (hud->cal_client, hud->comp_uid,
403 				hud->comp_rid, &hud->existing_icomp, cancellable, &local_error))
404 				g_clear_object (&hud->cal_client);
405 		}
406 	}
407 
408 	e_util_propagate_open_source_job_error (job_data, extension_name, local_error, error);
409 
410 	g_clear_object (&source);
411 }
412 
413 static void
populate_g_date(GDate * date,time_t utc_time,ICalTimezone * zone)414 populate_g_date (GDate *date,
415                  time_t utc_time,
416                  ICalTimezone *zone)
417 {
418 	ICalTime *itt;
419 
420 	g_return_if_fail (date != NULL);
421 
422 	if ((gint) utc_time == -1)
423 		return;
424 
425 	itt = i_cal_time_new_from_timet_with_zone (utc_time, FALSE, zone);
426 
427 	if (itt && !i_cal_time_is_null_time (itt) &&
428 	    i_cal_time_is_valid_time (itt)) {
429 		g_date_set_dmy (date, i_cal_time_get_day (itt), i_cal_time_get_month (itt), i_cal_time_get_year (itt));
430 	}
431 
432 	g_clear_object (&itt);
433 }
434 
435 static time_t
convert_time_from_isodate(const gchar * text,ICalTimezone * use_date_zone)436 convert_time_from_isodate (const gchar *text,
437 			   ICalTimezone *use_date_zone)
438 {
439 	time_t res;
440 
441 	g_return_val_if_fail (text != NULL, (time_t) 0);
442 
443 	res = time_from_isodate (text);
444 
445 	/* Is it date only? Then use the date zone to match the right day */
446 	if (use_date_zone && strlen (text) == 8) {
447 		ICalTime *itt;
448 
449 		itt = i_cal_time_new_from_timet_with_zone (res, TRUE, NULL);
450 		res = i_cal_time_as_timet_with_zone (itt, use_date_zone);
451 		g_clear_object (&itt);
452 	}
453 
454 	return res;
455 }
456 
457 gboolean
e_cal_base_shell_backend_util_handle_uri(EShellBackend * shell_backend,ECalClientSourceType source_type,const gchar * uri,ECalBaseShellBackendHandleStartEndDatesFunc handle_start_end_dates)458 e_cal_base_shell_backend_util_handle_uri (EShellBackend *shell_backend,
459 					  ECalClientSourceType source_type,
460 					  const gchar *uri,
461 					  ECalBaseShellBackendHandleStartEndDatesFunc handle_start_end_dates)
462 {
463 	EShell *shell;
464 	EShellWindow *shell_window;
465 	SoupURI *soup_uri;
466 	const gchar *cp;
467 	gchar *source_uid = NULL;
468 	gchar *comp_uid = NULL;
469 	gchar *comp_rid = NULL;
470 	gchar *new_ics = NULL;
471 	gboolean attendees = FALSE;
472 	gboolean handled = FALSE;
473 	GSettings *settings;
474 	GList *windows, *link;
475 	GDate start_date;
476 	GDate end_date;
477 	ICalTimezone *zone = NULL;
478 	const gchar *extension_name;
479 
480 	g_return_val_if_fail (E_IS_CAL_BASE_SHELL_BACKEND (shell_backend), FALSE);
481 	g_return_val_if_fail (uri != NULL, FALSE);
482 
483 	switch (source_type) {
484 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
485 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
486 			break;
487 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
488 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
489 			break;
490 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
491 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
492 			break;
493 		default:
494 			g_warn_if_reached ();
495 			return FALSE;
496 	}
497 
498 	shell = e_shell_backend_get_shell (shell_backend);
499 
500 	soup_uri = soup_uri_new (uri);
501 
502 	if (soup_uri == NULL)
503 		return FALSE;
504 
505 	g_date_clear (&start_date, 1);
506 	g_date_clear (&end_date, 1);
507 
508 	settings = e_util_ref_settings ("org.gnome.evolution.calendar");
509 
510 	if (g_settings_get_boolean (settings, "use-system-timezone"))
511 		zone = e_cal_util_get_system_timezone ();
512 	else {
513 		gchar *location;
514 
515 		location = g_settings_get_string (settings, "timezone");
516 
517 		if (location != NULL) {
518 			zone = i_cal_timezone_get_builtin_timezone (location);
519 			g_free (location);
520 		}
521 	}
522 
523 	if (zone == NULL)
524 		zone = i_cal_timezone_get_utc_timezone ();
525 
526 	g_object_unref (settings);
527 
528 	cp = soup_uri_get_query (soup_uri);
529 	if (cp == NULL)
530 		goto exit;
531 
532 	while (*cp != '\0') {
533 		gchar *header;
534 		gchar *content;
535 		gsize header_len;
536 		gsize content_len;
537 
538 		header_len = strcspn (cp, "=&");
539 
540 		/* If it's malformed, give up. */
541 		if (cp[header_len] != '=')
542 			break;
543 
544 		header = (gchar *) cp;
545 		header[header_len] = '\0';
546 		cp += header_len + 1;
547 
548 		content_len = strcspn (cp, "&");
549 
550 		content = g_strndup (cp, content_len);
551 		if (g_ascii_strcasecmp (header, "startdate") == 0)
552 			populate_g_date (&start_date, convert_time_from_isodate (content, zone), zone);
553 		else if (g_ascii_strcasecmp (header, "enddate") == 0)
554 			populate_g_date (&end_date, convert_time_from_isodate (content, zone) - 1, zone);
555 		else if (g_ascii_strcasecmp (header, "source-uid") == 0)
556 			source_uid = g_uri_unescape_string (content, NULL);
557 		else if (g_ascii_strcasecmp (header, "comp-uid") == 0)
558 			comp_uid = g_uri_unescape_string (content, NULL);
559 		else if (g_ascii_strcasecmp (header, "comp-rid") == 0)
560 			comp_rid = g_uri_unescape_string (content, NULL);
561 		else if (g_ascii_strcasecmp (header, "new-ics") == 0)
562 			new_ics = g_uri_unescape_string (content, NULL);
563 		else if (g_ascii_strcasecmp (header, "attendees") == 0)
564 			attendees = g_strcmp0 (content, "true") == 0 || g_strcmp0 (content, "1") == 0;
565 		g_free (content);
566 
567 		cp += content_len;
568 		if (*cp == '&') {
569 			cp++;
570 			if (strcmp (cp, "amp;") == 0)
571 				cp += 4;
572 		}
573 	}
574 
575 	/* This is primarily for launching Evolution
576 	 * from the calendar in the clock applet. */
577 	if (g_date_valid (&start_date) && handle_start_end_dates) {
578 		if (g_date_valid (&end_date) && g_date_compare (&start_date, &end_date) > 0)
579 			end_date = start_date;
580 
581 		handle_start_end_dates (shell_backend, &start_date, &end_date);
582 		handled = TRUE;
583 		goto exit;
584 	}
585 
586 	if (!new_ics && (!source_uid || !comp_uid))
587 		goto exit;
588 
589 	/* URI is valid, so consider it handled.  Whether
590 	 * we successfully open it is another matter... */
591 	handled = TRUE;
592 
593 	shell_window = NULL;
594 	windows = gtk_application_get_windows (GTK_APPLICATION (shell));
595 	for (link = windows; link; link = g_list_next (link)) {
596 		GtkWindow *window = link->data;
597 
598 		if (E_IS_SHELL_WINDOW (window)) {
599 			shell_window = E_SHELL_WINDOW (window);
600 			break;
601 		}
602 	}
603 
604 	if (new_ics) {
605 		gchar *content = NULL;
606 		ICalComponent *icomp;
607 		GError *error = NULL;
608 
609 		if (!g_file_get_contents (new_ics, &content, NULL, &error)) {
610 			if (error)
611 				g_warning ("Cannot create new ics: %s", error->message);
612 			else
613 				g_warning ("Cannot create new ics: Failed to open file '%s': Unknown error", new_ics);
614 			g_clear_error (&error);
615 			goto exit;
616 		}
617 
618 		icomp = content ? i_cal_component_new_from_string (content) : NULL;
619 		if (!icomp) {
620 			g_warning ("Cannot create new ics: File '%s' doesn't contain valid iCalendar component", new_ics);
621 			g_free (content);
622 			goto exit;
623 		}
624 
625 		if (i_cal_component_isa (icomp) == I_CAL_VEVENT_COMPONENT &&
626 		    source_type != E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
627 			g_warning ("Cannot create new ics: Expected %s, but got VEVENT", source_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? "VTODO" : "VJOURNAL");
628 		} else if (i_cal_component_isa (icomp) == I_CAL_VJOURNAL_COMPONENT &&
629 			   source_type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
630 			g_warning ("Cannot create new ics: Expected %s, but got VJOURNAL", source_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? "VTODO" : "VEVENT");
631 		} else if (i_cal_component_isa (icomp) == I_CAL_VTODO_COMPONENT &&
632 			   source_type != E_CAL_CLIENT_SOURCE_TYPE_TASKS) {
633 			g_warning ("Cannot create new ics: Expected %s, but got VTODO", source_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS ? "VJOURNAL" : "VEVENT");
634 		} else if (i_cal_component_isa (icomp) != I_CAL_VEVENT_COMPONENT &&
635 			   i_cal_component_isa (icomp) != I_CAL_VJOURNAL_COMPONENT &&
636 			   i_cal_component_isa (icomp) != I_CAL_VTODO_COMPONENT) {
637 			g_warning ("Cannot create new ics: Received unexpected component type '%s'", i_cal_component_kind_to_string (i_cal_component_isa (icomp)));
638 		} else {
639 			ECompEditor *comp_editor;
640 			ESource *source = NULL;
641 			ECompEditorFlags flags;
642 
643 			if (source_uid) {
644 				ESourceRegistry *registry;
645 
646 				registry = e_shell_get_registry (shell);
647 				source = e_source_registry_ref_source (registry, source_uid);
648 			}
649 
650 			flags = E_COMP_EDITOR_FLAG_IS_NEW | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER |
651 				(attendees ? E_COMP_EDITOR_FLAG_WITH_ATTENDEES : 0);
652 
653 			comp_editor = e_comp_editor_open_for_component (NULL, shell, source, icomp, flags);
654 
655 			if (comp_editor)
656 				gtk_window_present (GTK_WINDOW (comp_editor));
657 
658 			g_clear_object (&source);
659 		}
660 
661 		g_object_unref (icomp);
662 		g_free (content);
663 	} else if (shell_window) {
664 		HandleUriData *hud;
665 		ESourceRegistry *registry;
666 		ESource *source;
667 		EShellView *shell_view;
668 		EActivity *activity;
669 		gchar *description = NULL, *alert_ident = NULL, *alert_arg_0 = NULL;
670 		gchar *source_display_name = NULL;
671 
672 		hud = g_slice_new0 (HandleUriData);
673 		hud->shell_backend = g_object_ref (shell_backend);
674 		hud->source_type = source_type;
675 		hud->source_uid = g_strdup (source_uid);
676 		hud->comp_uid = g_strdup (comp_uid);
677 		hud->comp_rid = g_strdup (comp_rid);
678 		hud->cal_client = NULL;
679 		hud->existing_icomp = NULL;
680 
681 		registry = e_shell_get_registry (shell);
682 		source = e_source_registry_ref_source (registry, source_uid);
683 		if (source)
684 			source_display_name = e_util_get_source_full_name (registry, source);
685 
686 		shell_view = e_shell_window_get_shell_view (shell_window,
687 			e_shell_window_get_active_view (shell_window));
688 
689 		g_warn_if_fail (e_util_get_open_source_job_info (extension_name,
690 			source_display_name ? source_display_name : "", &description, &alert_ident, &alert_arg_0));
691 
692 		activity = e_shell_view_submit_thread_job (
693 			shell_view, description, alert_ident, alert_arg_0,
694 			cal_base_shell_backend_handle_uri_thread, hud, handle_uri_data_free);
695 
696 		g_clear_object (&activity);
697 		g_clear_object (&source);
698 		g_free (source_display_name);
699 		g_free (description);
700 		g_free (alert_ident);
701 		g_free (alert_arg_0);
702 	} else {
703 		g_warn_if_reached ();
704 	}
705 
706  exit:
707 	g_free (source_uid);
708 	g_free (comp_uid);
709 	g_free (comp_rid);
710 	g_free (new_ics);
711 
712 	soup_uri_free (soup_uri);
713 
714 	return handled;
715 }
716