[evolution] Bug 707647 - gnome-autoar integration in attachments



commit 2d345f3d94a85360e92ef566257c1bc738b6cdc2
Author: Ting-Wei Lan <lantw src gnome org>
Date:   Wed Sep 3 09:10:11 2014 +0200

    Bug 707647 - gnome-autoar integration in attachments

 configure.ac                |   30 +++
 e-util/Makefile.am          |    2 +
 e-util/e-attachment-store.c |  207 ++++++++++++++++-
 e-util/e-attachment.c       |  523 +++++++++++++++++++++++++++++++++++++------
 e-util/e-attachment.h       |    6 +
 5 files changed, 687 insertions(+), 81 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 2a7e4bb..c4243dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,7 @@ m4_define([geoclue_minimum_version], [0.12.0])
 m4_define([geocode_glib_minimum_version], [3.10])
 m4_define([gladeui_minimum_version], [3.10.0])
 m4_define([gweather_minimum_version], [3.8])
+m4_define([gnome_autoar_minimum_version], [0.1])
 m4_define([libcanberra_gtk_minimum_version], [0.25])
 m4_define([libnotify_minimum_version], [0.7])
 
@@ -340,6 +341,34 @@ fi
 AC_SUBST(CANBERRA_CFLAGS)
 AC_SUBST(CANBERRA_LIBS)
 
+dnl ***********************************
+dnl Archives Integration / gnome-autoar
+dnl ***********************************
+AC_ARG_ENABLE([autoar],
+       [AS_HELP_STRING([--enable-autoar],
+       [Enable archives support in attachments @<:@default=yes@:>@])],
+       [enable_autoar="$enableval"], [enable_autoar=yes])
+
+if test x"$enable_autoar" = xyes; then
+       PKG_CHECK_MODULES(
+               [AUTOAR],
+               [gnome-autoar >= gnome_autoar_minimum_version
+                gnome-autoar-gtk >= gnome_autoar_minimum_version],,
+               [AC_MSG_ERROR([
+
+       gnome-autoar or gnome-autoar-gtk not found
+       (or version < gnome_autoar_minimum_version)
+
+       If you want to disable support for automatic archives handling,
+       please append --disable-autoar to configure.
+
+       ])])
+
+       AC_DEFINE(HAVE_AUTOAR, 1, [Define if gnome-autoar is enabled])
+fi
+AC_SUBST(AUTOAR_CFLAGS)
+AC_SUBST(AUTOAR_LIBS)
+
 dnl ******************
 dnl User Documentation
 dnl ******************
@@ -1612,6 +1641,7 @@ echo "
        LDAP support:           $msg_ldap
        Contact Maps:           $enable_contact_maps
        Libcryptui:             $enable_libcryptui
+       Archives support:       $enable_autoar
        Libnotify:              $HAVE_LIBNOTIFY
        SSL support:            $msg_ssl
        SMIME support:          $msg_smime
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 8e91dc8..61ee3f8 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -104,6 +104,7 @@ libevolution_util_la_CPPFLAGS = \
        $(ENCHANT_CFLAGS) \
        $(GTKSPELL_CFLAGS) \
        $(CODE_COVERAGE_CFLAGS) \
+       $(AUTOAR_CFLAGS) \
        $(NULL)
 
 evolution_util_include_HEADERS =  \
@@ -649,6 +650,7 @@ libevolution_util_la_LIBADD =  \
        $(GEO_LIBS) \
        $(ENCHANT_LIBS) \
        $(GTKSPELL_LIBS) \
+       $(AUTOAR_LIBS) \
        $(INTLLIBS) \
        $(MATH_LIB) \
        $(NULL)
diff --git a/e-util/e-attachment-store.c b/e-util/e-attachment-store.c
index e867327..10970bb 100644
--- a/e-util/e-attachment-store.c
+++ b/e-util/e-attachment-store.c
@@ -28,6 +28,11 @@
 #include <errno.h>
 #include <glib/gi18n.h>
 
+#ifdef HAVE_AUTOAR
+#include <gnome-autoar/autoar.h>
+#include <gnome-autoar/autoar-gtk.h>
+#endif
+
 #include "e-mktemp.h"
 
 #define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \
