/* * camel-filter-output-stream.c * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * */ /** * SECTION: camel-filter-output-stream * @short_description: Filtered output stream * @include: camel/camel.h * @see_also: #GOutputStream, #CamelMimeFilter * * #CamelFilterOutputStream is similar to #GConverterOutputStream, except it * operates on a #CamelMimeFilter instead of a #GConverter. * * This class is meant to be a temporary solution until all of Camel's MIME * filters are ported to the #GConverter interface. **/ #include "camel-filter-output-stream.h" #include #define CAMEL_FILTER_OUTPUT_STREAM_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_FILTER_OUTPUT_STREAM, CamelFilterOutputStreamPrivate)) #define READ_PAD (128) /* bytes padded before buffer */ #define READ_SIZE (4096) struct _CamelFilterOutputStreamPrivate { CamelMimeFilter *filter; }; enum { PROP_0, PROP_FILTER }; G_DEFINE_TYPE ( CamelFilterOutputStream, camel_filter_output_stream, G_TYPE_FILTER_OUTPUT_STREAM) static void filter_output_stream_set_filter (CamelFilterOutputStream *filter_stream, CamelMimeFilter *filter) { g_return_if_fail (CAMEL_IS_MIME_FILTER (filter)); g_return_if_fail (filter_stream->priv->filter == NULL); filter_stream->priv->filter = g_object_ref (filter); } static void filter_output_stream_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_FILTER: filter_output_stream_set_filter ( CAMEL_FILTER_OUTPUT_STREAM (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void filter_output_stream_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_FILTER: g_value_set_object ( value, camel_filter_output_stream_get_filter ( CAMEL_FILTER_OUTPUT_STREAM (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void filter_output_stream_dispose (GObject *object) { CamelFilterOutputStreamPrivate *priv; priv = CAMEL_FILTER_OUTPUT_STREAM_GET_PRIVATE (object); /* XXX GOutputStream calls flush() one last time during * dispose(), so chain up before clearing our filter. */ G_OBJECT_CLASS (camel_filter_output_stream_parent_class)-> dispose (object); g_clear_object (&priv->filter); } static gssize filter_output_stream_write (GOutputStream *stream, gconstpointer buffer, gsize count, GCancellable *cancellable, GError **error) { CamelMimeFilter *filter; GOutputStream *base_stream; gchar real_buffer[READ_SIZE + READ_PAD]; const gchar *input_buffer = buffer; gsize bytes_left = count; filter = camel_filter_output_stream_get_filter ( CAMEL_FILTER_OUTPUT_STREAM (stream)); base_stream = g_filter_output_stream_get_base_stream ( G_FILTER_OUTPUT_STREAM (stream)); while (bytes_left > 0) { gsize length; gsize presize; gchar *bufptr; gboolean success; bufptr = real_buffer + READ_PAD; length = MIN (READ_SIZE, bytes_left); memcpy (bufptr, input_buffer, length); input_buffer += length; bytes_left -= length; presize = READ_PAD; camel_mime_filter_filter ( filter, bufptr, length, presize, &bufptr, &length, &presize); /* XXX The bytes_written argument can be NULL, * even though the API docs don't say so. */ success = g_output_stream_write_all ( base_stream, bufptr, length, NULL, cancellable, error); if (!success) return -1; } return count; } static gboolean filter_output_stream_flush (GOutputStream *stream, GCancellable *cancellable, GError **error) { CamelMimeFilter *filter; GOutputStream *base_stream; gchar *bufptr = (gchar *) ""; gsize length = 0; gsize presize = 0; gboolean success = TRUE; filter = camel_filter_output_stream_get_filter ( CAMEL_FILTER_OUTPUT_STREAM (stream)); base_stream = g_filter_output_stream_get_base_stream ( G_FILTER_OUTPUT_STREAM (stream)); camel_mime_filter_complete ( filter, bufptr, length, presize, &bufptr, &length, &presize); if (length > 0) { /* XXX The bytes_written argument can be NULL, * even though the API docs don't say so. */ success = g_output_stream_write_all ( base_stream, bufptr, length, NULL, cancellable, error); } if (success) { success = g_output_stream_flush ( base_stream, cancellable, error); } return success; } static void camel_filter_output_stream_class_init (CamelFilterOutputStreamClass *class) { GObjectClass *object_class; GOutputStreamClass *stream_class; g_type_class_add_private ( class, sizeof (CamelFilterOutputStreamPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = filter_output_stream_set_property; object_class->get_property = filter_output_stream_get_property; object_class->dispose = filter_output_stream_dispose; stream_class = G_OUTPUT_STREAM_CLASS (class); stream_class->write_fn = filter_output_stream_write; stream_class->flush = filter_output_stream_flush; g_object_class_install_property ( object_class, PROP_FILTER, g_param_spec_object ( "filter", "Filter", "The MIME filter object", CAMEL_TYPE_MIME_FILTER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void camel_filter_output_stream_init (CamelFilterOutputStream *filter_stream) { filter_stream->priv = CAMEL_FILTER_OUTPUT_STREAM_GET_PRIVATE (filter_stream); } /** * camel_filter_output_stream_new: * @base_stream: a #GOutputStream * @filter: a #CamelMimeFilter * * Creates a new filtered output stream for the @base_stream. * * Returns: a new #GOutputStream * * Since: 3.12 **/ GOutputStream * camel_filter_output_stream_new (GOutputStream *base_stream, CamelMimeFilter *filter) { g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL); g_return_val_if_fail (CAMEL_IS_MIME_FILTER (filter), NULL); return g_object_new ( CAMEL_TYPE_FILTER_OUTPUT_STREAM, "base-stream", base_stream, "filter", filter, NULL); } /** * camel_filter_output_stream_get_filter: * @filter_stream: a #CamelFilterOutputStream * * Gets the #CamelMimeFilter that is used by @filter_stream. * * Returns: a #CamelMimeFilter * * Since: 3.12 **/ CamelMimeFilter * camel_filter_output_stream_get_filter (CamelFilterOutputStream *filter_stream) { g_return_val_if_fail ( CAMEL_IS_FILTER_OUTPUT_STREAM (filter_stream), NULL); return filter_stream->priv->filter; }