Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.git] / libsoup / soup-converter-wrapper.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-converter-wrapper.c
4  *
5  * Copyright 2011 Red Hat, Inc.
6  */
7
8 #include "config.h"
9
10 #include <string.h>
11
12 #include <glib/gi18n-lib.h>
13
14 #include "soup-converter-wrapper.h"
15 #include "soup-message.h"
16
17 /* SoupConverterWrapper is a GConverter that wraps another GConverter.
18  * Mostly it is transparent, but it implements three special fallbacks
19  * for Content-Encoding handling: (1) "deflate" can mean either raw
20  * deflate or zlib-encoded default, (2) the server may mistakenly
21  * claim that a response is encoded when actually it isn't, (3) the
22  * response may contain trailing junk after the end of the encoded
23  * portion that we want to ignore.
24  *
25  * If the wrapped conversion succeeds, then the wrapper will set the
26  * %SOUP_MESSAGE_CONTENT_DECODED flag on its message.
27  */
28
29 enum {
30         PROP_0,
31         PROP_BASE_CONVERTER,
32         PROP_MESSAGE
33 };
34
35 static void soup_converter_wrapper_iface_init (GConverterIface *iface);
36
37 G_DEFINE_TYPE_WITH_CODE (SoupConverterWrapper, soup_converter_wrapper, G_TYPE_OBJECT,
38                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
39                                                 soup_converter_wrapper_iface_init))
40
41 struct _SoupConverterWrapperPrivate
42 {
43         GConverter *base_converter;
44         SoupMessage *msg;
45         gboolean try_deflate_fallback;
46         gboolean started;
47         gboolean discarding;
48 };
49
50 static void
51 soup_converter_wrapper_finalize (GObject *object)
52 {
53         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
54
55         if (priv->base_converter)
56                 g_object_unref (priv->base_converter);
57
58         G_OBJECT_CLASS (soup_converter_wrapper_parent_class)->finalize (object);
59 }
60
61
62 static void
63 soup_converter_wrapper_set_property (GObject      *object,
64                                      guint         prop_id,
65                                      const GValue *value,
66                                      GParamSpec   *pspec)
67 {
68         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
69
70         switch (prop_id) {
71         case PROP_BASE_CONVERTER:
72                 priv->base_converter = g_value_dup_object (value);
73                 if (G_IS_ZLIB_DECOMPRESSOR (priv->base_converter)) {
74                         GZlibCompressorFormat format;
75
76                         g_object_get (G_OBJECT (priv->base_converter),
77                                       "format", &format,
78                                       NULL);
79                         if (format == G_ZLIB_COMPRESSOR_FORMAT_ZLIB)
80                                 priv->try_deflate_fallback = TRUE;
81                 }
82                 break;
83
84         case PROP_MESSAGE:
85                 priv->msg = g_value_dup_object (value);
86                 break;
87
88         default:
89                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
90                 break;
91         }
92 }
93
94 static void
95 soup_converter_wrapper_get_property (GObject    *object,
96                                      guint       prop_id,
97                                      GValue     *value,
98                                      GParamSpec *pspec)
99 {
100         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
101
102         switch (prop_id) {
103         case PROP_BASE_CONVERTER:
104                 g_value_set_object (value, priv->base_converter);
105                 break;
106
107         case PROP_MESSAGE:
108                 g_value_set_object (value, priv->msg);
109                 break;
110
111         default:
112                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113                 break;
114         }
115 }
116
117 static void
118 soup_converter_wrapper_init (SoupConverterWrapper *converter)
119 {
120         converter->priv = G_TYPE_INSTANCE_GET_PRIVATE (converter,
121                                                        SOUP_TYPE_CONVERTER_WRAPPER,
122                                                        SoupConverterWrapperPrivate);
123 }
124
125 static void
126 soup_converter_wrapper_class_init (SoupConverterWrapperClass *klass)
127 {
128         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
129
130         g_type_class_add_private (klass, sizeof (SoupConverterWrapperPrivate));
131
132         gobject_class->finalize = soup_converter_wrapper_finalize;
133         gobject_class->get_property = soup_converter_wrapper_get_property;
134         gobject_class->set_property = soup_converter_wrapper_set_property;
135
136         g_object_class_install_property (gobject_class,
137                                          PROP_BASE_CONVERTER,
138                                          g_param_spec_object ("base-converter",
139                                                               "Base GConverter",
140                                                               "GConverter to wrap",
141                                                               G_TYPE_CONVERTER,
142                                                               G_PARAM_READWRITE |
143                                                               G_PARAM_CONSTRUCT_ONLY |
144                                                               G_PARAM_STATIC_STRINGS));
145         g_object_class_install_property (gobject_class,
146                                          PROP_MESSAGE,
147                                          g_param_spec_object ("message",
148                                                               "Message",
149                                                               "Associated SoupMessage",
150                                                               SOUP_TYPE_MESSAGE,
151                                                               G_PARAM_READWRITE |
152                                                               G_PARAM_CONSTRUCT_ONLY |
153                                                               G_PARAM_STATIC_STRINGS));
154 }
155
156 GConverter *
157 soup_converter_wrapper_new (GConverter  *base_converter,
158                             SoupMessage *msg)
159 {
160         return g_object_new (SOUP_TYPE_CONVERTER_WRAPPER,
161                              "base-converter", base_converter,
162                              "message", msg,
163                              NULL);
164 }
165
166 static void
167 soup_converter_wrapper_reset (GConverter *converter)
168 {
169         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
170
171         if (priv->base_converter)
172                 g_converter_reset (priv->base_converter);
173 }
174
175 static GConverterResult
176 soup_converter_wrapper_fallback_convert (GConverter *converter,
177                                          const void *inbuf,
178                                          gsize       inbuf_size,
179                                          void       *outbuf,
180                                          gsize       outbuf_size,
181                                          GConverterFlags flags,
182                                          gsize      *bytes_read,
183                                          gsize      *bytes_written,
184                                          GError    **error)
185 {
186         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
187
188         if (outbuf_size == 0) {
189                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
190                              _("Output buffer is too small"));
191                 return G_CONVERTER_ERROR;
192         }
193
194         if (priv->discarding) {
195                 *bytes_read = inbuf_size;
196                 *bytes_written = 0;
197         } else if (outbuf_size >= inbuf_size) {
198                 memcpy (outbuf, inbuf, inbuf_size);
199                 *bytes_read = *bytes_written = inbuf_size;
200         } else {
201                 memcpy (outbuf, inbuf, outbuf_size);
202                 *bytes_read = *bytes_written = outbuf_size;
203         }
204
205         if (*bytes_read < inbuf_size)
206                 return G_CONVERTER_CONVERTED;
207
208         if (flags & G_CONVERTER_INPUT_AT_END)
209                 return G_CONVERTER_FINISHED;
210         else if (flags & G_CONVERTER_FLUSH)
211                 return G_CONVERTER_FLUSHED;
212         else if (inbuf_size)
213                 return G_CONVERTER_CONVERTED;
214         else {
215                 /* Force it to either read more input or
216                  * try again with G_CONVERTER_INPUT_AT_END.
217                  */
218                 g_set_error_literal (error, G_IO_ERROR,
219                                      G_IO_ERROR_PARTIAL_INPUT,
220                                      "");
221                 return G_CONVERTER_ERROR;
222         }
223 }
224
225 static GConverterResult
226 soup_converter_wrapper_real_convert (GConverter *converter,
227                                      const void *inbuf,
228                                      gsize       inbuf_size,
229                                      void       *outbuf,
230                                      gsize       outbuf_size,
231                                      GConverterFlags flags,
232                                      gsize      *bytes_read,
233                                      gsize      *bytes_written,
234                                      GError    **error)
235 {
236         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
237         GConverterResult result;
238         GError *my_error = NULL;
239
240  try_again:
241         result = g_converter_convert (priv->base_converter,
242                                       inbuf, inbuf_size,
243                                       outbuf, outbuf_size,
244                                       flags, bytes_read, bytes_written,
245                                       &my_error);
246         if (result != G_CONVERTER_ERROR) {
247                 if (!priv->started) {
248                         SoupMessageFlags flags = soup_message_get_flags (priv->msg);
249                         soup_message_set_flags (priv->msg, flags | SOUP_MESSAGE_CONTENT_DECODED);
250                         priv->started = TRUE;
251                 }
252
253                 if (result == G_CONVERTER_FINISHED &&
254                     !(flags & G_CONVERTER_INPUT_AT_END)) {
255                         /* We need to keep reading (and discarding)
256                          * input to the end of the message body.
257                          */
258                         g_clear_object (&priv->base_converter);
259                         priv->discarding = TRUE;
260
261                         if (*bytes_written)
262                                 return G_CONVERTER_CONVERTED;
263                         else {
264                                 g_set_error_literal (error, G_IO_ERROR,
265                                                      G_IO_ERROR_PARTIAL_INPUT,
266                                                      "");
267                                 return G_CONVERTER_ERROR;
268                         }
269                 }
270
271                 return result;
272         }
273
274         if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA) ||
275             priv->started) {
276                 g_propagate_error (error, my_error);
277                 return result;
278         }
279         g_clear_error (&my_error);
280
281         /* Deflate hack: some servers (especially Apache with
282          * mod_deflate) return raw compressed data without the zlib
283          * headers when the client claims to support deflate.
284          */
285         if (priv->try_deflate_fallback) {
286                 priv->try_deflate_fallback = FALSE;
287                 g_object_unref (priv->base_converter);
288                 priv->base_converter = (GConverter *)
289                         g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW);
290                 goto try_again;
291         }
292
293         /* Passthrough hack: some servers mistakenly claim to be
294          * sending encoded data when in fact they aren't, so fall
295          * back to just not decoding.
296          */
297         g_clear_object (&priv->base_converter);
298         return soup_converter_wrapper_fallback_convert (converter,
299                                                         inbuf, inbuf_size,
300                                                         outbuf, outbuf_size,
301                                                         flags, bytes_read,
302                                                         bytes_written, error);
303 }
304
305 static GConverterResult
306 soup_converter_wrapper_convert (GConverter *converter,
307                                 const void *inbuf,
308                                 gsize       inbuf_size,
309                                 void       *outbuf,
310                                 gsize       outbuf_size,
311                                 GConverterFlags flags,
312                                 gsize      *bytes_read,
313                                 gsize      *bytes_written,
314                                 GError    **error)
315 {
316         SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (converter)->priv;
317
318         if (priv->base_converter) {
319                 return soup_converter_wrapper_real_convert (converter,
320                                                             inbuf, inbuf_size,
321                                                             outbuf, outbuf_size,
322                                                             flags, bytes_read,
323                                                             bytes_written, error);
324         } else {
325                 return soup_converter_wrapper_fallback_convert (converter,
326                                                                 inbuf, inbuf_size,
327                                                                 outbuf, outbuf_size,
328                                                                 flags, bytes_read,
329                                                                 bytes_written, error);
330         }
331 }
332
333 static void
334 soup_converter_wrapper_iface_init (GConverterIface *iface)
335 {
336         iface->convert = soup_converter_wrapper_convert;
337         iface->reset = soup_converter_wrapper_reset;
338 }