Blame libsoup/soup-content-sniffer-stream.c

rpm-build 4f3c61
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
rpm-build 4f3c61
/*
rpm-build 4f3c61
 * soup-content-sniffer-stream.c
rpm-build 4f3c61
 *
rpm-build 4f3c61
 * Copyright (C) 2010 Red Hat, Inc.
rpm-build 4f3c61
 */
rpm-build 4f3c61
rpm-build 4f3c61
#ifdef HAVE_CONFIG_H
rpm-build 4f3c61
#include <config.h>
rpm-build 4f3c61
#endif
rpm-build 4f3c61
rpm-build 4f3c61
#include <string.h>
rpm-build 4f3c61
rpm-build 4f3c61
#include "soup-content-sniffer-stream.h"
rpm-build 4f3c61
#include "soup.h"
rpm-build 4f3c61
rpm-build 4f3c61
enum {
rpm-build 4f3c61
	PROP_0,
rpm-build 4f3c61
rpm-build 4f3c61
	PROP_SNIFFER,
rpm-build 4f3c61
	PROP_MESSAGE,
rpm-build 4f3c61
};
rpm-build 4f3c61
rpm-build 4f3c61
struct _SoupContentSnifferStreamPrivate {
rpm-build 4f3c61
	SoupContentSniffer *sniffer;
rpm-build 4f3c61
	SoupMessage *msg;
rpm-build 4f3c61
rpm-build 4f3c61
	guchar *buffer;
rpm-build 4f3c61
	gsize buffer_size, buffer_nread;
rpm-build 4f3c61
	gboolean sniffing;
rpm-build 4f3c61
	GError *error;
rpm-build 4f3c61
rpm-build 4f3c61
	char *sniffed_type;
rpm-build 4f3c61
	GHashTable *sniffed_params;
rpm-build 4f3c61
};
rpm-build 4f3c61
rpm-build 4f3c61
static void soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
rpm-build 4f3c61
rpm-build 4f3c61
G_DEFINE_TYPE_WITH_CODE (SoupContentSnifferStream, soup_content_sniffer_stream, G_TYPE_FILTER_INPUT_STREAM,
rpm-build 4f3c61
                         G_ADD_PRIVATE (SoupContentSnifferStream)
rpm-build 4f3c61
			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
rpm-build 4f3c61
						soup_content_sniffer_stream_pollable_init))
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_finalize (GObject *object)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	g_clear_object (&sniffer->priv->sniffer);
rpm-build 4f3c61
	g_clear_object (&sniffer->priv->msg);
rpm-build 4f3c61
	g_free (sniffer->priv->buffer);
rpm-build 4f3c61
	g_clear_error (&sniffer->priv->error);
rpm-build 4f3c61
	g_free (sniffer->priv->sniffed_type);
rpm-build 4f3c61
	g_clear_pointer (&sniffer->priv->sniffed_params, g_hash_table_unref);
