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