[evolution-data-server] CamelDataWrapper: Add GOutputStream-based methods.



commit ee476ad296a7b2815b2a5227b5bc2d5c76e4fab0
Author: Matthew Barnes <mbarnes redhat com>
Date:   Mon Jan 27 10:46:01 2014 -0500

    CamelDataWrapper: Add GOutputStream-based methods.
    
    These will eventually replace their CamelStream-based counterparts.
    
    New functions:
    
      camel_data_wrapper_write_to_output_stream_sync()
      camel_data_wrapper_write_to_output_stream()
      camel_data_wrapper_write_to_output_stream_finish()
      camel_data_wrapper_decode_to_output_stream_sync()
      camel_data_wrapper_decode_to_output_stream()
      camel_data_wrapper_decode_to_output_stream_finish()
    
    Also added/adapted subclass methods in:
    
      CamelMimeMessage
      CamelMimePart
      CamelMultipart
      CamelMultipartSigned

 camel/camel-data-wrapper.c              |  402 ++++++++++++++++++++++++++++++-
 camel/camel-data-wrapper.h              |   44 ++++-
 camel/camel-mime-message.c              |   72 ++++--
 camel/camel-mime-part.c                 |  263 +++++++++++++++++++-
 camel/camel-multipart-signed.c          |  143 +++++++++++
 camel/camel-multipart.c                 |   93 +++++++
 docs/reference/camel/camel-sections.txt |    6 +
 7 files changed, 988 insertions(+), 35 deletions(-)
---
diff --git a/camel/camel-data-wrapper.c b/camel/camel-data-wrapper.c
index 5d3e3b6..291ee51 100644
--- a/camel/camel-data-wrapper.c
+++ b/camel/camel-data-wrapper.c
@@ -27,6 +27,7 @@
 
 #include "camel-data-wrapper.h"
 #include "camel-debug.h"
+#include "camel-filter-output-stream.h"
 #include "camel-mime-filter-basic.h"
 #include "camel-mime-filter-crlf.h"
 #include "camel-stream-filter.h"
