Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-content-decoder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-content-decoder.c
4  *
5  * Copyright (C) 2009 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include "soup-content-decoder.h"
13 #include "soup-converter-wrapper.h"
14 #include "soup.h"
15 #include "soup-message-private.h"
16
17 /**
18  * SECTION:soup-content-decoder
19  * @short_description: Content-Encoding handler
20  *
21  * #SoupContentDecoder handles adding the "Accept-Encoding" header on
22  * outgoing messages, and processing the "Content-Encoding" header on
23  * incoming ones. Currently it supports the "gzip" and "deflate"
24  * content codings.
25  *
26  * If you are using a plain #SoupSession (ie, not #SoupSessionAsync or
27  * #SoupSessionSync), then a #SoupContentDecoder will automatically be
28  * added to the session by default. (You can use
29  * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE at construct time if you don't
30  * want this.) If you are using one of the deprecated #SoupSession
31  * subclasses, you can add a #SoupContentDecoder to your session with
32  * soup_session_add_feature() or soup_session_add_feature_by_type().
33  *
34  * If #SoupContentDecoder successfully decodes the Content-Encoding,
35  * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
36  * and the message body and the chunks in the #SoupMessage::got_chunk
37  * signals will contain the decoded data; however, the message headers
38  * will be unchanged (and so "Content-Encoding" will still be present,
39  * "Content-Length" will describe the original encoded length, etc).
40  *
41  * If "Content-Encoding" contains any encoding types that
42  * #SoupContentDecoder doesn't recognize, then none of the encodings
43  * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
44  * not be set).
45  *
46  * (Note that currently there is no way to (automatically) use
47  * Content-Encoding when sending a request body, or to pick specific
48  * encoding types to support.)
49  *
50  * Since: 2.30
51  **/
52
53 struct _SoupContentDecoderPrivate {
54         GHashTable *decoders;
55 };
56
57 typedef GConverter * (*SoupContentDecoderCreator) (void);
58
59 static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
60
61 static SoupContentProcessorInterface *soup_content_decoder_default_content_processor_interface;
62 static void soup_content_decoder_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
63
64
65 G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
66                          G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
67                                                 soup_content_decoder_session_feature_init)
68                          G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
69                                                 soup_content_decoder_content_processor_init))
70
71 static GSList *
72 soup_content_decoder_get_decoders_for_msg (SoupContentDecoder *decoder, SoupMessage *msg)
73 {
74         const char *header;
75         GSList *encodings, *e, *decoders = NULL;
76         SoupContentDecoderCreator converter_creator;
77         GConverter *converter;
78
79         header = soup_message_headers_get_list (msg->response_headers,
80                                                 "Content-Encoding");
81         if (!header)
82                 return NULL;
83
84         /* Workaround for an apache bug (bgo 613361) */
85         if (!g_ascii_strcasecmp (header, "gzip") ||
86             !g_ascii_strcasecmp (header, "x-gzip")) {
87                 const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
88
89                 if (content_type &&
90                     (!g_ascii_strcasecmp (content_type, "application/gzip") ||
91                      !g_ascii_strcasecmp (content_type, "application/x-gzip")))
92                         return NULL;
93         }
94
95         /* OK, really, no one is ever going to use more than one
96          * encoding, but we'll be robust.
97          */
98         encodings = soup_header_parse_list (header);
99         if (!encodings)
100                 return NULL;
101
102         for (e = encodings; e; e = e->next) {
103                 if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
104                         soup_header_free_list (encodings);
105                         return NULL;
106                 }
107         }
108
109         for (e = encodings; e; e = e->next) {
110                 converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
111                 converter = converter_creator ();
112
113                 /* Content-Encoding lists the codings in the order
114                  * they were applied in, so we put decoders in reverse
115                  * order so the last-applied will be the first
116                  * decoded.
117                  */
118                 decoders = g_slist_prepend (decoders, converter);
119         }
120         soup_header_free_list (encodings);
121
122         return decoders;
123 }
124
125 static GInputStream*
126 soup_content_decoder_content_processor_wrap_input (SoupContentProcessor *processor,
127                                                    GInputStream *base_stream,
128                                                    SoupMessage *msg,
129                                                    GError **error)
130 {
131         GSList *decoders, *d;
132         GInputStream *istream;
133
134         decoders = soup_content_decoder_get_decoders_for_msg (SOUP_CONTENT_DECODER (processor), msg);
135         if (!decoders)
136                 return NULL;
137
138         istream = g_object_ref (base_stream);
139         for (d = decoders; d; d = d->next) {
140                 GConverter *decoder, *wrapper;
141                 GInputStream *filter;
142
143                 decoder = d->data;
144                 wrapper = soup_converter_wrapper_new (decoder, msg);
145                 filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
146                                        "base-stream", istream,
147                                        "converter", wrapper,
148                                        NULL);
149                 g_object_unref (istream);
150                 g_object_unref (wrapper);
151                 istream = filter;
152         }
153
154         g_slist_free_full (decoders, g_object_unref);
155
156         return istream;
157 }
158
159 static void
160 soup_content_decoder_content_processor_init (SoupContentProcessorInterface *processor_interface,
161                                              gpointer interface_data)
162 {
163         soup_content_decoder_default_content_processor_interface =
164                 g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
165
166         processor_interface->processing_stage = SOUP_STAGE_CONTENT_ENCODING;
167         processor_interface->wrap_input = soup_content_decoder_content_processor_wrap_input;
168 }
169
170 /* This is constant for now */
171 #define ACCEPT_ENCODING_HEADER "gzip, deflate"
172
173 static GConverter *
174 gzip_decoder_creator (void)
175 {
176         return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
177 }
178
179 static GConverter *
180 zlib_decoder_creator (void)
181 {
182         return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
183 }
184
185 static void
186 soup_content_decoder_init (SoupContentDecoder *decoder)
187 {
188         decoder->priv = G_TYPE_INSTANCE_GET_PRIVATE (decoder,
189                                                      SOUP_TYPE_CONTENT_DECODER,
190                                                      SoupContentDecoderPrivate);
191
192         decoder->priv->decoders = g_hash_table_new (g_str_hash, g_str_equal);
193         /* Hardcoded for now */
194         g_hash_table_insert (decoder->priv->decoders, "gzip",
195                              gzip_decoder_creator);
196         g_hash_table_insert (decoder->priv->decoders, "x-gzip",
197                              gzip_decoder_creator);
198         g_hash_table_insert (decoder->priv->decoders, "deflate",
199                              zlib_decoder_creator);
200 }
201
202 static void
203 soup_content_decoder_finalize (GObject *object)
204 {
205         SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (object);
206
207         g_hash_table_destroy (decoder->priv->decoders);
208
209         G_OBJECT_CLASS (soup_content_decoder_parent_class)->finalize (object);
210 }
211
212 static void
213 soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
214 {
215         GObjectClass *object_class = G_OBJECT_CLASS (decoder_class);
216
217         g_type_class_add_private (decoder_class, sizeof (SoupContentDecoderPrivate));
218
219         object_class->finalize = soup_content_decoder_finalize;
220 }
221
222 static void
223 soup_content_decoder_request_queued (SoupSessionFeature *feature,
224                                      SoupSession *session,
225                                      SoupMessage *msg)
226 {
227         if (!soup_message_headers_get_one (msg->request_headers,
228                                            "Accept-Encoding")) {
229                 soup_message_headers_append (msg->request_headers,
230                                              "Accept-Encoding",
231                                              ACCEPT_ENCODING_HEADER);
232         }
233 }
234
235 static void
236 soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
237                                            gpointer interface_data)
238 {
239         feature_interface->request_queued = soup_content_decoder_request_queued;
240 }