#include "gskmimemultipartencoder.h"
#include "gskmimeencodings.h"
#include "gskmimeencodings.h"
#include "../gskmacros.h"
#include "../gskmemory.h"
static GObjectClass *parent_class = NULL;
#define DEFAULT_MAX_BUFFERED 4096
static gboolean dequeue_next_piece (GskMimeMultipartEncoder *encoder,
GError **error);
static void
check_write_terminator (GskMimeMultipartEncoder *encoder)
{
if (encoder->shutdown
&& encoder->outgoing_pieces->head == NULL
&& encoder->active_stream == NULL
&& !encoder->wrote_terminator)
{
gsk_buffer_printf (&encoder->outgoing_data, "\r\n--%s--\r\n",
encoder->boundary_str);
encoder->wrote_terminator = TRUE;
gsk_stream_mark_idle_notify_read (GSK_STREAM (encoder));
}
}
static gboolean
handle_active_stream_readable (GskStream *stream,
gpointer data)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (data);
GError *suberror = NULL;
if (!gsk_stream_read_buffer (stream, &encoder->outgoing_data, &suberror))
{
GskErrorCode code;
if (suberror->domain == GSK_G_ERROR_DOMAIN)
code = suberror->code;
else
code = GSK_ERROR_IO;
gsk_io_set_error (GSK_IO (stream), GSK_IO_ERROR_READ, code,
_("error from encoding stream: %s"),
suberror->message);
return FALSE;
}
if (encoder->outgoing_data.size > 0)
gsk_stream_mark_idle_notify_read (GSK_STREAM (encoder));
if (encoder->outgoing_data.size > encoder->max_buffered
&& !encoder->blocked_active_stream)
{
encoder->blocked_active_stream = 1;
gsk_io_block_read (stream);
return FALSE;
}
return TRUE;
}
static gboolean
handle_active_stream_read_shutdown (GskStream *stream,
gpointer data)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (data);
/* Don't do anything: instead do the work in the destroy-notify */
(void) encoder;
return FALSE;
}
static void
handle_active_stream_read_destroyed (gpointer data)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (data);
g_object_unref (encoder->active_stream);
encoder->blocked_active_stream = FALSE;
encoder->active_stream = NULL;
if (encoder->outgoing_pieces->head != NULL)
{
GError *error = NULL;
if (!dequeue_next_piece (encoder, &error) && error != NULL)
{
gsk_io_set_gerror (GSK_IO (encoder), GSK_IO_ERROR_READ, error);
return;
}
}
else
check_write_terminator (encoder);
}
static gboolean
dequeue_next_piece (GskMimeMultipartEncoder *encoder,
GError **error)
{
GskMimeMultipartPiece *piece;
GskBuffer *buffer = &encoder->outgoing_data;
GskStream *raw_stream;
GskStream *read_end, *write_end;
g_return_val_if_fail (encoder->active_stream == NULL, FALSE);
piece = g_queue_pop_head (encoder->outgoing_pieces);
if (piece == NULL)
{
check_write_terminator (encoder);
#if 0
if (encoder->shutdown && !encoder->wrote_terminator)
{
gsk_buffer_printf (buffer, "\r\n--%s--\r\n", encoder->boundary_str);
gsk_stream_mark_idle_notify_read (GSK_STREAM (encoder));
}
#endif
return FALSE;
}
/* append header */
gsk_buffer_printf (buffer, "\r\n--%s\r\n", encoder->boundary_str);
if (piece->type != NULL)
{
gsk_buffer_printf (buffer, "Content-Type: %s/%s",
piece->type, piece->subtype ? piece->subtype : "*");
if (piece->charset != NULL)
gsk_buffer_printf (buffer, "; charset=%s", piece->charset);
if (piece->other_fields)
{
char **at;
for (at = piece->other_fields;
at[0] != NULL && at[1] != NULL;
at += 2)
{
gsk_buffer_printf (buffer, "; %s=%s", at[0], at[1]);
}
}
gsk_buffer_append (buffer, "\r\n", 2);
}
if (piece->id != NULL)
gsk_buffer_printf (buffer, "Content-ID: %s\r\n", piece->id);
if (piece->description != NULL)
gsk_buffer_printf (buffer, "Content-Description: %s\r\n",
piece->description);
if (piece->location != NULL)
gsk_buffer_printf (buffer, "Content-Location: %s\r\n", piece->location);
if (piece->transfer_encoding != NULL)
gsk_buffer_printf (buffer, "Content-Transfer-Encoding: %s\r\n",
piece->transfer_encoding);
if (piece->disposition != NULL)
gsk_buffer_printf (buffer, "Content-Disposition: %s\r\n",
piece->disposition);
gsk_buffer_append (buffer, "\r\n", 2);
/* create encoded stream */
if (piece->is_memory)
raw_stream = gsk_memory_slab_source_new (piece->content_data,
piece->content_length,
(GDestroyNotify) gsk_mime_multipart_piece_unref,
gsk_mime_multipart_piece_ref (piece));
else
raw_stream = g_object_ref (piece->content);
if (!gsk_mime_make_transfer_encoding_encoders (piece->transfer_encoding,
&write_end, &read_end,
encoder->boundary_str,
error))
{
g_object_unref (raw_stream);
return FALSE;
}
if (!gsk_stream_attach (raw_stream, write_end, error))
{
g_object_unref (raw_stream);
g_object_unref (write_end);
g_object_unref (read_end);
return FALSE;
}
encoder->active_stream = g_object_ref (read_end);
gsk_stream_trap_readable (read_end,
handle_active_stream_readable,
handle_active_stream_read_shutdown,
encoder,
handle_active_stream_read_destroyed);
gsk_stream_mark_idle_notify_read (GSK_STREAM (encoder));
g_object_unref (raw_stream);
g_object_unref (read_end);
g_object_unref (write_end);
gsk_mime_multipart_piece_unref (piece);
return TRUE;
}
static void
check_maybe_unblock (GskMimeMultipartEncoder *encoder)
{
if (encoder->blocked_active_stream
&& encoder->outgoing_data.size < encoder->max_buffered)
{
encoder->blocked_active_stream = 0;
gsk_io_unblock_read (encoder);
}
if (encoder->outgoing_data.size > 0)
gsk_stream_mark_idle_notify_read (GSK_STREAM (encoder));
}
static void
check_shutdown_notify (GskMimeMultipartEncoder *encoder)
{
if (encoder->outgoing_pieces->head == NULL
&& encoder->active_stream == NULL
&& encoder->shutdown
&& encoder->outgoing_data.size == 0)
{
g_assert (encoder->wrote_terminator);
gsk_io_notify_read_shutdown (GSK_IO (encoder));
}
}
static void
gsk_mime_multipart_encoder_new_part_needed_shutdown (GskMimeMultipartEncoder *encoder)
{
encoder->shutdown = 1;
check_write_terminator (encoder);
check_maybe_unblock (encoder);
check_shutdown_notify (encoder);
}
/* --- GskStream methods --- */
static guint
gsk_mime_multipart_encoder_raw_read (GskStream *stream,
gpointer data,
guint length,
GError **error)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (stream);
guint rv = gsk_buffer_read (&encoder->outgoing_data, data, length);
check_write_terminator (encoder);
check_maybe_unblock (encoder);
check_shutdown_notify (encoder);
if (encoder->outgoing_data.size == 0)
gsk_stream_clear_idle_notify_read (GSK_STREAM (encoder));
return rv;
}
static guint
gsk_mime_multipart_encoder_raw_read_buffer (GskStream *stream,
GskBuffer *buffer,
GError **error)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (stream);
guint rv = gsk_buffer_drain (buffer, &encoder->outgoing_data);
check_write_terminator (encoder);
check_maybe_unblock (encoder);
check_shutdown_notify (encoder);
if (encoder->outgoing_data.size == 0)
gsk_stream_clear_idle_notify_read (GSK_STREAM (encoder));
return rv;
}
static void
gsk_mime_multipart_encoder_finalize (GObject *object)
{
GskMimeMultipartEncoder *encoder = GSK_MIME_MULTIPART_ENCODER (object);
if (encoder->active_stream != NULL)
gsk_stream_untrap_readable (encoder->active_stream);
g_list_foreach (encoder->outgoing_pieces->head, (GFunc) gsk_mime_multipart_piece_unref, NULL);
g_queue_free (encoder->outgoing_pieces);
gsk_hook_destruct (&encoder->new_part_needed);
g_free (encoder->boundary_str);
gsk_buffer_destruct (&encoder->outgoing_data);
(*parent_class->finalize) (object);
}
/* --- functions --- */
static void
gsk_mime_multipart_encoder_init (GskMimeMultipartEncoder *encoder)
{
GSK_HOOK_INIT (encoder,
GskMimeMultipartEncoder,
new_part_needed,
0,
new_part_needed_set_poll, new_part_needed_shutdown);
GSK_HOOK_SET_FLAG (_GSK_MIME_MULTIPART_ENCODER_HOOK (encoder), IS_AVAILABLE);
gsk_hook_mark_idle_notify (_GSK_MIME_MULTIPART_ENCODER_HOOK (encoder));
gsk_io_mark_is_readable (encoder);
encoder->outgoing_pieces = g_queue_new ();
encoder->max_buffered = DEFAULT_MAX_BUFFERED;
}
static void
gsk_mime_multipart_encoder_class_init (GskMimeMultipartEncoderClass *class)
{
GskStreamClass *stream_class = GSK_STREAM_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
parent_class = g_type_class_peek_parent (class);
GSK_HOOK_CLASS_INIT (object_class, "new-part-needed", GskMimeMultipartEncoder, new_part_needed);
class->new_part_needed_shutdown = gsk_mime_multipart_encoder_new_part_needed_shutdown;
stream_class->raw_read = gsk_mime_multipart_encoder_raw_read;
stream_class->raw_read_buffer = gsk_mime_multipart_encoder_raw_read_buffer;
object_class->finalize = gsk_mime_multipart_encoder_finalize;
}
GType gsk_mime_multipart_encoder_get_type()
{
static GType mime_multipart_encoder_type = 0;
if (!mime_multipart_encoder_type)
{
static const GTypeInfo mime_multipart_encoder_info =
{
sizeof(GskMimeMultipartEncoderClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_mime_multipart_encoder_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskMimeMultipartEncoder),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_mime_multipart_encoder_init,
NULL /* value_table */
};
mime_multipart_encoder_type = g_type_register_static (GSK_TYPE_STREAM,
"GskMimeMultipartEncoder",
&mime_multipart_encoder_info, 0);
}
return mime_multipart_encoder_type;
}
/**
* gsk_mime_multipart_encoder_new:
* @boundary: string used to separate pieces of content
* on the underlying stream.
* This only affects quoted-printable and identity
* encoded text-- if your content has
* a line starting with '--' then the boundary string,
* it will be confused for a content-part separator.
* You should use GSK_MIME_MULTIPART_ENCODER_GOOD_BOUNDARY
* for this string-- then quoted-printable may be used
* without restriction.
*
* Create a read-only stream which encodes MIME pieces given
* to it.
*
* returns: the newly allocated encoder.
*/
GskMimeMultipartEncoder *
gsk_mime_multipart_encoder_new (const char *boundary)
{
GskMimeMultipartEncoder *rv = g_object_new (GSK_TYPE_MIME_MULTIPART_ENCODER, NULL);
rv->boundary_str = g_strdup (boundary);
return rv;
}
/**
* gsk_mime_multipart_encoder_add_part:
* @encoder: the encoder to add the part to.
* @piece: the content to append.
* @error: place to store the error code if something goes wrong.
*
* Add a new part to @encoder. The pieces will be transmitted in
* the order receieved.
*
* returns: whether the part could be added to the stream.
*/
gboolean
gsk_mime_multipart_encoder_add_part (GskMimeMultipartEncoder *encoder,
GskMimeMultipartPiece *piece,
GError **error)
{
g_return_val_if_fail (encoder->shutdown == FALSE, FALSE);
g_queue_push_tail (encoder->outgoing_pieces, piece);
gsk_mime_multipart_piece_ref (piece);
if (encoder->active_stream == NULL)
if (!dequeue_next_piece (encoder, error))
return FALSE;
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1