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