1 /*
2 * e-attachment.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 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18 *
19 */
20
21 #include "evolution-config.h"
22
23 #include "e-attachment.h"
24
25 #include <errno.h>
26 #include <glib/gi18n.h>
27 #include <glib/gstdio.h>
28
29 #ifdef HAVE_AUTOAR
30 #include <gnome-autoar/gnome-autoar.h>
31 #endif
32
33 #include <libedataserver/libedataserver.h>
34
35 #include "e-icon-factory.h"
36 #include "e-mktemp.h"
37 #include "e-misc-utils.h"
38
39 #define E_ATTACHMENT_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
42
43 /* Fallback Icon */
44 #define DEFAULT_ICON_NAME "mail-attachment"
45
46 /* Emblems */
47 #define EMBLEM_CANCELLED "process-stop"
48 #define EMBLEM_LOADING "emblem-downloads"
49 #define EMBLEM_SAVING "document-save"
50 #define EMBLEM_ENCRYPT_WEAK "security-low"
51 #define EMBLEM_ENCRYPT_STRONG "security-high"
52 #define EMBLEM_ENCRYPT_UNKNOWN "security-medium"
53 #define EMBLEM_SIGN_BAD "stock_signature-bad"
54 #define EMBLEM_SIGN_GOOD "stock_signature-ok"
55 #define EMBLEM_SIGN_UNKNOWN "stock_signature"
56
57 /* Attributes needed for EAttachmentStore columns. */
58 #define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
59
60 struct _EAttachmentPrivate {
61 GMutex property_lock;
62
63 GFile *file;
64 GIcon *icon;
65 GFileInfo *file_info;
66 GCancellable *cancellable;
67 CamelMimePart *mime_part;
68 guint emblem_timeout_id;
69 gchar *disposition;
70 gint percent;
71 gint64 last_percent_notify; /* to avoid excessive notifications */
72
73 guint can_show : 1;
74 guint loading : 1;
75 guint saving : 1;
76 guint initially_shown : 1;
77
78 guint save_self : 1;
79 guint save_extracted : 1;
80
81 CamelCipherValidityEncrypt encrypted;
82 CamelCipherValiditySign signed_;
83
84 /* These are IDs for idle callbacks,
85 * protected by the idle_lock mutex. */
86 GMutex idle_lock;
87 guint update_icon_column_idle_id;
88 guint update_progress_columns_idle_id;
89 guint update_file_info_columns_idle_id;
90 };
91
92 enum {
93 PROP_0,
94 PROP_CAN_SHOW,
95 PROP_DISPOSITION,
96 PROP_ENCRYPTED,
97 PROP_FILE,
98 PROP_FILE_INFO,
99 PROP_ICON,
100 PROP_LOADING,
101 PROP_MIME_PART,
102 PROP_PERCENT,
103 PROP_SAVE_SELF,
104 PROP_SAVE_EXTRACTED,
105 PROP_SAVING,
106 PROP_INITIALLY_SHOWN,
107 PROP_SIGNED
108 };
109
110 enum {
111 LOAD_FAILED,
112 UPDATE_FILE_INFO,
113 UPDATE_ICON,
114 UPDATE_PROGRESS,
115 LAST_SIGNAL
116 };
117
118 static guint signals[LAST_SIGNAL];
119
G_DEFINE_TYPE(EAttachment,e_attachment,G_TYPE_OBJECT)120 G_DEFINE_TYPE (
121 EAttachment,
122 e_attachment,
123 G_TYPE_OBJECT)
124
125 static gboolean
126 create_system_thumbnail (EAttachment *attachment,
127 GIcon **icon)
128 {
129 GFile *file;
130 GFile *icon_file;
131 gchar *file_path = NULL;
132 gchar *thumbnail = NULL;
133 gboolean success = FALSE;
134
135 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
136 g_return_val_if_fail (icon != NULL, FALSE);
137
138 file = e_attachment_ref_file (attachment);
139 if (file != NULL)
140 file_path = g_file_get_path (file);
141
142 if (file_path != NULL) {
143 thumbnail = e_icon_factory_create_thumbnail (file_path);
144 g_free (file_path);
145 }
146
147 if (thumbnail == NULL)
148 goto exit;
149
150 icon_file = g_file_new_for_path (thumbnail);
151
152 if (*icon != NULL)
153 g_object_unref (*icon);
154
155 *icon = g_file_icon_new (icon_file);
156
157 g_object_unref (icon_file);
158
159 if (file != NULL) {
160 GFileInfo *file_info;
161 const gchar *attribute;
162
163 file_info = e_attachment_ref_file_info (attachment);
164 attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
165
166 if (file_info != NULL) {
167 g_file_info_set_attribute_byte_string (
168 file_info, attribute, thumbnail);
169 g_object_unref (file_info);
170 }
171 }
172
173 g_free (thumbnail);
174
175 success = TRUE;
176
177 exit:
178 g_clear_object (&file);
179
180 return success;
181 }
182
183 static gchar *
attachment_get_default_charset(void)184 attachment_get_default_charset (void)
185 {
186 GSettings *settings;
187 gchar *charset;
188
189 /* XXX This doesn't really belong here. */
190
191 settings = e_util_ref_settings ("org.gnome.evolution.mail");
192 charset = g_settings_get_string (settings, "composer-charset");
193 if (charset == NULL || *charset == '\0') {
194 g_free (charset);
195 /* FIXME This was "/apps/evolution/mail/format/charset",
196 * not sure it relates to "charset" */
197 charset = g_settings_get_string (settings, "charset");
198 if (charset == NULL || *charset == '\0') {
199 g_free (charset);
200 charset = NULL;
201 }
202 }
203 g_object_unref (settings);
204
205 if (charset == NULL)
206 charset = g_strdup (camel_iconv_locale_charset ());
207
208 if (charset == NULL)
209 charset = g_strdup ("us-ascii");
210
211 return charset;
212 }
213
214 static GFile*
attachment_get_temporary(GError ** error)215 attachment_get_temporary (GError **error)
216 {
217 gchar *template;
218 gchar *path;
219 GFile *temp_directory;
220
221 errno = 0;
222
223 /* Save the file to a temporary directory.
224 * We use a directory so the files can retain their basenames.
225 * XXX This could trigger a blocking temp directory cleanup. */
226 template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
227 path = e_mkdtemp (template);
228 g_free (template);
229
230 /* XXX Let's hope errno got set properly. */
231 if (path == NULL) {
232 g_set_error (
233 error, G_FILE_ERROR,
234 g_file_error_from_errno (errno),
235 "%s", g_strerror (errno));
236 return NULL;
237 }
238
239 temp_directory = g_file_new_for_path (path);
240 g_free (path);
241
242 return temp_directory;
243 }
244
245
246 static gboolean
attachment_update_file_info_columns_idle_cb(gpointer weak_ref)247 attachment_update_file_info_columns_idle_cb (gpointer weak_ref)
248 {
249 EAttachment *attachment;
250 GFileInfo *file_info;
251 const gchar *content_type;
252 const gchar *display_name;
253 gchar *content_desc;
254 gchar *display_size;
255 gchar *description;
256 gchar *caption;
257 goffset size;
258
259 attachment = g_weak_ref_get (weak_ref);
260 if (attachment == NULL)
261 goto exit;
262
263 g_mutex_lock (&attachment->priv->idle_lock);
264 attachment->priv->update_file_info_columns_idle_id = 0;
265 g_mutex_unlock (&attachment->priv->idle_lock);
266
267 file_info = e_attachment_ref_file_info (attachment);
268 if (file_info == NULL)
269 goto exit;
270
271 content_type = g_file_info_get_content_type (file_info);
272 display_name = g_file_info_get_display_name (file_info);
273 size = g_file_info_get_size (file_info);
274
275 content_desc = g_content_type_get_description (content_type);
276 display_size = g_format_size (size);
277
278 description = e_attachment_dup_description (attachment);
279 if (description == NULL || *description == '\0') {
280 g_free (description);
281 description = g_strdup (display_name);
282 }
283
284 if (size > 0)
285 caption = g_strdup_printf ("%s\n(%s)", description, display_size);
286 else
287 caption = g_strdup (description);
288
289 g_signal_emit (attachment, signals[UPDATE_FILE_INFO], 0, caption, content_desc, description, (gint64) size);
290
291 g_free (content_desc);
292 g_free (display_size);
293 g_free (description);
294 g_free (caption);
295
296 g_clear_object (&file_info);
297
298 exit:
299 g_clear_object (&attachment);
300
301 return FALSE;
302 }
303
304 static void
attachment_update_file_info_columns(EAttachment * attachment)305 attachment_update_file_info_columns (EAttachment *attachment)
306 {
307 g_mutex_lock (&attachment->priv->idle_lock);
308
309 if (attachment->priv->update_file_info_columns_idle_id == 0) {
310 guint idle_id;
311
312 idle_id = g_idle_add_full (
313 G_PRIORITY_HIGH_IDLE,
314 attachment_update_file_info_columns_idle_cb,
315 e_weak_ref_new (attachment),
316 (GDestroyNotify) e_weak_ref_free);
317 attachment->priv->update_file_info_columns_idle_id = idle_id;
318 }
319
320 g_mutex_unlock (&attachment->priv->idle_lock);
321 }
322
323 static gboolean
attachment_update_icon_column_idle_cb(gpointer weak_ref)324 attachment_update_icon_column_idle_cb (gpointer weak_ref)
325 {
326 EAttachment *attachment;
327 GFileInfo *file_info;
328 GCancellable *cancellable;
329 GIcon *icon = NULL;
330 const gchar *emblem_name = NULL;
331 const gchar *thumbnail_path = NULL;
332
333 attachment = g_weak_ref_get (weak_ref);
334 if (attachment == NULL)
335 goto exit;
336
337 g_mutex_lock (&attachment->priv->idle_lock);
338 attachment->priv->update_icon_column_idle_id = 0;
339 g_mutex_unlock (&attachment->priv->idle_lock);
340
341 cancellable = attachment->priv->cancellable;
342 file_info = e_attachment_ref_file_info (attachment);
343
344 if (file_info != NULL) {
345 icon = g_file_info_get_icon (file_info);
346 /* add the reference here, thus the create_system_thumbnail() can unref the *icon. */
347 if (icon)
348 g_object_ref (icon);
349 thumbnail_path = g_file_info_get_attribute_byte_string (
350 file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
351 }
352
353 if (e_attachment_is_mail_note (attachment)) {
354 g_clear_object (&icon);
355 icon = g_themed_icon_new ("evolution-memos");
356
357 /* Prefer the thumbnail if we have one. */
358 } else if (thumbnail_path != NULL && *thumbnail_path != '\0') {
359 GFile *file;
360
361 file = g_file_new_for_path (thumbnail_path);
362 icon = g_file_icon_new (file);
363 g_object_unref (file);
364
365 /* Try the system thumbnailer. */
366 } else if (create_system_thumbnail (attachment, &icon)) {
367 /* Nothing to do, just use the icon. */
368
369 /* Else use the standard icon for the content type. */
370 } else if (icon != NULL) {
371 /* Nothing to do, just use the already reffed icon. */
372
373 /* Last ditch fallback. (GFileInfo not yet loaded?) */
374 } else
375 icon = g_themed_icon_new (DEFAULT_ICON_NAME);
376
377 /* Pick an emblem, limit one. Choices listed by priority. */
378
379 if (g_cancellable_is_cancelled (cancellable))
380 emblem_name = EMBLEM_CANCELLED;
381
382 else if (e_attachment_get_loading (attachment))
383 emblem_name = EMBLEM_LOADING;
384
385 else if (e_attachment_get_saving (attachment))
386 emblem_name = EMBLEM_SAVING;
387
388 else if (e_attachment_get_encrypted (attachment))
389 switch (e_attachment_get_encrypted (attachment)) {
390 case CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK:
391 emblem_name = EMBLEM_ENCRYPT_WEAK;
392 break;
393
394 case CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED:
395 emblem_name = EMBLEM_ENCRYPT_UNKNOWN;
396 break;
397
398 case CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG:
399 emblem_name = EMBLEM_ENCRYPT_STRONG;
400 break;
401
402 default:
403 g_warn_if_reached ();
404 break;
405 }
406
407 else if (e_attachment_get_signed (attachment))
408 switch (e_attachment_get_signed (attachment)) {
409 case CAMEL_CIPHER_VALIDITY_SIGN_GOOD:
410 emblem_name = EMBLEM_SIGN_GOOD;
411 break;
412
413 case CAMEL_CIPHER_VALIDITY_SIGN_BAD:
414 emblem_name = EMBLEM_SIGN_BAD;
415 break;
416
417 case CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN:
418 case CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY:
419 emblem_name = EMBLEM_SIGN_UNKNOWN;
420 break;
421
422 default:
423 g_warn_if_reached ();
424 break;
425 }
426
427 if (emblem_name != NULL) {
428 GIcon *emblemed_icon;
429 GEmblem *emblem;
430
431 emblemed_icon = g_themed_icon_new (emblem_name);
432 emblem = g_emblem_new (emblemed_icon);
433 g_object_unref (emblemed_icon);
434
435 emblemed_icon = g_emblemed_icon_new (icon, emblem);
436 g_object_unref (emblem);
437 g_object_unref (icon);
438
439 icon = emblemed_icon;
440 }
441
442 g_signal_emit (attachment, signals[UPDATE_ICON], 0, icon);
443
444 /* Cache the icon to reuse for things like drag-n-drop. */
445 if (attachment->priv->icon != NULL)
446 g_object_unref (attachment->priv->icon);
447 attachment->priv->icon = icon;
448 g_object_notify (G_OBJECT (attachment), "icon");
449
450 g_clear_object (&file_info);
451
452 exit:
453 g_clear_object (&attachment);
454
455 return FALSE;
456 }
457
458 static void
attachment_update_icon_column(EAttachment * attachment)459 attachment_update_icon_column (EAttachment *attachment)
460 {
461 g_mutex_lock (&attachment->priv->idle_lock);
462
463 if (attachment->priv->update_icon_column_idle_id == 0) {
464 guint idle_id;
465
466 idle_id = g_idle_add_full (
467 G_PRIORITY_HIGH_IDLE,
468 attachment_update_icon_column_idle_cb,
469 e_weak_ref_new (attachment),
470 (GDestroyNotify) e_weak_ref_free);
471 attachment->priv->update_icon_column_idle_id = idle_id;
472 }
473
474 g_mutex_unlock (&attachment->priv->idle_lock);
475 }
476
477 static gboolean
attachment_update_progress_columns_idle_cb(gpointer weak_ref)478 attachment_update_progress_columns_idle_cb (gpointer weak_ref)
479 {
480 EAttachment *attachment;
481 gboolean loading;
482 gboolean saving;
483 gint percent;
484
485 attachment = g_weak_ref_get (weak_ref);
486 if (attachment == NULL)
487 goto exit;
488
489 g_mutex_lock (&attachment->priv->idle_lock);
490 attachment->priv->update_progress_columns_idle_id = 0;
491 g_mutex_unlock (&attachment->priv->idle_lock);
492
493 /* Don't show progress bars until we have progress to report. */
494 percent = e_attachment_get_percent (attachment);
495 loading = e_attachment_get_loading (attachment) && (percent > 0);
496 saving = e_attachment_get_saving (attachment) && (percent > 0);
497
498 g_signal_emit (attachment, signals[UPDATE_PROGRESS], 0, loading, saving, percent);
499
500 exit:
501 g_clear_object (&attachment);
502
503 return FALSE;
504 }
505
506 static void
attachment_update_progress_columns(EAttachment * attachment)507 attachment_update_progress_columns (EAttachment *attachment)
508 {
509 g_mutex_lock (&attachment->priv->idle_lock);
510
511 if (attachment->priv->update_progress_columns_idle_id == 0) {
512 guint idle_id;
513
514 idle_id = g_idle_add_full (
515 G_PRIORITY_HIGH_IDLE,
516 attachment_update_progress_columns_idle_cb,
517 e_weak_ref_new (attachment),
518 (GDestroyNotify) e_weak_ref_free);
519 attachment->priv->update_progress_columns_idle_id = idle_id;
520 }
521
522 g_mutex_unlock (&attachment->priv->idle_lock);
523 }
524
525 static void
attachment_set_loading(EAttachment * attachment,gboolean loading)526 attachment_set_loading (EAttachment *attachment,
527 gboolean loading)
528 {
529 attachment->priv->percent = 0;
530 attachment->priv->loading = loading;
531 attachment->priv->last_percent_notify = 0;
532
533 g_object_freeze_notify (G_OBJECT (attachment));
534 g_object_notify (G_OBJECT (attachment), "percent");
535 g_object_notify (G_OBJECT (attachment), "loading");
536 g_object_thaw_notify (G_OBJECT (attachment));
537 }
538
539 static void
attachment_set_saving(EAttachment * attachment,gboolean saving)540 attachment_set_saving (EAttachment *attachment,
541 gboolean saving)
542 {
543 attachment->priv->percent = 0;
544 attachment->priv->saving = saving;
545 attachment->priv->last_percent_notify = 0;
546 }
547
548 static void
attachment_progress_cb(goffset current_num_bytes,goffset total_num_bytes,EAttachment * attachment)549 attachment_progress_cb (goffset current_num_bytes,
550 goffset total_num_bytes,
551 EAttachment *attachment)
552 {
553 gint new_percent;
554
555 /* Avoid dividing by zero. */
556 if (total_num_bytes == 0)
557 return;
558
559 /* do not notify too often, 5 times per second is sufficient */
560 if (g_get_monotonic_time () - attachment->priv->last_percent_notify < 200000)
561 return;
562
563 attachment->priv->last_percent_notify = g_get_monotonic_time ();
564
565 new_percent = (current_num_bytes * 100) / total_num_bytes;
566
567 if (new_percent != attachment->priv->percent)
568 attachment->priv->percent = new_percent;
569 }
570
571 static gboolean
attachment_cancelled_timeout_cb(gpointer user_data)572 attachment_cancelled_timeout_cb (gpointer user_data)
573 {
574 EAttachment *attachment;
575
576 attachment = E_ATTACHMENT (user_data);
577 attachment->priv->emblem_timeout_id = 0;
578 g_cancellable_reset (attachment->priv->cancellable);
579
580 attachment_update_icon_column (attachment);
581
582 return FALSE;
583 }
584
585 static void
attachment_cancelled_cb(EAttachment * attachment)586 attachment_cancelled_cb (EAttachment *attachment)
587 {
588 /* Reset the GCancellable after one second. This causes a
589 * cancel emblem to be briefly shown on the attachment icon
590 * as visual feedback that an operation was cancelled. */
591
592 if (attachment->priv->emblem_timeout_id > 0)
593 g_source_remove (attachment->priv->emblem_timeout_id);
594
595 attachment->priv->emblem_timeout_id = e_named_timeout_add_seconds (
596 1, attachment_cancelled_timeout_cb, attachment);
597
598 attachment_update_icon_column (attachment);
599 }
600
601 static void
attachment_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)602 attachment_set_property (GObject *object,
603 guint property_id,
604 const GValue *value,
605 GParamSpec *pspec)
606 {
607 switch (property_id) {
608 case PROP_CAN_SHOW:
609 e_attachment_set_can_show (
610 E_ATTACHMENT (object),
611 g_value_get_boolean (value));
612 return;
613
614 case PROP_DISPOSITION:
615 e_attachment_set_disposition (
616 E_ATTACHMENT (object),
617 g_value_get_string (value));
618 return;
619
620 case PROP_ENCRYPTED:
621 e_attachment_set_encrypted (
622 E_ATTACHMENT (object),
623 g_value_get_int (value));
624 return;
625
626 case PROP_FILE:
627 e_attachment_set_file (
628 E_ATTACHMENT (object),
629 g_value_get_object (value));
630 return;
631
632 case PROP_INITIALLY_SHOWN:
633 e_attachment_set_initially_shown (
634 E_ATTACHMENT (object),
635 g_value_get_boolean (value));
636 return;
637
638 case PROP_MIME_PART:
639 e_attachment_set_mime_part (
640 E_ATTACHMENT (object),
641 g_value_get_object (value));
642 return;
643
644 case PROP_SIGNED:
645 e_attachment_set_signed (
646 E_ATTACHMENT (object),
647 g_value_get_int (value));
648 return;
649
650 case PROP_SAVE_SELF:
651 e_attachment_set_save_self (
652 E_ATTACHMENT (object),
653 g_value_get_boolean (value));
654 return;
655
656 case PROP_SAVE_EXTRACTED:
657 e_attachment_set_save_extracted (
658 E_ATTACHMENT (object),
659 g_value_get_boolean (value));
660 return;
661 }
662
663 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
664 }
665
666 static void
attachment_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)667 attachment_get_property (GObject *object,
668 guint property_id,
669 GValue *value,
670 GParamSpec *pspec)
671 {
672 switch (property_id) {
673 case PROP_CAN_SHOW:
674 g_value_set_boolean (
675 value,
676 e_attachment_get_can_show (
677 E_ATTACHMENT (object)));
678 return;
679
680 case PROP_DISPOSITION:
681 g_value_set_string (
682 value,
683 e_attachment_get_disposition (
684 E_ATTACHMENT (object)));
685 return;
686
687 case PROP_ENCRYPTED:
688 g_value_set_int (
689 value,
690 e_attachment_get_encrypted (
691 E_ATTACHMENT (object)));
692 return;
693
694 case PROP_FILE:
695 g_value_take_object (
696 value,
697 e_attachment_ref_file (
698 E_ATTACHMENT (object)));
699 return;
700
701 case PROP_FILE_INFO:
702 g_value_take_object (
703 value,
704 e_attachment_ref_file_info (
705 E_ATTACHMENT (object)));
706 return;
707
708 case PROP_ICON:
709 g_value_take_object (
710 value,
711 e_attachment_ref_icon (
712 E_ATTACHMENT (object)));
713 return;
714
715 case PROP_INITIALLY_SHOWN:
716 g_value_set_boolean (
717 value,
718 e_attachment_get_initially_shown (
719 E_ATTACHMENT (object)));
720 return;
721
722 case PROP_LOADING:
723 g_value_set_boolean (
724 value,
725 e_attachment_get_loading (
726 E_ATTACHMENT (object)));
727 return;
728
729 case PROP_MIME_PART:
730 g_value_take_object (
731 value,
732 e_attachment_ref_mime_part (
733 E_ATTACHMENT (object)));
734 return;
735
736 case PROP_PERCENT:
737 g_value_set_int (
738 value,
739 e_attachment_get_percent (
740 E_ATTACHMENT (object)));
741 return;
742
743 case PROP_SAVE_SELF:
744 g_value_set_boolean (
745 value,
746 e_attachment_get_save_self (
747 E_ATTACHMENT (object)));
748 return;
749
750 case PROP_SAVE_EXTRACTED:
751 g_value_set_boolean (
752 value,
753 e_attachment_get_save_extracted (
754 E_ATTACHMENT (object)));
755 return;
756
757 case PROP_SAVING:
758 g_value_set_boolean (
759 value,
760 e_attachment_get_saving (
761 E_ATTACHMENT (object)));
762 return;
763
764 case PROP_SIGNED:
765 g_value_set_int (
766 value,
767 e_attachment_get_signed (
768 E_ATTACHMENT (object)));
769 return;
770 }
771
772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
773 }
774
775 static void
attachment_dispose(GObject * object)776 attachment_dispose (GObject *object)
777 {
778 EAttachmentPrivate *priv;
779
780 priv = E_ATTACHMENT_GET_PRIVATE (object);
781
782 g_clear_object (&priv->file);
783 g_clear_object (&priv->icon);
784 g_clear_object (&priv->file_info);
785 g_clear_object (&priv->cancellable);
786 g_clear_object (&priv->mime_part);
787
788 if (priv->emblem_timeout_id > 0) {
789 g_source_remove (priv->emblem_timeout_id);
790 priv->emblem_timeout_id = 0;
791 }
792
793 /* Chain up to parent's dispose() method. */
794 G_OBJECT_CLASS (e_attachment_parent_class)->dispose (object);
795 }
796
797 static void
attachment_finalize(GObject * object)798 attachment_finalize (GObject *object)
799 {
800 EAttachmentPrivate *priv;
801
802 priv = E_ATTACHMENT_GET_PRIVATE (object);
803
804 if (priv->update_icon_column_idle_id > 0)
805 g_source_remove (priv->update_icon_column_idle_id);
806
807 if (priv->update_progress_columns_idle_id > 0)
808 g_source_remove (priv->update_progress_columns_idle_id);
809
810 if (priv->update_file_info_columns_idle_id > 0)
811 g_source_remove (priv->update_file_info_columns_idle_id);
812
813 g_mutex_clear (&priv->property_lock);
814 g_mutex_clear (&priv->idle_lock);
815
816 g_free (priv->disposition);
817
818 /* Chain up to parent's finalize() method. */
819 G_OBJECT_CLASS (e_attachment_parent_class)->finalize (object);
820 }
821
822 static void
e_attachment_class_init(EAttachmentClass * class)823 e_attachment_class_init (EAttachmentClass *class)
824 {
825 GObjectClass *object_class;
826
827 g_type_class_add_private (class, sizeof (EAttachmentPrivate));
828
829 object_class = G_OBJECT_CLASS (class);
830 object_class->set_property = attachment_set_property;
831 object_class->get_property = attachment_get_property;
832 object_class->dispose = attachment_dispose;
833 object_class->finalize = attachment_finalize;
834
835 g_object_class_install_property (
836 object_class,
837 PROP_CAN_SHOW,
838 g_param_spec_boolean (
839 "can-show",
840 "Can Show",
841 NULL,
842 FALSE,
843 G_PARAM_READWRITE |
844 G_PARAM_CONSTRUCT));
845
846 g_object_class_install_property (
847 object_class,
848 PROP_DISPOSITION,
849 g_param_spec_string (
850 "disposition",
851 "Disposition",
852 NULL,
853 "attachment",
854 G_PARAM_READWRITE |
855 G_PARAM_CONSTRUCT));
856
857 /* FIXME Define a GEnumClass for this. */
858 g_object_class_install_property (
859 object_class,
860 PROP_ENCRYPTED,
861 g_param_spec_int (
862 "encrypted",
863 "Encrypted",
864 NULL,
865 CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
866 CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG,
867 CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE,
868 G_PARAM_READWRITE |
869 G_PARAM_CONSTRUCT));
870
871 g_object_class_install_property (
872 object_class,
873 PROP_FILE,
874 g_param_spec_object (
875 "file",
876 "File",
877 NULL,
878 G_TYPE_FILE,
879 G_PARAM_READWRITE |
880 G_PARAM_CONSTRUCT));
881
882 g_object_class_install_property (
883 object_class,
884 PROP_FILE_INFO,
885 g_param_spec_object (
886 "file-info",
887 "File Info",
888 NULL,
889 G_TYPE_FILE_INFO,
890 G_PARAM_READABLE));
891
892 g_object_class_install_property (
893 object_class,
894 PROP_ICON,
895 g_param_spec_object (
896 "icon",
897 "Icon",
898 NULL,
899 G_TYPE_ICON,
900 G_PARAM_READABLE));
901
902 g_object_class_install_property (
903 object_class,
904 PROP_LOADING,
905 g_param_spec_boolean (
906 "loading",
907 "Loading",
908 NULL,
909 FALSE,
910 G_PARAM_READABLE));
911
912 g_object_class_install_property (
913 object_class,
914 PROP_MIME_PART,
915 g_param_spec_object (
916 "mime-part",
917 "MIME Part",
918 NULL,
919 CAMEL_TYPE_MIME_PART,
920 G_PARAM_READWRITE));
921
922 g_object_class_install_property (
923 object_class,
924 PROP_PERCENT,
925 g_param_spec_int (
926 "percent",
927 "Percent",
928 NULL,
929 0,
930 100,
931 0,
932 G_PARAM_READABLE));
933
934 g_object_class_install_property (
935 object_class,
936 PROP_SAVE_SELF,
937 g_param_spec_boolean (
938 "save-self",
939 "Save self",
940 NULL,
941 TRUE,
942 G_PARAM_READWRITE));
943
944 g_object_class_install_property (
945 object_class,
946 PROP_SAVE_EXTRACTED,
947 g_param_spec_boolean (
948 "save-extracted",
949 "Save extracted",
950 NULL,
951 FALSE,
952 G_PARAM_READWRITE));
953
954 g_object_class_install_property (
955 object_class,
956 PROP_SAVING,
957 g_param_spec_boolean (
958 "saving",
959 "Saving",
960 NULL,
961 FALSE,
962 G_PARAM_READABLE));
963
964 g_object_class_install_property (
965 object_class,
966 PROP_INITIALLY_SHOWN,
967 g_param_spec_boolean (
968 "initially-shown",
969 "Initially Shown",
970 NULL,
971 FALSE,
972 G_PARAM_READWRITE |
973 G_PARAM_CONSTRUCT));
974
975 /* FIXME Define a GEnumClass for this. */
976 g_object_class_install_property (
977 object_class,
978 PROP_SIGNED,
979 g_param_spec_int (
980 "signed",
981 "Signed",
982 NULL,
983 CAMEL_CIPHER_VALIDITY_SIGN_NONE,
984 CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY,
985 CAMEL_CIPHER_VALIDITY_SIGN_NONE,
986 G_PARAM_READWRITE |
987 G_PARAM_CONSTRUCT));
988
989 signals[UPDATE_FILE_INFO] = g_signal_new (
990 "update-file-info",
991 G_TYPE_FROM_CLASS (class),
992 G_SIGNAL_RUN_LAST,
993 G_STRUCT_OFFSET (EAttachmentClass, update_file_info),
994 NULL, NULL, NULL,
995 G_TYPE_NONE, 4,
996 G_TYPE_STRING,
997 G_TYPE_STRING,
998 G_TYPE_STRING,
999 G_TYPE_INT64);
1000
1001 signals[UPDATE_ICON] = g_signal_new (
1002 "update-icon",
1003 G_TYPE_FROM_CLASS (class),
1004 G_SIGNAL_RUN_LAST,
1005 G_STRUCT_OFFSET (EAttachmentClass, update_icon),
1006 NULL, NULL, NULL,
1007 G_TYPE_NONE, 1,
1008 G_TYPE_ICON);
1009
1010 signals[UPDATE_PROGRESS] = g_signal_new (
1011 "update-progress",
1012 G_TYPE_FROM_CLASS (class),
1013 G_SIGNAL_RUN_LAST,
1014 G_STRUCT_OFFSET (EAttachmentClass, update_progress),
1015 NULL, NULL, NULL,
1016 G_TYPE_NONE, 3,
1017 G_TYPE_BOOLEAN,
1018 G_TYPE_BOOLEAN,
1019 G_TYPE_INT);
1020
1021 signals[LOAD_FAILED] = g_signal_new (
1022 "load-failed",
1023 G_TYPE_FROM_CLASS (class),
1024 G_SIGNAL_RUN_LAST,
1025 G_STRUCT_OFFSET (EAttachmentClass, load_failed),
1026 NULL, NULL, NULL,
1027 G_TYPE_NONE, 0,
1028 G_TYPE_NONE);
1029 }
1030
1031 static void
e_attachment_init(EAttachment * attachment)1032 e_attachment_init (EAttachment *attachment)
1033 {
1034 attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment);
1035 attachment->priv->cancellable = g_cancellable_new ();
1036 attachment->priv->encrypted = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
1037 attachment->priv->signed_ = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
1038
1039 g_mutex_init (&attachment->priv->property_lock);
1040 g_mutex_init (&attachment->priv->idle_lock);
1041
1042 e_signal_connect_notify (
1043 attachment, "notify::encrypted",
1044 G_CALLBACK (attachment_update_icon_column), NULL);
1045
1046 g_signal_connect (
1047 attachment, "notify::file-info",
1048 G_CALLBACK (attachment_update_file_info_columns), NULL);
1049
1050 g_signal_connect (
1051 attachment, "notify::file-info",
1052 G_CALLBACK (attachment_update_icon_column), NULL);
1053
1054 e_signal_connect_notify (
1055 attachment, "notify::loading",
1056 G_CALLBACK (attachment_update_icon_column), NULL);
1057
1058 e_signal_connect_notify (
1059 attachment, "notify::loading",
1060 G_CALLBACK (attachment_update_progress_columns), NULL);
1061
1062 e_signal_connect_notify (
1063 attachment, "notify::percent",
1064 G_CALLBACK (attachment_update_progress_columns), NULL);
1065
1066 e_signal_connect_notify (
1067 attachment, "notify::saving",
1068 G_CALLBACK (attachment_update_icon_column), NULL);
1069
1070 e_signal_connect_notify (
1071 attachment, "notify::saving",
1072 G_CALLBACK (attachment_update_progress_columns), NULL);
1073
1074 e_signal_connect_notify (
1075 attachment, "notify::signed",
1076 G_CALLBACK (attachment_update_icon_column), NULL);
1077
1078 g_signal_connect_swapped (
1079 attachment->priv->cancellable, "cancelled",
1080 G_CALLBACK (attachment_cancelled_cb), attachment);
1081 }
1082
1083 EAttachment *
e_attachment_new(void)1084 e_attachment_new (void)
1085 {
1086 return g_object_new (E_TYPE_ATTACHMENT, NULL);
1087 }
1088
1089 EAttachment *
e_attachment_new_for_path(const gchar * path)1090 e_attachment_new_for_path (const gchar *path)
1091 {
1092 EAttachment *attachment;
1093 GFile *file;
1094
1095 g_return_val_if_fail (path != NULL, NULL);
1096
1097 file = g_file_new_for_path (path);
1098 attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
1099 g_object_unref (file);
1100
1101 return attachment;
1102 }
1103
1104 EAttachment *
e_attachment_new_for_uri(const gchar * uri)1105 e_attachment_new_for_uri (const gchar *uri)
1106 {
1107 EAttachment *attachment;
1108 GFile *file;
1109
1110 g_return_val_if_fail (uri != NULL, NULL);
1111
1112 file = g_file_new_for_uri (uri);
1113 attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
1114 g_object_unref (file);
1115
1116 return attachment;
1117 }
1118
1119 EAttachment *
e_attachment_new_for_message(CamelMimeMessage * message)1120 e_attachment_new_for_message (CamelMimeMessage *message)
1121 {
1122 CamelDataWrapper *wrapper;
1123 CamelMimePart *mime_part;
1124 EAttachment *attachment;
1125 GString *description;
1126 const gchar *subject;
1127
1128 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1129
1130 mime_part = camel_mime_part_new ();
1131 camel_mime_part_set_disposition (mime_part, "inline");
1132 subject = camel_mime_message_get_subject (message);
1133
1134 /* To Translators: This text is set as a description of an attached
1135 * message when, for example, attaching it to a composer. When the
1136 * message to be attached has also filled Subject, then this text is
1137 * of form "Attached message - Subject", otherwise it's left as is. */
1138 description = g_string_new (_("Attached message"));
1139 if (subject != NULL)
1140 g_string_append_printf (description, " - %s", subject);
1141 camel_mime_part_set_description (mime_part, description->str);
1142 g_string_free (description, TRUE);
1143
1144 wrapper = CAMEL_DATA_WRAPPER (message);
1145 camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper);
1146 camel_mime_part_set_content_type (mime_part, "message/rfc822");
1147
1148 attachment = e_attachment_new ();
1149 e_attachment_set_mime_part (attachment, mime_part);
1150 g_object_unref (mime_part);
1151
1152 return attachment;
1153 }
1154
1155 void
e_attachment_add_to_multipart(EAttachment * attachment,CamelMultipart * multipart,const gchar * default_charset)1156 e_attachment_add_to_multipart (EAttachment *attachment,
1157 CamelMultipart *multipart,
1158 const gchar *default_charset)
1159 {
1160 CamelContentType *content_type;
1161 CamelDataWrapper *wrapper;
1162 CamelMimePart *mime_part;
1163
1164 /* XXX EMsgComposer might be a better place for this function. */
1165
1166 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1167 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
1168
1169 /* Still loading? Too bad. */
1170 mime_part = e_attachment_ref_mime_part (attachment);
1171 if (mime_part == NULL)
1172 return;
1173
1174 content_type = camel_mime_part_get_content_type (mime_part);
1175 wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
1176
1177 if (CAMEL_IS_MULTIPART (wrapper))
1178 goto exit;
1179
1180 /* For text content, determine the best encoding and character set. */
1181 if (camel_content_type_is (content_type, "text", "*")) {
1182 CamelTransferEncoding encoding;
1183 CamelStream *filtered_stream;
1184 CamelMimeFilter *filter;
1185 CamelStream *stream;
1186 const gchar *charset;
1187
1188 charset = camel_content_type_param (content_type, "charset");
1189
1190 /* Determine the best encoding by writing the MIME
1191 * part to a NULL stream with a "bestenc" filter. */
1192 stream = camel_stream_null_new ();
1193 filtered_stream = camel_stream_filter_new (stream);
1194 filter = camel_mime_filter_bestenc_new (
1195 CAMEL_BESTENC_GET_ENCODING);
1196 camel_stream_filter_add (
1197 CAMEL_STREAM_FILTER (filtered_stream),
1198 CAMEL_MIME_FILTER (filter));
1199 camel_data_wrapper_decode_to_stream_sync (
1200 wrapper, filtered_stream, NULL, NULL);
1201 g_object_unref (filtered_stream);
1202 g_object_unref (stream);
1203
1204 /* Retrieve the best encoding from the filter. */
1205 encoding = camel_mime_filter_bestenc_get_best_encoding (
1206 CAMEL_MIME_FILTER_BESTENC (filter),
1207 CAMEL_BESTENC_8BIT);
1208 camel_mime_part_set_encoding (mime_part, encoding);
1209 g_object_unref (filter);
1210
1211 if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
1212 /* The text fits within us-ascii, so this is safe.
1213 * FIXME Check that this isn't iso-2022-jp? */
1214 default_charset = "us-ascii";
1215
1216 } else if (charset == NULL && default_charset == NULL) {
1217 default_charset = attachment_get_default_charset ();
1218 /* FIXME Check that this fits within the
1219 * default_charset and if not, find one
1220 * that does and/or allow the user to
1221 * specify. */
1222 }
1223
1224 if (charset == NULL) {
1225 gchar *type;
1226
1227 camel_content_type_set_param (
1228 content_type, "charset", default_charset);
1229 type = camel_content_type_format (content_type);
1230 camel_mime_part_set_content_type (mime_part, type);
1231 g_free (type);
1232 }
1233
1234 /* Otherwise, unless it's a message/rfc822, Base64 encode it. */
1235 } else if (!CAMEL_IS_MIME_MESSAGE (wrapper))
1236 camel_mime_part_set_encoding (
1237 mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
1238
1239 exit:
1240 camel_multipart_add_part (multipart, mime_part);
1241
1242 g_clear_object (&mime_part);
1243 }
1244
1245 void
e_attachment_cancel(EAttachment * attachment)1246 e_attachment_cancel (EAttachment *attachment)
1247 {
1248 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1249
1250 g_cancellable_cancel (attachment->priv->cancellable);
1251 }
1252
1253 gboolean
e_attachment_is_mail_note(EAttachment * attachment)1254 e_attachment_is_mail_note (EAttachment *attachment)
1255 {
1256 CamelContentType *ct;
1257
1258 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1259
1260 if (!attachment->priv->mime_part)
1261 return FALSE;
1262
1263 ct = camel_mime_part_get_content_type (attachment->priv->mime_part);
1264 if (!ct || !camel_content_type_is (ct, "message", "rfc822"))
1265 return FALSE;
1266
1267 return camel_medium_get_header (CAMEL_MEDIUM (attachment->priv->mime_part), "X-Evolution-Note") != NULL;
1268 }
1269
1270 gboolean
e_attachment_get_can_show(EAttachment * attachment)1271 e_attachment_get_can_show (EAttachment *attachment)
1272 {
1273 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1274
1275 return attachment->priv->can_show;
1276 }
1277
1278 void
e_attachment_set_can_show(EAttachment * attachment,gboolean can_show)1279 e_attachment_set_can_show (EAttachment *attachment,
1280 gboolean can_show)
1281 {
1282 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1283
1284 attachment->priv->can_show = can_show;
1285
1286 g_object_notify (G_OBJECT (attachment), "can-show");
1287 }
1288
1289 const gchar *
e_attachment_get_disposition(EAttachment * attachment)1290 e_attachment_get_disposition (EAttachment *attachment)
1291 {
1292 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1293
1294 return attachment->priv->disposition;
1295 }
1296
1297 gchar *
e_attachment_dup_disposition(EAttachment * attachment)1298 e_attachment_dup_disposition (EAttachment *attachment)
1299 {
1300 const gchar *protected;
1301 gchar *duplicate;
1302
1303 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1304
1305 g_mutex_lock (&attachment->priv->property_lock);
1306
1307 protected = e_attachment_get_disposition (attachment);
1308 duplicate = g_strdup (protected);
1309
1310 g_mutex_unlock (&attachment->priv->property_lock);
1311
1312 return duplicate;
1313 }
1314
1315 void
e_attachment_set_disposition(EAttachment * attachment,const gchar * disposition)1316 e_attachment_set_disposition (EAttachment *attachment,
1317 const gchar *disposition)
1318 {
1319 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1320
1321 g_mutex_lock (&attachment->priv->property_lock);
1322
1323 g_free (attachment->priv->disposition);
1324 attachment->priv->disposition = g_strdup (disposition);
1325
1326 g_mutex_unlock (&attachment->priv->property_lock);
1327
1328 g_object_notify (G_OBJECT (attachment), "disposition");
1329 }
1330
1331 GFile *
e_attachment_ref_file(EAttachment * attachment)1332 e_attachment_ref_file (EAttachment *attachment)
1333 {
1334 GFile *file = NULL;
1335
1336 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1337
1338 g_mutex_lock (&attachment->priv->property_lock);
1339
1340 if (attachment->priv->file != NULL)
1341 file = g_object_ref (attachment->priv->file);
1342
1343 g_mutex_unlock (&attachment->priv->property_lock);
1344
1345 return file;
1346 }
1347
1348 void
e_attachment_set_file(EAttachment * attachment,GFile * file)1349 e_attachment_set_file (EAttachment *attachment,
1350 GFile *file)
1351 {
1352 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1353
1354 if (file != NULL) {
1355 g_return_if_fail (G_IS_FILE (file));
1356 g_object_ref (file);
1357 }
1358
1359 g_mutex_lock (&attachment->priv->property_lock);
1360
1361 g_clear_object (&attachment->priv->file);
1362 attachment->priv->file = file;
1363
1364 g_mutex_unlock (&attachment->priv->property_lock);
1365
1366 g_object_notify (G_OBJECT (attachment), "file");
1367 }
1368
1369 GFileInfo *
e_attachment_ref_file_info(EAttachment * attachment)1370 e_attachment_ref_file_info (EAttachment *attachment)
1371 {
1372 GFileInfo *file_info = NULL;
1373
1374 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1375
1376 g_mutex_lock (&attachment->priv->property_lock);
1377
1378 if (attachment->priv->file_info != NULL)
1379 file_info = g_object_ref (attachment->priv->file_info);
1380
1381 g_mutex_unlock (&attachment->priv->property_lock);
1382
1383 return file_info;
1384 }
1385
1386 void
e_attachment_set_file_info(EAttachment * attachment,GFileInfo * file_info)1387 e_attachment_set_file_info (EAttachment *attachment,
1388 GFileInfo *file_info)
1389 {
1390 GIcon *icon;
1391
1392 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1393
1394 if (file_info != NULL) {
1395 g_return_if_fail (G_IS_FILE_INFO (file_info));
1396 g_object_ref (file_info);
1397 }
1398
1399 g_mutex_lock (&attachment->priv->property_lock);
1400
1401 g_clear_object (&attachment->priv->file_info);
1402 attachment->priv->file_info = file_info;
1403
1404 /* If the GFileInfo contains a GThemedIcon, append a
1405 * fallback icon name to ensure we display something. */
1406 icon = g_file_info_get_icon (file_info);
1407 if (G_IS_THEMED_ICON (icon))
1408 g_themed_icon_append_name (
1409 G_THEMED_ICON (icon), DEFAULT_ICON_NAME);
1410
1411 g_mutex_unlock (&attachment->priv->property_lock);
1412
1413 g_object_notify (G_OBJECT (attachment), "file-info");
1414 }
1415
1416 /**
1417 * e_attachment_dup_mime_type:
1418 * @attachment: an #EAttachment
1419 *
1420 * Returns the MIME type of @attachment according to its #GFileInfo.
1421 * If the @attachment has no #GFileInfo then the function returns %NULL.
1422 * Free the returned MIME type string with g_free().
1423 *
1424 * Returns: a newly-allocated MIME type string, or %NULL
1425 **/
1426 gchar *
e_attachment_dup_mime_type(EAttachment * attachment)1427 e_attachment_dup_mime_type (EAttachment *attachment)
1428 {
1429 GFileInfo *file_info;
1430 const gchar *content_type = NULL;
1431 gchar *mime_type = NULL;
1432
1433 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1434
1435 file_info = e_attachment_ref_file_info (attachment);
1436 if (file_info != NULL)
1437 content_type = g_file_info_get_content_type (file_info);
1438
1439 if (content_type != NULL)
1440 mime_type = g_content_type_get_mime_type (content_type);
1441
1442 if (mime_type != NULL)
1443 camel_strdown (mime_type);
1444
1445 g_clear_object (&file_info);
1446
1447 return mime_type;
1448 }
1449
1450 GIcon *
e_attachment_ref_icon(EAttachment * attachment)1451 e_attachment_ref_icon (EAttachment *attachment)
1452 {
1453 GIcon *icon = NULL;
1454
1455 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1456
1457 g_mutex_lock (&attachment->priv->property_lock);
1458
1459 if (attachment->priv->icon != NULL)
1460 icon = g_object_ref (attachment->priv->icon);
1461
1462 g_mutex_unlock (&attachment->priv->property_lock);
1463
1464 return icon;
1465 }
1466
1467 gboolean
e_attachment_get_loading(EAttachment * attachment)1468 e_attachment_get_loading (EAttachment *attachment)
1469 {
1470 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1471
1472 return attachment->priv->loading;
1473 }
1474
1475 CamelMimePart *
e_attachment_ref_mime_part(EAttachment * attachment)1476 e_attachment_ref_mime_part (EAttachment *attachment)
1477 {
1478 CamelMimePart *mime_part = NULL;
1479
1480 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1481
1482 g_mutex_lock (&attachment->priv->property_lock);
1483
1484 if (attachment->priv->mime_part != NULL)
1485 mime_part = g_object_ref (attachment->priv->mime_part);
1486
1487 g_mutex_unlock (&attachment->priv->property_lock);
1488
1489 return mime_part;
1490 }
1491
1492 void
e_attachment_set_mime_part(EAttachment * attachment,CamelMimePart * mime_part)1493 e_attachment_set_mime_part (EAttachment *attachment,
1494 CamelMimePart *mime_part)
1495 {
1496 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1497
1498 if (mime_part != NULL) {
1499 g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
1500 g_object_ref (mime_part);
1501 }
1502
1503 g_mutex_lock (&attachment->priv->property_lock);
1504
1505 g_clear_object (&attachment->priv->mime_part);
1506 attachment->priv->mime_part = mime_part;
1507
1508 g_mutex_unlock (&attachment->priv->property_lock);
1509
1510 g_object_notify (G_OBJECT (attachment), "mime-part");
1511 }
1512
1513 gint
e_attachment_get_percent(EAttachment * attachment)1514 e_attachment_get_percent (EAttachment *attachment)
1515 {
1516 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
1517
1518 return attachment->priv->percent;
1519 }
1520
1521 gboolean
e_attachment_get_saving(EAttachment * attachment)1522 e_attachment_get_saving (EAttachment *attachment)
1523 {
1524 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1525
1526 return attachment->priv->saving;
1527 }
1528
1529 gboolean
e_attachment_get_initially_shown(EAttachment * attachment)1530 e_attachment_get_initially_shown (EAttachment *attachment)
1531 {
1532 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1533
1534 return attachment->priv->initially_shown;
1535 }
1536
1537 void
e_attachment_set_initially_shown(EAttachment * attachment,gboolean initially_shown)1538 e_attachment_set_initially_shown (EAttachment *attachment,
1539 gboolean initially_shown)
1540 {
1541 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1542
1543 attachment->priv->initially_shown = initially_shown;
1544
1545 g_object_notify (G_OBJECT (attachment), "initially-shown");
1546 }
1547
1548 gboolean
e_attachment_get_save_self(EAttachment * attachment)1549 e_attachment_get_save_self (EAttachment *attachment)
1550 {
1551 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), TRUE);
1552
1553 return attachment->priv->save_self;
1554 }
1555
1556 void
e_attachment_set_save_self(EAttachment * attachment,gboolean save_self)1557 e_attachment_set_save_self (EAttachment *attachment,
1558 gboolean save_self)
1559 {
1560 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1561
1562 attachment->priv->save_self = save_self;
1563 }
1564
1565 gboolean
e_attachment_get_save_extracted(EAttachment * attachment)1566 e_attachment_get_save_extracted (EAttachment *attachment)
1567 {
1568 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1569
1570 return attachment->priv->save_extracted;
1571 }
1572
1573 void
e_attachment_set_save_extracted(EAttachment * attachment,gboolean save_extracted)1574 e_attachment_set_save_extracted (EAttachment *attachment,
1575 gboolean save_extracted)
1576 {
1577 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1578
1579 attachment->priv->save_extracted = save_extracted;
1580 }
1581
1582 CamelCipherValidityEncrypt
e_attachment_get_encrypted(EAttachment * attachment)1583 e_attachment_get_encrypted (EAttachment *attachment)
1584 {
1585 g_return_val_if_fail (
1586 E_IS_ATTACHMENT (attachment),
1587 CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE);
1588
1589 return attachment->priv->encrypted;
1590 }
1591
1592 void
e_attachment_set_encrypted(EAttachment * attachment,CamelCipherValidityEncrypt encrypted)1593 e_attachment_set_encrypted (EAttachment *attachment,
1594 CamelCipherValidityEncrypt encrypted)
1595 {
1596 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1597
1598 attachment->priv->encrypted = encrypted;
1599
1600 g_object_notify (G_OBJECT (attachment), "encrypted");
1601 }
1602
1603 CamelCipherValiditySign
e_attachment_get_signed(EAttachment * attachment)1604 e_attachment_get_signed (EAttachment *attachment)
1605 {
1606 g_return_val_if_fail (
1607 E_IS_ATTACHMENT (attachment),
1608 CAMEL_CIPHER_VALIDITY_SIGN_NONE);
1609
1610 return attachment->priv->signed_;
1611 }
1612
1613 void
e_attachment_set_signed(EAttachment * attachment,CamelCipherValiditySign signed_)1614 e_attachment_set_signed (EAttachment *attachment,
1615 CamelCipherValiditySign signed_)
1616 {
1617 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1618
1619 attachment->priv->signed_ = signed_;
1620
1621 g_object_notify (G_OBJECT (attachment), "signed");
1622 }
1623
1624 gchar *
e_attachment_dup_description(EAttachment * attachment)1625 e_attachment_dup_description (EAttachment *attachment)
1626 {
1627 GFileInfo *file_info;
1628 const gchar *attribute;
1629 const gchar *protected;
1630 gchar *duplicate;
1631
1632 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1633
1634 file_info = e_attachment_ref_file_info (attachment);
1635 if (file_info == NULL)
1636 return NULL;
1637
1638 attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
1639 protected = g_file_info_get_attribute_string (file_info, attribute);
1640 duplicate = g_strdup (protected);
1641
1642 g_object_unref (file_info);
1643
1644 return duplicate;
1645 }
1646
1647 gchar *
e_attachment_dup_thumbnail_path(EAttachment * attachment)1648 e_attachment_dup_thumbnail_path (EAttachment *attachment)
1649 {
1650 GFileInfo *file_info;
1651 const gchar *attribute;
1652 const gchar *protected;
1653 gchar *duplicate;
1654
1655 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1656
1657 file_info = e_attachment_ref_file_info (attachment);
1658 if (file_info == NULL)
1659 return NULL;
1660
1661 attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
1662 protected = g_file_info_get_attribute_string (file_info, attribute);
1663 duplicate = g_strdup (protected);
1664
1665 g_object_unref (file_info);
1666
1667 return duplicate;
1668 }
1669
1670 gboolean
e_attachment_is_rfc822(EAttachment * attachment)1671 e_attachment_is_rfc822 (EAttachment *attachment)
1672 {
1673 gchar *mime_type;
1674 gboolean is_rfc822;
1675
1676 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
1677
1678 mime_type = e_attachment_dup_mime_type (attachment);
1679 is_rfc822 =
1680 (mime_type != NULL) &&
1681 (g_ascii_strcasecmp (mime_type, "message/rfc822") == 0);
1682 g_free (mime_type);
1683
1684 return is_rfc822;
1685 }
1686
1687 GAppInfo *
e_attachment_ref_default_app(EAttachment * attachment)1688 e_attachment_ref_default_app (EAttachment *attachment)
1689 {
1690 GFileInfo *file_info;
1691 GAppInfo *default_app = NULL;
1692 const gchar *content_type;
1693
1694 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1695
1696 file_info = e_attachment_ref_file_info (attachment);
1697 if (file_info == NULL)
1698 return NULL;
1699
1700 content_type = g_file_info_get_content_type (file_info);
1701 if (content_type && !g_content_type_equals (content_type, "application/octet-stream"))
1702 default_app = g_app_info_get_default_for_type (content_type, FALSE);
1703
1704 g_object_unref (file_info);
1705
1706 return default_app;
1707 }
1708
1709 GList *
e_attachment_list_apps(EAttachment * attachment)1710 e_attachment_list_apps (EAttachment *attachment)
1711 {
1712 GList *app_info_list;
1713 GList *guessed_infos;
1714 GFileInfo *file_info;
1715 GAppInfo *default_app;
1716 const gchar *content_type;
1717 const gchar *display_name;
1718 gboolean type_is_unknown;
1719 gchar *allocated;
1720
1721 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
1722
1723 file_info = e_attachment_ref_file_info (attachment);
1724 if (file_info == NULL)
1725 return NULL;
1726
1727 content_type = g_file_info_get_content_type (file_info);
1728 display_name = g_file_info_get_display_name (file_info);
1729 g_return_val_if_fail (content_type != NULL, NULL);
1730
1731 app_info_list = g_app_info_get_all_for_type (content_type);
1732 type_is_unknown = g_content_type_is_unknown (content_type);
1733
1734 if (app_info_list != NULL && !type_is_unknown)
1735 goto exit;
1736
1737 if (display_name == NULL)
1738 goto exit;
1739
1740 allocated = g_content_type_guess (display_name, NULL, 0, NULL);
1741 guessed_infos = g_app_info_get_all_for_type (allocated);
1742 app_info_list = g_list_concat (guessed_infos, app_info_list);
1743 g_free (allocated);
1744
1745 exit:
1746 default_app = e_attachment_ref_default_app (attachment);
1747 if (default_app) {
1748 GList *link;
1749
1750 for (link = app_info_list; link; link = g_list_next (link)) {
1751 GAppInfo *app_info = link->data;
1752
1753 if (g_app_info_equal (default_app, app_info)) {
1754 if (link != app_info_list) {
1755 app_info_list = g_list_delete_link (app_info_list, link);
1756 g_object_unref (app_info);
1757
1758 app_info_list = g_list_prepend (app_info_list, default_app);
1759 default_app = NULL;
1760 }
1761 break;
1762 }
1763 }
1764
1765 g_clear_object (&default_app);
1766 }
1767
1768 g_clear_object (&file_info);
1769
1770 return app_info_list;
1771 }
1772
1773 void
e_attachment_update_store_columns(EAttachment * attachment)1774 e_attachment_update_store_columns (EAttachment *attachment)
1775 {
1776 g_return_if_fail (E_IS_ATTACHMENT (attachment));
1777
1778 attachment_update_file_info_columns (attachment);
1779 attachment_update_icon_column (attachment);
1780 attachment_update_progress_columns (attachment);
1781 }
1782
1783 /************************* e_attachment_load_async() *************************/
1784
1785 typedef struct _LoadContext LoadContext;
1786
1787 struct _LoadContext {
1788 EAttachment *attachment;
1789 CamelMimePart *mime_part;
1790 GSimpleAsyncResult *simple;
1791
1792 GInputStream *input_stream;
1793 GOutputStream *output_stream;
1794 GFileInfo *file_info;
1795 goffset total_num_bytes;
1796 gssize bytes_read;
1797 gchar buffer[4096];
1798 };
1799
1800 /* Forward Declaration */
1801 static void
1802 attachment_load_stream_read_cb (GInputStream *input_stream,
1803 GAsyncResult *result,
1804 LoadContext *load_context);
1805 static void
1806 attachment_load_query_info_cb (GFile *file,
1807 GAsyncResult *result,
1808 LoadContext *load_context);
1809
1810 static LoadContext *
attachment_load_context_new(EAttachment * attachment,GAsyncReadyCallback callback,gpointer user_data)1811 attachment_load_context_new (EAttachment *attachment,
1812 GAsyncReadyCallback callback,
1813 gpointer user_data)
1814 {
1815 LoadContext *load_context;
1816 GSimpleAsyncResult *simple;
1817
1818 simple = g_simple_async_result_new (
1819 G_OBJECT (attachment), callback,
1820 user_data, e_attachment_load_async);
1821
1822 load_context = g_slice_new0 (LoadContext);
1823 load_context->attachment = g_object_ref (attachment);
1824 load_context->simple = simple;
1825
1826 attachment_set_loading (load_context->attachment, TRUE);
1827
1828 return load_context;
1829 }
1830
1831 static void
attachment_load_context_free(LoadContext * load_context)1832 attachment_load_context_free (LoadContext *load_context)
1833 {
1834 g_object_unref (load_context->attachment);
1835
1836 if (load_context->mime_part != NULL)
1837 g_object_unref (load_context->mime_part);
1838
1839 if (load_context->simple)
1840 g_object_unref (load_context->simple);
1841
1842 if (load_context->input_stream != NULL)
1843 g_object_unref (load_context->input_stream);
1844
1845 if (load_context->output_stream != NULL)
1846 g_object_unref (load_context->output_stream);
1847
1848 if (load_context->file_info != NULL)
1849 g_object_unref (load_context->file_info);
1850
1851 g_slice_free (LoadContext, load_context);
1852 }
1853
1854 static gboolean
attachment_load_check_for_error(LoadContext * load_context,GError * error)1855 attachment_load_check_for_error (LoadContext *load_context,
1856 GError *error)
1857 {
1858 GSimpleAsyncResult *simple;
1859
1860 if (error == NULL)
1861 return FALSE;
1862
1863 simple = load_context->simple;
1864 g_simple_async_result_take_error (simple, error);
1865 g_simple_async_result_complete (simple);
1866
1867 attachment_load_context_free (load_context);
1868
1869 return TRUE;
1870 }
1871
1872 static void
attachment_load_finish(LoadContext * load_context)1873 attachment_load_finish (LoadContext *load_context)
1874 {
1875 GFileInfo *file_info;
1876 EAttachment *attachment;
1877 GMemoryOutputStream *output_stream;
1878 GSimpleAsyncResult *simple;
1879 CamelDataWrapper *wrapper;
1880 CamelMimePart *mime_part;
1881 CamelStream *stream;
1882 const gchar *attribute;
1883 const gchar *content_type;
1884 const gchar *display_name;
1885 const gchar *description;
1886 const gchar *disposition;
1887 gchar *mime_type;
1888 gpointer data;
1889 gsize size;
1890
1891 simple = load_context->simple;
1892
1893 file_info = load_context->file_info;
1894 attachment = load_context->attachment;
1895 output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
1896
1897 if (e_attachment_is_rfc822 (attachment))
1898 wrapper = (CamelDataWrapper *) camel_mime_message_new ();
1899 else
1900 wrapper = camel_data_wrapper_new ();
1901
1902 content_type = g_file_info_get_content_type (file_info);
1903 mime_type = g_content_type_get_mime_type (content_type);
1904
1905 data = g_memory_output_stream_get_data (output_stream);
1906 size = g_memory_output_stream_get_data_size (output_stream);
1907
1908 stream = camel_stream_mem_new_with_buffer (data, size);
1909 camel_data_wrapper_construct_from_stream_sync (
1910 wrapper, stream, NULL, NULL);
1911 camel_data_wrapper_set_mime_type (wrapper, mime_type);
1912 camel_stream_close (stream, NULL, NULL);
1913 g_object_unref (stream);
1914
1915 mime_part = camel_mime_part_new ();
1916 camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper);
1917
1918 g_object_unref (wrapper);
1919 g_free (mime_type);
1920
1921 display_name = g_file_info_get_display_name (file_info);
1922 if (display_name != NULL)
1923 camel_mime_part_set_filename (mime_part, display_name);
1924
1925 attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
1926 description = g_file_info_get_attribute_string (file_info, attribute);
1927 if (description != NULL)
1928 camel_mime_part_set_description (mime_part, description);
1929
1930 disposition = e_attachment_get_disposition (attachment);
1931 if (disposition != NULL)
1932 camel_mime_part_set_disposition (mime_part, disposition);
1933
1934 /* Correctly report the size of zero length special files. */
1935 if (g_file_info_get_size (file_info) == 0)
1936 g_file_info_set_size (file_info, size);
1937
1938 load_context->mime_part = mime_part;
1939
1940 g_simple_async_result_set_op_res_gpointer (
1941 simple, load_context,
1942 (GDestroyNotify) attachment_load_context_free);
1943
1944 g_simple_async_result_complete (simple);
1945
1946 /* Make sure it's freed on operation end. */
1947 g_clear_object (&load_context->simple);
1948 }
1949
1950 static void
attachment_load_write_cb(GOutputStream * output_stream,GAsyncResult * result,LoadContext * load_context)1951 attachment_load_write_cb (GOutputStream *output_stream,
1952 GAsyncResult *result,
1953 LoadContext *load_context)
1954 {
1955 EAttachment *attachment;
1956 GCancellable *cancellable;
1957 GInputStream *input_stream;
1958 gssize bytes_written;
1959 GError *error = NULL;
1960
1961 bytes_written = g_output_stream_write_finish (
1962 output_stream, result, &error);
1963
1964 if (attachment_load_check_for_error (load_context, error))
1965 return;
1966
1967 attachment = load_context->attachment;
1968 cancellable = attachment->priv->cancellable;
1969 input_stream = load_context->input_stream;
1970
1971 attachment_progress_cb (
1972 g_seekable_tell (G_SEEKABLE (output_stream)),
1973 load_context->total_num_bytes, attachment);
1974
1975 if (bytes_written < load_context->bytes_read) {
1976 memmove (
1977 load_context->buffer,
1978 load_context->buffer + bytes_written,
1979 load_context->bytes_read - bytes_written);
1980 load_context->bytes_read -= bytes_written;
1981
1982 g_output_stream_write_async (
1983 output_stream,
1984 load_context->buffer,
1985 load_context->bytes_read,
1986 G_PRIORITY_DEFAULT, cancellable,
1987 (GAsyncReadyCallback) attachment_load_write_cb,
1988 load_context);
1989 } else
1990 g_input_stream_read_async (
1991 input_stream,
1992 load_context->buffer,
1993 sizeof (load_context->buffer),
1994 G_PRIORITY_DEFAULT, cancellable,
1995 (GAsyncReadyCallback) attachment_load_stream_read_cb,
1996 load_context);
1997 }
1998
1999 static void
attachment_load_stream_read_cb(GInputStream * input_stream,GAsyncResult * result,LoadContext * load_context)2000 attachment_load_stream_read_cb (GInputStream *input_stream,
2001 GAsyncResult *result,
2002 LoadContext *load_context)
2003 {
2004 EAttachment *attachment;
2005 GCancellable *cancellable;
2006 GOutputStream *output_stream;
2007 gssize bytes_read;
2008 GError *error = NULL;
2009
2010 bytes_read = g_input_stream_read_finish (
2011 input_stream, result, &error);
2012
2013 if (attachment_load_check_for_error (load_context, error))
2014 return;
2015
2016 if (bytes_read == 0) {
2017 attachment_load_finish (load_context);
2018 return;
2019 }
2020
2021 attachment = load_context->attachment;
2022 cancellable = attachment->priv->cancellable;
2023 output_stream = load_context->output_stream;
2024 load_context->bytes_read = bytes_read;
2025
2026 g_output_stream_write_async (
2027 output_stream,
2028 load_context->buffer,
2029 load_context->bytes_read,
2030 G_PRIORITY_DEFAULT, cancellable,
2031 (GAsyncReadyCallback) attachment_load_write_cb,
2032 load_context);
2033 }
2034
2035 static void
attachment_load_file_read_cb(GFile * file,GAsyncResult * result,LoadContext * load_context)2036 attachment_load_file_read_cb (GFile *file,
2037 GAsyncResult *result,
2038 LoadContext *load_context)
2039 {
2040 EAttachment *attachment;
2041 GCancellable *cancellable;
2042 GFileInputStream *input_stream;
2043 GOutputStream *output_stream;
2044 GError *error = NULL;
2045
2046 /* Input stream might be NULL, so don't use cast macro. */
2047 input_stream = g_file_read_finish (file, result, &error);
2048 load_context->input_stream = (GInputStream *) input_stream;
2049
2050 if (attachment_load_check_for_error (load_context, error))
2051 return;
2052
2053 /* Load the contents into a GMemoryOutputStream. */
2054 output_stream = g_memory_output_stream_new (
2055 NULL, 0, g_realloc, g_free);
2056
2057 attachment = load_context->attachment;
2058 cancellable = attachment->priv->cancellable;
2059 load_context->output_stream = output_stream;
2060
2061 g_input_stream_read_async (
2062 load_context->input_stream,
2063 load_context->buffer,
2064 sizeof (load_context->buffer),
2065 G_PRIORITY_DEFAULT, cancellable,
2066 (GAsyncReadyCallback) attachment_load_stream_read_cb,
2067 load_context);
2068 }
2069
2070 #ifdef HAVE_AUTOAR
2071 static void
attachment_load_created_decide_dest_cb(AutoarCompressor * compressor,GFile * destination,EAttachment * attachment)2072 attachment_load_created_decide_dest_cb (AutoarCompressor *compressor,
2073 GFile *destination,
2074 EAttachment *attachment)
2075 {
2076 e_attachment_set_file (attachment, destination);
2077 }
2078
2079 static void
attachment_load_created_cancelled_cb(AutoarCompressor * compressor,LoadContext * load_context)2080 attachment_load_created_cancelled_cb (AutoarCompressor *compressor,
2081 LoadContext *load_context)
2082 {
2083 attachment_load_check_for_error (load_context,
2084 g_error_new_literal (
2085 G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")));
2086 g_object_unref (compressor);
2087 }
2088
2089 static void
attachment_load_created_completed_cb(AutoarCompressor * compressor,LoadContext * load_context)2090 attachment_load_created_completed_cb (AutoarCompressor *compressor,
2091 LoadContext *load_context)
2092 {
2093 EAttachment *attachment;
2094 GFile *file;
2095
2096 g_object_unref (compressor);
2097
2098 /* We have set the file to the created temporary archive, so we can
2099 * query info again and use the regular procedure to load the
2100 * attachment. */
2101 attachment = load_context->attachment;
2102 file = e_attachment_ref_file (attachment);
2103 g_file_query_info_async (
2104 file, ATTACHMENT_QUERY,
2105 G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
2106 attachment->priv->cancellable, (GAsyncReadyCallback)
2107 attachment_load_query_info_cb, load_context);
2108
2109 g_clear_object (&file);
2110 }
2111
2112 static void
attachment_load_created_error_cb(AutoarCompressor * compressor,GError * error,LoadContext * load_context)2113 attachment_load_created_error_cb (AutoarCompressor *compressor,
2114 GError *error,
2115 LoadContext *load_context)
2116 {
2117 attachment_load_check_for_error (load_context, g_error_copy (error));
2118 g_object_unref (compressor);
2119 }
2120 #endif
2121
2122 static void
attachment_load_query_info_cb(GFile * file,GAsyncResult * result,LoadContext * load_context)2123 attachment_load_query_info_cb (GFile *file,
2124 GAsyncResult *result,
2125 LoadContext *load_context)
2126 {
2127 EAttachment *attachment;
2128 GCancellable *cancellable;
2129 GFileInfo *file_info;
2130 GError *error = NULL;
2131
2132 attachment = load_context->attachment;
2133 cancellable = attachment->priv->cancellable;
2134
2135 file_info = g_file_query_info_finish (file, result, &error);
2136 if (attachment_load_check_for_error (load_context, error))
2137 return;
2138
2139 e_attachment_set_file_info (attachment, file_info);
2140 load_context->file_info = file_info;
2141
2142 load_context->total_num_bytes = g_file_info_get_size (file_info);
2143
2144 #ifdef HAVE_AUTOAR
2145 if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) {
2146 AutoarCompressor *compressor;
2147 GFile *temporary;
2148 GSettings *settings;
2149 GList *files = NULL;
2150 char *format_string;
2151 char *filter_string;
2152 gint format;
2153 gint filter;
2154
2155 temporary = attachment_get_temporary (&error);
2156 if (attachment_load_check_for_error (load_context, error))
2157 return;
2158
2159 settings = e_util_ref_settings ("org.gnome.evolution.shell");
2160
2161 format_string = g_settings_get_string (settings, "autoar-format");
2162 filter_string = g_settings_get_string (settings, "autoar-filter");
2163
2164 if (!e_enum_from_string (AUTOAR_TYPE_FORMAT, format_string, &format)) {
2165 format = AUTOAR_FORMAT_ZIP;
2166 }
2167 if (!e_enum_from_string (AUTOAR_TYPE_FILTER, filter_string, &filter)) {
2168 filter = AUTOAR_FILTER_NONE;
2169 }
2170
2171 files = g_list_prepend (files, file);
2172
2173 compressor = autoar_compressor_new (
2174 files, temporary, format, filter, FALSE);
2175 g_signal_connect (compressor, "decide-dest",
2176 G_CALLBACK (attachment_load_created_decide_dest_cb), attachment);
2177 g_signal_connect (compressor, "cancelled",
2178 G_CALLBACK (attachment_load_created_cancelled_cb), load_context);
2179 g_signal_connect (compressor, "completed",
2180 G_CALLBACK (attachment_load_created_completed_cb), load_context);
2181 g_signal_connect (compressor, "error",
2182 G_CALLBACK (attachment_load_created_error_cb), load_context);
2183 autoar_compressor_start_async (compressor, cancellable);
2184
2185 g_object_unref (settings);
2186 g_free (format_string);
2187 g_free (filter_string);
2188 g_list_free (files);
2189 g_object_unref (temporary);
2190 } else {
2191 #endif
2192 g_file_read_async (
2193 file, G_PRIORITY_DEFAULT,
2194 cancellable, (GAsyncReadyCallback)
2195 attachment_load_file_read_cb, load_context);
2196 #ifdef HAVE_AUTOAR
2197 }
2198 #endif
2199 }
2200
2201 #define ATTACHMENT_LOAD_CONTEXT "attachment-load-context-data"
2202
2203 static void
attachment_load_from_mime_part_thread(GSimpleAsyncResult * simple,GObject * object,GCancellable * cancellable)2204 attachment_load_from_mime_part_thread (GSimpleAsyncResult *simple,
2205 GObject *object,
2206 GCancellable *cancellable)
2207 {
2208 LoadContext *load_context;
2209 GFileInfo *file_info;
2210 EAttachment *attachment;
2211 CamelContentType *content_type;
2212 CamelMimePart *mime_part;
2213 const gchar *attribute;
2214 const gchar *string;
2215 gchar *allocated, *decoded_string = NULL;
2216 gsize bytes_written;
2217 CamelDataWrapper *dw;
2218
2219 load_context = g_object_get_data (
2220 G_OBJECT (simple), ATTACHMENT_LOAD_CONTEXT);
2221 g_return_if_fail (load_context != NULL);
2222 g_object_set_data (G_OBJECT (simple), ATTACHMENT_LOAD_CONTEXT, NULL);
2223
2224 attachment = load_context->attachment;
2225 mime_part = e_attachment_ref_mime_part (attachment);
2226
2227 file_info = g_file_info_new ();
2228 load_context->file_info = file_info;
2229
2230 content_type = camel_mime_part_get_content_type (mime_part);
2231 allocated = camel_content_type_simple (content_type);
2232 if (allocated != NULL) {
2233 GIcon *icon;
2234 gchar *cp;
2235
2236 /* GIO expects lowercase MIME types. */
2237 for (cp = allocated; *cp != '\0'; cp++)
2238 *cp = g_ascii_tolower (*cp);
2239
2240 /* Swap the MIME type for a content type. */
2241 cp = g_content_type_from_mime_type (allocated);
2242 g_free (allocated);
2243 allocated = cp;
2244
2245 /* Use the MIME part's filename if we have to. */
2246 if (g_content_type_is_unknown (allocated)) {
2247 string = camel_mime_part_get_filename (mime_part);
2248 if (string != NULL) {
2249 g_free (allocated);
2250 allocated = g_content_type_guess (
2251 string, NULL, 0, NULL);
2252 }
2253 }
2254
2255 g_file_info_set_content_type (file_info, allocated);
2256
2257 icon = g_content_type_get_icon (allocated);
2258 if (icon != NULL) {
2259 g_file_info_set_icon (file_info, icon);
2260 g_object_unref (icon);
2261 }
2262 }
2263 g_free (allocated);
2264 allocated = NULL;
2265
2266 /* Strip any path components from the filename. */
2267 string = camel_mime_part_get_filename (mime_part);
2268 if (string == NULL) {
2269 /* Translators: Default attachment filename. */
2270 string = _("attachment.dat");
2271
2272 if (camel_content_type_is (content_type, "message", "rfc822")) {
2273 CamelMimeMessage *msg = NULL;
2274 const gchar *subject = NULL;
2275
2276 if (CAMEL_IS_MIME_MESSAGE (mime_part)) {
2277 msg = CAMEL_MIME_MESSAGE (mime_part);
2278 } else {
2279 CamelDataWrapper *content;
2280
2281 content = camel_medium_get_content (
2282 CAMEL_MEDIUM (mime_part));
2283 if (CAMEL_IS_MIME_MESSAGE (content))
2284 msg = CAMEL_MIME_MESSAGE (content);
2285 }
2286
2287 if (msg != NULL)
2288 subject = camel_mime_message_get_subject (msg);
2289
2290 if (subject != NULL && *subject != '\0')
2291 string = subject;
2292 }
2293 } else {
2294 decoded_string = camel_header_decode_string (string, "UTF-8");
2295 if (decoded_string != NULL &&
2296 *decoded_string != '\0' &&
2297 !g_str_equal (decoded_string, string)) {
2298 string = decoded_string;
2299 } else {
2300 g_free (decoded_string);
2301 decoded_string = NULL;
2302 }
2303
2304 if (string && *string) {
2305 allocated = g_path_get_basename (string);
2306 string = allocated;
2307 }
2308 }
2309 g_file_info_set_display_name (file_info, string);
2310 g_free (decoded_string);
2311 g_free (allocated);
2312
2313 attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
2314 string = camel_mime_part_get_description (mime_part);
2315 if (string != NULL)
2316 g_file_info_set_attribute_string (
2317 file_info, attribute, string);
2318
2319 dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
2320 /* this actually downloads the part and makes it available later */
2321 bytes_written = camel_data_wrapper_calculate_decoded_size_sync (dw, attachment->priv->cancellable, NULL);
2322 g_file_info_set_size (file_info, bytes_written);
2323
2324 load_context->mime_part = g_object_ref (mime_part);
2325
2326 /* Make sure it's freed on operation end. */
2327 g_clear_object (&load_context->simple);
2328
2329 g_simple_async_result_set_op_res_gpointer (
2330 simple, load_context,
2331 (GDestroyNotify) attachment_load_context_free);
2332
2333 g_clear_object (&mime_part);
2334 }
2335
2336 void
e_attachment_load_async(EAttachment * attachment,GAsyncReadyCallback callback,gpointer user_data)2337 e_attachment_load_async (EAttachment *attachment,
2338 GAsyncReadyCallback callback,
2339 gpointer user_data)
2340 {
2341 LoadContext *load_context;
2342 GCancellable *cancellable;
2343 CamelMimePart *mime_part;
2344 GFile *file;
2345
2346 g_return_if_fail (E_IS_ATTACHMENT (attachment));
2347
2348 if (e_attachment_get_loading (attachment)) {
2349 g_simple_async_report_error_in_idle (
2350 G_OBJECT (attachment), callback, user_data,
2351 G_IO_ERROR, G_IO_ERROR_BUSY,
2352 _("A load operation is already in progress"));
2353 return;
2354 }
2355
2356 if (e_attachment_get_saving (attachment)) {
2357 g_simple_async_report_error_in_idle (
2358 G_OBJECT (attachment), callback, user_data,
2359 G_IO_ERROR, G_IO_ERROR_BUSY,
2360 _("A save operation is already in progress"));
2361 return;
2362 }
2363
2364 file = e_attachment_ref_file (attachment);
2365 mime_part = e_attachment_ref_mime_part (attachment);
2366 g_return_if_fail (file != NULL || mime_part != NULL);
2367
2368 load_context = attachment_load_context_new (
2369 attachment, callback, user_data);
2370
2371 cancellable = attachment->priv->cancellable;
2372 g_cancellable_reset (cancellable);
2373
2374 if (file != NULL) {
2375 g_file_query_info_async (
2376 file, ATTACHMENT_QUERY,
2377 G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
2378 cancellable, (GAsyncReadyCallback)
2379 attachment_load_query_info_cb, load_context);
2380
2381 } else if (mime_part != NULL) {
2382 g_object_set_data (
2383 G_OBJECT (load_context->simple),
2384 ATTACHMENT_LOAD_CONTEXT, load_context);
2385
2386 g_simple_async_result_run_in_thread (
2387 load_context->simple,
2388 attachment_load_from_mime_part_thread,
2389 G_PRIORITY_DEFAULT,
2390 cancellable);
2391 }
2392
2393 g_clear_object (&file);
2394 g_clear_object (&mime_part);
2395 }
2396
2397 gboolean
e_attachment_load_finish(EAttachment * attachment,GAsyncResult * result,GError ** error)2398 e_attachment_load_finish (EAttachment *attachment,
2399 GAsyncResult *result,
2400 GError **error)
2401 {
2402 GSimpleAsyncResult *simple;
2403 const LoadContext *load_context;
2404
2405 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
2406 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
2407
2408 simple = G_SIMPLE_ASYNC_RESULT (result);
2409 if (g_simple_async_result_propagate_error (simple, error)) {
2410 attachment_set_loading (attachment, FALSE);
2411 return FALSE;
2412 }
2413
2414 load_context = g_simple_async_result_get_op_res_gpointer (simple);
2415
2416 if (load_context != NULL && load_context->mime_part != NULL) {
2417 const gchar *string;
2418
2419 string = camel_mime_part_get_disposition (
2420 load_context->mime_part);
2421 e_attachment_set_disposition (attachment, string);
2422
2423 e_attachment_set_file_info (
2424 attachment, load_context->file_info);
2425 e_attachment_set_mime_part (
2426 attachment, load_context->mime_part);
2427 }
2428
2429 attachment_set_loading (attachment, FALSE);
2430
2431 return (load_context != NULL);
2432 }
2433
2434 void
e_attachment_load_handle_error(EAttachment * attachment,GAsyncResult * result,GtkWindow * parent)2435 e_attachment_load_handle_error (EAttachment *attachment,
2436 GAsyncResult *result,
2437 GtkWindow *parent)
2438 {
2439 GtkWidget *dialog;
2440 GFileInfo *file_info;
2441 const gchar *display_name;
2442 const gchar *primary_text;
2443 GError *error = NULL;
2444
2445 g_return_if_fail (E_IS_ATTACHMENT (attachment));
2446 g_return_if_fail (G_IS_ASYNC_RESULT (result));
2447 g_return_if_fail (!parent || GTK_IS_WINDOW (parent));
2448
2449 if (e_attachment_load_finish (attachment, result, &error))
2450 return;
2451
2452 g_signal_emit (attachment, signals[LOAD_FAILED], 0, NULL);
2453
2454 /* Ignore cancellations. */
2455 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2456 g_error_free (error);
2457 return;
2458 }
2459
2460 file_info = e_attachment_ref_file_info (attachment);
2461
2462 if (file_info != NULL)
2463 display_name = g_file_info_get_display_name (file_info);
2464 else
2465 display_name = NULL;
2466
2467 if (display_name != NULL)
2468 primary_text = g_strdup_printf (
2469 _("Could not load “%s”"), display_name);
2470 else
2471 primary_text = g_strdup_printf (
2472 _("Could not load the attachment"));
2473
2474 g_clear_object (&file_info);
2475
2476 dialog = gtk_message_dialog_new_with_markup (
2477 parent, GTK_DIALOG_DESTROY_WITH_PARENT,
2478 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
2479 "<big><b>%s</b></big>", primary_text);
2480
2481 gtk_message_dialog_format_secondary_text (
2482 GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
2483
2484 gtk_dialog_run (GTK_DIALOG (dialog));
2485
2486 gtk_widget_destroy (dialog);
2487 g_error_free (error);
2488 }
2489
2490 gboolean
e_attachment_load(EAttachment * attachment,GError ** error)2491 e_attachment_load (EAttachment *attachment,
2492 GError **error)
2493 {
2494 EAsyncClosure *closure;
2495 GAsyncResult *result;
2496 gboolean success;
2497
2498 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
2499
2500 closure = e_async_closure_new ();
2501
2502 e_attachment_load_async (
2503 attachment, e_async_closure_callback, closure);
2504
2505 result = e_async_closure_wait (closure);
2506
2507 success = e_attachment_load_finish (attachment, result, error);
2508
2509 e_async_closure_free (closure);
2510
2511 return success;
2512 }
2513
2514 /************************* e_attachment_open_async() *************************/
2515
2516 typedef struct _OpenContext OpenContext;
2517
2518 struct _OpenContext {
2519 EAttachment *attachment;
2520 GSimpleAsyncResult *simple;
2521
2522 GAppInfo *app_info;
2523 };
2524
2525 static OpenContext *
attachment_open_context_new(EAttachment * attachment,GAsyncReadyCallback callback,gpointer user_data)2526 attachment_open_context_new (EAttachment *attachment,
2527 GAsyncReadyCallback callback,
2528 gpointer user_data)
2529 {
2530 OpenContext *open_context;
2531 GSimpleAsyncResult *simple;
2532
2533 simple = g_simple_async_result_new (
2534 G_OBJECT (attachment), callback,
2535 user_data, e_attachment_open_async);
2536
2537 open_context = g_slice_new0 (OpenContext);
2538 open_context->attachment = g_object_ref (attachment);
2539 open_context->simple = simple;
2540
2541 return open_context;
2542 }
2543
2544 static void
attachment_open_context_free(OpenContext * open_context)2545 attachment_open_context_free (OpenContext *open_context)
2546 {
2547 g_object_unref (open_context->attachment);
2548 g_object_unref (open_context->simple);
2549
2550 if (open_context->app_info != NULL)
2551 g_object_unref (open_context->app_info);
2552
2553 g_slice_free (OpenContext, open_context);
2554 }
2555
2556 static gboolean
attachment_open_check_for_error(OpenContext * open_context,GError * error)2557 attachment_open_check_for_error (OpenContext *open_context,
2558 GError *error)
2559 {
2560 GSimpleAsyncResult *simple;
2561
2562 if (error == NULL)
2563 return FALSE;
2564
2565 simple = open_context->simple;
2566 g_simple_async_result_take_error (simple, error);
2567 g_simple_async_result_complete (simple);
2568
2569 attachment_open_context_free (open_context);
2570
2571 return TRUE;
2572 }
2573
2574 static void
attachment_open_file(GFile * file,OpenContext * open_context)2575 attachment_open_file (GFile *file,
2576 OpenContext *open_context)
2577 {
2578 GdkAppLaunchContext *context;
2579 GSimpleAsyncResult *simple;
2580 GdkDisplay *display;
2581 gboolean success;
2582 GError *error = NULL;
2583
2584 simple = open_context->simple;
2585
2586 display = gdk_display_get_default ();
2587 context = gdk_display_get_app_launch_context (display);
2588
2589 if (open_context->app_info != NULL) {
2590 GList *file_list;
2591
2592 file_list = g_list_prepend (NULL, file);
2593 success = g_app_info_launch (
2594 open_context->app_info, file_list,
2595 G_APP_LAUNCH_CONTEXT (context), &error);
2596 g_list_free (file_list);
2597 } else {
2598 gchar *uri;
2599
2600 uri = g_file_get_uri (file);
2601 success = g_app_info_launch_default_for_uri (
2602 uri, G_APP_LAUNCH_CONTEXT (context), &error);
2603 g_free (uri);
2604 }
2605
2606 g_object_unref (context);
2607
2608 g_simple_async_result_set_op_res_gboolean (simple, success);
2609
2610 if (error != NULL)
2611 g_simple_async_result_take_error (simple, error);
2612
2613 g_simple_async_result_complete (simple);
2614 attachment_open_context_free (open_context);
2615 }
2616
2617 static void
attachment_open_save_finished_cb(EAttachment * attachment,GAsyncResult * result,OpenContext * open_context)2618 attachment_open_save_finished_cb (EAttachment *attachment,
2619 GAsyncResult *result,
2620 OpenContext *open_context)
2621 {
2622 GFile *file;
2623 gchar *path;
2624 GError *error = NULL;
2625
2626 file = e_attachment_save_finish (attachment, result, &error);
2627
2628 if (attachment_open_check_for_error (open_context, error))
2629 return;
2630
2631 /* Make the temporary file read-only.
2632 *
2633 * This step is non-critical, so if an error occurs just
2634 * emit a warning and move on.
2635 *
2636 * XXX I haven't figured out how to do this through GIO.
2637 * Attempting to set the "access::can-write" attribute via
2638 * g_file_set_attribute() returned G_IO_ERROR_NOT_SUPPORTED
2639 * and the only other possibility I see is "unix::mode",
2640 * which is obviously not portable.
2641 */
2642 path = g_file_get_path (file);
2643 #ifndef G_OS_WIN32
2644 if (g_chmod (path, S_IRUSR | S_IRGRP | S_IROTH) < 0)
2645 g_warning ("%s", g_strerror (errno));
2646 #endif
2647 g_free (path);
2648
2649 attachment_open_file (file, open_context);
2650 g_object_unref (file);
2651 }
2652
2653 static void
attachment_open_save_temporary(OpenContext * open_context)2654 attachment_open_save_temporary (OpenContext *open_context)
2655 {
2656 GFile *temp_directory;
2657 GError *error = NULL;
2658
2659 temp_directory = attachment_get_temporary (&error);
2660
2661 /* We already know if there's an error, but this does the cleanup. */
2662 if (attachment_open_check_for_error (open_context, error))
2663 return;
2664
2665 e_attachment_save_async (
2666 open_context->attachment,
2667 temp_directory, (GAsyncReadyCallback)
2668 attachment_open_save_finished_cb, open_context);
2669
2670 g_object_unref (temp_directory);
2671 }
2672
2673 void
e_attachment_open_async(EAttachment * attachment,GAppInfo * app_info,GAsyncReadyCallback callback,gpointer user_data)2674 e_attachment_open_async (EAttachment *attachment,
2675 GAppInfo *app_info,
2676 GAsyncReadyCallback callback,
2677 gpointer user_data)
2678 {
2679 OpenContext *open_context;
2680 CamelMimePart *mime_part;
2681 GFile *file;
2682
2683 g_return_if_fail (E_IS_ATTACHMENT (attachment));
2684
2685 file = e_attachment_ref_file (attachment);
2686 mime_part = e_attachment_ref_mime_part (attachment);
2687 g_return_if_fail (file != NULL || mime_part != NULL);
2688
2689 open_context = attachment_open_context_new (
2690 attachment, callback, user_data);
2691
2692 if (G_IS_APP_INFO (app_info))
2693 open_context->app_info = g_object_ref (app_info);
2694
2695 /* If the attachment already references a GFile, we can launch
2696 * the application directly. Otherwise we have to save the MIME
2697 * part to a temporary file and launch the application from that. */
2698 if (file != NULL) {
2699 attachment_open_file (file, open_context);
2700
2701 } else if (mime_part != NULL)
2702 attachment_open_save_temporary (open_context);
2703
2704 g_clear_object (&file);
2705 g_clear_object (&mime_part);
2706 }
2707
2708 gboolean
e_attachment_open_finish(EAttachment * attachment,GAsyncResult * result,GError ** error)2709 e_attachment_open_finish (EAttachment *attachment,
2710 GAsyncResult *result,
2711 GError **error)
2712 {
2713 GSimpleAsyncResult *simple;
2714 gboolean success;
2715
2716 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
2717 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
2718
2719 simple = G_SIMPLE_ASYNC_RESULT (result);
2720 success = !g_simple_async_result_propagate_error (simple, error) &&
2721 g_simple_async_result_get_op_res_gboolean (simple);
2722
2723 return success;
2724 }
2725
2726 void
e_attachment_open_handle_error(EAttachment * attachment,GAsyncResult * result,GtkWindow * parent)2727 e_attachment_open_handle_error (EAttachment *attachment,
2728 GAsyncResult *result,
2729 GtkWindow *parent)
2730 {
2731 GtkWidget *dialog;
2732 GFileInfo *file_info;
2733 const gchar *display_name;
2734 const gchar *primary_text;
2735 GError *error = NULL;
2736
2737 g_return_if_fail (E_IS_ATTACHMENT (attachment));
2738 g_return_if_fail (G_IS_ASYNC_RESULT (result));
2739 g_return_if_fail (GTK_IS_WINDOW (parent));
2740
2741 if (e_attachment_open_finish (attachment, result, &error))
2742 return;
2743
2744 /* Ignore cancellations. */
2745 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
2746 return;
2747
2748 file_info = e_attachment_ref_file_info (attachment);
2749
2750 if (file_info != NULL)
2751 display_name = g_file_info_get_display_name (file_info);
2752 else
2753 display_name = NULL;
2754
2755 if (display_name != NULL)
2756 primary_text = g_strdup_printf (
2757 _("Could not open “%s”"), display_name);
2758 else
2759 primary_text = g_strdup_printf (
2760 _("Could not open the attachment"));
2761
2762 g_clear_object (&file_info);
2763
2764 dialog = gtk_message_dialog_new_with_markup (
2765 parent, GTK_DIALOG_DESTROY_WITH_PARENT,
2766 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
2767 "<big><b>%s</b></big>", primary_text);
2768
2769 gtk_message_dialog_format_secondary_text (
2770 GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
2771
2772 gtk_dialog_run (GTK_DIALOG (dialog));
2773
2774 gtk_widget_destroy (dialog);
2775 g_error_free (error);
2776 }
2777
2778 gboolean
e_attachment_open(EAttachment * attachment,GAppInfo * app_info,GError ** error)2779 e_attachment_open (EAttachment *attachment,
2780 GAppInfo *app_info,
2781 GError **error)
2782 {
2783 EAsyncClosure *closure;
2784 GAsyncResult *result;
2785 gboolean success;
2786
2787 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
2788
2789 closure = e_async_closure_new ();
2790
2791 e_attachment_open_async (
2792 attachment, app_info,
2793 e_async_closure_callback, closure);
2794
2795 result = e_async_closure_wait (closure);
2796
2797 success = e_attachment_open_finish (attachment, result, error);
2798
2799 e_async_closure_free (closure);
2800
2801 return success;
2802 }
2803
2804 /************************* e_attachment_save_async() *************************/
2805
2806 typedef struct _SaveContext SaveContext;
2807
2808 struct _SaveContext {
2809 EAttachment *attachment;
2810 GSimpleAsyncResult *simple;
2811
2812 GFile *directory;
2813 GFile *destination;
2814 GInputStream *input_stream;
2815 GOutputStream *output_stream;
2816 goffset total_num_bytes;
2817 gssize bytes_read;
2818 gchar buffer[4096];
2819 gint count;
2820
2821 GByteArray *input_buffer;
2822 gchar *suggested_destname;
2823 GFile *temporary_file;
2824
2825 guint total_tasks : 2;
2826 guint completed_tasks : 2;
2827 guint prepared_tasks : 2;
2828
2829 GMutex completed_tasks_mutex;
2830 GMutex prepared_tasks_mutex;
2831 };
2832
2833 /* Forward Declaration */
2834 static void
2835 attachment_save_read_cb (GInputStream *input_stream,
2836 GAsyncResult *result,
2837 SaveContext *save_context);
2838
2839 static SaveContext *
attachment_save_context_new(EAttachment * attachment,GAsyncReadyCallback callback,gpointer user_data)2840 attachment_save_context_new (EAttachment *attachment,
2841 GAsyncReadyCallback callback,
2842 gpointer user_data)
2843 {
2844 SaveContext *save_context;
2845 GSimpleAsyncResult *simple;
2846
2847 simple = g_simple_async_result_new (
2848 G_OBJECT (attachment), callback,
2849 user_data, e_attachment_save_async);
2850
2851 save_context = g_slice_new0 (SaveContext);
2852 save_context->attachment = g_object_ref (attachment);
2853 save_context->simple = simple;
2854
2855 g_mutex_init (&(save_context->completed_tasks_mutex));
2856 g_mutex_init (&(save_context->prepared_tasks_mutex));
2857
2858 attachment_set_saving (save_context->attachment, TRUE);
2859
2860 return save_context;
2861 }
2862
2863 static void
attachment_save_context_free(SaveContext * save_context)2864 attachment_save_context_free (SaveContext *save_context)
2865 {
2866 g_object_unref (save_context->attachment);
2867 g_object_unref (save_context->simple);
2868 g_clear_object (&save_context->directory);
2869 g_clear_object (&save_context->destination);
2870 g_clear_object (&save_context->input_stream);
2871 g_clear_object (&save_context->output_stream);
2872 g_clear_pointer (&save_context->input_buffer, g_byte_array_unref);
2873 g_free (save_context->suggested_destname);
2874 g_clear_object (&save_context->temporary_file);
2875 g_mutex_clear (&(save_context->completed_tasks_mutex));
2876 g_mutex_clear (&(save_context->prepared_tasks_mutex));
2877 g_slice_free (SaveContext, save_context);
2878 }
2879
2880 static gboolean
attachment_save_check_for_error(SaveContext * save_context,GError * error)2881 attachment_save_check_for_error (SaveContext *save_context,
2882 GError *error)
2883 {
2884 GSimpleAsyncResult *simple;
2885
2886 if (error == NULL)
2887 return FALSE;
2888
2889 simple = save_context->simple;
2890 g_simple_async_result_take_error (simple, error);
2891
2892 g_mutex_lock (&(save_context->completed_tasks_mutex));
2893 if (++save_context->completed_tasks >= save_context->total_tasks) {
2894 g_simple_async_result_complete (simple);
2895 g_mutex_unlock (&(save_context->completed_tasks_mutex));
2896 attachment_save_context_free (save_context);
2897 } else {
2898 g_mutex_unlock (&(save_context->completed_tasks_mutex));
2899 }
2900
2901 return TRUE;
2902 }
2903
2904 static void
attachment_save_complete(SaveContext * save_context)2905 attachment_save_complete (SaveContext *save_context) {
2906 g_mutex_lock (&(save_context->completed_tasks_mutex));
2907 if (++save_context->completed_tasks >= save_context->total_tasks) {
2908 GSimpleAsyncResult *simple;
2909 GFile *result;
2910
2911 /* Steal the destination. */
2912 result = save_context->destination;
2913 save_context->destination = NULL;
2914
2915 if (result == NULL) {
2916 result = save_context->directory;
2917 save_context->directory = NULL;
2918 }
2919
2920 simple = save_context->simple;
2921 g_simple_async_result_set_op_res_gpointer (
2922 simple, result, (GDestroyNotify) g_object_unref);
2923 g_simple_async_result_complete (simple);
2924 g_mutex_unlock (&(save_context->completed_tasks_mutex));
2925 attachment_save_context_free (save_context);
2926 } else {
2927 g_mutex_unlock (&(save_context->completed_tasks_mutex));
2928 }
2929 }
2930
2931 static gchar *
get_new_name_with_count(const gchar * initial_name,gint count)2932 get_new_name_with_count (const gchar *initial_name,
2933 gint count)
2934 {
2935 GString *string;
2936 const gchar *ext;
2937 gsize length;
2938
2939 if (count == 0) {
2940 return g_strdup (initial_name);
2941 }
2942
2943 string = g_string_sized_new (strlen (initial_name));
2944 ext = g_utf8_strchr (initial_name, -1, '.');
2945
2946 if (ext != NULL)
2947 length = ext - initial_name;
2948 else
2949 length = strlen (initial_name);
2950
2951 g_string_append_len (string, initial_name, length);
2952 g_string_append_printf (string, " (%d)", count);
2953 g_string_append (string, ext ? ext : "");
2954
2955 return g_string_free (string, FALSE);
2956 }
2957
2958 static GFile *
attachment_save_new_candidate(SaveContext * save_context)2959 attachment_save_new_candidate (SaveContext *save_context)
2960 {
2961 GFile *candidate;
2962 GFileInfo *file_info;
2963 EAttachment *attachment;
2964 const gchar *display_name = NULL;
2965 gchar *basename, *allocated;
2966
2967 attachment = save_context->attachment;
2968 file_info = e_attachment_ref_file_info (attachment);
2969
2970 if (file_info != NULL)
2971 display_name = g_file_info_get_display_name (file_info);
2972 if (display_name == NULL)
2973 /* Translators: Default attachment filename. */
2974 display_name = _("attachment.dat");
2975
2976 allocated = g_strdup (display_name);
2977 e_util_make_safe_filename (allocated);
2978
2979 basename = get_new_name_with_count (allocated, save_context->count);
2980
2981 save_context->count++;
2982
2983 candidate = g_file_get_child (save_context->directory, basename);
2984
2985 g_free (allocated);
2986 g_free (basename);
2987
2988 g_clear_object (&file_info);
2989
2990 return candidate;
2991 }
2992
2993 static void
attachment_save_write_cb(GOutputStream * output_stream,GAsyncResult * result,SaveContext * save_context)2994 attachment_save_write_cb (GOutputStream *output_stream,
2995 GAsyncResult *result,
2996 SaveContext *save_context)
2997 {
2998 EAttachment *attachment;
2999 GCancellable *cancellable;
3000 GInputStream *input_stream;
3001 gssize bytes_written;
3002 GError *error = NULL;
3003
3004 bytes_written = g_output_stream_write_finish (
3005 output_stream, result, &error);
3006
3007 if (attachment_save_check_for_error (save_context, error))
3008 return;
3009
3010 attachment = save_context->attachment;
3011 cancellable = attachment->priv->cancellable;
3012 input_stream = save_context->input_stream;
3013
3014 if (bytes_written < save_context->bytes_read) {
3015 memmove (
3016 save_context->buffer,
3017 save_context->buffer + bytes_written,
3018 save_context->bytes_read - bytes_written);
3019 save_context->bytes_read -= bytes_written;
3020
3021 g_output_stream_write_async (
3022 output_stream,
3023 save_context->buffer,
3024 save_context->bytes_read,
3025 G_PRIORITY_DEFAULT, cancellable,
3026 (GAsyncReadyCallback) attachment_save_write_cb,
3027 save_context);
3028 } else
3029 g_input_stream_read_async (
3030 input_stream,
3031 save_context->buffer,
3032 sizeof (save_context->buffer),
3033 G_PRIORITY_DEFAULT, cancellable,
3034 (GAsyncReadyCallback) attachment_save_read_cb,
3035 save_context);
3036 }
3037
3038 static void
attachment_save_read_cb(GInputStream * input_stream,GAsyncResult * result,SaveContext * save_context)3039 attachment_save_read_cb (GInputStream *input_stream,
3040 GAsyncResult *result,
3041 SaveContext *save_context)
3042 {
3043 EAttachment *attachment;
3044 GCancellable *cancellable;
3045 GOutputStream *output_stream;
3046 gssize bytes_read;
3047 GError *error = NULL;
3048
3049 bytes_read = g_input_stream_read_finish (
3050 input_stream, result, &error);
3051
3052 if (attachment_save_check_for_error (save_context, error))
3053 return;
3054
3055 if (bytes_read == 0) {
3056 attachment_save_complete (save_context);
3057 return;
3058 }
3059
3060 attachment = save_context->attachment;
3061 cancellable = attachment->priv->cancellable;
3062 output_stream = save_context->output_stream;
3063 save_context->bytes_read = bytes_read;
3064
3065 attachment_progress_cb (
3066 g_seekable_tell (G_SEEKABLE (input_stream)),
3067 save_context->total_num_bytes, attachment);
3068
3069 g_output_stream_write_async (
3070 output_stream,
3071 save_context->buffer,
3072 save_context->bytes_read,
3073 G_PRIORITY_DEFAULT, cancellable,
3074 (GAsyncReadyCallback) attachment_save_write_cb,
3075 save_context);
3076 }
3077
3078 #ifdef HAVE_AUTOAR
3079 static GFile*
attachment_save_extracted_decide_destination_cb(AutoarExtractor * extractor,GFile * destination,GList * files,SaveContext * save_context)3080 attachment_save_extracted_decide_destination_cb (AutoarExtractor *extractor,
3081 GFile *destination,
3082 GList *files,
3083 SaveContext *save_context)
3084 {
3085 gchar *basename;
3086 GFile *destination_directory;
3087 GFile *new_destination;
3088 gint count = 0;
3089
3090 basename = g_file_get_basename (destination);
3091 destination_directory = g_file_get_parent (destination);
3092
3093 new_destination = g_object_ref (destination);
3094
3095 while (g_file_query_exists (new_destination, NULL)) {
3096 gchar *new_basename;
3097
3098 new_basename = get_new_name_with_count (basename, ++count);
3099
3100 g_object_unref (new_destination);
3101
3102 new_destination = g_file_get_child (
3103 destination_directory, new_basename);
3104
3105 g_free (new_basename);
3106 }
3107
3108 g_object_unref (destination_directory);
3109 g_free (basename);
3110
3111 return new_destination;
3112 }
3113
3114 static void
attachment_save_extracted_progress_cb(AutoarExtractor * extractor,guint64 completed_size,guint completed_files,SaveContext * save_context)3115 attachment_save_extracted_progress_cb (AutoarExtractor *extractor,
3116 guint64 completed_size,
3117 guint completed_files,
3118 SaveContext *save_context)
3119 {
3120 attachment_progress_cb (
3121 autoar_extractor_get_total_size (extractor),
3122 completed_size, save_context->attachment);
3123 }
3124
3125 static void
attachment_save_extracted_cancelled_cb(AutoarExtractor * extractor,SaveContext * save_context)3126 attachment_save_extracted_cancelled_cb (AutoarExtractor *extractor,
3127 SaveContext *save_context)
3128 {
3129 if (attachment_save_check_for_error (save_context,
3130 g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")))) {
3131 ;
3132 }
3133
3134 g_object_unref (extractor);
3135 }
3136
3137 static void
attachment_save_extracted_completed_cb(AutoarExtractor * extractor,SaveContext * save_context)3138 attachment_save_extracted_completed_cb (AutoarExtractor *extractor,
3139 SaveContext *save_context)
3140 {
3141 attachment_save_complete (save_context);
3142 g_object_unref (extractor);
3143 }
3144
3145 static void
attachment_save_extracted_error_cb(AutoarExtractor * extractor,GError * error,SaveContext * save_context)3146 attachment_save_extracted_error_cb (AutoarExtractor *extractor,
3147 GError *error,
3148 SaveContext *save_context)
3149 {
3150 if (attachment_save_check_for_error (save_context, g_error_copy (error))) {
3151 ;
3152 }
3153
3154 g_object_unref (extractor);
3155 }
3156
3157 static void
attachment_save_write_archive_cb(GOutputStream * output_stream,GAsyncResult * result,SaveContext * save_context)3158 attachment_save_write_archive_cb (GOutputStream *output_stream,
3159 GAsyncResult *result,
3160 SaveContext *save_context)
3161 {
3162 AutoarExtractor *extractor;
3163 GError *error = NULL;
3164 gsize bytes_written;
3165
3166 g_output_stream_write_all_finish (
3167 output_stream, result, &bytes_written, &error);
3168
3169 g_object_unref (output_stream);
3170
3171 if (attachment_save_check_for_error (save_context, error)) {
3172 return;
3173 }
3174
3175 extractor = autoar_extractor_new (
3176 save_context->temporary_file, save_context->directory);
3177
3178 autoar_extractor_set_delete_after_extraction (extractor, TRUE);
3179
3180 g_signal_connect (extractor, "decide-destination",
3181 G_CALLBACK (attachment_save_extracted_decide_destination_cb),
3182 save_context);
3183 g_signal_connect (extractor, "progress",
3184 G_CALLBACK (attachment_save_extracted_progress_cb),
3185 save_context);
3186 g_signal_connect (extractor, "cancelled",
3187 G_CALLBACK (attachment_save_extracted_cancelled_cb),
3188 save_context);
3189 g_signal_connect (extractor, "error",
3190 G_CALLBACK (attachment_save_extracted_error_cb),
3191 save_context);
3192 g_signal_connect (extractor, "completed",
3193 G_CALLBACK (attachment_save_extracted_completed_cb),
3194 save_context);
3195
3196 autoar_extractor_start_async (
3197 extractor, save_context->attachment->priv->cancellable);
3198
3199 /* We do not g_object_unref (extractor); here because
3200 * autoar_extractor_run_start_async () does not increase the
3201 * reference count of extractor. We unref the object in
3202 * callbacks instead. */
3203 }
3204
3205 static void
attachment_save_create_archive_cb(GFile * file,GAsyncResult * result,SaveContext * save_context)3206 attachment_save_create_archive_cb (GFile *file,
3207 GAsyncResult *result,
3208 SaveContext *save_context)
3209 {
3210 GFileOutputStream *output_stream;
3211 GError *error = NULL;
3212
3213 output_stream = g_file_create_finish (file, result, &error);
3214
3215 if (attachment_save_check_for_error (save_context, error)) {
3216 return;
3217 }
3218
3219 g_output_stream_write_all_async (
3220 G_OUTPUT_STREAM (output_stream),
3221 save_context->input_buffer->data,
3222 save_context->input_buffer->len,
3223 G_PRIORITY_DEFAULT,
3224 save_context->attachment->priv->cancellable,
3225 (GAsyncReadyCallback) attachment_save_write_archive_cb,
3226 save_context);
3227 }
3228
3229 #endif
3230
3231 static void
attachment_save_got_output_stream(SaveContext * save_context)3232 attachment_save_got_output_stream (SaveContext *save_context)
3233 {
3234 GCancellable *cancellable;
3235 GInputStream *input_stream;
3236 CamelDataWrapper *wrapper;
3237 CamelMimePart *mime_part;
3238 CamelStream *stream;
3239 EAttachment *attachment;
3240 GByteArray *buffer;
3241
3242 attachment = save_context->attachment;
3243 cancellable = attachment->priv->cancellable;
3244 mime_part = e_attachment_ref_mime_part (attachment);
3245
3246 /* Decode the MIME part to an in-memory buffer. We have to do
3247 * this because CamelStream is synchronous-only, and using threads
3248 * is dangerous because CamelDataWrapper is not reentrant. */
3249 buffer = g_byte_array_new ();
3250 stream = camel_stream_mem_new ();
3251 camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buffer);
3252 wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
3253 camel_data_wrapper_decode_to_stream_sync (wrapper, stream, NULL, NULL);
3254 g_object_unref (stream);
3255
3256 save_context->input_buffer = buffer;
3257
3258 if (attachment->priv->save_self) {
3259 /* Load the buffer into a GMemoryInputStream.
3260 * But watch out for zero length MIME parts. */
3261 input_stream = g_memory_input_stream_new ();
3262 if (buffer->len > 0)
3263 g_memory_input_stream_add_data (
3264 G_MEMORY_INPUT_STREAM (input_stream),
3265 buffer->data, (gssize) buffer->len, NULL);
3266 save_context->input_stream = input_stream;
3267 save_context->total_num_bytes = (goffset) buffer->len;
3268
3269 g_input_stream_read_async (
3270 input_stream,
3271 save_context->buffer,
3272 sizeof (save_context->buffer),
3273 G_PRIORITY_DEFAULT, cancellable,
3274 (GAsyncReadyCallback) attachment_save_read_cb,
3275 save_context);
3276 }
3277
3278 #ifdef HAVE_AUTOAR
3279 if (attachment->priv->save_extracted) {
3280 GFile *temporary_directory;
3281 GError *error = NULL;
3282
3283 temporary_directory = attachment_get_temporary (&error);
3284 if (attachment_save_check_for_error (save_context, error))
3285 return;
3286
3287 save_context->temporary_file = g_file_get_child (
3288 temporary_directory, save_context->suggested_destname);
3289
3290 g_file_create_async (
3291 save_context->temporary_file,
3292 G_FILE_CREATE_NONE,
3293 G_PRIORITY_DEFAULT,
3294 cancellable,
3295 (GAsyncReadyCallback) attachment_save_create_archive_cb,
3296 save_context);
3297
3298 g_object_unref (temporary_directory);
3299 }
3300 #endif
3301
3302 g_clear_object (&mime_part);
3303 }
3304
3305 static void
attachment_save_create_cb(GFile * destination,GAsyncResult * result,SaveContext * save_context)3306 attachment_save_create_cb (GFile *destination,
3307 GAsyncResult *result,
3308 SaveContext *save_context)
3309 {
3310 EAttachment *attachment;
3311 GCancellable *cancellable;
3312 GFileOutputStream *output_stream;
3313 GError *error = NULL;
3314
3315 /* Output stream might be NULL, so don't use cast macro. */
3316 output_stream = g_file_create_finish (destination, result, &error);
3317 save_context->output_stream = (GOutputStream *) output_stream;
3318
3319 attachment = save_context->attachment;
3320 cancellable = attachment->priv->cancellable;
3321
3322 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
3323 destination = attachment_save_new_candidate (save_context);
3324
3325 g_file_create_async (
3326 destination, G_FILE_CREATE_NONE,
3327 G_PRIORITY_DEFAULT, cancellable,
3328 (GAsyncReadyCallback) attachment_save_create_cb,
3329 save_context);
3330
3331 g_object_unref (destination);
3332 g_error_free (error);
3333 return;
3334 }
3335
3336 if (attachment_save_check_for_error (save_context, error))
3337 return;
3338
3339 save_context->destination = g_object_ref (destination);
3340
3341 g_mutex_lock (&(save_context->prepared_tasks_mutex));
3342 if (++save_context->prepared_tasks >= save_context->total_tasks)
3343 attachment_save_got_output_stream (save_context);
3344 g_mutex_unlock (&(save_context->prepared_tasks_mutex));
3345 }
3346
3347 static void
attachment_save_replace_cb(GFile * destination,GAsyncResult * result,SaveContext * save_context)3348 attachment_save_replace_cb (GFile *destination,
3349 GAsyncResult *result,
3350 SaveContext *save_context)
3351 {
3352 GFileOutputStream *output_stream;
3353 GError *error = NULL;
3354
3355 /* Output stream might be NULL, so don't use cast macro. */
3356 output_stream = g_file_replace_finish (destination, result, &error);
3357 save_context->output_stream = (GOutputStream *) output_stream;
3358
3359 if (attachment_save_check_for_error (save_context, error))
3360 return;
3361
3362 save_context->destination = g_object_ref (destination);
3363
3364 g_mutex_lock (&(save_context->prepared_tasks_mutex));
3365 if (++save_context->prepared_tasks >= save_context->total_tasks)
3366 attachment_save_got_output_stream (save_context);
3367 g_mutex_unlock (&(save_context->prepared_tasks_mutex));
3368 }
3369
3370 static void
attachment_save_query_info_cb(GFile * destination,GAsyncResult * result,SaveContext * save_context)3371 attachment_save_query_info_cb (GFile *destination,
3372 GAsyncResult *result,
3373 SaveContext *save_context)
3374 {
3375 EAttachment *attachment;
3376 GCancellable *cancellable;
3377 GFileInfo *file_info;
3378 GFileType file_type;
3379 GError *error = NULL;
3380
3381 attachment = save_context->attachment;
3382 cancellable = attachment->priv->cancellable;
3383
3384 file_info = g_file_query_info_finish (destination, result, &error);
3385
3386 /* G_IO_ERROR_NOT_FOUND just means we're creating a new file. */
3387 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
3388 g_error_free (error);
3389 goto replace;
3390 }
3391
3392 if (attachment_save_check_for_error (save_context, error))
3393 return;
3394
3395 file_type = g_file_info_get_file_type (file_info);
3396 g_object_unref (file_info);
3397
3398 if (file_type == G_FILE_TYPE_DIRECTORY) {
3399 save_context->directory = g_object_ref (destination);
3400
3401 if (attachment->priv->save_self) {
3402 destination = attachment_save_new_candidate (save_context);
3403
3404 g_file_create_async (
3405 destination, G_FILE_CREATE_NONE,
3406 G_PRIORITY_DEFAULT, cancellable,
3407 (GAsyncReadyCallback) attachment_save_create_cb,
3408 save_context);
3409
3410 g_object_unref (destination);
3411 }
3412
3413 #ifdef HAVE_AUTOAR
3414 if (attachment->priv->save_extracted) {
3415 EAttachment *attachment;
3416 GFileInfo *info;
3417 gchar *suggested;
3418
3419 attachment = save_context->attachment;
3420 suggested = NULL;
3421 info = e_attachment_ref_file_info (attachment);
3422 if (info != NULL)
3423 suggested = g_strdup (
3424 g_file_info_get_display_name (info));
3425 if (suggested == NULL)
3426 suggested = g_strdup (_("attachment.dat"));
3427
3428 save_context->suggested_destname = suggested;
3429
3430 g_mutex_lock (&(save_context->prepared_tasks_mutex));
3431 if (++save_context->prepared_tasks >= save_context->total_tasks)
3432 attachment_save_got_output_stream (save_context);
3433 g_mutex_unlock (&(save_context->prepared_tasks_mutex));
3434 }
3435 #endif
3436 return;
3437 }
3438
3439 replace:
3440 if (attachment->priv->save_self) {
3441 g_file_replace_async (
3442 destination, NULL, FALSE,
3443 G_FILE_CREATE_REPLACE_DESTINATION,
3444 G_PRIORITY_DEFAULT, cancellable,
3445 (GAsyncReadyCallback) attachment_save_replace_cb,
3446 save_context);
3447 }
3448
3449 #ifdef HAVE_AUTOAR
3450 if (attachment->priv->save_extracted) {
3451 /* We can safely use save_context->directory here because
3452 * attachment_save_replace_cb never calls
3453 * attachment_save_new_candidate, the only function using
3454 * the value of save_context->directory. */
3455
3456 save_context->suggested_destname =
3457 g_file_get_basename (destination);
3458 save_context->directory = g_file_get_parent (destination);
3459 if (save_context->directory == NULL)
3460 save_context->directory = g_object_ref (destination);
3461
3462 g_mutex_lock (&(save_context->prepared_tasks_mutex));
3463 if (++save_context->prepared_tasks >= save_context->total_tasks)
3464 attachment_save_got_output_stream (save_context);
3465 g_mutex_unlock (&(save_context->prepared_tasks_mutex));
3466 }
3467 #endif
3468 }
3469
3470 void
e_attachment_save_async(EAttachment * attachment,GFile * destination,GAsyncReadyCallback callback,gpointer user_data)3471 e_attachment_save_async (EAttachment *attachment,
3472 GFile *destination,
3473 GAsyncReadyCallback callback,
3474 gpointer user_data)
3475 {
3476 SaveContext *save_context;
3477 GCancellable *cancellable;
3478
3479 g_return_if_fail (E_IS_ATTACHMENT (attachment));
3480 g_return_if_fail (G_IS_FILE (destination));
3481
3482 if (e_attachment_get_loading (attachment)) {
3483 g_simple_async_report_error_in_idle (
3484 G_OBJECT (attachment), callback, user_data,
3485 G_IO_ERROR, G_IO_ERROR_BUSY,
3486 _("A load operation is already in progress"));
3487 return;
3488 }
3489
3490 if (e_attachment_get_saving (attachment)) {
3491 g_simple_async_report_error_in_idle (
3492 G_OBJECT (attachment), callback, user_data,
3493 G_IO_ERROR, G_IO_ERROR_BUSY,
3494 _("A save operation is already in progress"));
3495 return;
3496 }
3497
3498 /* Just peek, don't reference. */
3499 if (attachment->priv->mime_part == NULL) {
3500 g_simple_async_report_error_in_idle (
3501 G_OBJECT (attachment), callback, user_data,
3502 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
3503 _("Attachment contents not loaded"));
3504 return;
3505 }
3506
3507 save_context = attachment_save_context_new (
3508 attachment, callback, user_data);
3509
3510 /* No task is not allowed. */
3511 if (!attachment->priv->save_self && !attachment->priv->save_extracted)
3512 attachment->priv->save_self = TRUE;
3513
3514 if (attachment->priv->save_self)
3515 save_context->total_tasks++;
3516 #ifdef HAVE_AUTOAR
3517 if (attachment->priv->save_extracted)
3518 save_context->total_tasks++;
3519 #endif
3520
3521 cancellable = attachment->priv->cancellable;
3522 g_cancellable_reset (cancellable);
3523
3524 /* First we need to know if destination is a directory. */
3525 g_file_query_info_async (
3526 destination, G_FILE_ATTRIBUTE_STANDARD_TYPE,
3527 G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
3528 cancellable, (GAsyncReadyCallback)
3529 attachment_save_query_info_cb, save_context);
3530 }
3531
3532 GFile *
e_attachment_save_finish(EAttachment * attachment,GAsyncResult * result,GError ** error)3533 e_attachment_save_finish (EAttachment *attachment,
3534 GAsyncResult *result,
3535 GError **error)
3536 {
3537 GSimpleAsyncResult *simple;
3538 GFile *destination;
3539
3540 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
3541 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
3542
3543 simple = G_SIMPLE_ASYNC_RESULT (result);
3544 if (g_simple_async_result_propagate_error (simple, error)) {
3545 attachment_set_saving (attachment, FALSE);
3546 return NULL;
3547 }
3548
3549 destination = g_simple_async_result_get_op_res_gpointer (simple);
3550 if (destination != NULL)
3551 g_object_ref (destination);
3552
3553 attachment_set_saving (attachment, FALSE);
3554
3555 return destination;
3556 }
3557
3558 void
e_attachment_save_handle_error(EAttachment * attachment,GAsyncResult * result,GtkWindow * parent)3559 e_attachment_save_handle_error (EAttachment *attachment,
3560 GAsyncResult *result,
3561 GtkWindow *parent)
3562 {
3563 GFile *file;
3564 GFileInfo *file_info;
3565 GtkWidget *dialog;
3566 const gchar *display_name;
3567 const gchar *primary_text;
3568 GError *error = NULL;
3569
3570 g_return_if_fail (E_IS_ATTACHMENT (attachment));
3571 g_return_if_fail (G_IS_ASYNC_RESULT (result));
3572 g_return_if_fail (GTK_IS_WINDOW (parent));
3573
3574 file = e_attachment_save_finish (attachment, result, &error);
3575
3576 if (file != NULL) {
3577 g_object_unref (file);
3578 return;
3579 }
3580
3581 /* Ignore cancellations. */
3582 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
3583 return;
3584
3585 file_info = e_attachment_ref_file_info (attachment);
3586
3587 if (file_info != NULL)
3588 display_name = g_file_info_get_display_name (file_info);
3589 else
3590 display_name = NULL;
3591
3592 if (display_name != NULL)
3593 primary_text = g_strdup_printf (
3594 _("Could not save “%s”"), display_name);
3595 else
3596 primary_text = g_strdup_printf (
3597 _("Could not save the attachment"));
3598
3599 g_clear_object (&file_info);
3600
3601 dialog = gtk_message_dialog_new_with_markup (
3602 parent, GTK_DIALOG_DESTROY_WITH_PARENT,
3603 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
3604 "<big><b>%s</b></big>", primary_text);
3605
3606 gtk_message_dialog_format_secondary_text (
3607 GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
3608
3609 gtk_dialog_run (GTK_DIALOG (dialog));
3610
3611 gtk_widget_destroy (dialog);
3612 g_error_free (error);
3613 }
3614
3615 gboolean
e_attachment_save(EAttachment * attachment,GFile * in_destination,GFile ** out_destination,GError ** error)3616 e_attachment_save (EAttachment *attachment,
3617 GFile *in_destination,
3618 GFile **out_destination,
3619 GError **error)
3620 {
3621 EAsyncClosure *closure;
3622 GAsyncResult *result;
3623
3624 g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
3625 g_return_val_if_fail (out_destination != NULL, FALSE);
3626
3627 closure = e_async_closure_new ();
3628
3629 e_attachment_save_async (
3630 attachment, in_destination,
3631 e_async_closure_callback, closure);
3632
3633 result = e_async_closure_wait (closure);
3634
3635 *out_destination =
3636 e_attachment_save_finish (attachment, result, error);
3637
3638 e_async_closure_free (closure);
3639
3640 return *out_destination != NULL;
3641 }
3642