• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-content-sniffer-stream.c
4  *
5  * Copyright (C) 2010 Red Hat, Inc.
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11 
12 #include <string.h>
13 
14 #include "soup-content-sniffer-stream.h"
15 #include "soup.h"
16 
17 enum {
18 	PROP_0,
19 
20 	PROP_SNIFFER,
21 	PROP_MESSAGE,
22 };
23 
24 struct _SoupContentSnifferStreamPrivate {
25 	SoupContentSniffer *sniffer;
26 	SoupMessage *msg;
27 
28 	guchar *buffer;
29 	gsize buffer_size, buffer_nread;
30 	gboolean sniffing;
31 	GError *error;
32 
33 	char *sniffed_type;
34 	GHashTable *sniffed_params;
35 };
36 
37 static void soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
38 
G_DEFINE_TYPE_WITH_CODE(SoupContentSnifferStream,soup_content_sniffer_stream,G_TYPE_FILTER_INPUT_STREAM,G_ADD_PRIVATE (SoupContentSnifferStream)G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,soup_content_sniffer_stream_pollable_init))39 G_DEFINE_TYPE_WITH_CODE (SoupContentSnifferStream, soup_content_sniffer_stream, G_TYPE_FILTER_INPUT_STREAM,
40                          G_ADD_PRIVATE (SoupContentSnifferStream)
41 			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
42 						soup_content_sniffer_stream_pollable_init))
43 
44 static void
45 soup_content_sniffer_stream_finalize (GObject *object)
46 {
47 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
48 
49 	g_clear_object (&sniffer->priv->sniffer);
50 	g_clear_object (&sniffer->priv->msg);
51 	g_free (sniffer->priv->buffer);
52 	g_clear_error (&sniffer->priv->error);
53 	g_free (sniffer->priv->sniffed_type);
54 	g_clear_pointer (&sniffer->priv->sniffed_params, g_hash_table_unref);
55 
56 	G_OBJECT_CLASS (soup_content_sniffer_stream_parent_class)->finalize (object);
57 }
58 
59 static void
soup_content_sniffer_stream_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)60 soup_content_sniffer_stream_set_property (GObject *object, guint prop_id,
61 					  const GValue *value, GParamSpec *pspec)
62 {
63 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
64 
65 	switch (prop_id) {
66 	case PROP_SNIFFER:
67 		sniffer->priv->sniffer = g_value_dup_object (value);
68 		/* FIXME: supposed to wait until after got-headers for this */
69 		sniffer->priv->buffer_size = soup_content_sniffer_get_buffer_size (sniffer->priv->sniffer);
70 		sniffer->priv->buffer = g_malloc (sniffer->priv->buffer_size);
71 		break;
72 	case PROP_MESSAGE:
73 		sniffer->priv->msg = g_value_dup_object (value);
74 		break;
75 	default:
76 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
77 		break;
78 	}
79 }
80 
81 static void
soup_content_sniffer_stream_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)82 soup_content_sniffer_stream_get_property (GObject *object, guint prop_id,
83 					  GValue *value, GParamSpec *pspec)
84 {
85 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (object);
86 
87 	switch (prop_id) {
88 	case PROP_SNIFFER:
89 		g_value_set_object (value, sniffer->priv->sniffer);
90 		break;
91 	case PROP_MESSAGE:
92 		g_value_set_object (value, sniffer->priv->msg);
93 		break;
94 	default:
95 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96 		break;
97 	}
98 }
99 
100 static gssize
read_and_sniff(GInputStream * stream,gboolean blocking,GCancellable * cancellable,GError ** error)101 read_and_sniff (GInputStream *stream, gboolean blocking,
102 		GCancellable *cancellable, GError **error)
103 {
104 	SoupContentSnifferStreamPrivate *priv = SOUP_CONTENT_SNIFFER_STREAM (stream)->priv;
105 	gssize nread;
106 	GError *my_error = NULL;
107 	SoupBuffer *buf;
108 
109 	do {
110 		nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
111 						priv->buffer + priv->buffer_nread,
112 						priv->buffer_size - priv->buffer_nread,
113 						blocking, cancellable, &my_error);
114 		if (nread <= 0)
115 			break;
116 		priv->buffer_nread += nread;
117 	} while (priv->buffer_nread < priv->buffer_size);
118 
119 	/* If we got EAGAIN or cancellation before filling the buffer,
120 	 * just return that right away. Likewise if we got any other
121 	 * error without ever reading any data. Otherwise, save the
122 	 * error to return after we're done sniffing.
123 	 */
124 	if (my_error) {
125 		if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
126 		    g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
127 		    priv->buffer_nread == 0) {
128 			g_propagate_error (error, my_error);
129 			return -1;
130 		} else
131 			priv->error = my_error;
132 	}
133 
134 	/* Sniff, then return the data */
135 	buf = soup_buffer_new (SOUP_MEMORY_TEMPORARY, priv->buffer, priv->buffer_nread);
136 	priv->sniffed_type =
137 		soup_content_sniffer_sniff (priv->sniffer, priv->msg, buf,
138 					    &priv->sniffed_params);
139 	soup_buffer_free (buf);
140 	priv->sniffing = FALSE;
141 
142 	return priv->buffer_nread;
143 }
144 
145 static gssize
read_internal(GInputStream * stream,void * buffer,gsize count,gboolean blocking,GCancellable * cancellable,GError ** error)146 read_internal (GInputStream  *stream,
147 	       void          *buffer,
148 	       gsize          count,
149 	       gboolean       blocking,
150 	       GCancellable  *cancellable,
151 	       GError       **error)
152 {
153 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
154 	gssize nread;
155 
156 	if (sniffer->priv->error) {
157 		g_propagate_error (error, sniffer->priv->error);
158 		sniffer->priv->error = NULL;
159 		return -1;
160 	}
161 
162 	if (sniffer->priv->sniffing) {
163 		nread = read_and_sniff (stream, blocking, cancellable, error);
164 		if (nread <= 0)
165 			return nread;
166 	}
167 
168 	if (sniffer->priv->buffer) {
169 		nread = MIN (count, sniffer->priv->buffer_nread);
170 		if (buffer)
171 			memcpy (buffer, sniffer->priv->buffer, nread);
172 		if (nread == sniffer->priv->buffer_nread) {
173 			g_free (sniffer->priv->buffer);
174 			sniffer->priv->buffer = NULL;
175 		} else {
176 			/* FIXME, inefficient */
177 			memmove (sniffer->priv->buffer,
178 				 sniffer->priv->buffer + nread,
179 				 sniffer->priv->buffer_nread - nread);
180 			sniffer->priv->buffer_nread -= nread;
181 		}
182 	} else {
183 		nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
184 						buffer, count, blocking,
185 						cancellable, error);
186 	}
187 	return nread;
188 }
189 
190 static gssize
soup_content_sniffer_stream_read(GInputStream * stream,void * buffer,gsize count,GCancellable * cancellable,GError ** error)191 soup_content_sniffer_stream_read (GInputStream  *stream,
192 				  void          *buffer,
193 				  gsize          count,
194 				  GCancellable  *cancellable,
195 				  GError       **error)
196 {
197 	return read_internal (stream, buffer, count, TRUE,
198 			      cancellable, error);
199 }
200 
201 static gssize
soup_content_sniffer_stream_skip(GInputStream * stream,gsize count,GCancellable * cancellable,GError ** error)202 soup_content_sniffer_stream_skip (GInputStream  *stream,
203 				  gsize          count,
204 				  GCancellable  *cancellable,
205 				  GError       **error)
206 {
207 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
208 	gssize nskipped;
209 
210 	if (sniffer->priv->sniffing) {
211 		/* Read into the internal buffer... */
212 		nskipped = soup_content_sniffer_stream_read (stream, NULL, 0, cancellable, error);
213 		if (nskipped == -1)
214 			return -1;
215 		/* Now fall through */
216 	}
217 
218 	if (sniffer->priv->buffer) {
219 		nskipped = MIN (count, sniffer->priv->buffer_nread);
220 		if (nskipped == sniffer->priv->buffer_nread) {
221 			g_free (sniffer->priv->buffer);
222 			sniffer->priv->buffer = NULL;
223 		} else {
224 			/* FIXME */
225 			memmove (sniffer->priv->buffer,
226 				 sniffer->priv->buffer + nskipped,
227 				 sniffer->priv->buffer_nread - nskipped);
228 			sniffer->priv->buffer_nread -= nskipped;
229 		}
230 	} else {
231 		nskipped = G_INPUT_STREAM_CLASS (soup_content_sniffer_stream_parent_class)->
232 			skip (stream, count, cancellable, error);
233 	}
234 	return nskipped;
235 }
236 
237 static gboolean
soup_content_sniffer_stream_can_poll(GPollableInputStream * pollable)238 soup_content_sniffer_stream_can_poll (GPollableInputStream *pollable)
239 {
240 	GInputStream *base_stream = G_FILTER_INPUT_STREAM (pollable)->base_stream;
241 
242 	return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
243 		g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
244 }
245 
246 
247 static gboolean
soup_content_sniffer_stream_is_readable(GPollableInputStream * stream)248 soup_content_sniffer_stream_is_readable (GPollableInputStream *stream)
249 {
250 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
251 
252 	if (sniffer->priv->error ||
253 	    (!sniffer->priv->sniffing && sniffer->priv->buffer))
254 		return TRUE;
255 
256 	return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream));
257 }
258 
259 static gssize
soup_content_sniffer_stream_read_nonblocking(GPollableInputStream * stream,void * buffer,gsize count,GError ** error)260 soup_content_sniffer_stream_read_nonblocking (GPollableInputStream  *stream,
261 					      void                  *buffer,
262 					      gsize                  count,
263 					      GError               **error)
264 {
265 	return read_internal (G_INPUT_STREAM (stream), buffer, count,
266 			      FALSE, NULL, error);
267 }
268 
269 static GSource *
soup_content_sniffer_stream_create_source(GPollableInputStream * stream,GCancellable * cancellable)270 soup_content_sniffer_stream_create_source (GPollableInputStream *stream,
271 					   GCancellable         *cancellable)
272 {
273 	SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
274 	GSource *base_source, *pollable_source;
275 
276 	if (sniffer->priv->error ||
277 	    (!sniffer->priv->sniffing && sniffer->priv->buffer))
278 		base_source = g_timeout_source_new (0);
279 	else
280 		base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (stream)->base_stream), cancellable);
281 
282 	g_source_set_dummy_callback (base_source);
283 	pollable_source = g_pollable_source_new (G_OBJECT (stream));
284 	g_source_add_child_source (pollable_source, base_source);
285 	g_source_unref (base_source);
286 
287 	return pollable_source;
288 }
289 
290 static void
soup_content_sniffer_stream_init(SoupContentSnifferStream * sniffer)291 soup_content_sniffer_stream_init (SoupContentSnifferStream *sniffer)
292 {
293 	sniffer->priv = soup_content_sniffer_stream_get_instance_private (sniffer);
294 	sniffer->priv->sniffing = TRUE;
295 }
296 
297 static void
soup_content_sniffer_stream_class_init(SoupContentSnifferStreamClass * sniffer_class)298 soup_content_sniffer_stream_class_init (SoupContentSnifferStreamClass *sniffer_class)
299 {
300 	GObjectClass *object_class = G_OBJECT_CLASS (sniffer_class);
301 	GInputStreamClass *input_stream_class =
302 		G_INPUT_STREAM_CLASS (sniffer_class);
303 
304 	object_class->finalize = soup_content_sniffer_stream_finalize;
305 	object_class->set_property = soup_content_sniffer_stream_set_property;
306 	object_class->get_property = soup_content_sniffer_stream_get_property;
307 
308 	input_stream_class->read_fn = soup_content_sniffer_stream_read;
309 	input_stream_class->skip = soup_content_sniffer_stream_skip;
310 
311 	g_object_class_install_property (
312 		object_class, PROP_SNIFFER,
313 		g_param_spec_object ("sniffer",
314 				     "Sniffer",
315 				     "The stream's SoupContentSniffer",
316 				     SOUP_TYPE_CONTENT_SNIFFER,
317 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
318 	g_object_class_install_property (
319 		object_class, PROP_MESSAGE,
320 		g_param_spec_object ("message",
321 				     "Message",
322 				     "The stream's SoupMessage",
323 				     SOUP_TYPE_MESSAGE,
324 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
325 }
326 
327 static void
soup_content_sniffer_stream_pollable_init(GPollableInputStreamInterface * pollable_interface,gpointer interface_data)328 soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
329 					   gpointer                       interface_data)
330 {
331 	pollable_interface->can_poll = soup_content_sniffer_stream_can_poll;
332 	pollable_interface->is_readable = soup_content_sniffer_stream_is_readable;
333 	pollable_interface->read_nonblocking = soup_content_sniffer_stream_read_nonblocking;
334 	pollable_interface->create_source = soup_content_sniffer_stream_create_source;
335 }
336 
337 gboolean
soup_content_sniffer_stream_is_ready(SoupContentSnifferStream * sniffer,gboolean blocking,GCancellable * cancellable,GError ** error)338 soup_content_sniffer_stream_is_ready (SoupContentSnifferStream  *sniffer,
339 				      gboolean                   blocking,
340 				      GCancellable              *cancellable,
341 				      GError                   **error)
342 {
343 	if (!sniffer->priv->sniffing)
344 		return TRUE;
345 
346 	return read_and_sniff (G_INPUT_STREAM (sniffer), blocking,
347 			       cancellable, error) != -1;
348 }
349 
350 const char *
soup_content_sniffer_stream_sniff(SoupContentSnifferStream * sniffer,GHashTable ** params)351 soup_content_sniffer_stream_sniff (SoupContentSnifferStream  *sniffer,
352 				   GHashTable               **params)
353 {
354 	if (params)
355 		*params = sniffer->priv->sniffed_params;
356 	return sniffer->priv->sniffed_type;
357 }
358