[SoupCodingGzip] discard trailing junk after decoding, for compatibility
[platform/upstream/libsoup.git] / libsoup / soup-coding-gzip.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-coding-gzip.c: "gzip" coding
4  *
5  * Copyright (C) 2005 Novell, Inc.
6  * Copyright (C) 2008 Red Hat, Inc.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include "soup-coding-gzip.h"
14
15 #include <zlib.h>
16
17 typedef struct {
18         z_stream stream;
19
20 } SoupCodingGzipPrivate;
21 #define SOUP_CODING_GZIP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CODING_GZIP, SoupCodingGzipPrivate))
22
23 G_DEFINE_TYPE (SoupCodingGzip, soup_coding_gzip, SOUP_TYPE_CODING)
24
25 static void             constructed (GObject *object);
26 static void             finalize    (GObject *object);
27 static SoupCodingStatus apply_into  (SoupCoding *coding,
28                                      gconstpointer input, gsize input_length,
29                                      gsize *input_used,
30                                      gpointer output, gsize output_length,
31                                      gsize *output_used,
32                                      gboolean done, GError **error);
33
34 static void
35 soup_coding_gzip_init (SoupCodingGzip *gzip)
36 {
37         SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (gzip);
38
39         priv->stream.zalloc = Z_NULL;
40         priv->stream.zfree = Z_NULL;
41         priv->stream.opaque = Z_NULL;
42 }
43
44 static void
45 soup_coding_gzip_class_init (SoupCodingGzipClass *gzip_class)
46 {
47         GObjectClass *object_class = G_OBJECT_CLASS (gzip_class);
48         SoupCodingClass *coding_class = SOUP_CODING_CLASS (gzip_class);
49
50         g_type_class_add_private (gzip_class, sizeof (SoupCodingGzipPrivate));
51
52         coding_class->name = "gzip";
53
54         object_class->constructed = constructed;
55         object_class->finalize = finalize;
56
57         coding_class->apply_into = apply_into;
58 }
59
60 static void
61 constructed (GObject *object)
62 {
63         SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (object);
64
65         /* All of these values are the defaults according to the zlib
66          * documentation. "16" is a magic number that means gzip
67          * instead of zlib.
68          */
69         if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE) {
70                 deflateInit2 (&priv->stream, Z_DEFAULT_COMPRESSION,
71                               Z_DEFLATED, MAX_WBITS | 16, 8,
72                               Z_DEFAULT_STRATEGY);
73         } else
74                 inflateInit2 (&priv->stream, MAX_WBITS | 16);
75 }
76
77 static void
78 finalize (GObject *object)
79 {
80         SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (object);
81
82         if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE)
83                 deflateEnd (&priv->stream);
84         else
85                 inflateEnd (&priv->stream);
86
87         G_OBJECT_CLASS (soup_coding_gzip_parent_class)->finalize (object);
88 }
89
90 static SoupCodingStatus
91 apply_into (SoupCoding *coding,
92             gconstpointer input, gsize input_length, gsize *input_used,
93             gpointer output, gsize output_length, gsize *output_used,
94             gboolean done, GError **error)
95 {
96         SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (coding);
97         int ret;
98
99         priv->stream.avail_in = input_length;
100         priv->stream.next_in  = (gpointer)input;
101         priv->stream.total_in = 0;
102
103         priv->stream.avail_out = output_length;
104         priv->stream.next_out  = output;
105         priv->stream.total_out = 0;
106
107         if (coding->direction == SOUP_CODING_ENCODE)
108                 ret = deflate (&priv->stream, done ? Z_FINISH : Z_NO_FLUSH);
109         else
110                 ret = inflate (&priv->stream, Z_SYNC_FLUSH);
111
112         *input_used = priv->stream.total_in;
113         *output_used = priv->stream.total_out;
114
115         switch (ret) {
116         case Z_NEED_DICT:
117         case Z_DATA_ERROR:
118         case Z_STREAM_ERROR:
119                 g_set_error_literal (error, SOUP_CODING_ERROR,
120                                      SOUP_CODING_ERROR_DATA_ERROR,
121                                      priv->stream.msg ? priv->stream.msg : "Bad data");
122                 return SOUP_CODING_STATUS_ERROR;
123
124         case Z_BUF_ERROR:
125         case Z_MEM_ERROR:
126                 g_set_error_literal (error, SOUP_CODING_ERROR,
127                                      SOUP_CODING_ERROR_INTERNAL_ERROR,
128                                      priv->stream.msg ? priv->stream.msg : "Internal error");
129                 return SOUP_CODING_STATUS_ERROR;
130
131         case Z_STREAM_END:
132                 /* Discard any trailing junk, for compatibility with
133                  * other browsers. FIXME: this really belongs in
134                  * soup-message-io, but it's not possible to do there
135                  * with the current API.
136                  */
137                 *input_used = input_length;
138                 return SOUP_CODING_STATUS_COMPLETE;
139
140         case Z_OK:
141         default:
142                 if (*output_used == output_length &&
143                     *input_used < input_length)
144                         return SOUP_CODING_STATUS_NEED_SPACE;
145                 else
146                         return SOUP_CODING_STATUS_OK;
147         }
148 }