rpm-build 4f3c61
rpm-build 4f3c61
	G_OBJECT_CLASS (soup_content_sniffer_stream_parent_class)->finalize (object);
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_set_property (GObject *object, guint prop_id,
rpm-build 4f3c61
					  const GValue *value, GParamSpec *pspec)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	switch (prop_id) {
rpm-build 4f3c61
	case PROP_SNIFFER:
rpm-build 4f3c61
		sniffer->priv->sniffer = g_value_dup_object (value);
rpm-build 4f3c61
		/* FIXME: supposed to wait until after got-headers for this */
rpm-build 4f3c61
		sniffer->priv->buffer_size = soup_content_sniffer_get_buffer_size (sniffer->priv->sniffer);
rpm-build 4f3c61
		sniffer->priv->buffer = g_malloc (sniffer->priv->buffer_size);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	case PROP_MESSAGE:
rpm-build 4f3c61
		sniffer->priv->msg = g_value_dup_object (value);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_get_property (GObject *object, guint prop_id,
rpm-build 4f3c61
					  GValue *value, GParamSpec *pspec)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	switch (prop_id) {
rpm-build 4f3c61
	case PROP_SNIFFER:
rpm-build 4f3c61
		g_value_set_object (value, sniffer->priv->sniffer);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	case PROP_MESSAGE:
rpm-build 4f3c61
		g_value_set_object (value, sniffer->priv->msg);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
read_and_sniff (GInputStream *stream, gboolean blocking,
rpm-build 4f3c61
		GCancellable *cancellable, GError **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStreamPrivate *priv = SOUP_CONTENT_SNIFFER_STREAM (stream)->priv;
rpm-build 4f3c61
	gssize nread;
rpm-build 4f3c61
	GError *my_error = NULL;
rpm-build 4f3c61
	SoupBuffer *buf;
rpm-build 4f3c61
rpm-build 4f3c61
	do {
rpm-build 4f3c61
		nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
rpm-build 4f3c61
						priv->buffer + priv->buffer_nread,
rpm-build 4f3c61
						priv->buffer_size - priv->buffer_nread,
rpm-build 4f3c61
						blocking, cancellable, &my_error);
rpm-build 4f3c61
		if (nread <= 0)
rpm-build 4f3c61
			break;
rpm-build 4f3c61
		priv->buffer_nread += nread;
rpm-build 4f3c61
	} while (priv->buffer_nread < priv->buffer_size);
rpm-build 4f3c61
rpm-build 4f3c61
	/* If we got EAGAIN or cancellation before filling the buffer,
rpm-build 4f3c61
	 * just return that right away. Likewise if we got any other
rpm-build 4f3c61
	 * error without ever reading any data. Otherwise, save the
rpm-build 4f3c61
	 * error to return after we're done sniffing.
rpm-build 4f3c61
	 */
rpm-build 4f3c61
	if (my_error) {
rpm-build 4f3c61
		if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
rpm-build 4f3c61
		    g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
rpm-build 4f3c61
		    priv->buffer_nread == 0) {
rpm-build 4f3c61
			g_propagate_error (error, my_error);
rpm-build 4f3c61
			return -1;
rpm-build 4f3c61
		} else
rpm-build 4f3c61
			priv->error = my_error;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	/* Sniff, then return the data */
rpm-build 4f3c61
	buf = soup_buffer_new (SOUP_MEMORY_TEMPORARY, priv->buffer, priv->buffer_nread);
rpm-build 4f3c61
	priv->sniffed_type =
rpm-build 4f3c61
		soup_content_sniffer_sniff (priv->sniffer, priv->msg, buf,
rpm-build 4f3c61
					    &priv->sniffed_params);
rpm-build 4f3c61
	soup_buffer_free (buf);
rpm-build 4f3c61
	priv->sniffing = FALSE;
rpm-build 4f3c61
rpm-build 4f3c61
	return priv->buffer_nread;
rpm-build 4f3c61
}	
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
read_internal (GInputStream  *stream,
rpm-build 4f3c61
	       void          *buffer,
rpm-build 4f3c61
	       gsize          count,
rpm-build 4f3c61
	       gboolean       blocking,
rpm-build 4f3c61
	       GCancellable  *cancellable,
rpm-build 4f3c61
	       GError       **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
rpm-build 4f3c61
	gssize nread;
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->error) {
rpm-build 4f3c61
		g_propagate_error (error, sniffer->priv->error);
rpm-build 4f3c61
		sniffer->priv->error = NULL;
rpm-build 4f3c61
		return -1;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->sniffing) {
rpm-build 4f3c61
		nread = read_and_sniff (stream, blocking, cancellable, error);
rpm-build 4f3c61
		if (nread <= 0)
rpm-build 4f3c61
			return nread;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->buffer) {
rpm-build 4f3c61
		nread = MIN (count, sniffer->priv->buffer_nread);
rpm-build 4f3c61
		if (buffer)
rpm-build 4f3c61
			memcpy (buffer, sniffer->priv->buffer, nread);
rpm-build 4f3c61
		if (nread == sniffer->priv->buffer_nread) {
rpm-build 4f3c61
			g_free (sniffer->priv->buffer);
rpm-build 4f3c61
			sniffer->priv->buffer = NULL;
rpm-build 4f3c61
		} else {
rpm-build 4f3c61
			/* FIXME, inefficient */
rpm-build 4f3c61
			memmove (sniffer->priv->buffer,
rpm-build 4f3c61
				 sniffer->priv->buffer + nread,
rpm-build 4f3c61
				 sniffer->priv->buffer_nread - nread);
rpm-build 4f3c61
			sniffer->priv->buffer_nread -= nread;
rpm-build 4f3c61
		}
rpm-build 4f3c61
	} else {
rpm-build 4f3c61
		nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
rpm-build 4f3c61
						buffer, count, blocking,
rpm-build 4f3c61
						cancellable, error);
rpm-build 4f3c61
	}
