diff options
-rw-r--r-- | mail/e-mail-folder-utils.c | 242 | ||||
-rw-r--r-- | mail/e-mail-folder-utils.h | 18 | ||||
-rw-r--r-- | mail/e-mail-reader-utils.c | 111 | ||||
-rw-r--r-- | mail/e-mail-reader-utils.h | 1 | ||||
-rw-r--r-- | mail/e-mail-reader.c | 65 | ||||
-rw-r--r-- | mail/mail-ops.c | 169 | ||||
-rw-r--r-- | mail/mail-ops.h | 5 | ||||
-rw-r--r-- | mail/mail.error.xml | 5 |
8 files changed, 378 insertions, 238 deletions
diff --git a/mail/e-mail-folder-utils.c b/mail/e-mail-folder-utils.c index 10546c15d5..f244260bf5 100644 --- a/mail/e-mail-folder-utils.c +++ b/mail/e-mail-folder-utils.c @@ -34,6 +34,7 @@ struct _AsyncContext { CamelMimePart *part; GHashTable *hash_table; GPtrArray *ptr_array; + GFile *destination; gchar *fwd_subject; gchar *message_uid; }; @@ -56,6 +57,9 @@ async_context_free (AsyncContext *context) if (context->ptr_array != NULL) g_ptr_array_unref (context->ptr_array); + if (context->destination != NULL) + g_object_unref (context->destination); + g_free (context->fwd_subject); g_free (context->message_uid); @@ -884,6 +888,244 @@ e_mail_folder_remove_attachments_finish (CamelFolder *folder, return !g_simple_async_result_propagate_error (simple, error); } +static void +mail_folder_save_messages_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *context; + GError *error = NULL; + + context = g_simple_async_result_get_op_res_gpointer (simple); + + e_mail_folder_save_messages_sync ( + CAMEL_FOLDER (object), context->ptr_array, + context->destination, cancellable, &error); + + if (error != NULL) { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } +} + +/* Helper for e_mail_folder_save_messages_sync() */ +static void +mail_folder_save_prepare_part (CamelMimePart *mime_part) +{ + CamelDataWrapper *content; + + content = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); + + if (content == NULL) + return; + + if (CAMEL_IS_MULTIPART (content)) { + guint n_parts, ii; + + n_parts = camel_multipart_get_number ( + CAMEL_MULTIPART (content)); + for (ii = 0; ii < n_parts; ii++) { + mime_part = camel_multipart_get_part ( + CAMEL_MULTIPART (content), ii); + mail_folder_save_prepare_part (mime_part); + } + + } else if (CAMEL_IS_MIME_MESSAGE (content)) { + mail_folder_save_prepare_part (CAMEL_MIME_PART (content)); + + } else { + CamelContentType *type; + + /* Save textual parts as 8-bit, not encoded. */ + type = camel_data_wrapper_get_mime_type_field (content); + if (camel_content_type_is (type, "text", "*")) + camel_mime_part_set_encoding ( + mime_part, CAMEL_TRANSFER_ENCODING_8BIT); + } +} + +gboolean +e_mail_folder_save_messages_sync (CamelFolder *folder, + GPtrArray *message_uids, + GFile *destination, + GCancellable *cancellable, + GError **error) +{ + GFileOutputStream *file_output_stream; + GByteArray *byte_array; + CamelMimeFilter *filter; + CamelStream *base_stream; + CamelStream *stream; + gboolean success = TRUE; + guint ii; + + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE); + g_return_val_if_fail (message_uids != NULL, FALSE); + g_return_val_if_fail (G_IS_FILE (destination), FALSE); + + /* Need at least one message UID to save. */ + g_return_val_if_fail (message_uids->len > 0, FALSE); + + camel_operation_push_message ( + cancellable, ngettext ( + "Saving %d message", + "Saving %d messages", + message_uids->len), + message_uids->len); + + file_output_stream = g_file_replace ( + destination, NULL, FALSE, + G_FILE_CREATE_PRIVATE | + G_FILE_CREATE_REPLACE_DESTINATION, + cancellable, error); + + if (file_output_stream == NULL) { + camel_operation_pop_message (cancellable); + return FALSE; + } + + /* CamelStreamMem takes ownership of the GByteArray. */ + byte_array = g_byte_array_new (); + filter = camel_mime_filter_from_new (); + base_stream = camel_stream_mem_new_with_byte_array (byte_array); + stream = camel_stream_filter_new (base_stream); + camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter); + g_object_unref (base_stream); + g_object_unref (filter); + + for (ii = 0; ii < message_uids->len; ii++) { + CamelMimeMessage *message; + const gchar *uid; + gchar *from_line; + gint percent; + gint retval; + + uid = g_ptr_array_index (message_uids, ii); + + message = camel_folder_get_message_sync ( + folder, uid, cancellable, error); + if (message == NULL) { + success = FALSE; + goto exit; + } + + mail_folder_save_prepare_part (CAMEL_MIME_PART (message)); + + from_line = camel_mime_message_build_mbox_from (message); + g_return_val_if_fail (from_line != NULL, FALSE); + + success = g_output_stream_write_all ( + G_OUTPUT_STREAM (file_output_stream), + from_line, strlen (from_line), NULL, + cancellable, error); + + g_free (from_line); + + if (!success) { + g_object_unref (message); + goto exit; + } + + retval = camel_data_wrapper_write_to_stream_sync ( + CAMEL_DATA_WRAPPER (message), + stream, cancellable, error); + + if (retval == -1) { + g_object_unref (message); + goto exit; + } + + g_byte_array_append (byte_array, (guint8 *) "\n", 1); + + success = g_output_stream_write_all ( + G_OUTPUT_STREAM (file_output_stream), + byte_array->data, byte_array->len, + NULL, cancellable, error); + + if (!success) { + g_object_unref (message); + goto exit; + } + + percent = ((ii + 1) * 100) / message_uids->len; + camel_operation_progress (cancellable, percent); + + /* Flush the buffer for the next message. + * For memory streams this never fails. */ + camel_stream_reset (stream, NULL); + + g_object_unref (message); + } + +exit: + g_object_unref (file_output_stream); + g_object_unref (stream); + + camel_operation_pop_message (cancellable); + + if (!success) { + /* Try deleting the destination file. */ + g_file_delete (destination, NULL, NULL); + } + + return success; +} + +void +e_mail_folder_save_messages (CamelFolder *folder, + GPtrArray *message_uids, + GFile *destination, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *context; + + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (message_uids != NULL); + g_return_if_fail (G_IS_FILE (destination)); + + /* Need at least one message UID to save. */ + g_return_if_fail (message_uids->len > 0); + + context = g_slice_new0 (AsyncContext); + context->ptr_array = g_ptr_array_ref (message_uids); + context->destination = g_object_ref (destination); + + simple = g_simple_async_result_new ( + G_OBJECT (folder), callback, user_data, + e_mail_folder_save_messages); + + g_simple_async_result_set_op_res_gpointer ( + simple, context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, mail_folder_save_messages_thread, + io_priority, cancellable); + + g_object_unref (simple); +} + +gboolean +e_mail_folder_save_messages_finish (CamelFolder *folder, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (folder), + e_mail_folder_save_messages), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + /** * e_mail_folder_uri_build: * @store: a #CamelStore diff --git a/mail/e-mail-folder-utils.h b/mail/e-mail-folder-utils.h index f526c3273b..5b178f4a90 100644 --- a/mail/e-mail-folder-utils.h +++ b/mail/e-mail-folder-utils.h @@ -114,6 +114,24 @@ gboolean e_mail_folder_remove_attachments_finish GAsyncResult *result, GError **error); +gboolean e_mail_folder_save_messages_sync + (CamelFolder *folder, + GPtrArray *message_uids, + GFile *destination, + GCancellable *cancellable, + GError **error); +void e_mail_folder_save_messages (CamelFolder *folder, + GPtrArray *message_uids, + GFile *destination, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_mail_folder_save_messages_finish + (CamelFolder *folder, + GAsyncResult *result, + GError **error); + gchar * e_mail_folder_uri_build (CamelStore *store, const gchar *folder_name); gboolean e_mail_folder_uri_parse (CamelSession *session, diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c index 281acbbe52..8e88d7d31a 100644 --- a/mail/e-mail-reader-utils.c +++ b/mail/e-mail-reader-utils.c @@ -31,6 +31,7 @@ #include "e-util/e-alert-dialog.h" #include "filter/e-filter-rule.h" #include "misc/e-web-view.h" +#include "shell/e-shell-utils.h" #include "mail/e-mail-backend.h" #include "mail/e-mail-browser.h" @@ -753,6 +754,116 @@ whole_message: reply_type, reply_style, EM_FORMAT (formatter)); } +static void +mail_reader_save_messages_cb (CamelFolder *folder, + GAsyncResult *result, + AsyncContext *context) +{ + EAlertSink *alert_sink; + GError *error = NULL; + + alert_sink = e_mail_reader_get_alert_sink (context->reader); + + e_mail_folder_save_messages_finish (folder, result, &error); + + if (e_activity_handle_cancellation (context->activity, error)) { + g_error_free (error); + + } else if (error != NULL) { + e_alert_submit ( + alert_sink, + "mail:save-messages", + error->message, NULL); + g_error_free (error); + } + + async_context_free (context); +} + +void +e_mail_reader_save_messages (EMailReader *reader) +{ + EShell *shell; + EActivity *activity; + AsyncContext *context; + EMailBackend *backend; + GCancellable *cancellable; + EShellBackend *shell_backend; + CamelMessageInfo *info; + CamelFolder *folder; + GFile *destination; + GPtrArray *uids; + const gchar *message_uid; + const gchar *title; + gchar *suggestion = NULL; + + folder = e_mail_reader_get_folder (reader); + backend = e_mail_reader_get_backend (reader); + + uids = e_mail_reader_get_selected_uids (reader); + g_return_if_fail (uids != NULL && uids->len > 0); + message_uid = g_ptr_array_index (uids, 0); + + /* XXX Either e_mail_reader_get_selected_uids() + * or MessageList should do this itself. */ + g_ptr_array_set_free_func (uids, (GDestroyNotify) g_free); + + title = ngettext ("Save Message", "Save Messages", uids->len); + + /* Suggest as a filename the subject of the first message. */ + info = camel_folder_get_message_info (folder, message_uid); + if (info != NULL) { + const gchar *subject; + + subject = camel_message_info_subject (info); + if (subject != NULL) + suggestion = g_strconcat (subject, ".mbox", NULL); + camel_folder_free_message_info (folder, info); + } + + if (suggestion == NULL) { + const gchar *basename; + + /* Translators: This is part of a suggested file name + * used when saving a message or multiple messages to + * mbox format, when the first message doesn't have a + * subject. The extension ".mbox" is appended to the + * string; for example "Message.mbox". */ + basename = ngettext ("Message", "Messages", uids->len); + suggestion = g_strconcat (basename, ".mbox", NULL); + } + + shell_backend = E_SHELL_BACKEND (backend); + shell = e_shell_backend_get_shell (shell_backend); + + destination = e_shell_run_save_dialog ( + shell, title, suggestion, + "*.mbox:application/mbox,message/rfc822", NULL, NULL); + + if (destination == NULL) { + g_ptr_array_unref (uids); + return; + } + + /* Save messages asynchronously. */ + + activity = e_mail_reader_new_activity (reader); + cancellable = e_activity_get_cancellable (activity); + + context = g_slice_new0 (AsyncContext); + context->activity = activity; + context->reader = g_object_ref (reader); + + e_mail_folder_save_messages ( + folder, uids, + destination, G_PRIORITY_DEFAULT, + cancellable, (GAsyncReadyCallback) + mail_reader_save_messages_cb, context); + + g_object_unref (destination); + g_ptr_array_unref (uids); +} + void e_mail_reader_select_next_message (EMailReader *reader, gboolean or_else_previous) diff --git a/mail/e-mail-reader-utils.h b/mail/e-mail-reader-utils.h index b574f03bfe..b59ff3c8b7 100644 --- a/mail/e-mail-reader-utils.h +++ b/mail/e-mail-reader-utils.h @@ -53,6 +53,7 @@ void e_mail_reader_remove_duplicates (EMailReader *reader); void e_mail_reader_reply_to_message (EMailReader *reader, CamelMimeMessage *message, EMailReplyType reply_type); +void e_mail_reader_save_messages (EMailReader *reader); void e_mail_reader_select_next_message (EMailReader *reader, gboolean or_else_previous); diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index 0a6cb06848..27096c4a03 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -1502,70 +1502,7 @@ static void action_mail_save_as_cb (GtkAction *action, EMailReader *reader) { - EShell *shell; - EMailBackend *backend; - EShellBackend *shell_backend; - CamelMessageInfo *info; - CamelFolder *folder; - GPtrArray *uids; - GFile *file; - const gchar *message_uid; - const gchar *title; - gchar *suggestion = NULL; - gchar *uri; - - folder = e_mail_reader_get_folder (reader); - backend = e_mail_reader_get_backend (reader); - - uids = e_mail_reader_get_selected_uids (reader); - g_return_if_fail (uids != NULL && uids->len == 1); - message_uid = g_ptr_array_index (uids, 0); - - title = ngettext ("Save Message", "Save Messages", uids->len); - - /* Suggest as a filename the subject of the first message. */ - info = camel_folder_get_message_info (folder, message_uid); - if (info != NULL) { - const gchar *subject = camel_message_info_subject (info); - - if (subject) - suggestion = g_strconcat (subject, ".mbox", NULL); - camel_folder_free_message_info (folder, info); - } - - if (!suggestion) { - const gchar *basename; - - /* Translators: This is a part of a suggested file name - * used when saving a message or multiple messages to an - * mbox format, when the first message doesn't have a - * Subject. The extension ".mbox" is appended to this - * string, thus it will be something like "Message.mbox" - * at the end. */ - basename = ngettext ("Message", "Messages", uids->len); - suggestion = g_strconcat (basename, ".mbox", NULL); - } - - shell_backend = E_SHELL_BACKEND (backend); - shell = e_shell_backend_get_shell (shell_backend); - - file = e_shell_run_save_dialog ( - shell, title, suggestion, - "*.mbox:application/mbox,message/rfc822", NULL, NULL); - - if (file == NULL) { - em_utils_uids_free (uids); - return; - } - - uri = g_file_get_uri (file); - - /* This eats the UID array, so do not free it. */ - mail_save_messages (folder, uids, uri, NULL, NULL); - - g_free (uri); - - g_object_unref (file); + e_mail_reader_save_messages (reader); } static void diff --git a/mail/mail-ops.c b/mail/mail-ops.c index 1438805321..ad86c772d6 100644 --- a/mail/mail-ops.c +++ b/mail/mail-ops.c @@ -1725,175 +1725,6 @@ mail_empty_trash (EMailSession *session, mail_msg_slow_ordered_push (m); } -/* ** SAVE MESSAGES ******************************************************* */ - -struct _save_messages_msg { - MailMsg base; - - CamelFolder *folder; - GPtrArray *uids; - gchar *path; - void (*done)(CamelFolder *folder, GPtrArray *uids, gchar *path, gpointer data); - gpointer data; -}; - -static gchar * -save_messages_desc (struct _save_messages_msg *m) -{ - return g_strdup_printf(ngettext("Saving %d message", - "Saving %d messages", m->uids->len), - m->uids->len); -} - -static void -save_prepare_part (CamelMimePart *mime_part) -{ - CamelDataWrapper *wrapper; - gint parts, i; - - wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); - if (!wrapper) - return; - - if (CAMEL_IS_MULTIPART (wrapper)) { - parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper)); - for (i = 0; i < parts; i++) { - CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i); - - save_prepare_part (part); - } - } else { - if (CAMEL_IS_MIME_MESSAGE (wrapper)) { - /* prepare the message parts' subparts */ - save_prepare_part (CAMEL_MIME_PART (wrapper)); - } else { - CamelContentType *type; - - /* We want to save textual parts as 8bit instead of encoded */ - type = camel_data_wrapper_get_mime_type_field (wrapper); - if (camel_content_type_is (type, "text", "*")) - camel_mime_part_set_encoding (mime_part, CAMEL_TRANSFER_ENCODING_8BIT); - } - } -} - -static void -save_messages_exec (struct _save_messages_msg *m, - GCancellable *cancellable, - GError **error) -{ - CamelStream *filtered_stream; - CamelMimeFilter *from_filter; - CamelStream *stream; - gint i; - gchar *from, *path; - - if (strstr (m->path, "://")) - path = m->path; - else - path = g_filename_to_uri (m->path, NULL, NULL); - - stream = camel_stream_vfs_new_with_uri (path, CAMEL_STREAM_VFS_CREATE); - from_filter = camel_mime_filter_from_new (); - filtered_stream = camel_stream_filter_new (stream); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), from_filter); - g_object_unref (from_filter); - - if (path != m->path) - g_free (path); - - for (i=0; i<m->uids->len; i++) { - CamelMimeMessage *message; - gint pc = ((i+1) * 100) / m->uids->len; - - message = camel_folder_get_message_sync ( - m->folder, m->uids->pdata[i], - cancellable, error); - camel_operation_progress (cancellable, pc); - if (message == NULL) - break; - - save_prepare_part (CAMEL_MIME_PART (message)); - - /* we need to flush after each stream write since we are writing to the same fd */ - from = camel_mime_message_build_mbox_from (message); - if (camel_stream_write_string ( - stream, from, - cancellable, error) == -1 - || camel_stream_flush ( - stream, cancellable, error) == -1 - || camel_data_wrapper_write_to_stream_sync ( - (CamelDataWrapper *) message, - (CamelStream *) filtered_stream, - cancellable, error) == -1 - || camel_stream_flush ( - (CamelStream *) filtered_stream, - cancellable, error) == -1 - || camel_stream_write_string ( - stream, "\n", - cancellable, error) == -1 - || camel_stream_flush (stream, - cancellable, error) == -1) { - g_prefix_error ( - error, _("Error saving messages to: %s:\n"), - m->path); - g_free (from); - g_object_unref ((CamelObject *) message); - break; - } - g_free (from); - g_object_unref (message); - } - - g_object_unref (filtered_stream); - g_object_unref (stream); -} - -static void -save_messages_done (struct _save_messages_msg *m) -{ - if (m->done) - m->done (m->folder, m->uids, m->path, m->data); -} - -static void -save_messages_free (struct _save_messages_msg *m) -{ - em_utils_uids_free (m->uids); - g_object_unref (m->folder); - g_free (m->path); -} - -static MailMsgInfo save_messages_info = { - sizeof (struct _save_messages_msg), - (MailMsgDescFunc) save_messages_desc, - (MailMsgExecFunc) save_messages_exec, - (MailMsgDoneFunc) save_messages_done, - (MailMsgFreeFunc) save_messages_free -}; - -gint -mail_save_messages (CamelFolder *folder, GPtrArray *uids, const gchar *path, - void (*done) (CamelFolder *folder, GPtrArray *uids, gchar *path, gpointer data), gpointer data) -{ - struct _save_messages_msg *m; - gint id; - - m = mail_msg_new (&save_messages_info); - m->folder = folder; - g_object_ref (folder); - m->uids = uids; - m->path = g_strdup (path); - m->data = data; - m->done = done; - - id = m->base.seq; - mail_msg_unordered_push (m); - - return id; -} - /* ** Execute Shell Command ***************************************************** */ void diff --git a/mail/mail-ops.h b/mail/mail-ops.h index 2d18b1a24f..520cae61ce 100644 --- a/mail/mail-ops.h +++ b/mail/mail-ops.h @@ -77,11 +77,6 @@ void mail_xfer_folder (const gchar *src_uri, const gchar *dest_uri, gboolean rem CamelFolder *folder, gpointer data), gpointer data); -/* save messages */ -gint mail_save_messages (CamelFolder *folder, GPtrArray *uids, const gchar *path, - void (*done) (CamelFolder *folder, GPtrArray *uids, gchar *path, gpointer data), - gpointer data); - /* yeah so this is messy, but it does a lot, maybe i can consolidate all user_data's to be the one */ void mail_send_queue (EMailSession *session, CamelFolder *queue, diff --git a/mail/mail.error.xml b/mail/mail.error.xml index 82767fca18..0fa46c726c 100644 --- a/mail/mail.error.xml +++ b/mail/mail.error.xml @@ -530,5 +530,10 @@ An mbox account will be created to preserve the old mbox folders. You can delete <_secondary>The reported error was "{0}".</_secondary> </error> + <error id="save-messages" type="error"> + <_primary>Failed to save messages to disk.</_primary> + <_secondary>The reported error was "{0}".</_secondary> + </error> + </error-list> |