From b10ef4f2725440f51b9a1136781b42176abacde7 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 9 Sep 2014 13:25:18 +0200 Subject: Bug 724909 - Highlight module hangs on large attachments --- .../e-mail-formatter-text-highlight.c | 176 ++++++++------------- 1 file changed, 66 insertions(+), 110 deletions(-) (limited to 'modules') diff --git a/modules/text-highlight/e-mail-formatter-text-highlight.c b/modules/text-highlight/e-mail-formatter-text-highlight.c index 5946615c27..c35f90a44b 100644 --- a/modules/text-highlight/e-mail-formatter-text-highlight.c +++ b/modules/text-highlight/e-mail-formatter-text-highlight.c @@ -22,10 +22,6 @@ #include "e-mail-formatter-text-highlight.h" #include "languages.h" -/* FIXME Delete these once we can use GSubprocess. */ -#include -#include - #include #include #include @@ -35,7 +31,6 @@ #include #include -#include #include typedef EMailFormatterExtension EMailFormatterTextHighlight; @@ -47,9 +42,10 @@ typedef EExtensionClass EMailFormatterTextHighlightLoaderClass; typedef struct _TextHighlightClosure TextHighlightClosure; struct _TextHighlightClosure { - GMainLoop *main_loop; - GError *input_error; - GError *output_error; + CamelStream *read_stream; + GOutputStream *output_stream; + GCancellable *cancellable; + GError *error; }; GType e_mail_formatter_text_highlight_get_type (void); @@ -122,30 +118,29 @@ get_syntax (EMailPart *part, return syntax; } -static void -text_highlight_input_spliced (GObject *source_object, - GAsyncResult *result, - gpointer user_data) +static gpointer +text_hightlight_read_data_thread (gpointer user_data) { TextHighlightClosure *closure = user_data; + gchar buffer[10240]; - g_output_stream_splice_finish ( - G_OUTPUT_STREAM (source_object), - result, &closure->input_error); -} + g_return_val_if_fail (closure != NULL, NULL); -static void -text_highlight_output_spliced (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - TextHighlightClosure *closure = user_data; + while (!camel_stream_eos (closure->read_stream) && + !g_cancellable_set_error_if_cancelled (closure->cancellable, &closure->error)) { + gssize read; + gsize wrote = 0; - g_output_stream_splice_finish ( - G_OUTPUT_STREAM (source_object), - result, &closure->output_error); + read = camel_stream_read (closure->read_stream, buffer, 10240, closure->cancellable, &closure->error); + if (read < 0 || closure->error) + break; + + if (!g_output_stream_write_all (closure->output_stream, buffer, read, &wrote, closure->cancellable, &closure->error) || + (gssize) wrote != read || closure->error) + break; + } - g_main_loop_quit (closure->main_loop); + return NULL; } static gboolean @@ -157,105 +152,66 @@ text_highlight_feed_data (GOutputStream *output_stream, GError **error) { TextHighlightClosure closure; - GInputStream *input_stream = NULL; - GOutputStream *temp_stream = NULL; - GInputStream *stdout_stream = NULL; - GOutputStream *stdin_stream = NULL; - GMainContext *main_context; - gchar *utf8_data; - gconstpointer data; - gsize size; - gboolean success; - - /* We need to dump CamelDataWrapper to a buffer, force the content - * to valid UTF-8, feed the UTF-8 data to the 'highlight' process, - * read the converted data back and feed it to the CamelStream. */ - - /* FIXME Use GSubprocess once we can require GLib 2.40. */ - - temp_stream = g_memory_output_stream_new_resizable (); - - success = camel_data_wrapper_decode_to_output_stream_sync ( - data_wrapper, temp_stream, cancellable, error); - - if (!success) - goto exit; - - main_context = g_main_context_new (); - - closure.main_loop = g_main_loop_new (main_context, FALSE); - closure.input_error = NULL; - closure.output_error = NULL; + CamelContentType *content_type; + CamelStream *write_stream; + gboolean success = TRUE; + GThread *thread; - g_main_context_push_thread_default (main_context); + closure.read_stream = camel_stream_fs_new_with_fd (pipe_stdout); + closure.output_stream = output_stream; + closure.cancellable = cancellable; + closure.error = NULL; - data = g_memory_output_stream_get_data ( - G_MEMORY_OUTPUT_STREAM (temp_stream)); - size = g_memory_output_stream_get_data_size ( - G_MEMORY_OUTPUT_STREAM (temp_stream)); + write_stream = camel_stream_fs_new_with_fd (pipe_stdin); - /* FIXME Write a GConverter that does this so we can decode - * straight to the stdin pipe and skip all this extra - * buffering. */ - utf8_data = e_util_utf8_data_make_valid ((gchar *) data, size); + thread = g_thread_new (NULL, text_hightlight_read_data_thread, &closure); - g_clear_object (&temp_stream); + content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); + if (content_type) { + const gchar *charset = camel_content_type_param (content_type, "charset"); - /* Takes ownership of the UTF-8 string. */ - input_stream = g_memory_input_stream_new_from_data ( - utf8_data, -1, (GDestroyNotify) g_free); + /* Convert to UTF-8 charset, if needed, which the 'highlight' expects; + it can cope with non-UTF-8 letters, thus no need for a content UTF-8-validation */ + if (charset && g_ascii_strcasecmp (charset, "utf-8") != 0) { + CamelMimeFilter *filter; - stdin_stream = g_unix_output_stream_new (pipe_stdin, TRUE); - stdout_stream = g_unix_input_stream_new (pipe_stdout, TRUE); + filter = camel_mime_filter_charset_new (charset, "UTF-8"); + if (filter != NULL) { + CamelStream *filtered = camel_stream_filter_new (write_stream); - /* Splice the streams together. */ + if (filtered) { + camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter); + g_object_unref (write_stream); + write_stream = filtered; + } - /* GCancellable is only supposed to be used in one operation - * at a time. Skip it here and use it for reading converted - * data, since that operation terminates the main loop. */ - g_output_stream_splice_async ( - stdin_stream, input_stream, - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | - G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - G_PRIORITY_DEFAULT, NULL, - text_highlight_input_spliced, - &closure); - - g_output_stream_splice_async ( - output_stream, stdout_stream, - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | - G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - G_PRIORITY_DEFAULT, cancellable, - text_highlight_output_spliced, - &closure); + g_object_unref (filter); + } + } + } - g_main_loop_run (closure.main_loop); + if (camel_data_wrapper_decode_to_stream_sync (data_wrapper, write_stream, cancellable, error) < 0) { + g_cancellable_cancel (cancellable); + success = FALSE; + } else { + /* Close the stream, thus the highlight knows no more data will come */ + g_clear_object (&write_stream); + } - g_main_context_pop_thread_default (main_context); + g_thread_join (thread); - g_main_context_unref (main_context); - g_main_loop_unref (closure.main_loop); + g_clear_object (&closure.read_stream); + g_clear_object (&write_stream); - g_clear_object (&input_stream); - g_clear_object (&stdin_stream); - g_clear_object (&stdout_stream); + if (closure.error) { + if (error && !*error) + g_propagate_error (error, closure.error); + else + g_clear_error (&closure.error); - if (closure.input_error != NULL) { - g_propagate_error (error, closure.input_error); - g_clear_error (&closure.output_error); - success = FALSE; - goto exit; + return FALSE; } - if (closure.output_error != NULL) { - g_propagate_error (error, closure.output_error); - success = FALSE; - goto exit; - } - -exit: - g_clear_object (&temp_stream); - return success; } -- cgit v1.2.3