aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-attachment.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-attachment.c')
-rw-r--r--e-util/e-attachment.c523
1 files changed, 450 insertions, 73 deletions
diff --git a/e-util/e-attachment.c b/e-util/e-attachment.c
index 1ce400767d..97287f4a40 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);