@@ -47,6 +48,7 @@ struct _CamelDataWrapperPrivate {
 
 struct _AsyncContext {
        CamelStream *stream;
+       GOutputStream *output_stream;
 };
 
 G_DEFINE_TYPE (CamelDataWrapper, camel_data_wrapper, G_TYPE_OBJECT)
@@ -54,8 +56,8 @@ G_DEFINE_TYPE (CamelDataWrapper, camel_data_wrapper, G_TYPE_OBJECT)
 static void
 async_context_free (AsyncContext *async_context)
 {
-       if (async_context->stream != NULL)
-               g_object_unref (async_context->stream);
+       g_clear_object (&async_context->stream);
+       g_clear_object (&async_context->output_stream);
 
        g_slice_free (AsyncContext, async_context);
 }
@@ -254,6 +256,105 @@ data_wrapper_construct_from_stream_sync (CamelDataWrapper *data_wrapper,
        return (bytes_written >= 0);
 }
 
+static gssize
+data_wrapper_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                          GOutputStream *output_stream,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       GInputStream *input_stream;
+       gssize bytes_written;
+
+       /* XXX Should keep the internal data as a reference-counted
+        *     GBytes to avoid locking while writing to the stream. */
+
+       g_mutex_lock (&data_wrapper->priv->stream_lock);
+
+       /* We retain ownership of the byte array content. */
+       input_stream = g_memory_input_stream_new_from_data (
+               data_wrapper->priv->byte_array->data,
+               data_wrapper->priv->byte_array->len,
+               (GDestroyNotify) NULL);
+
+       bytes_written = g_output_stream_splice (
+               output_stream, input_stream,
+               G_OUTPUT_STREAM_SPLICE_NONE,
+               cancellable, error);
+
+       g_object_unref (input_stream);
+
+       g_mutex_unlock (&data_wrapper->priv->stream_lock);
+
+       return bytes_written;
+}
+
+static gssize
+data_wrapper_decode_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                           GOutputStream *output_stream,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       CamelMimeFilter *filter = NULL;
+       GOutputStream *filter_stream = NULL;
+       gboolean content_type_is_text;
+       gssize bytes_written;
+
+       switch (data_wrapper->encoding) {
+               case CAMEL_TRANSFER_ENCODING_BASE64:
+                       filter = camel_mime_filter_basic_new (
+                               CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+                       filter_stream = camel_filter_output_stream_new (
+                               output_stream, filter);
+                       g_object_unref (filter);
+                       break;
+               case CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE:
+                       filter = camel_mime_filter_basic_new (
+                               CAMEL_MIME_FILTER_BASIC_QP_DEC);
+                       filter_stream = camel_filter_output_stream_new (
+                               output_stream, filter);
+                       g_object_unref (filter);
+                       break;
+               case CAMEL_TRANSFER_ENCODING_UUENCODE:
+                       filter = camel_mime_filter_basic_new (
+                               CAMEL_MIME_FILTER_BASIC_UU_DEC);
+                       filter_stream = camel_filter_output_stream_new (
+                               output_stream, filter);
+                       g_object_unref (filter);
+                       break;
+               default:
+                       /* Write directly to the output stream. */
+                       filter_stream = g_object_ref (output_stream);
+                       break;
+       }
+
+       content_type_is_text =
+               camel_content_type_is (
+               data_wrapper->mime_type, "text", "*") &&
+               !camel_content_type_is (
+               data_wrapper->mime_type, "text", "pdf");
+
+       if (content_type_is_text) {
+               GOutputStream *temp_stream;
+
+               filter = camel_mime_filter_crlf_new (
+                       CAMEL_MIME_FILTER_CRLF_DECODE,
+                       CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
+               temp_stream = camel_filter_output_stream_new (
+                       filter_stream, filter);
+               g_object_unref (filter);
+
+               g_object_unref (filter_stream);
+               filter_stream = temp_stream;
+       }
+
+       bytes_written = camel_data_wrapper_write_to_output_stream_sync (
+               data_wrapper, filter_stream, cancellable, error);
+
+       g_object_unref (filter_stream);
+
+       return bytes_written;
+}
+
 static void
 camel_data_wrapper_class_init (CamelDataWrapperClass *class)
 {
@@ -274,6 +375,8 @@ camel_data_wrapper_class_init (CamelDataWrapperClass *class)
        class->write_to_stream_sync = data_wrapper_write_to_stream_sync;
        class->decode_to_stream_sync = data_wrapper_decode_to_stream_sync;
        class->construct_from_stream_sync = data_wrapper_construct_from_stream_sync;
+       class->write_to_output_stream_sync = data_wrapper_write_to_output_stream_sync;
+       class->decode_to_output_stream_sync = data_wrapper_decode_to_output_stream_sync;
 }
 
 static void
@@ -863,3 +966,298 @@ camel_data_wrapper_construct_from_stream_finish (CamelDataWrapper *data_wrapper,
 
        return g_task_propagate_boolean (G_TASK (result), error);
 }
+
+/**
+ * camel_data_wrapper_write_to_output_stream_sync:
+ * @data_wrapper: a #CamelDataWrapper
+ * @output_stream: a #GOutputStream
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Writes the content of @data_wrapper to @output_stream in a
+ * machine-independent format appropriate for the data.
+ *
+ * <note>
+ *   <para>
+ *     This function may block even if the given output stream does not.
+ *     For example, the content may have to be fetched across a network
+ *     before it can be written to @output_stream.
+ *   </para>
+ * </note>
+ *
+ * Returns: the number of bytes written, or %-1 on error
+ *
+ * Since: 3.12
+ **/
+gssize
+camel_data_wrapper_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+       CamelDataWrapperClass *class;
+       gssize bytes_written;
+
+       g_return_val_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper), -1);
+       g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), -1);
+
+       class = CAMEL_DATA_WRAPPER_GET_CLASS (data_wrapper);
+       g_return_val_if_fail (class->write_to_output_stream_sync != NULL, -1);
+
+       bytes_written = class->write_to_output_stream_sync (
+               data_wrapper, output_stream, cancellable, error);
+       CAMEL_CHECK_GERROR (
+               data_wrapper, write_to_output_stream_sync,
+               bytes_written >= 0, error);
+
+       return bytes_written;
+}
+
+/* Helper for camel_data_wrapper_write_to_output_stream() */
+static void
+data_wrapper_write_to_output_stream_thread (GTask *task,
+                                            gpointer source_object,
+                                            gpointer task_data,
+                                            GCancellable *cancellable)
+{
+       gssize bytes_written;
+       AsyncContext *async_context;
+       GError *local_error = NULL;
+
+       async_context = (AsyncContext *) task_data;
+
+       bytes_written = camel_data_wrapper_write_to_output_stream_sync (
+               CAMEL_DATA_WRAPPER (source_object),
+               async_context->output_stream,
+               cancellable, &local_error);
+
+       if (local_error != NULL) {
+               g_task_return_error (task, local_error);
+       } else {
+               g_task_return_int (task, bytes_written);
+       }
+}
+
+/**
+ * camel_data_wrapper_write_to_output_stream:
+ * @data_wrapper: a #CamelDataWrapper
+ * @output_stream: a #GOutputStream
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously writes the content of @data_wrapper to @output_stream in
+ * a machine-independent format appropriate for the data.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call camel_data_wrapper_write_to_output_stream_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.12
+ **/
+void
+camel_data_wrapper_write_to_output_stream (CamelDataWrapper *data_wrapper,
+                                           GOutputStream *output_stream,
+                                           gint io_priority,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+       GTask *task;
+       AsyncContext *async_context;
+
+       g_return_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper));
+       g_return_if_fail (G_IS_OUTPUT_STREAM (output_stream));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->output_stream = g_object_ref (output_stream);
+
+       task = g_task_new (data_wrapper, cancellable, callback, user_data);
+       g_task_set_source_tag (task, camel_data_wrapper_write_to_output_stream);
+       g_task_set_priority (task, io_priority);
+
+       g_task_set_task_data (
+               task, async_context,
+               (GDestroyNotify) async_context_free);
+
+       g_task_run_in_thread (
+               task, data_wrapper_write_to_output_stream_thread);
+
+       g_object_unref (task);
+}
+
+/**
+ * camel_data_wrapper_write_to_output_stream_finish:
+ * @data_wrapper: a #CamelDataWrapper
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with
+ * camel_data_wrapper_write_to_output_stream().
+ *
+ * Returns: the number of bytes written, or %-1 on error
+ *
+ * Since: 3.12
+ **/
+gssize
+camel_data_wrapper_write_to_output_stream_finish (CamelDataWrapper *data_wrapper,
+                                                  GAsyncResult *result,
+                                                  GError **error)
+{
+       g_return_val_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper), -1);
+       g_return_val_if_fail (g_task_is_valid (result, data_wrapper), -1);
+
+       g_return_val_if_fail (
+               g_async_result_is_tagged (
+               result, camel_data_wrapper_write_to_output_stream), -1);
+
+       return g_task_propagate_int (G_TASK (result), error);
+}
+
+/**
+ * camel_data_wrapper_decode_to_output_stream_sync:
+ * @data_wrapper: a #CamelDataWrapper
+ * @output_stream: a #GOutputStream
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Writes the decoded data content to @output_stream.
+ *
+ * <note>
+ *   <para>
+ *     This function may block even if the given output stream does not.
+ *     For example, the content may have to be fetched across a network
+ *     before it can be written to @output_stream.
+ *   </para>
+ * </note>
+ *
+ * Returns: the number of bytes written, or %-1 on error
+ *
+ * Since: 3.12
+ **/
+gssize
+camel_data_wrapper_decode_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                                 GOutputStream *output_stream,
+                                                 GCancellable *cancellable,
+                                                 GError **error)
+{
+       CamelDataWrapperClass *class;
+       gssize bytes_written;
+
+       g_return_val_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper), -1);
+       g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), -1);
+
+       class = CAMEL_DATA_WRAPPER_GET_CLASS (data_wrapper);
+       g_return_val_if_fail (class->decode_to_output_stream_sync != NULL, -1);
+
+       bytes_written = class->decode_to_output_stream_sync (
+               data_wrapper, output_stream, cancellable, error);
+       CAMEL_CHECK_GERROR (
+               data_wrapper, decode_to_output_stream_sync,
+               bytes_written >= 0, error);
+
+       return bytes_written;
+}
+
+/* Helper for camel_data_wrapper_decode_to_output_stream() */
+static void
+data_wrapper_decode_to_output_stream_thread (GTask *task,
+                                             gpointer source_object,
+                                             gpointer task_data,
+                                             GCancellable *cancellable)
+{
+       gssize bytes_written;
+       AsyncContext *async_context;
+       GError *local_error = NULL;
+
+       async_context = (AsyncContext *) task_data;
+
+       bytes_written = camel_data_wrapper_decode_to_output_stream_sync (
+               CAMEL_DATA_WRAPPER (source_object),
+               async_context->output_stream,
+               cancellable, &local_error);
+
+       if (local_error != NULL) {
+               g_task_return_error (task, local_error);
+       } else {
+               g_task_return_int (task, bytes_written);
+       }
+}
+
+/**
+ * camel_data_wrapper_decode_to_output_stream:
+ * @data_wrapper: a #CamelDataWrapper
+ * @output_stream: a #GOutputStream
+ * @io_priority: the I/O priority of the request
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously writes the decoded data content to @output_stream.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call camel_data_wrapper_decode_to_output_stream_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.12
+ **/
+void
+camel_data_wrapper_decode_to_output_stream (CamelDataWrapper *data_wrapper,
+                                            GOutputStream *output_stream,
+                                            gint io_priority,
+                                            GCancellable *cancellable,
+                                            GAsyncReadyCallback callback,
+                                            gpointer user_data)
+{
+       GTask *task;
+       AsyncContext *async_context;
+
+       g_return_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper));
+       g_return_if_fail (G_IS_OUTPUT_STREAM (output_stream));
+
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->output_stream = g_object_ref (output_stream);
+
+       task = g_task_new (data_wrapper, cancellable, callback, user_data);
+       g_task_set_source_tag (task, camel_data_wrapper_decode_to_output_stream);
+       g_task_set_priority (task, io_priority);
+
+       g_task_set_task_data (
+               task, async_context,
+               (GDestroyNotify) async_context_free);
+
+       g_task_run_in_thread (
+               task, data_wrapper_decode_to_output_stream_thread);
+
+       g_object_unref (task);
+}
+
+/**
+ * camel_data_wrapper_decode_to_output_stream_finish:
+ * @data_wrapper: a #CamelDataWrapper
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with
+ * camel_data_wrapper_decode_to_output_stream().
+ *
+ * Returns: the number of bytes written, or %-1 on error
+ *
+ * Since: 3.12
+ **/
+gssize
+camel_data_wrapper_decode_to_output_stream_finish (CamelDataWrapper *data_wrapper,
+                                                   GAsyncResult *result,
+                                                   GError **error)
+{
+       g_return_val_if_fail (CAMEL_IS_DATA_WRAPPER (data_wrapper), -1);
+       g_return_val_if_fail (g_task_is_valid (result, data_wrapper), -1);
+
+       g_return_val_if_fail (
+               g_async_result_is_tagged (
+               result, camel_data_wrapper_decode_to_output_stream), -1);
+
+       return g_task_propagate_int (G_TASK (result), error);
+}
+
diff --git a/camel/camel-data-wrapper.h b/camel/camel-data-wrapper.h
index 8913371..9199167 100644
--- a/camel/camel-data-wrapper.h
+++ b/camel/camel-data-wrapper.h
@@ -95,9 +95,19 @@ struct _CamelDataWrapperClass {
                                                 CamelStream *stream,
                                                 GCancellable *cancellable,
                                                 GError **error);
