Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.git] / libsoup / soup-body-input-stream.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-body-input-stream.c
4  *
5  * Copyright 2012 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <gio/gio.h>
15
16 #include <glib/gi18n-lib.h>
17
18 #include "soup-body-input-stream.h"
19 #include "soup-enum-types.h"
20 #include "soup-filter-input-stream.h"
21 #include "soup-marshal.h"
22 #include "soup-message-headers.h"
23
24 typedef enum {
25         SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE,
26         SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END,
27         SOUP_BODY_INPUT_STREAM_STATE_CHUNK,
28         SOUP_BODY_INPUT_STREAM_STATE_TRAILERS,
29         SOUP_BODY_INPUT_STREAM_STATE_DONE
30 } SoupBodyInputStreamState;
31
32 struct _SoupBodyInputStreamPrivate {
33         GInputStream *base_stream;
34
35         SoupEncoding  encoding;
36         goffset       read_length;
37         SoupBodyInputStreamState chunked_state;
38         gboolean      eof;
39 };
40
41 enum {
42         CLOSED,
43         LAST_SIGNAL
44 };
45
46 static guint signals[LAST_SIGNAL] = { 0 };
47
48 enum {
49         PROP_0,
50
51         PROP_ENCODING,
52         PROP_CONTENT_LENGTH
53 };
54
55 static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
56
57 G_DEFINE_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM,
58                          G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
59                                                 soup_body_input_stream_pollable_init))
60
61 static void
62 soup_body_input_stream_init (SoupBodyInputStream *bistream)
63 {
64         bistream->priv = G_TYPE_INSTANCE_GET_PRIVATE (bistream,
65                                                       SOUP_TYPE_BODY_INPUT_STREAM,
66                                                       SoupBodyInputStreamPrivate);
67         bistream->priv->encoding = SOUP_ENCODING_NONE;
68 }
69
70 static void
71 constructed (GObject *object)
72 {
73         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
74
75         bistream->priv->base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (bistream));
76
77         if (bistream->priv->encoding == SOUP_ENCODING_NONE ||
78             (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH &&
79              bistream->priv->read_length == 0))
80                 bistream->priv->eof = TRUE;
81 }
82
83 static void
84 set_property (GObject *object, guint prop_id,
85               const GValue *value, GParamSpec *pspec)
86 {
87         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
88
89         switch (prop_id) {
90         case PROP_ENCODING:
91                 bistream->priv->encoding = g_value_get_enum (value);
92                 if (bistream->priv->encoding == SOUP_ENCODING_CHUNKED)
93                         bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
94                 break;
95         case PROP_CONTENT_LENGTH:
96                 bistream->priv->read_length = g_value_get_int64 (value);
97                 break;
98         default:
99                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100                 break;
101         }
102 }
103
104 static void
105 get_property (GObject *object, guint prop_id,
106               GValue *value, GParamSpec *pspec)
107 {
108         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (object);
109
110         switch (prop_id) {
111         case PROP_ENCODING:
112                 g_value_set_enum (value, bistream->priv->encoding);
113                 break;
114         default:
115                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116                 break;
117         }
118 }
119
120 static gssize
121 soup_body_input_stream_read_raw (SoupBodyInputStream  *bistream,
122                                  void                 *buffer,
123                                  gsize                 count,
124                                  gboolean              blocking,
125                                  GCancellable         *cancellable,
126                                  GError              **error)
127 {
128         gssize nread;
129
130         nread = g_pollable_stream_read (bistream->priv->base_stream,
131                                         buffer, count,
132                                         blocking,
133                                         cancellable, error);
134         if (nread == 0) {
135                 bistream->priv->eof = TRUE;
136                 if (bistream->priv->encoding != SOUP_ENCODING_EOF) {
137                         g_set_error_literal (error, G_IO_ERROR,
138                                              G_IO_ERROR_PARTIAL_INPUT,
139                                              _("Connection terminated unexpectedly"));
140                         return -1;
141                 }
142         }
143         return nread;
144 }
145
146 static gssize
147 soup_body_input_stream_read_chunked (SoupBodyInputStream  *bistream,
148                                      void                 *buffer,
149                                      gsize                 count,
150                                      gboolean              blocking,
151                                      GCancellable         *cancellable,
152                                      GError              **error)
153 {
154         SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream);
155         char metabuf[128];
156         gssize nread;
157         gboolean got_line;
158
159 again:
160         switch (bistream->priv->chunked_state) {
161         case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE:
162                 nread = soup_filter_input_stream_read_line (
163                         fstream, metabuf, sizeof (metabuf), blocking,
164                         &got_line, cancellable, error);
165                 if (nread <= 0)
166                         return nread;
167                 if (!got_line) {
168                         g_set_error_literal (error, G_IO_ERROR,
169                                              G_IO_ERROR_PARTIAL_INPUT,
170                                              _("Connection terminated unexpectedly"));
171                         return -1;
172                 }
173
174                 bistream->priv->read_length = strtoul (metabuf, NULL, 16);
175                 if (bistream->priv->read_length > 0)
176                         bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK;
177                 else
178                         bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_TRAILERS;
179                 break;
180
181         case SOUP_BODY_INPUT_STREAM_STATE_CHUNK:
182                 nread = soup_body_input_stream_read_raw (
183                         bistream, buffer,
184                         MIN (count, bistream->priv->read_length),
185                         blocking, cancellable, error);
186                 if (nread > 0) {
187                         bistream->priv->read_length -= nread;
188                         if (bistream->priv->read_length == 0)
189                                 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END;
190                 }
191                 return nread;
192
193         case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END:
194                 nread = soup_filter_input_stream_read_line (
195                         SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream),
196                         metabuf, sizeof (metabuf), blocking,
197                         &got_line, cancellable, error);
198                 if (nread <= 0)
199                         return nread;
200                 if (!got_line) {
201                         g_set_error_literal (error, G_IO_ERROR,
202                                              G_IO_ERROR_PARTIAL_INPUT,
203                                              _("Connection terminated unexpectedly"));
204                         return -1;
205                 }
206
207                 bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE;
208                 break;
209
210         case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS:
211                 nread = soup_filter_input_stream_read_line (
212                         fstream, buffer, count, blocking,
213                         &got_line, cancellable, error);
214                 if (nread <= 0)
215                         return nread;
216
217                 if (strncmp (buffer, "\r\n", nread) || strncmp (buffer, "\n", nread))
218                         bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE;
219                 break;
220
221         case SOUP_BODY_INPUT_STREAM_STATE_DONE:
222                 return 0;
223         }
224
225         goto again;
226 }
227
228 static gssize
229 read_internal (GInputStream  *stream,
230                void          *buffer,
231                gsize          count,
232                gboolean       blocking,
233                GCancellable  *cancellable,
234                GError       **error)
235 {
236         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
237         gssize nread;
238
239         if (bistream->priv->eof)
240                 return 0;
241
242         switch (bistream->priv->encoding) {
243         case SOUP_ENCODING_NONE:
244                 return 0;
245
246         case SOUP_ENCODING_CHUNKED:
247                 return soup_body_input_stream_read_chunked (bistream, buffer, count,
248                                                             blocking, cancellable, error);
249
250         case SOUP_ENCODING_CONTENT_LENGTH:
251         case SOUP_ENCODING_EOF:
252                 if (bistream->priv->read_length != -1) {
253                         count = MIN (count, bistream->priv->read_length);
254                         if (count == 0)
255                                 return 0;
256                 }
257
258                 nread = soup_body_input_stream_read_raw (bistream, buffer, count,
259                                                          blocking, cancellable, error);
260                 if (bistream->priv->read_length != -1 && nread > 0)
261                         bistream->priv->read_length -= nread;
262                 return nread;
263
264         default:
265                 g_return_val_if_reached (-1);
266         }
267 }
268
269 static gssize
270 soup_body_input_stream_read_fn (GInputStream  *stream,
271                                 void          *buffer,
272                                 gsize          count,
273                                 GCancellable  *cancellable,
274                                 GError       **error)
275 {
276         return read_internal (stream, buffer, count, TRUE,
277                               cancellable, error);
278 }
279
280 static gboolean
281 soup_body_input_stream_close_fn (GInputStream  *stream,
282                                  GCancellable  *cancellable,
283                                  GError       **error)
284 {
285         g_signal_emit (stream, signals[CLOSED], 0);
286
287         return G_INPUT_STREAM_CLASS (soup_body_input_stream_parent_class)->close_fn (stream, cancellable, error);
288 }
289
290 static gboolean
291 soup_body_input_stream_is_readable (GPollableInputStream *stream)
292 {
293         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
294
295         return bistream->priv->eof ||
296                 g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream));
297 }
298
299 static gssize
300 soup_body_input_stream_read_nonblocking (GPollableInputStream  *stream,
301                                          void                  *buffer,
302                                          gsize                  count,
303                                          GError               **error)
304 {
305         return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE,
306                               NULL, error);
307 }
308
309 static GSource *
310 soup_body_input_stream_create_source (GPollableInputStream *stream,
311                                       GCancellable *cancellable)
312 {
313         SoupBodyInputStream *bistream = SOUP_BODY_INPUT_STREAM (stream);
314         GSource *base_source, *pollable_source;
315
316         if (bistream->priv->eof)
317                 base_source = g_timeout_source_new (0);
318         else
319                 base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream), cancellable);
320         g_source_set_dummy_callback (base_source);
321
322         pollable_source = g_pollable_source_new (G_OBJECT (stream));
323         g_source_add_child_source (pollable_source, base_source);
324         g_source_unref (base_source);
325
326         return pollable_source;
327 }
328
329 static void
330 soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
331 {
332         GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
333         GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
334
335         g_type_class_add_private (stream_class, sizeof (SoupBodyInputStreamPrivate));
336
337         object_class->constructed = constructed;
338         object_class->set_property = set_property;
339         object_class->get_property = get_property;
340
341         input_stream_class->read_fn = soup_body_input_stream_read_fn;
342         input_stream_class->close_fn = soup_body_input_stream_close_fn;
343
344         signals[CLOSED] =
345                 g_signal_new ("closed",
346                               G_OBJECT_CLASS_TYPE (object_class),
347                               G_SIGNAL_RUN_LAST,
348                               0,
349                               NULL, NULL,
350                               _soup_marshal_NONE__NONE,
351                               G_TYPE_NONE, 0);
352
353         g_object_class_install_property (
354                 object_class, PROP_ENCODING,
355                 g_param_spec_enum ("encoding",
356                                    "Encoding",
357                                    "Message body encoding",
358                                    SOUP_TYPE_ENCODING,
359                                    SOUP_ENCODING_NONE,
360                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
361         g_object_class_install_property (
362                 object_class, PROP_CONTENT_LENGTH,
363                 g_param_spec_int64 ("content-length",
364                                     "Content-Length",
365                                     "Message body Content-Length",
366                                     -1, G_MAXINT64, -1,
367                                     G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
368 }
369
370 static void
371 soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
372                                  gpointer interface_data)
373 {
374         pollable_interface->is_readable = soup_body_input_stream_is_readable;
375         pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking;
376         pollable_interface->create_source = soup_body_input_stream_create_source;
377 }
378
379 GInputStream *
380 soup_body_input_stream_new (SoupFilterInputStream *base_stream,
381                             SoupEncoding           encoding,
382                             goffset                content_length)
383 {
384         return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
385                              "base-stream", base_stream,
386                              "close-base-stream", FALSE,
387                              "encoding", encoding,
388                              "content-length", content_length,
389                              NULL);
390 }