@@ -448,26 +453,56 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
 {
        GtkFileChooser *file_chooser;
        GtkWidget *dialog;
-       GtkWidget *option;
+
+       GtkBox *extra_box;
+       GtkWidget *extra_box_widget;
+       GtkWidget *option_display;
+
+#ifdef HAVE_AUTOAR
+       GtkBox *option_format_box;
+       GtkWidget *option_format_box_widget;
+       GtkWidget *option_format_label;
+       GtkWidget *option_format_combo;
+#endif
+
        GtkImage *preview;
+
        GSList *files, *iter;
        const gchar *disposition;
        gboolean active;
        gint response;
 
+#ifdef HAVE_AUTOAR
+       GSettings *settings;
+       AutoarPref *arpref;
+       gint format, filter;
+#endif
+
        g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
        g_return_if_fail (GTK_IS_WINDOW (parent));
 
        dialog = gtk_file_chooser_dialog_new (
                _("Add Attachment"), parent,
                GTK_FILE_CHOOSER_ACTION_OPEN,
+#ifdef HAVE_AUTOAR
+               _("_Open"), GTK_RESPONSE_OK,
+#endif
                _("_Cancel"), GTK_RESPONSE_CANCEL,
-               _("A_ttach"), GTK_RESPONSE_OK, NULL);
+#ifdef HAVE_AUTOAR
+               _("A_ttach"), GTK_RESPONSE_CLOSE,
+#else
+               _("A_ttach"), GTK_RESPONSE_OK,
+#endif
+               NULL);
 
        file_chooser = GTK_FILE_CHOOSER (dialog);
        gtk_file_chooser_set_local_only (file_chooser, FALSE);
        gtk_file_chooser_set_select_multiple (file_chooser, TRUE);
+#ifdef HAVE_AUTOAR
+       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+#else
        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+#endif
        gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
 
        preview = GTK_IMAGE (gtk_image_new ());
@@ -478,20 +513,52 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
                file_chooser, "update-preview",
                G_CALLBACK (update_preview_cb), preview);
 
-       option = gtk_check_button_new_with_mnemonic (
+       extra_box_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       extra_box = GTK_BOX (extra_box_widget);
+
+       option_display = gtk_check_button_new_with_mnemonic (
                _("_Suggest automatic display of attachment"));
-       gtk_file_chooser_set_extra_widget (file_chooser, option);
-       gtk_widget_show (option);
+       gtk_box_pack_start (extra_box, option_display, FALSE, FALSE, 0);
+
+#ifdef HAVE_AUTOAR
+       option_format_box_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       option_format_box = GTK_BOX (option_format_box_widget);
+       gtk_box_pack_start (extra_box, option_format_box_widget, FALSE, FALSE, 0);
+
+       settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+       arpref = autoar_pref_new_with_gsettings (settings);
+
+       option_format_label = gtk_label_new (
+               _("Archive selected directories using this format:"));
+       option_format_combo = autoar_gtk_chooser_simple_new (
+               autoar_pref_get_default_format (arpref),
+               autoar_pref_get_default_filter (arpref));
+       gtk_box_pack_start (option_format_box, option_format_label, FALSE, FALSE, 0);
+       gtk_box_pack_start (option_format_box, option_format_combo, FALSE, FALSE, 0);
+#endif
+
+       gtk_file_chooser_set_extra_widget (file_chooser, extra_box_widget);
+       gtk_widget_show_all (extra_box_widget);
 
        response = gtk_dialog_run (GTK_DIALOG (dialog));
 
+#ifdef HAVE_AUTOAR
+       if (response != GTK_RESPONSE_OK && response != GTK_RESPONSE_CLOSE)
+#else
        if (response != GTK_RESPONSE_OK)
+#endif
                goto exit;
 
        files = gtk_file_chooser_get_files (file_chooser);
-       active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option));
+       active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option_display));
        disposition = active ? "inline" : "attachment";
 
+#ifdef HAVE_AUTOAR
+       autoar_gtk_chooser_simple_get (option_format_combo, &format, &filter);
+       autoar_pref_set_default_format (arpref, format);
+       autoar_pref_set_default_filter (arpref, filter);
+#endif
+
        for (iter = files; iter != NULL; iter = g_slist_next (iter)) {
                EAttachment *attachment;
                GFile *file = iter->data;
@@ -500,6 +567,12 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
                e_attachment_set_file (attachment, file);
                e_attachment_set_disposition (attachment, disposition);
                e_attachment_store_add_attachment (store, attachment);
+
+#ifdef HAVE_AUTOAR
+               g_object_set_data_full (G_OBJECT (attachment),
+                       "autoar-pref", g_object_ref (arpref), g_object_unref);
+#endif
+
                e_attachment_load_async (
                        attachment, (GAsyncReadyCallback)
                        e_attachment_load_handle_error, parent);
@@ -511,6 +584,10 @@ e_attachment_store_run_load_dialog (EAttachmentStore *store,
 
 exit:
        gtk_widget_destroy (dialog);
+#ifdef HAVE_AUTOAR
+       g_object_unref (settings);
+       g_object_unref (arpref);
+#endif
 }
 
 GFile *
@@ -521,6 +598,18 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
        GtkFileChooser *file_chooser;
        GtkFileChooserAction action;
        GtkWidget *dialog;
+
+#ifdef HAVE_AUTOAR
+       GtkBox *extra_box;
+       GtkWidget *extra_box_widget;
+
+       GtkBox *extract_box;
+       GtkWidget *extract_box_widget;
+
+       GSList *extract_group;
+       GtkWidget *extract_dont, *extract_only, *extract_org;
+#endif
+
        GFile *destination;
        const gchar *title;
        gint response;
@@ -551,11 +640,45 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
        gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
        gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment");
 
+#ifdef HAVE_AUTOAR
+       extra_box_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       extra_box = GTK_BOX (extra_box_widget);
+
+       extract_box_widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       extract_box = GTK_BOX (extract_box_widget);
+       gtk_box_pack_start (extra_box, extract_box_widget, FALSE, FALSE, 5);
+
+       extract_dont = gtk_radio_button_new_with_mnemonic (NULL,
+               _("Do _not extract files from the attachment"));
+       gtk_box_pack_start (extract_box, extract_dont, FALSE, FALSE, 0);
+
+       extract_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (extract_dont));
+       extract_only = gtk_radio_button_new_with_mnemonic (extract_group,
+               _("Save extracted files _only"));
+       gtk_box_pack_start (extract_box, extract_only, FALSE, FALSE, 0);
+
+       extract_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (extract_only));
+       extract_org = gtk_radio_button_new_with_mnemonic (extract_group,
+               _("Save extracted files and the original _archive"));
+       gtk_box_pack_start (extract_box, extract_org, FALSE, FALSE, 0);
+
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extract_dont), TRUE);
+
+       gtk_widget_show_all (extra_box_widget);
+       gtk_file_chooser_set_extra_widget (file_chooser, extra_box_widget);
+#endif
+
        if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
                EAttachment *attachment;
                GFileInfo *file_info;
                const gchar *name = NULL;
 