+       gssize          (*write_to_output_stream_sync)
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                GCancellable *cancellable,
+                                                GError **error);
+       gssize          (*decode_to_output_stream_sync)
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
        /* Reserved slots. */
-       gpointer reserved[6];
+       gpointer reserved[4];
 };
 
 GType          camel_data_wrapper_get_type     (void);
@@ -166,6 +176,38 @@ gboolean   camel_data_wrapper_construct_from_stream_finish
                                                (CamelDataWrapper *data_wrapper,
                                                 GAsyncResult *result,
                                                 GError **error);
+gssize         camel_data_wrapper_write_to_output_stream_sync
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           camel_data_wrapper_write_to_output_stream
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                gint io_priority,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gssize         camel_data_wrapper_write_to_output_stream_finish
+                                               (CamelDataWrapper *data_wrapper,
+                                                GAsyncResult *result,
+                                                GError **error);
+gssize         camel_data_wrapper_decode_to_output_stream_sync
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           camel_data_wrapper_decode_to_output_stream
+                                               (CamelDataWrapper *data_wrapper,
+                                                GOutputStream *output_stream,
+                                                gint io_priority,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gssize         camel_data_wrapper_decode_to_output_stream_finish
+                                               (CamelDataWrapper *data_wrapper,
+                                                GAsyncResult *result,
+                                                GError **error);
 
 G_END_DECLS
 
diff --git a/camel/camel-mime-message.c b/camel/camel-mime-message.c
index f2a156f..eaac94a 100644
--- a/camel/camel-mime-message.c
+++ b/camel/camel-mime-message.c
@@ -179,6 +179,32 @@ unref_recipient (gpointer key,
 }
 
 static void
+mime_message_ensure_required_headers (CamelMimeMessage *message)
+{
+       CamelMedium *medium = CAMEL_MEDIUM (message);
+
+       if (message->from == NULL) {
+               /* FIXME: should we just abort?  Should we make one up? */
+               g_warning ("No from set for message");
+               camel_medium_set_header (medium, "From", "");
+       }
+       if (!camel_medium_get_header (medium, "Date"))
+               camel_mime_message_set_date (
+                       message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+
+       if (message->subject == NULL)
+               camel_mime_message_set_subject (message, "No Subject");
+
+       if (message->message_id == NULL)
+               camel_mime_message_set_message_id (message, NULL);
+
+       /* FIXME: "To" header needs to be set explicitly as well ... */
+
+       if (!camel_medium_get_header (medium, "Mime-Version"))
+               camel_medium_set_header (medium, "Mime-Version", "1.0");
+}
+
+static void
 mime_message_dispose (GObject *object)
 {
        CamelMimeMessage *message = CAMEL_MIME_MESSAGE (object);
@@ -219,34 +245,32 @@ mime_message_write_to_stream_sync (CamelDataWrapper *data_wrapper,
                                    GCancellable *cancellable,
                                    GError **error)
 {
-       CamelDataWrapperClass *data_wrapper_class;
-       CamelMimeMessage *mm = CAMEL_MIME_MESSAGE (data_wrapper);
+       CamelMimeMessage *message;
 
-       /* force mandatory headers ... */
-       if (mm->from == NULL) {
-               /* FIXME: should we just abort?  Should we make one up? */
-               g_warning ("No from set for message");
-               camel_medium_set_header ((CamelMedium *) mm, "From", "");
-       }
-       if (!camel_medium_get_header ((CamelMedium *) mm, "Date"))
-               camel_mime_message_set_date (mm, CAMEL_MESSAGE_DATE_CURRENT, 0);
+       message = CAMEL_MIME_MESSAGE (data_wrapper);
+       mime_message_ensure_required_headers (message);
 
-       if (mm->subject == NULL)
-               camel_mime_message_set_subject (mm, "No Subject");
-
-       if (mm->message_id == NULL)
-               camel_mime_message_set_message_id (mm, NULL);
+       /* Chain up to parent's write_to_stream_sync() method. */
+       return CAMEL_DATA_WRAPPER_CLASS (camel_mime_message_parent_class)->
+               write_to_stream_sync (
+               data_wrapper, stream, cancellable, error);
+}
 
-       /* FIXME: "To" header needs to be set explicitly as well ... */
+static gssize
+mime_message_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                          GOutputStream *output_stream,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       CamelMimeMessage *message;
 
-       if (!camel_medium_get_header ((CamelMedium *) mm, "Mime-Version"))
-               camel_medium_set_header ((CamelMedium *) mm, "Mime-Version", "1.0");
+       message = CAMEL_MIME_MESSAGE (data_wrapper);
+       mime_message_ensure_required_headers (message);
 
-       /* Chain up to parent's write_to_stream_sync() method. */
-       data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (
-               camel_mime_message_parent_class);
-       return data_wrapper_class->write_to_stream_sync (
-               data_wrapper, stream, cancellable, error);
+       /* Chain up to parent's write_to_output_stream_sync() method. */
+       return CAMEL_DATA_WRAPPER_CLASS (camel_mime_message_parent_class)->
+               write_to_output_stream_sync (
+               data_wrapper, output_stream, cancellable, error);
 }
 
 static void
@@ -351,6 +375,8 @@ camel_mime_message_class_init (CamelMimeMessageClass *class)
        data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
        data_wrapper_class->write_to_stream_sync = mime_message_write_to_stream_sync;
        data_wrapper_class->decode_to_stream_sync = mime_message_write_to_stream_sync;
+       data_wrapper_class->write_to_output_stream_sync = mime_message_write_to_output_stream_sync;
+       data_wrapper_class->decode_to_output_stream_sync = mime_message_write_to_output_stream_sync;
 
        medium_class = CAMEL_MEDIUM_CLASS (class);
        medium_class->add_header = mime_message_add_header;
diff --git a/camel/camel-mime-part.c b/camel/camel-mime-part.c
index f71a110..5563f6e 100644
--- a/camel/camel-mime-part.c
+++ b/camel/camel-mime-part.c
@@ -33,6 +33,7 @@
 #include "camel-charset-map.h"
 #include "camel-debug.h"
 #include "camel-iconv.h"
+#include "camel-filter-output-stream.h"
 #include "camel-mime-filter-basic.h"
 #include "camel-mime-filter-charset.h"
 #include "camel-mime-filter-crlf.h"
@@ -104,14 +105,14 @@ async_context_free (AsyncContext *async_context)
 }
 
 static gssize
-write_header (CamelStream *stream,
+write_header (gpointer stream,
               const gchar *name,
               const gchar *value,
               GCancellable *cancellable,
               GError **error)
 {
        GString *buffer;
-       gssize n_written;
+       gssize n_written = 0;
 
        buffer = g_string_new (name);
        g_string_append_c (buffer, ':');
@@ -120,8 +121,28 @@ write_header (CamelStream *stream,
        g_string_append (buffer, value);
        g_string_append_c (buffer, '\n');
 
-       n_written = camel_stream_write (
-               stream, buffer->str, buffer->len, cancellable, error);
+       /* XXX For now we handle both types of streams. */
+
+       if (CAMEL_IS_STREAM (stream)) {
+               n_written = camel_stream_write (
+                       CAMEL_STREAM (stream),
+                       buffer->str, buffer->len,
+                       cancellable, error);
+       } else if (G_IS_OUTPUT_STREAM (stream)) {
+               gboolean success;
+               gsize bytes_written = 0;
+
+               success = g_output_stream_write_all (
+                       G_OUTPUT_STREAM (stream),
+                       buffer->str, buffer->len,
+                       &bytes_written, cancellable, error);
+               if (success)
+                       n_written = (gssize) bytes_written;
+               else
+                       n_written = -1;
+       } else {
+               g_warn_if_reached ();
+       }
 
        g_string_free (buffer, TRUE);
 
@@ -129,7 +150,7 @@ write_header (CamelStream *stream,
 }
 
 static gssize
-write_references (CamelStream *stream,
+write_references (gpointer stream,
                   const gchar *name,
                   const gchar *value,
                   GCancellable *cancellable,
@@ -137,7 +158,7 @@ write_references (CamelStream *stream,
 {
        GString *buffer;
        const gchar *ids, *ide;
-       gssize n_written;
+       gssize n_written = 0;
        gsize len;
 
        /* this is only approximate, based on the next >, this way it retains
@@ -171,8 +192,28 @@ write_references (CamelStream *stream,
 
        g_string_append_c (buffer, '\n');
 
-       n_written = camel_stream_write (
-               stream, buffer->str, buffer->len, cancellable, error);
+       /* XXX For now we handle both types of streams. */
+
+       if (CAMEL_IS_STREAM (stream)) {
+               n_written = camel_stream_write (
+                       CAMEL_STREAM (stream),
+                       buffer->str, buffer->len,
+                       cancellable, error);
+       } else if (G_IS_OUTPUT_STREAM (stream)) {
+               gboolean success;
+               gsize bytes_written = 0;
+
+               success = g_output_stream_write_all (
+                       G_OUTPUT_STREAM (stream),
+                       buffer->str, buffer->len,
+                       &bytes_written, cancellable, error);
+               if (success)
+                       n_written = (gssize) bytes_written;
+               else
+                       n_written = -1;
+       } else {
+               g_warn_if_reached ();
+       }
 
        g_string_free (buffer, TRUE);
 
@@ -540,7 +581,7 @@ mime_part_write_to_stream_sync (CamelDataWrapper *dw,
                struct _camel_header_raw *h = mp->headers;
                gchar *val;
                gssize (*writefn) (
-                       CamelStream *stream,
+                       gpointer stream,
                        const gchar *name,
                        const gchar *value,
                        GCancellable *cancellable,
@@ -721,6 +762,209 @@ mime_part_construct_from_stream_sync (CamelDataWrapper *dw,
        return success;
 }
 
+static gssize
+mime_part_write_to_output_stream_sync (CamelDataWrapper *dw,
+                                       GOutputStream *output_stream,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+       CamelMimePart *mp = CAMEL_MIME_PART (dw);
+       CamelMedium *medium = CAMEL_MEDIUM (dw);
+       CamelDataWrapper *content;
+       gsize bytes_written;
+       gssize total = 0;
+       gssize result;
+       gboolean success;
+
+       d (printf ("mime_part::write_to_stream\n"));
+
+       /* FIXME: something needs to be done about this ... */
+       /* TODO: content-languages header? */
+
+       if (mp->headers) {
+               struct _camel_header_raw *h = mp->headers;
+               gchar *val;
+               gssize (*writefn) (
+                       gpointer stream,
+                       const gchar *name,
+                       const gchar *value,
+                       GCancellable *cancellable,
+                       GError **error);
+
+               /* fold/write the headers.   But dont fold headers that are already formatted
+                * (e.g. ones with parameter-lists, that we know about, and have created) */
+               while (h) {
+                       val = h->value;
+                       if (val == NULL) {
+                               g_warning ("h->value is NULL here for %s", h->name);
+                               bytes_written = 0;
+                       } else if ((writefn = g_hash_table_lookup (header_formatted_table, h->name)) == NULL) 
{
+                               val = camel_header_fold (val, strlen (h->name));
+                               result = write_header (
+                                       output_stream, h->name, val,
+                                       cancellable, error);
+                               g_free (val);
+                       } else {
+                               result = writefn (
+                                       output_stream, h->name, h->value,
+                                       cancellable, error);
+                       }
+                       if (result == -1)
+                               return -1;
+                       total += result;
+                       h = h->next;
+               }
+       }
+
+       success = g_output_stream_write_all (
+               output_stream, "\n", 1,
+               &bytes_written, cancellable, error);
+       if (!success)
+               return -1;
+       total += (gssize) bytes_written;
+
+       content = camel_medium_get_content (medium);
+       if (content) {
+               CamelMimeFilter *filter = NULL;
+               GOutputStream *filter_stream;
+               const gchar *content_charset = NULL;
+               const gchar *part_charset = NULL;
+               gboolean content_type_is_text;
+               gboolean uuencoded = FALSE;
+               gboolean reencode = FALSE;
+               const gchar *filename;
+
+               content_type_is_text =
+                       camel_content_type_is (dw->mime_type, "text", "*");
+
+               if (content_type_is_text) {
+                       content_charset = camel_content_type_param (content->mime_type, "charset");
+                       part_charset = camel_content_type_param (dw->mime_type, "charset");
+
+                       if (content_charset && part_charset) {
+                               content_charset = camel_iconv_charset_name (content_charset);
+                               part_charset = camel_iconv_charset_name (part_charset);
+                       }
+               }
+
+               if (mp->priv->encoding != content->encoding) {
+                       gchar *content;
+
+                       switch (mp->priv->encoding) {
+                       case CAMEL_TRANSFER_ENCODING_BASE64:
+                               filter = camel_mime_filter_basic_new (
+                                       CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
+                               break;
+                       case CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE:
+                               filter = camel_mime_filter_basic_new (
+                                       CAMEL_MIME_FILTER_BASIC_QP_ENC);
+                               break;
+                       case CAMEL_TRANSFER_ENCODING_UUENCODE:
+                               filename = camel_mime_part_get_filename (mp);
+                               if (filename == NULL)
+                                       filename = "untitled";
+
+                               content = g_strdup_printf (
+                                       "begin 644 %s\n", filename);
+                               success = g_output_stream_write_all (
+                                       output_stream,
+                                       content, strlen (content),
+                                       &bytes_written, cancellable, error);
+                               g_free (content);
+
+                               if (!success)
+                                       return -1;
+
+                               uuencoded = TRUE;
+
+                               total += bytes_written;
+                               filter = camel_mime_filter_basic_new (
+                                       CAMEL_MIME_FILTER_BASIC_UU_ENC);
+                               break;
+                       default:
+                               /* content is encoded but the part doesn't want to be... */
+                               reencode = TRUE;
+                               break;
+                       }
+               }
+
+               filter_stream = g_object_ref (output_stream);
+
+               if (content_charset && part_charset && part_charset != content_charset) {
+                       CamelMimeFilter *charenc;
+                       GOutputStream *temp_stream;
+
+                       charenc = camel_mime_filter_charset_new (
+                               content_charset, part_charset);
+                       temp_stream = camel_filter_output_stream_new (
+                               filter_stream, charenc);
+                       g_object_unref (charenc);
+
+                       g_object_unref (filter_stream);
+                       filter_stream = temp_stream;
+
+                       reencode = TRUE;
+               }
+
+               if (filter != NULL && content_type_is_text) {
+                       CamelMimeFilter *crlf;
+                       GOutputStream *temp_stream;
+
+                       crlf = camel_mime_filter_crlf_new (
+                               CAMEL_MIME_FILTER_CRLF_ENCODE,
+                               CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
+                       temp_stream = camel_filter_output_stream_new (
+                               filter_stream, crlf);
+                       g_object_unref (crlf);
+
+                       g_object_unref (filter_stream);
+                       filter_stream = temp_stream;
+
+                       reencode = TRUE;
+               }
+
+               if (filter != NULL) {
+                       GOutputStream *temp_stream;
+
+                       temp_stream = camel_filter_output_stream_new (
+                               filter_stream, filter);
+                       g_object_unref (filter);
+
+                       g_object_unref (filter_stream);
+                       filter_stream = temp_stream;
+
+                       reencode = TRUE;
+               }
+
+               if (reencode)
+                       result = camel_data_wrapper_decode_to_output_stream_sync (
+                               content, filter_stream, cancellable, error);
+               else
+                       result = camel_data_wrapper_write_to_output_stream_sync (
+                               content, filter_stream, cancellable, error);
+
+               g_object_unref (filter_stream);
+
+               if (result == -1)
+                       return -1;
+
+               total += result;
+
+               if (uuencoded) {
+                       success = g_output_stream_write_all (
+                               output_stream, "end\n", 4,
+                               &bytes_written, cancellable, error);
+                       if (!success)
+                               return -1;
+                       total += (gssize) bytes_written;
+               }
+       } else {
+               g_warning ("No content for medium, nothing to write");
+       }
+
+       return total;
+}
+
 static gboolean
 mime_part_construct_from_parser_sync (CamelMimePart *mime_part,
                                       CamelMimeParser *parser,
@@ -808,6 +1052,7 @@ camel_mime_part_class_init (CamelMimePartClass *class)
        data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
        data_wrapper_class->write_to_stream_sync = mime_part_write_to_stream_sync;
        data_wrapper_class->construct_from_stream_sync = mime_part_construct_from_stream_sync;
+       data_wrapper_class->write_to_output_stream_sync = mime_part_write_to_output_stream_sync;
 
        class->construct_from_parser_sync = mime_part_construct_from_parser_sync;
 
diff --git a/camel/camel-multipart-signed.c b/camel/camel-multipart-signed.c
index 47d73e4..a02200f 100644
--- a/camel/camel-multipart-signed.c
+++ b/camel/camel-multipart-signed.c
@@ -387,6 +387,147 @@ multipart_signed_construct_from_stream_sync (CamelDataWrapper *data_wrapper,
        return success;
 }
 
+static gssize
+multipart_signed_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                              GOutputStream *output_stream,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+       CamelMultipartSignedPrivate *priv;
+       CamelMultipart *mp = (CamelMultipart *) data_wrapper;
+       GByteArray *byte_array;
+       const gchar *boundary;
+       const gchar *preface;
+       const gchar *postface;
+       gsize bytes_written;
+       gssize total = 0;
+       gssize result;
+       gchar *content;
+       gboolean success;
+
+       priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (data_wrapper);
+
+       byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
+
+       /* we have 3 basic cases:
+        * 1. constructed, we write out the data wrapper stream we got
+        * 2. signed content, we create and write out a new stream
+        * 3. invalid
+        */
+
+       /* 1 */
+       /* FIXME: locking? */
+       if (byte_array->len > 0) {
+               success = g_output_stream_write_all (
+                       output_stream,
+                       byte_array->data, byte_array->len,
+                       &bytes_written, cancellable, error);
+               return success ? (gssize) bytes_written : -1;
+       }
+
+       /* 3 */
+       if (priv->contentraw == NULL) {
+               g_set_error (
+                       error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+                       _("No content available"));
+               return -1;
+       }
+
+       /* 3 */
+       if (priv->signature == NULL) {
+               g_set_error (
+                       error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+                       _("No signature available"));
+               return -1;
+       }
+
+       boundary = camel_multipart_get_boundary (mp);
+       preface = camel_multipart_get_preface (mp);
+       postface = camel_multipart_get_postface (mp);
+
+       /* 2 */
+       if (preface != NULL) {
+               success = g_output_stream_write_all (
+                       output_stream,
+                       preface, strlen (preface),
+                       &bytes_written, cancellable, error);
+               if (!success)
+                       return -1;
+               total += (gssize) bytes_written;
+       }
+
+       /* first boundary */
+       content = g_strdup_printf ("\n--%s\n", boundary);
+       success = g_output_stream_write_all (
+               output_stream,
+               content, strlen (content),
+               &bytes_written, cancellable, error);
+       g_free (content);
+       if (!success)
+               return -1;
+       total += (gssize) bytes_written;
+
+       /* output content part */
+       /* XXX Both CamelGpgContext and CamelSMIMEContext set this
+        *     to a memory stream, so we'll assume it to be so and
+        *     grab its GByteArray.  Would be better to store the
+        *     raw message content as a reference-counted GBytes
+        *     rather than a stream. */
+       g_return_val_if_fail (CAMEL_IS_STREAM_MEM (priv->contentraw), -1);
+       byte_array = camel_stream_mem_get_byte_array (
+               CAMEL_STREAM_MEM (priv->contentraw));
+       success = g_output_stream_write_all (
+               output_stream,
+               byte_array->data, byte_array->len,
+               &bytes_written, cancellable, error);
+       if (!success)
+               return -1;
+       total += (gssize) bytes_written;
+
+       /* boundary */
+       content = g_strdup_printf ("\n--%s\n", boundary);
+       success = g_output_stream_write_all (
+               output_stream,
+               content, strlen (content),
+               &bytes_written, cancellable, error);
+       g_free (content);
+       if (!success)
+               return -1;
+       total += (gssize) bytes_written;
+
+       /* signature */
+       result = camel_data_wrapper_write_to_output_stream_sync (
+               CAMEL_DATA_WRAPPER (priv->signature),
+               output_stream, cancellable, error);
+       if (result == -1)
+               return -1;
+       total += result;
+
+       /* write the terminating boudary delimiter */
+       content = g_strdup_printf ("\n--%s--\n", boundary);
+       success = g_output_stream_write_all (
+               output_stream,
+               content, strlen (content),
+               &bytes_written, cancellable, error);
+       g_free (content);
+       if (!success)
+               return -1;
+       total += (gssize) bytes_written;
+
+       /* and finally the postface */
+       if (postface != NULL) {
+               success = g_output_stream_write_all (
+                       output_stream,
+                       postface, strlen (postface),
+                       &bytes_written, cancellable, error);
+               if (!success)
+                       return -1;
+               total += (gssize) bytes_written;
+       }
+
+       return total;
+}
+
 static void
 multipart_signed_add_part (CamelMultipart *multipart,
                            CamelMimePart *part)
@@ -551,6 +692,8 @@ camel_multipart_signed_class_init (CamelMultipartSignedClass *class)
        data_wrapper_class->write_to_stream_sync = multipart_signed_write_to_stream_sync;
        data_wrapper_class->decode_to_stream_sync = multipart_signed_write_to_stream_sync;
        data_wrapper_class->construct_from_stream_sync = multipart_signed_construct_from_stream_sync;
+       data_wrapper_class->write_to_output_stream_sync = multipart_signed_write_to_output_stream_sync;
+       data_wrapper_class->decode_to_output_stream_sync = multipart_signed_write_to_output_stream_sync;
 
        multipart_class = CAMEL_MULTIPART_CLASS (class);
        multipart_class->add_part = multipart_signed_add_part;
diff --git a/camel/camel-multipart.c b/camel/camel-multipart.c
index d8d2691..22972d1 100644
--- a/camel/camel-multipart.c
+++ b/camel/camel-multipart.c
@@ -177,6 +177,97 @@ multipart_write_to_stream_sync (CamelDataWrapper *data_wrapper,
        return total;
 }
 
+/* this is MIME specific, doesn't belong here really */
+static gssize
+multipart_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
+                                       GOutputStream *output_stream,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+       CamelMultipartPrivate *priv;
+       const gchar *boundary;
+       gchar *content;
+       gsize bytes_written;
+       gssize total = 0;
+       gboolean success;
+       guint ii;
+
+       priv = CAMEL_MULTIPART_GET_PRIVATE (data_wrapper);
+
+       /* get the bundary text */
+       boundary = camel_multipart_get_boundary (
+               CAMEL_MULTIPART (data_wrapper));
+
+       /* we cannot write a multipart without a boundary string */
+       g_return_val_if_fail (boundary, -1);
+
+       /*
+        * write the preface text (usually something like
+        *   "This is a mime message, if you see this, then
+        *    your mail client probably doesn't support ...."
+        */
+       if (priv->preface != NULL) {
+               success = g_output_stream_write_all (
+                       output_stream,
+                       priv->preface, strlen (priv->preface),
+                       &bytes_written, cancellable, error);
+               if (!success)
+                       return -1;
+               total += (gsize) bytes_written;
+       }
+
+       /*
+        * Now, write all the parts, separated by the boundary
+        * delimiter
+        */
+       for (ii = 0; ii < priv->parts->len; ii++) {
+               CamelDataWrapper *part;
+               gssize result;
+
+               part = g_ptr_array_index (priv->parts, ii);
+
+               content = g_strdup_printf ("\n--%s\n", boundary);
+               success = g_output_stream_write_all (
+                       output_stream,
+                       content, strlen (content),
+                       &bytes_written, cancellable, error);
+               g_free (content);
+               if (!success)
+                       return -1;
+               total += (gsize) bytes_written;
+
+               result = camel_data_wrapper_write_to_output_stream_sync (
+                       part, output_stream, cancellable, error);
+               if (result == -1)
+                       return -1;
+               total += result;
+       }
+
+       /* write the terminating boudary delimiter */
+       content = g_strdup_printf ("\n--%s--\n", boundary);
+       success = g_output_stream_write_all (
+               output_stream,
+               content, strlen (content),
+               &bytes_written, cancellable, error);
+       g_free (content);
+       if (!success)
+               return -1;
+       total += (gsize) bytes_written;
+
+       /* and finally the postface */
+       if (priv->postface != NULL) {
+               success = g_output_stream_write_all (
+                       output_stream,
+                       priv->postface, strlen (priv->postface),
+                       &bytes_written, cancellable, error);
+               if (!success)
+                       return -1;
+               total += (gsize) bytes_written;
+       }
+
+       return total;
+}
+
 static void
 multipart_add_part (CamelMultipart *multipart,
                     CamelMimePart *part)
@@ -307,6 +398,8 @@ camel_multipart_class_init (CamelMultipartClass *class)
        data_wrapper_class->is_offline = multipart_is_offline;
        data_wrapper_class->write_to_stream_sync = multipart_write_to_stream_sync;
        data_wrapper_class->decode_to_stream_sync = multipart_write_to_stream_sync;
+       data_wrapper_class->write_to_output_stream_sync = multipart_write_to_output_stream_sync;
+       data_wrapper_class->decode_to_output_stream_sync = multipart_write_to_output_stream_sync;
 
        class->add_part = multipart_add_part;
        class->get_part = multipart_get_part;
diff --git a/docs/reference/camel/camel-sections.txt b/docs/reference/camel/camel-sections.txt
index 0ddef4c..0c6a9ee 100644
--- a/docs/reference/camel/camel-sections.txt
+++ b/docs/reference/camel/camel-sections.txt
@@ -220,6 +220,12 @@ camel_data_wrapper_decode_to_stream_finish
 camel_data_wrapper_construct_from_stream_sync
 camel_data_wrapper_construct_from_stream
 camel_data_wrapper_construct_from_stream_finish
+camel_data_wrapper_write_to_output_stream_sync
+camel_data_wrapper_write_to_output_stream
+camel_data_wrapper_write_to_output_stream_finish
+camel_data_wrapper_decode_to_output_stream_sync
+camel_data_wrapper_decode_to_output_stream
+camel_data_wrapper_decode_to_output_stream_finish
 <SUBSECTION Standard>
 CAMEL_DATA_WRAPPER
 CAMEL_IS_DATA_WRAPPER



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