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 }