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