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