+#ifdef HAVE_AUTOAR
+               AutoarPref *arpref;
+               GSettings *settings;
+               gchar *mime_type;
+#endif
+
                attachment = attachment_list->data;
                file_info = e_attachment_ref_file_info (attachment);
 
@@ -568,15 +691,83 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
 
                gtk_file_chooser_set_current_name (file_chooser, name);
 
+#ifdef HAVE_AUTOAR
+               mime_type = e_attachment_dup_mime_type (attachment);
+               settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+               arpref = autoar_pref_new_with_gsettings (settings);
+               if (!autoar_pref_check_file_name (arpref, name) &&
+                   !autoar_pref_check_mime_type_d (arpref, mime_type)) {
+                       gtk_widget_hide (extra_box_widget);
+               }
+
+               g_clear_object (&settings);
+               g_clear_object (&arpref);
+               g_free (mime_type);
+#endif
+
                g_clear_object (&file_info);
        }
 
        response = gtk_dialog_run (GTK_DIALOG (dialog));
 
-       if (response == GTK_RESPONSE_OK)
+       if (response == GTK_RESPONSE_OK) {
+#ifdef HAVE_AUTOAR
+               gboolean save_self, save_extracted;
+#endif
+
                destination = gtk_file_chooser_get_file (file_chooser);
-       else
+
+#ifdef HAVE_AUTOAR
+               save_self =
+                       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (extract_dont)) ||
+                       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (extract_org));
+               save_extracted =
+                       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (extract_only)) ||
+                       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (extract_org));
+
+               if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
+                       e_attachment_set_save_self (attachment_list->data, save_self);
+                       e_attachment_set_save_extracted (attachment_list->data, save_extracted);
+               } else {
+                       AutoarPref *arpref;
+                       GSettings *settings;
+                       GList *iter;
+
+                       settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+                       arpref = autoar_pref_new_with_gsettings (settings);
+
+                       for (iter = attachment_list; iter != NULL; iter = iter->next) {
+                               EAttachment *attachment;
+                               GFileInfo *file_info;
+                               const gchar *name;
+                               gchar *mime_type;
+
+                               attachment = iter->data;
+                               file_info = e_attachment_ref_file_info (attachment);
+                               name = g_file_info_get_display_name (file_info);
+                               mime_type = e_attachment_dup_mime_type (attachment);
+
+                               if ((name != NULL &&
+                                   autoar_pref_check_file_name (arpref, name)) ||
+                                   autoar_pref_check_mime_type_d (arpref, mime_type)) {
+                                       e_attachment_set_save_self (attachment, save_self);
+                                       e_attachment_set_save_extracted (attachment, save_extracted);
+                               } else {
+                                       e_attachment_set_save_self (attachment, TRUE);
+                                       e_attachment_set_save_extracted (attachment, FALSE);
+                               }
+
+                               g_object_unref (file_info);
+                               g_free (mime_type);
+                       }
+
+                       g_object_unref (settings);
+                       g_object_unref (arpref);
+               }
+#endif
+       } else {
                destination = NULL;
+       }
 
        gtk_widget_destroy (dialog);
 
