evolution-3.6.4/modules/calendar/e-cal-attachment-handler.c

No issues found

  1 /*
  2  * e-cal-attachment-handler.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  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-cal-attachment-handler.h"
 27 
 28 #include <glib/gi18n.h>
 29 #include <libical/ical.h>
 30 #include <camel/camel.h>
 31 #include <libecal/libecal.h>
 32 #include <libedataserverui/libedataserverui.h>
 33 
 34 #include <shell/e-shell.h>
 35 
 36 #define E_CAL_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \
 37 	(G_TYPE_INSTANCE_GET_PRIVATE \
 38 	((obj), E_TYPE_CAL_ATTACHMENT_HANDLER, ECalAttachmentHandlerPrivate))
 39 
 40 typedef struct _ImportContext ImportContext;
 41 
 42 struct _ECalAttachmentHandlerPrivate {
 43 	gint placeholder;
 44 };
 45 
 46 struct _ImportContext {
 47 	ECalClient *client;
 48 	icalcomponent *component;
 49 	ECalClientSourceType source_type;
 50 };
 51 
 52 static gpointer parent_class;
 53 static GType cal_attachment_handler_type;
 54 
 55 static const gchar *ui =
 56 "<ui>"
 57 "  <popup name='context'>"
 58 "    <placeholder name='custom-actions'>"
 59 "      <menuitem action='import-to-calendar'/>"
 60 "      <menuitem action='import-to-tasks'/>"
 61 "    </placeholder>"
 62 "  </popup>"
 63 "</ui>";
 64 
 65 static icalcomponent *
 66 attachment_handler_get_component (EAttachment *attachment)
 67 {
 68 	CamelDataWrapper *wrapper;
 69 	CamelMimePart *mime_part;
 70 	CamelStream *stream;
 71 	GByteArray *buffer;
 72 	icalcomponent *component;
 73 	const gchar *key = "__icalcomponent__";
 74 
 75 	component = g_object_get_data (G_OBJECT (attachment), key);
 76 	if (component != NULL)
 77 		return component;
 78 
 79 	if (e_attachment_get_loading (attachment) ||
 80 	    e_attachment_get_saving (attachment))
 81 		return NULL;
 82 
 83 	mime_part = e_attachment_get_mime_part (attachment);
 84 	if (!CAMEL_IS_MIME_PART (mime_part))
 85 		return NULL;
 86 
 87 	buffer = g_byte_array_new ();
 88 	stream = camel_stream_mem_new ();
 89 	camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buffer);
 90 	wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
 91 	camel_data_wrapper_decode_to_stream_sync (wrapper, stream, NULL, NULL);
 92 	g_object_unref (stream);
 93 
 94 	if (buffer->len > 0) {
 95 		const gchar *str;
 96 
 97 		/* ensure string being null-terminated  */
 98 		g_byte_array_append (buffer, (const guint8 *) "", 1);
 99 