rpm-build 4f3c61
	return nread;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_content_sniffer_stream_read (GInputStream  *stream,
rpm-build 4f3c61
				  void          *buffer,
rpm-build 4f3c61
				  gsize          count,
rpm-build 4f3c61
				  GCancellable  *cancellable,
rpm-build 4f3c61
				  GError       **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	return read_internal (stream, buffer, count, TRUE,
rpm-build 4f3c61
			      cancellable, error);
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_content_sniffer_stream_skip (GInputStream  *stream,
rpm-build 4f3c61
				  gsize          count,
rpm-build 4f3c61
				  GCancellable  *cancellable,
rpm-build 4f3c61
				  GError       **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
rpm-build 4f3c61
	gssize nskipped;
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->sniffing) {
rpm-build 4f3c61
		/* Read into the internal buffer... */
rpm-build 4f3c61
		nskipped = soup_content_sniffer_stream_read (stream, NULL, 0, cancellable, error);
rpm-build 4f3c61
		if (nskipped == -1)
rpm-build 4f3c61
			return -1;
rpm-build 4f3c61
		/* Now fall through */
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->buffer) {
rpm-build 4f3c61
		nskipped = MIN (count, sniffer->priv->buffer_nread);
rpm-build 4f3c61
		if (nskipped == sniffer->priv->buffer_nread) {
rpm-build 4f3c61
			g_free (sniffer->priv->buffer);
rpm-build 4f3c61
			sniffer->priv->buffer = NULL;
rpm-build 4f3c61
		} else {
rpm-build 4f3c61
			/* FIXME */
rpm-build 4f3c61
			memmove (sniffer->priv->buffer,
rpm-build 4f3c61
				 sniffer->priv->buffer + nskipped,
rpm-build 4f3c61
				 sniffer->priv->buffer_nread - nskipped);
rpm-build 4f3c61
			sniffer->priv->buffer_nread -= nskipped;
rpm-build 4f3c61
		}
rpm-build 4f3c61
	} else {
rpm-build 4f3c61
		nskipped = G_INPUT_STREAM_CLASS (soup_content_sniffer_stream_parent_class)->
rpm-build 4f3c61
			skip (stream, count, cancellable, error);
rpm-build 4f3c61
	}
rpm-build 4f3c61
	return nskipped;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gboolean
rpm-build 4f3c61
soup_content_sniffer_stream_can_poll (GPollableInputStream *pollable)
rpm-build 4f3c61
{
rpm-build 4f3c61
	GInputStream *base_stream = G_FILTER_INPUT_STREAM (pollable)->base_stream;
rpm-build 4f3c61
rpm-build 4f3c61
	return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
rpm-build 4f3c61
		g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
rpm-build 4f3c61
static gboolean
rpm-build 4f3c61
soup_content_sniffer_stream_is_readable (GPollableInputStream *stream)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->error ||
rpm-build 4f3c61
	    (!sniffer->priv->sniffing && sniffer->priv->buffer))
rpm-build 4f3c61
		return TRUE;
rpm-build 4f3c61
rpm-build 4f3c61
	return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_content_sniffer_stream_read_nonblocking (GPollableInputStream  *stream,
rpm-build 4f3c61
					      void                  *buffer,
rpm-build 4f3c61
					      gsize                  count,
rpm-build 4f3c61
					      GError               **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	return read_internal (G_INPUT_STREAM (stream), buffer, count,
rpm-build 4f3c61
			      FALSE, NULL, error);
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static GSource *
rpm-build 4f3c61
soup_content_sniffer_stream_create_source (GPollableInputStream *stream,
rpm-build 4f3c61
					   GCancellable         *cancellable)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
rpm-build 4f3c61
	GSource *base_source, *pollable_source;
rpm-build 4f3c61
rpm-build 4f3c61
	if (sniffer->priv->error ||
rpm-build 4f3c61
	    (!sniffer->priv->sniffing && sniffer->priv->buffer))
rpm-build 4f3c61
		base_source = g_timeout_source_new (0);
rpm-build 4f3c61
	else
rpm-build 4f3c61
		base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream), cancellable);
