[evolution-data-server] CamelDataWrapper: Add GOutputStream-based methods.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] CamelDataWrapper: Add GOutputStream-based methods.
- Date: Sun, 23 Feb 2014 16:09:35 +0000 (UTC)
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]