diff --git a/e-util/e-attachment.c b/e-util/e-attachment.c
index 1ce4007..97287f4 100644
--- a/e-util/e-attachment.c
+++ b/e-util/e-attachment.c
@@ -28,6 +28,11 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
+#ifdef HAVE_AUTOAR
+#include <gnome-autoar/autoar.h>
+#include <gnome-autoar/autoar-gtk.h>
+#endif
+
 #include <libedataserver/libedataserver.h>
 
 #include "e-attachment-store.h"
@@ -74,6 +79,9 @@ struct _EAttachmentPrivate {
        guint saving : 1;
        guint shown : 1;
 
+       guint save_self      : 1;
+       guint save_extracted : 1;
+
        camel_cipher_validity_encrypt_t encrypted;
        camel_cipher_validity_sign_t signed_;
 
@@ -103,6 +111,8 @@ enum {
        PROP_MIME_PART,
        PROP_PERCENT,
        PROP_REFERENCE,
+       PROP_SAVE_SELF,
+       PROP_SAVE_EXTRACTED,
        PROP_SAVING,
        PROP_SHOWN,
        PROP_SIGNED
@@ -202,6 +212,38 @@ attachment_get_default_charset (void)
        return charset;
 }
 
+static GFile*
+attachment_get_temporary (GError **error)
+{
+       gchar *template;
+       gchar *path;
+       GFile *temp_directory;
+
+       errno = 0;
+
+       /* Save the file to a temporary directory.
+        * We use a directory so the files can retain their basenames.
+        * XXX This could trigger a blocking temp directory cleanup. */
+       template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
+       path = e_mkdtemp (template);
+       g_free (template);
+
+       /* XXX Let's hope errno got set properly. */
+       if (path == NULL) {
+               g_set_error (
+                       error, G_FILE_ERROR,
+                       g_file_error_from_errno (errno),
+                       "%s", g_strerror (errno));
+               return NULL;
+       }
+
+       temp_directory = g_file_new_for_path (path);
+       g_free (path);
+
+       return temp_directory;
+}
+
+
 static gboolean
 attachment_update_file_info_columns_idle_cb (gpointer weak_ref)
 {
@@ -671,6 +713,18 @@ attachment_set_property (GObject *object,
                                E_ATTACHMENT (object),
                                g_value_get_int (value));
                        return;
+
+               case PROP_SAVE_SELF:
+                       e_attachment_set_save_self (
+                               E_ATTACHMENT (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_SAVE_EXTRACTED:
+                       e_attachment_set_save_extracted (
+                               E_ATTACHMENT (object),
+                               g_value_get_boolean (value));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -760,6 +814,20 @@ attachment_get_property (GObject *object,
                                E_ATTACHMENT (object)));
                        return;
 
+               case PROP_SAVE_SELF:
+                       g_value_set_boolean (
+                               value,
+                               e_attachment_get_save_self (
+                               E_ATTACHMENT (object)));
+                       return;
+
+               case PROP_SAVE_EXTRACTED:
+                       g_value_set_boolean (
+                               value,
+                               e_attachment_get_save_extracted (
+                               E_ATTACHMENT (object)));
+                       return;
+
                case PROP_SAVING:
                        g_value_set_boolean (
                                value,
@@ -953,6 +1021,26 @@ e_attachment_class_init (EAttachmentClass *class)
 
        g_object_class_install_property (
                object_class,
+               PROP_SAVE_SELF,
+               g_param_spec_boolean (
+                       "save-self",
+                       "Save self",
+                       NULL,
+                       TRUE,
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SAVE_EXTRACTED,
+               g_param_spec_boolean (
+                       "save-extracted",
+                       "Save extracted",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE));
+
+       g_object_class_install_property (
+               object_class,
                PROP_SAVING,
                g_param_spec_boolean (
                        "saving",
@@ -1531,6 +1619,40 @@ e_attachment_set_shown (EAttachment *attachment,
        g_object_notify (G_OBJECT (attachment), "shown");
 }
 
+gboolean
+e_attachment_get_save_self (EAttachment *attachment)
+{
+       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), TRUE);
+
+       return attachment->priv->save_self;
+}
+
+void
+e_attachment_set_save_self (EAttachment *attachment,
+                            gboolean save_self)
+{
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       attachment->priv->save_self = save_self;
+}
+
+gboolean
+e_attachment_get_save_extracted (EAttachment *attachment)
+{
+       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+       return attachment->priv->save_extracted;
+}
+
+void
+e_attachment_set_save_extracted (EAttachment *attachment,
+                                 gboolean save_extracted)
+{
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       attachment->priv->save_extracted = save_extracted;
+}
+
 camel_cipher_validity_encrypt_t
 e_attachment_get_encrypted (EAttachment *attachment)
 {
@@ -1699,6 +1821,10 @@ static void
 attachment_load_stream_read_cb (GInputStream *input_stream,
                                 GAsyncResult *result,
                                 LoadContext *load_context);
+static void
+attachment_load_query_info_cb (GFile *file,
+                               GAsyncResult *result,
+                               LoadContext *load_context);
 
 static LoadContext *
 attachment_load_context_new (EAttachment *attachment,
@@ -1960,6 +2086,58 @@ attachment_load_file_read_cb (GFile *file,
                load_context);
 }
 
+#ifdef HAVE_AUTOAR
+static void
+attachment_load_created_decide_dest_cb (AutoarCreate *arcreate,
+                                        GFile *destination,
+                                        EAttachment *attachment)
+{
+       e_attachment_set_file (attachment, destination);
+}
+
+static void
+attachment_load_created_cancelled_cb (AutoarCreate *arcreate,
+                                      LoadContext *load_context)
+{
+       attachment_load_check_for_error (load_context,
+               g_error_new_literal (
+                       G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")));
+       g_object_unref (arcreate);
+}
+
+static void
+attachment_load_created_completed_cb (AutoarCreate *arcreate,
+                                      LoadContext *load_context)
+{
+       EAttachment *attachment;
+       GFile *file;
+
+       g_object_unref (arcreate);
+
+       /* We have set the file to the created temporary archive, so we can
+        * query info again and use the regular procedure to load the
+        * attachment. */
+       attachment = load_context->attachment;
+       file = e_attachment_ref_file (attachment);
+       g_file_query_info_async (
+               file, ATTACHMENT_QUERY,
+               G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
+               attachment->priv->cancellable, (GAsyncReadyCallback)
+               attachment_load_query_info_cb, load_context);
+
+       g_clear_object (&file);
+}
+
+static void
+attachment_load_created_error_cb (AutoarCreate *arcreate,
+                                  GError *error,
+                                  LoadContext *load_context)
+{
+       attachment_load_check_for_error (load_context, g_error_copy (error));
+       g_object_unref (arcreate);
+}
+#endif
+
 static void
 attachment_load_query_info_cb (GFile *file,
                                GAsyncResult *result,
@@ -1982,10 +2160,37 @@ attachment_load_query_info_cb (GFile *file,
 
        load_context->total_num_bytes = g_file_info_get_size (file_info);
 
-       g_file_read_async (
-               file, G_PRIORITY_DEFAULT,
-               cancellable, (GAsyncReadyCallback)
-               attachment_load_file_read_cb, load_context);
+#ifdef HAVE_AUTOAR
+       if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) {
+               AutoarCreate *arcreate;
+               AutoarPref *arpref; /* Do not unref */
+               GFile *temporary;
+
+               arpref = g_object_get_data (G_OBJECT (attachment), "autoar-pref");
+               temporary = attachment_get_temporary (&error);
+               if (attachment_load_check_for_error (load_context, error))
+                       return;
+               arcreate = autoar_create_new_file (arpref, temporary, file, NULL);
+               g_signal_connect (arcreate, "decide-dest",
+                       G_CALLBACK (attachment_load_created_decide_dest_cb), attachment);
+               g_signal_connect (arcreate, "cancelled",
+                       G_CALLBACK (attachment_load_created_cancelled_cb), load_context);
+               g_signal_connect (arcreate, "completed",
+                       G_CALLBACK (attachment_load_created_completed_cb), load_context);
+               g_signal_connect (arcreate, "error",
+                       G_CALLBACK (attachment_load_created_error_cb), load_context);
+               autoar_create_start_async (arcreate, cancellable);
+
+               g_object_unref (temporary);
+       } else {
+#endif
+               g_file_read_async (
+                       file, G_PRIORITY_DEFAULT,
+                       cancellable, (GAsyncReadyCallback)
+                       attachment_load_file_read_cb, load_context);
+#ifdef HAVE_AUTOAR
+       }
+#endif
 }
 
 #define ATTACHMENT_LOAD_CONTEXT "attachment-load-context-data"
@@ -2453,39 +2658,20 @@ static void
 attachment_open_save_temporary (OpenContext *open_context)
 {
        GFile *temp_directory;
-       gchar *template;
-       gchar *path;
        GError *error = NULL;
 
-       errno = 0;
-
-       /* Save the file to a temporary directory.
-        * We use a directory so the files can retain their basenames.
-        * XXX This could trigger a blocking temp directory cleanup. */
-       template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
-       path = e_mkdtemp (template);
-       g_free (template);
-
-       /* XXX Let's hope errno got set properly. */
-       if (path == NULL)
-               g_set_error (
-                       &error, G_FILE_ERROR,
-                       g_file_error_from_errno (errno),
-                       "%s", g_strerror (errno));
+       temp_directory = attachment_get_temporary (&error);
 
        /* We already know if there's an error, but this does the cleanup. */
        if (attachment_open_check_for_error (open_context, error))
                return;
 
-       temp_directory = g_file_new_for_path (path);
-
        e_attachment_save_async (
                open_context->attachment,
                temp_directory, (GAsyncReadyCallback)
                attachment_open_save_finished_cb, open_context);
 
        g_object_unref (temp_directory);
-       g_free (path);
 }
 
 void
@@ -2635,6 +2821,16 @@ struct _SaveContext {
        gssize bytes_read;
        gchar buffer[4096];
        gint count;
+
+       GByteArray *input_buffer;
+       gchar *suggested_destname;
+
+       guint total_tasks : 2;
+       guint completed_tasks : 2;
+       guint prepared_tasks : 2;
+
+       GMutex completed_tasks_mutex;
+       GMutex prepared_tasks_mutex;
 };
 
 /* Forward Declaration */
@@ -2659,6 +2855,9 @@ attachment_save_context_new (EAttachment *attachment,
        save_context->attachment = g_object_ref (attachment);
        save_context->simple = simple;
 
+       g_mutex_init (&(save_context->completed_tasks_mutex));
+       g_mutex_init (&(save_context->prepared_tasks_mutex));
+
        attachment_set_saving (save_context->attachment, TRUE);
 
        return save_context;
@@ -2682,6 +2881,15 @@ attachment_save_context_free (SaveContext *save_context)
        if (save_context->output_stream != NULL)
                g_object_unref (save_context->output_stream);
 
+       if (save_context->input_buffer != NULL)
+               g_byte_array_unref (save_context->input_buffer);
+
+       if (save_context->suggested_destname != NULL)
+               g_free (save_context->suggested_destname);
+
+       g_mutex_clear (&(save_context->completed_tasks_mutex));
+       g_mutex_clear (&(save_context->prepared_tasks_mutex));
+
        g_slice_free (SaveContext, save_context);
 }
 
@@ -2696,13 +2904,46 @@ attachment_save_check_for_error (SaveContext *save_context,
 
        simple = save_context->simple;
        g_simple_async_result_take_error (simple, error);
-       g_simple_async_result_complete (simple);
 
-       attachment_save_context_free (save_context);
+       g_mutex_lock (&(save_context->completed_tasks_mutex));
+       if (++save_context->completed_tasks >= save_context->total_tasks) {
+               g_simple_async_result_complete (simple);
+               g_mutex_unlock (&(save_context->completed_tasks_mutex));
+               attachment_save_context_free (save_context);
+       } else {
+               g_mutex_unlock (&(save_context->completed_tasks_mutex));
+       }
 
        return TRUE;
 }
 
+static void
+attachment_save_complete (SaveContext *save_context) {
+       g_mutex_lock (&(save_context->completed_tasks_mutex));
+       if (++save_context->completed_tasks >= save_context->total_tasks) {
+               GSimpleAsyncResult *simple;
+               GFile *result;
+
+               /* Steal the destination. */
+               result = save_context->destination;
+               save_context->destination = NULL;
+
+               if (result == NULL) {
+                       result = save_context->directory;
+                       save_context->directory = NULL;
+               }
+
+               simple = save_context->simple;
+               g_simple_async_result_set_op_res_gpointer (
+                       simple, result, (GDestroyNotify) g_object_unref);
+               g_simple_async_result_complete (simple);
+               g_mutex_unlock (&(save_context->completed_tasks_mutex));
+               attachment_save_context_free (save_context);
+       } else {
+               g_mutex_unlock (&(save_context->completed_tasks_mutex));
+       }
+}
+
 static GFile *
 attachment_save_new_candidate (SaveContext *save_context)
 {
@@ -2817,20 +3058,7 @@ attachment_save_read_cb (GInputStream *input_stream,
                return;
 
        if (bytes_read == 0) {
-               GSimpleAsyncResult *simple;
-               GFile *destination;
-
-               /* Steal the destination. */
-               destination = save_context->destination;
-               save_context->destination = NULL;
-
-               simple = save_context->simple;
-               g_simple_async_result_set_op_res_gpointer (
-                       simple, destination, (GDestroyNotify) g_object_unref);
-               g_simple_async_result_complete (simple);
-
-               attachment_save_context_free (save_context);
-
+               attachment_save_complete (save_context);
                return;
        }
 
@@ -2852,6 +3080,46 @@ attachment_save_read_cb (GInputStream *input_stream,
                save_context);
 }
 
+#ifdef HAVE_AUTOAR
+static void
+attachment_save_extracted_progress_cb (AutoarExtract *arextract,
+                                       guint64 completed_size,
+                                       guint completed_files,
+                                       SaveContext *save_context)
+{
+       attachment_progress_cb (
+               autoar_extract_get_size (arextract),
+               completed_size, save_context->attachment);
+}
+
+static void
+attachment_save_extracted_cancelled_cb (AutoarExtract *arextract,
+                                        SaveContext *save_context)
+{
+       attachment_save_check_for_error (save_context,
+               g_error_new_literal (
+                       G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled")));
+       g_object_unref (arextract);
+}
+
+static void
+attachment_save_extracted_completed_cb (AutoarExtract *arextract,
+                                        SaveContext *save_context)
+{
+       attachment_save_complete (save_context);
+       g_object_unref (arextract);
+}
+
+static void
+attachment_save_extracted_error_cb (AutoarExtract *arextract,
+                                    GError *error,
+                                    SaveContext *save_context)
+{
+       attachment_save_check_for_error (save_context, g_error_copy (error));
+       g_object_unref (arextract);
+}
+#endif
+
 static void
 attachment_save_got_output_stream (SaveContext *save_context)
 {
@@ -2877,25 +3145,67 @@ attachment_save_got_output_stream (SaveContext *save_context)
        camel_data_wrapper_decode_to_stream_sync (wrapper, stream, NULL, NULL);
        g_object_unref (stream);
 
-       /* Load the buffer into a GMemoryInputStream.
-        * But watch out for zero length MIME parts. */
-       input_stream = g_memory_input_stream_new ();
-       if (buffer->len > 0)
-               g_memory_input_stream_add_data (
-                       G_MEMORY_INPUT_STREAM (input_stream),
-                       buffer->data, (gssize) buffer->len,
-                       (GDestroyNotify) g_free);
-       save_context->input_stream = input_stream;
-       save_context->total_num_bytes = (goffset) buffer->len;
-       g_byte_array_free (buffer, FALSE);
+       save_context->input_buffer = buffer;
 
-       g_input_stream_read_async (
-               input_stream,
-               save_context->buffer,
-               sizeof (save_context->buffer),
-               G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) attachment_save_read_cb,
-               save_context);
+       if (attachment->priv->save_self) {
+               /* Load the buffer into a GMemoryInputStream.
+                * But watch out for zero length MIME parts. */
+               input_stream = g_memory_input_stream_new ();
+               if (buffer->len > 0)
+                       g_memory_input_stream_add_data (
+                               G_MEMORY_INPUT_STREAM (input_stream),
+                               buffer->data, (gssize) buffer->len, NULL);
+               save_context->input_stream = input_stream;
+               save_context->total_num_bytes = (goffset) buffer->len;
+
+               g_input_stream_read_async (
+                       input_stream,
+                       save_context->buffer,
+                       sizeof (save_context->buffer),
+                       G_PRIORITY_DEFAULT, cancellable,
+                       (GAsyncReadyCallback) attachment_save_read_cb,
+                       save_context);
+       }
+
+#ifdef HAVE_AUTOAR
+       if (attachment->priv->save_extracted) {
+               GSettings *settings;
+               AutoarPref *arpref;
+               AutoarExtract *arextract;
+
+               settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+               arpref = autoar_pref_new_with_gsettings (settings);
+               autoar_pref_set_delete_if_succeed (arpref, FALSE);
+
+               arextract = autoar_extract_new_memory_file (
+                       buffer->data, buffer->len,
+                       save_context->suggested_destname,
+                       save_context->directory, arpref);
+
+               g_signal_connect (arextract, "progress",
+                       G_CALLBACK (attachment_save_extracted_progress_cb),
+                       save_context);
+               g_signal_connect (arextract, "cancelled",
+                       G_CALLBACK (attachment_save_extracted_cancelled_cb),
+                       save_context);
+               g_signal_connect (arextract, "error",
+                       G_CALLBACK (attachment_save_extracted_error_cb),
+                       save_context);
+               g_signal_connect (arextract, "completed",
+                       G_CALLBACK (attachment_save_extracted_completed_cb),
+                       save_context);
+
+               autoar_extract_start_async (arextract, cancellable);
+
+               g_object_unref (settings);
+               g_object_unref (arpref);
+
+               /* We do not g_object_unref (arextract); here because
+                * autoar_extract_run_start_async () do not increase the
+                * reference count of arextract. We unref the object in
+                * callbacks instead. */
+       }
+#endif
 
        g_clear_object (&mime_part);
 }
@@ -2935,7 +3245,11 @@ attachment_save_create_cb (GFile *destination,
                return;
 
        save_context->destination = g_object_ref (destination);
-       attachment_save_got_output_stream (save_context);
+
+       g_mutex_lock (&(save_context->prepared_tasks_mutex));
+       if (++save_context->prepared_tasks >= save_context->total_tasks)
+               attachment_save_got_output_stream (save_context);
+       g_mutex_unlock (&(save_context->prepared_tasks_mutex));
 }
 
 static void
@@ -2954,7 +3268,11 @@ attachment_save_replace_cb (GFile *destination,
                return;
 
        save_context->destination = g_object_ref (destination);
-       attachment_save_got_output_stream (save_context);
+
+       g_mutex_lock (&(save_context->prepared_tasks_mutex));
+       if (++save_context->prepared_tasks >= save_context->total_tasks)
+               attachment_save_got_output_stream (save_context);
+       g_mutex_unlock (&(save_context->prepared_tasks_mutex));
 }
 
 static void
@@ -2987,26 +3305,74 @@ attachment_save_query_info_cb (GFile *destination,
 
        if (file_type == G_FILE_TYPE_DIRECTORY) {
                save_context->directory = g_object_ref (destination);
-               destination = attachment_save_new_candidate (save_context);
 
-               g_file_create_async (
-                       destination, G_FILE_CREATE_NONE,
-                       G_PRIORITY_DEFAULT, cancellable,
-                       (GAsyncReadyCallback) attachment_save_create_cb,
-                       save_context);
+               if (attachment->priv->save_self) {
+                       destination = attachment_save_new_candidate (save_context);
 
-               g_object_unref (destination);
+                       g_file_create_async (
+                               destination, G_FILE_CREATE_NONE,
+                               G_PRIORITY_DEFAULT, cancellable,
+                               (GAsyncReadyCallback) attachment_save_create_cb,
+                               save_context);
 
+                       g_object_unref (destination);
+               }
+
+#ifdef HAVE_AUTOAR
+               if (attachment->priv->save_extracted) {
+                       EAttachment *attachment;
+                       GFileInfo *info;
+                       gchar *suggested;
+
+                       attachment = save_context->attachment;
+                       suggested = NULL;
+                       info = e_attachment_ref_file_info (attachment);
+                       if (info != NULL)
+                               suggested = g_strdup (
+                                       g_file_info_get_display_name (info));
+                       if (suggested == NULL)
+                               suggested = g_strdup (_("attachment.dat"));
+
+                       save_context->suggested_destname = suggested;
+
+                       g_mutex_lock (&(save_context->prepared_tasks_mutex));
+                       if (++save_context->prepared_tasks >= save_context->total_tasks)
+                               attachment_save_got_output_stream (save_context);
+                       g_mutex_unlock (&(save_context->prepared_tasks_mutex));
+               }
+#endif
                return;
        }
 
 replace:
-       g_file_replace_async (
-               destination, NULL, FALSE,
-               G_FILE_CREATE_REPLACE_DESTINATION,
-               G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) attachment_save_replace_cb,
-               save_context);
+       if (attachment->priv->save_self) {
+               g_file_replace_async (
+                       destination, NULL, FALSE,
+                       G_FILE_CREATE_REPLACE_DESTINATION,
+                       G_PRIORITY_DEFAULT, cancellable,
+                       (GAsyncReadyCallback) attachment_save_replace_cb,
+                       save_context);
+       }
+
+#ifdef HAVE_AUTOAR
+       if (attachment->priv->save_extracted) {
+               /* We can safely use save_context->directory here because
+                * attachment_save_replace_cb never calls
+                * attachment_save_new_candidate, the only function using
+                * the value of save_context->directory. */
+
+               save_context->suggested_destname =
+                       g_file_get_basename (destination);
+               save_context->directory = g_file_get_parent (destination);
+               if (save_context->directory == NULL)
+                       save_context->directory = g_object_ref (destination);
+
+               g_mutex_lock (&(save_context->prepared_tasks_mutex));
+               if (++save_context->prepared_tasks >= save_context->total_tasks)
+                       attachment_save_got_output_stream (save_context);
+               g_mutex_unlock (&(save_context->prepared_tasks_mutex));
+       }
+#endif
 }
 
 void
@@ -3049,6 +3415,17 @@ e_attachment_save_async (EAttachment *attachment,
        save_context = attachment_save_context_new (
                attachment, callback, user_data);
 
+       /* No task is not allowed. */
+       if (!attachment->priv->save_self && !attachment->priv->save_extracted)
+               attachment->priv->save_self = TRUE;
+
+       if (attachment->priv->save_self)
+               save_context->total_tasks++;
+#ifdef HAVE_AUTOAR
+       if (attachment->priv->save_extracted)
+               save_context->total_tasks++;
+#endif
+
        cancellable = attachment->priv->cancellable;
        g_cancellable_reset (cancellable);
 
diff --git a/e-util/e-attachment.h b/e-util/e-attachment.h
index cfacbf3..9fc4ea0 100644
--- a/e-util/e-attachment.h
+++ b/e-util/e-attachment.h
@@ -99,6 +99,12 @@ gboolean     e_attachment_get_saving         (EAttachment *attachment);
 gboolean       e_attachment_get_shown          (EAttachment *attachment);
 void           e_attachment_set_shown          (EAttachment *attachment,
                                                 gboolean shown);
+gboolean       e_attachment_get_save_self      (EAttachment *attachment);
+void           e_attachment_set_save_self      (EAttachment *attachment,
+                                                gboolean save_self);
+gboolean       e_attachment_get_save_extracted (EAttachment *attachment);
+void           e_attachment_set_save_extracted (EAttachment *attachment,
+                                                gboolean save_extracted);
 camel_cipher_validity_encrypt_t
                e_attachment_get_encrypted      (EAttachment *attachment);
 void           e_attachment_set_encrypted      (EAttachment *attachment,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]