rpm-build 4f3c61
rpm-build 4f3c61
	g_source_set_dummy_callback (base_source);
rpm-build 4f3c61
	pollable_source = g_pollable_source_new (G_OBJECT (stream));
rpm-build 4f3c61
	g_source_add_child_source (pollable_source, base_source);
rpm-build 4f3c61
	g_source_unref (base_source);
rpm-build 4f3c61
rpm-build 4f3c61
	return pollable_source;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_init (SoupContentSnifferStream *sniffer)
rpm-build 4f3c61
{
rpm-build 4f3c61
	sniffer->priv = soup_content_sniffer_stream_get_instance_private (sniffer);
rpm-build 4f3c61
	sniffer->priv->sniffing = TRUE;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_class_init (SoupContentSnifferStreamClass *sniffer_class)
rpm-build 4f3c61
{
rpm-build 4f3c61
	GObjectClass *object_class = G_OBJECT_CLASS (sniffer_class);
rpm-build 4f3c61
	GInputStreamClass *input_stream_class =
rpm-build 4f3c61
		G_INPUT_STREAM_CLASS (sniffer_class);
rpm-build 4f3c61
 
rpm-build 4f3c61
	object_class->finalize = soup_content_sniffer_stream_finalize;
rpm-build 4f3c61
	object_class->set_property = soup_content_sniffer_stream_set_property;
rpm-build 4f3c61
	object_class->get_property = soup_content_sniffer_stream_get_property;
rpm-build 4f3c61
rpm-build 4f3c61
	input_stream_class->read_fn = soup_content_sniffer_stream_read;
rpm-build 4f3c61
	input_stream_class->skip = soup_content_sniffer_stream_skip;
rpm-build 4f3c61
rpm-build 4f3c61
	g_object_class_install_property (
rpm-build 4f3c61
		object_class, PROP_SNIFFER,
rpm-build 4f3c61
		g_param_spec_object ("sniffer",
rpm-build 4f3c61
				     "Sniffer",
rpm-build 4f3c61
				     "The stream's SoupContentSniffer",
rpm-build 4f3c61
				     SOUP_TYPE_CONTENT_SNIFFER,
rpm-build 4f3c61
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
rpm-build 4f3c61
	g_object_class_install_property (
rpm-build 4f3c61
		object_class, PROP_MESSAGE,
rpm-build 4f3c61
		g_param_spec_object ("message",
rpm-build 4f3c61
				     "Message",
rpm-build 4f3c61
				     "The stream's SoupMessage",
rpm-build 4f3c61
				     SOUP_TYPE_MESSAGE,
rpm-build 4f3c61
				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
rpm-build 4f3c61
					   gpointer                       interface_data)
rpm-build 4f3c61
{
rpm-build 4f3c61
	pollable_interface->can_poll = soup_content_sniffer_stream_can_poll;
rpm-build 4f3c61
	pollable_interface->is_readable = soup_content_sniffer_stream_is_readable;
rpm-build 4f3c61
	pollable_interface->read_nonblocking = soup_content_sniffer_stream_read_nonblocking;
rpm-build 4f3c61
	pollable_interface->create_source = soup_content_sniffer_stream_create_source;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
gboolean
rpm-build 4f3c61
soup_content_sniffer_stream_is_ready (SoupContentSnifferStream  *sniffer,
rpm-build 4f3c61
				      gboolean                   blocking,
rpm-build 4f3c61
				      GCancellable              *cancellable,
rpm-build 4f3c61
				      GError                   **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	if (!sniffer->priv->sniffing)
rpm-build 4f3c61
		return TRUE;
rpm-build 4f3c61
rpm-build 4f3c61
	return read_and_sniff (G_INPUT_STREAM (sniffer), blocking,
rpm-build 4f3c61
			       cancellable, error) != -1;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
const char *
rpm-build 4f3c61
soup_content_sniffer_stream_sniff (SoupContentSnifferStream  *sniffer,
rpm-build 4f3c61
				   GHashTable               **params)
rpm-build 4f3c61
{
rpm-build 4f3c61
	if (params)
rpm-build 4f3c61
		*params = sniffer->priv->sniffed_params;
rpm-build 4f3c61
	return sniffer->priv->sniffed_type;
rpm-build 4f3c61
}