100 		str = (const gchar *) buffer->data;
101 		while (*str && g_ascii_isspace (*str))
102 			str++;
103 
104 		if (g_ascii_strncasecmp (str, "BEGIN:", 6) == 0)
105 			component = e_cal_util_parse_ics_string (str);
106 	}
107 
108 	g_byte_array_free (buffer, TRUE);
109 
110 	if (component == NULL)
111 		return NULL;
112 
113 	g_object_set_data_full (
114 		G_OBJECT (attachment), key, component,
115 		(GDestroyNotify) icalcomponent_free);
116 
117 	return component;
118 }
119 
120 static gboolean
121 attachment_handler_update_objects (ECalClient *client,
122                                    icalcomponent *component)
123 {
124 	icalcomponent_kind kind;
125 	icalcomponent *vcalendar;
126 	gboolean success;
127 	GError *error = NULL;
128 
129 	kind = icalcomponent_isa (component);
130 
131 	switch (kind) {
132 		case ICAL_VTODO_COMPONENT:
133 		case ICAL_VEVENT_COMPONENT:
134 			vcalendar = e_cal_util_new_top_level ();
135 			if (icalcomponent_get_method (component) == ICAL_METHOD_CANCEL)
136 				icalcomponent_set_method (vcalendar, ICAL_METHOD_CANCEL);
137 			else
138 				icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
139 			icalcomponent_add_component (
140 				vcalendar, icalcomponent_new_clone (component));
141 			break;
142 
143 		case ICAL_VCALENDAR_COMPONENT:
144 			vcalendar = icalcomponent_new_clone (component);
145 			if (!icalcomponent_get_first_property (vcalendar, ICAL_METHOD_PROPERTY))
146 				icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH);
147 			break;
148 
149 		default:
150 			return FALSE;
151 	}
152 
153 	success = e_cal_client_receive_objects_sync (
154 		client, vcalendar, NULL, &error);
155 
156 	if (error != NULL) {
157 		g_warning (
158 			"%s: Failed to receive objects: %s",
159 			G_STRFUNC, error->message);
160 		g_error_free (error);
161 	}
162 
163 	icalcomponent_free (vcalendar);
164 
165 	return success;
166 }
167 
168 static void
169 attachment_handler_import_event (GObject *source_object,
170                                  GAsyncResult *result,
171                                  gpointer user_data)
172 {
173 	ESource *source = E_SOURCE (source_object);
174 	EAttachment *attachment = user_data;
175 	EClient *client = NULL;
176 	GError *error = NULL;
177 	icalcomponent *component;
178 	icalcomponent *subcomponent;
179 	icalcompiter iter;
180 
181 	e_client_utils_open_new_finish (source, result, &client, &error);
182 
183 	if (error != NULL) {
184 		g_warn_if_fail (client == NULL);
185 		g_warning (
186 			"%s: Failed to open '%s': %s",
187 			G_STRFUNC, e_source_get_display_name (source),
188 			error->message);
189 		g_object_unref (attachment);
190 		g_error_free (error);
191 		return;
192 	}
193 
194 	g_return_if_fail (E_IS_CLIENT (client));
195 
196 	component = attachment_handler_get_component (attachment);
197 	g_return_if_fail (component != NULL);
198 
199 	iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
200 
201 	while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
202 		icalcomponent_kind kind;
203 
204 		kind = icalcomponent_isa (subcomponent);
205 		icalcompiter_next (&iter);
206 
207 		if (kind == ICAL_VEVENT_COMPONENT)
208 			continue;
209 
210 		if (kind == ICAL_VTIMEZONE_COMPONENT)
211 			continue;
212 
213 		icalcomponent_remove_component (component, subcomponent);
214 		icalcomponent_free (subcomponent);
215 	}
216 
217 	/* XXX Do something with the return value. */
218 	attachment_handler_update_objects (E_CAL_CLIENT (client), component);
219 
220 	g_object_unref (attachment);
221 	g_object_unref (client);
222 }
223 
224 static void
225 attachment_handler_import_todo (GObject *source_object,
226                                 GAsyncResult *result,
227                                 gpointer user_data)
228 {
229 	ESource *source = E_SOURCE (source_object);
230 	EAttachment *attachment = user_data;
231 	EClient *client = NULL;
232 	GError *error = NULL;
233 	icalcomponent *component;
234 	icalcomponent *subcomponent;
235 	icalcompiter iter;
236 
237 	e_client_utils_open_new_finish (source, result, &client, &error);
238 
239 	if (error != NULL) {
240 		g_warn_if_fail (client == NULL);
241 		g_warning (
242 			"%s: Failed to open '%s': %s",
243 			G_STRFUNC, e_source_get_display_name (source),
244 			error->message);
245 		g_object_unref (attachment);
246 		g_error_free (error);
247 		return;
248 	}
249 
250 	g_return_if_fail (E_IS_CLIENT (client));
251 
252 	component = attachment_handler_get_component (attachment);
253 	g_return_if_fail (component != NULL);
254 
255 	iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT);
256 
257 	while ((subcomponent = icalcompiter_deref (&iter)) != NULL) {
258 		icalcomponent_kind kind;
259 
260 		kind = icalcomponent_isa (subcomponent);
261 		icalcompiter_next (&iter);
262 
263 		if (kind == ICAL_VTODO_COMPONENT)
264 			continue;
265 
266 		if (kind == ICAL_VTIMEZONE_COMPONENT)
267 			continue;
268 
269 		icalcomponent_remove_component (component, subcomponent);
270 		icalcomponent_free (subcomponent);
271 	}
272 
273 	/* XXX Do something with the return value. */
274 	attachment_handler_update_objects (E_CAL_CLIENT (client), component);
275 
276 	g_object_unref (attachment);
277 	g_object_unref (client);
278 }
279 
280 static void
281 attachment_handler_row_activated_cb (GtkDialog *dialog)
282 {
283 	gtk_dialog_response (dialog, GTK_RESPONSE_OK);
284 }
285 
286 static void
287 attachment_handler_run_dialog (GtkWindow *parent,
288                                EAttachment *attachment,
289                                ECalClientSourceType source_type,
290                                const gchar *title)
291 {
292 	EShell *shell;
293 	GtkWidget *dialog;
294 	GtkWidget *container;
295 	GtkWidget *widget;
296 	ESourceRegistry *registry;
297 	ESourceSelector *selector;
298 	ESource *source;
299 	const gchar *extension_name;
300 	icalcomponent *component;
301 
302 	switch (source_type) {
303 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
304 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
305 			break;
306 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
307 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
308 			break;
309 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
310 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
311 			break;
312 		default:
313 			g_return_if_reached ();
314 	}
315 
316 	component = attachment_handler_get_component (attachment);
317 	g_return_if_fail (component != NULL);
318 
319 	dialog = gtk_dialog_new_with_buttons (
320 		title, parent, GTK_DIALOG_DESTROY_WITH_PARENT,
321 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
322 
323 	widget = gtk_button_new_with_mnemonic (_("I_mport"));
324 	gtk_button_set_image (
325 		GTK_BUTTON (widget), gtk_image_new_from_icon_name (
326 		"stock_mail-import", GTK_ICON_SIZE_MENU));
327 	gtk_dialog_add_action_widget (
328 		GTK_DIALOG (dialog), widget, GTK_RESPONSE_OK);
329 	gtk_widget_show (widget);
330 
331 	gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400);
332 
333 	container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
334 
335 	widget = gtk_scrolled_window_new (NULL, NULL);
336 	gtk_scrolled_window_set_policy (
337 		GTK_SCROLLED_WINDOW (widget),
338 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
339 	gtk_scrolled_window_set_shadow_type (
340 		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
341 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
342 	gtk_widget_show (widget);
343 
344 	container = widget;
345 
346 	shell = e_shell_get_default ();
347 	registry = e_shell_get_registry (shell);
348 	widget = e_source_selector_new (registry, extension_name);
349 	selector = E_SOURCE_SELECTOR (widget);
350 	e_source_selector_set_show_toggles (selector, FALSE);
351 	gtk_container_add (GTK_CONTAINER (container), widget);
352 	gtk_widget_show (widget);
353 
354 	g_signal_connect_swapped (
355 		widget, "row-activated",
356 		G_CALLBACK (attachment_handler_row_activated_cb), dialog);
357 
358 	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
359 		goto exit;
360 
361 	source = e_source_selector_ref_primary_selection (selector);
362 	if (source == NULL)
363 		goto exit;
364 
365 	switch (source_type) {
366 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
367 		e_client_utils_open_new (
368 			source, E_CLIENT_SOURCE_TYPE_EVENTS, FALSE, NULL,
369 			attachment_handler_import_event,
370 			g_object_ref (attachment));
371 		break;
372 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
373 		e_client_utils_open_new (
374 			source, E_CLIENT_SOURCE_TYPE_TASKS, FALSE, NULL,
375 			attachment_handler_import_todo,
376 			g_object_ref (attachment));
377 		break;
378 	default:
379 		break;
380 	}
381 
382 	g_object_unref (source);
383 
384  exit:
385 	gtk_widget_destroy (dialog);
386 }
387 
388 static void
389 attachment_handler_import_to_calendar (GtkAction *action,
390                                        EAttachmentHandler *handler)
391 {
392 	EAttachment *attachment;
393 	EAttachmentView *view;
394 	GList *selected;
395 	gpointer parent;
396 
397 	view = e_attachment_handler_get_view (handler);
398 
399 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
400 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
401 
402 	selected = e_attachment_view_get_selected_attachments (view);
403 	g_return_if_fail (g_list_length (selected) == 1);
404 	attachment = E_ATTACHMENT (selected->data);
405 
406 	attachment_handler_run_dialog (
407 		parent, attachment,
408 		E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
409 		_("Select a Calendar"));
410 
411 	g_object_unref (attachment);
412 	g_list_free (selected);
413 }
414 
415 static void
416 attachment_handler_import_to_tasks (GtkAction *action,
417                                     EAttachmentHandler *handler)
418 {
419 	EAttachment *attachment;
420 	EAttachmentView *view;
421 	GList *selected;
422 	gpointer parent;
423 
424 	view = e_attachment_handler_get_view (handler);
425 
426 	parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
427 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
428 
429 	selected = e_attachment_view_get_selected_attachments (view);
430 	g_return_if_fail (g_list_length (selected) == 1);
431 	attachment = E_ATTACHMENT (selected->data);
432 
433 	attachment_handler_run_dialog (
434 		parent, attachment,
435 		E_CAL_CLIENT_SOURCE_TYPE_TASKS,
436 		_("Select a Task List"));
437 
438 	g_object_unref (attachment);
439 	g_list_free (selected);
440 }
441 
442 static GtkActionEntry standard_entries[] = {
443 
444 	{ "import-to-calendar",
445 	  "stock_mail-import",
446 	  N_("I_mport to Calendar"),
447 	  NULL,
448 	  NULL,  /* XXX Add a tooltip! */
449 	  G_CALLBACK (attachment_handler_import_to_calendar) },
450 
451 	{ "import-to-tasks",
452 	  "stock_mail-import",
453 	  N_("I_mport to Tasks"),
454 	  NULL,
455 	  NULL,  /* XXX Add a tooltip! */
456 	  G_CALLBACK (attachment_handler_import_to_tasks) }
457 };
458 
459 static void
460 cal_attachment_handler_update_actions (EAttachmentView *view)
461 {
462 	EAttachment *attachment;
463 	GtkAction *action;
464 	GList *selected;
465 	icalcomponent *component;
466 	icalcomponent *subcomponent;
467 	icalcomponent_kind kind;
468 	gboolean is_vevent = FALSE;
469 	gboolean is_vtodo = FALSE;
470 
471 	selected = e_attachment_view_get_selected_attachments (view);
472 
473 	if (g_list_length (selected) != 1)
474 		goto exit;
475 
476 	attachment = E_ATTACHMENT (selected->data);
477 	component = attachment_handler_get_component (attachment);
478 
479 	if (component == NULL)
480 		goto exit;
481 
482 	subcomponent = icalcomponent_get_inner (component);
483 
484 	if (subcomponent == NULL)
485 		goto exit;
486 
487 	kind = icalcomponent_isa (subcomponent);
488 	is_vevent = (kind == ICAL_VEVENT_COMPONENT);
489 	is_vtodo = (kind == ICAL_VTODO_COMPONENT);
490 
491 exit:
492 	action = e_attachment_view_get_action (view, "import-to-calendar");
493 	gtk_action_set_visible (action, is_vevent);
494 
495 	action = e_attachment_view_get_action (view, "import-to-tasks");
496 	gtk_action_set_visible (action, is_vtodo);
497 
498 	g_list_foreach (selected, (GFunc) g_object_unref, NULL);
499 	g_list_free (selected);
500 }
501 
502 static void
503 cal_attachment_handler_constructed (GObject *object)
504 {
505 	EAttachmentHandler *handler;
506 	EAttachmentView *view;
507 	GtkActionGroup *action_group;
508 	GtkUIManager *ui_manager;
509 	GError *error = NULL;
510 
511 	handler = E_ATTACHMENT_HANDLER (object);
512 
513 	/* Chain up to parent's constructed() method. */
514 	G_OBJECT_CLASS (parent_class)->constructed (object);
515 
516 	view = e_attachment_handler_get_view (handler);
517 
518 	action_group = e_attachment_view_add_action_group (view, "calendar");
519 	gtk_action_group_add_actions (
520 		action_group, standard_entries,
521 		G_N_ELEMENTS (standard_entries), handler);
522 
523 	ui_manager = e_attachment_view_get_ui_manager (view);
524 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
525 
526 	if (error != NULL) {
527 		g_warning ("%s", error->message);
528 		g_error_free (error);
529 	}
530 
531 	g_signal_connect (
532 		view, "update_actions",
533 		G_CALLBACK (cal_attachment_handler_update_actions),
534 		NULL);
535 }
536 
537 static void
538 cal_attachment_handler_class_init (ECalAttachmentHandlerClass *class)
539 {
540 	GObjectClass *object_class;
541 
542 	parent_class = g_type_class_peek_parent (class);
543 	g_type_class_add_private (class, sizeof (ECalAttachmentHandlerPrivate));
544 
545 	object_class = G_OBJECT_CLASS (class);
546 	object_class->constructed = cal_attachment_handler_constructed;
547 }
548 
549 static void
550 cal_attachment_handler_init (ECalAttachmentHandler *handler)
551 {
552 	handler->priv = E_CAL_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
553 }
554 
555 GType
556 e_cal_attachment_handler_get_type (void)
557 {
558 	return cal_attachment_handler_type;
559 }
560 
561 void
562 e_cal_attachment_handler_register_type (GTypeModule *type_module)
563 {
564 	static const GTypeInfo type_info = {
565 		sizeof (ECalAttachmentHandlerClass),
566 		(GBaseInitFunc) NULL,
567 		(GBaseFinalizeFunc) NULL,
568 		(GClassInitFunc) cal_attachment_handler_class_init,
569 		(GClassFinalizeFunc) NULL,
570 		NULL,  /* class_data */
571 		sizeof (ECalAttachmentHandler),
572 		0,     /* n_preallocs */
573 		(GInstanceInitFunc) cal_attachment_handler_init,
574 		NULL   /* value_table */
575 	};
576 
577 	cal_attachment_handler_type = g_type_module_register_type (
578 		type_module, E_TYPE_ATTACHMENT_HANDLER,
579 		"ECalAttachmentHandler", &type_info, 0);
580 }