]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/content_encoding.c
hello world
[icculus/iodoom3.git] / neo / curl / lib / content_encoding.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: content_encoding.c,v 1.13 2004/02/15 13:58:57 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifdef HAVE_LIBZ
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "urldata.h"
32 #include <curl/curl.h>
33 #include <curl/types.h>
34 #include "sendf.h"
35 #include "content_encoding.h"
36
37 #define DSIZ 0x10000             /* buffer size for decompressed data */
38
39 #define GZIP_MAGIC_0 0x1f
40 #define GZIP_MAGIC_1 0x8b
41
42 /* gzip flag byte */
43 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
44 #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
45 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
46 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
47 #define COMMENT      0x10 /* bit 4 set: file comment present */
48 #define RESERVED     0xE0 /* bits 5..7: reserved */
49
50 static CURLcode
51 process_zlib_error(struct SessionHandle *data, z_stream *z)
52 {
53   if (z->msg)
54     failf (data, "Error while processing content unencoding.\n%s",
55            z->msg);
56   else
57     failf (data, "Error while processing content unencoding.\n"
58            "Unknown failure within decompression software.");
59
60   return CURLE_BAD_CONTENT_ENCODING;
61 }
62
63 static CURLcode
64 exit_zlib(z_stream *z, bool *zlib_init, CURLcode result)
65 {
66   inflateEnd(z);
67   *zlib_init = 0;
68   return result;
69 }
70
71 CURLcode
72 Curl_unencode_deflate_write(struct SessionHandle *data,
73                             struct Curl_transfer_keeper *k,
74                             ssize_t nread)
75 {
76   int status;                   /* zlib status */
77   CURLcode result = CURLE_OK;   /* Curl_client_write status */
78   char decomp[DSIZ];            /* Put the decompressed data here. */
79   z_stream *z = &k->z;          /* zlib state structure */
80
81   /* Initialize zlib? */
82   if (!k->zlib_init) {
83     z->zalloc = (alloc_func)Z_NULL;
84     z->zfree = (free_func)Z_NULL;
85     z->opaque = 0;              /* of dubious use 08/27/02 jhrg */
86     z->next_in = NULL;
87     z->avail_in = 0;
88     if (inflateInit(z) != Z_OK)
89       return process_zlib_error(data, z);
90     k->zlib_init = 1;
91   }
92
93   /* Set the compressed input when this function is called */
94   z->next_in = (Bytef *)k->str;
95   z->avail_in = nread;
96
97   /* because the buffer size is fixed, iteratively decompress
98      and transfer to the client via client_write. */
99   for (;;) {
100     /* (re)set buffer for decompressed output for every iteration */
101     z->next_out = (Bytef *)&decomp[0];
102     z->avail_out = DSIZ;
103
104     status = inflate(z, Z_SYNC_FLUSH);
105     if (status == Z_OK || status == Z_STREAM_END) {
106       if (DSIZ - z->avail_out) {
107         result = Curl_client_write(data, CLIENTWRITE_BODY, decomp,
108                                    DSIZ - z->avail_out);
109         /* if !CURLE_OK, clean up, return */
110         if (result)
111           return exit_zlib(z, &k->zlib_init, result);
112       }
113
114       /* Done?; clean up, return */
115       if (status == Z_STREAM_END) {
116         if (inflateEnd(z) == Z_OK)
117           return exit_zlib(z, &k->zlib_init, result);
118         else
119           return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
120       }
121
122       /* Done with these bytes, exit */
123       if (status == Z_OK && z->avail_in == 0 && z->avail_out > 0)
124         return result;
125     }
126     else {                      /* Error; exit loop, handle below */
127       return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
128     }
129   }
130 }
131
132 /* Skip over the gzip header */
133 static enum {
134   GZIP_OK,
135   GZIP_BAD,
136   GZIP_UNDERFLOW
137 } check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
138 {
139   int method, flags;
140   const ssize_t totallen = len;
141
142   /* The shortest header is 10 bytes */
143   if (len < 10)
144     return GZIP_UNDERFLOW;
145
146   if ((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
147     return GZIP_BAD;
148
149   method = data[2];
150   flags = data[3];
151
152   if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
153     /* Can't handle this compression method or unknown flag */
154     return GZIP_BAD;
155   }
156
157   /* Skip over time, xflags, OS code and all previous bytes */
158   len -= 10;
159   data += 10;
160
161   if (flags & EXTRA_FIELD) {
162     ssize_t extra_len;
163
164     if (len < 2)
165       return GZIP_UNDERFLOW;
166
167     extra_len = (data[1] << 8) | data[0];
168
169     if (len < (extra_len+2))
170       return GZIP_UNDERFLOW;
171
172     len -= (extra_len + 2);
173   }
174
175   if (flags & ORIG_NAME) {
176     /* Skip over NUL-terminated file name */
177     while (len && *data) {
178       --len;
179       ++data;
180     }
181     if (!len || *data)
182       return GZIP_UNDERFLOW;
183
184     /* Skip over the NUL */
185     --len;
186     ++data;
187   }
188
189   if (flags & COMMENT) {
190     /* Skip over NUL-terminated comment */
191     while (len && *data) {
192       --len;
193       ++data;
194     }
195     if (!len || *data)
196       return GZIP_UNDERFLOW;
197
198     /* Skip over the NUL */
199     --len;
200     ++data;
201   }
202
203   if (flags & HEAD_CRC) {
204     if (len < 2)
205       return GZIP_UNDERFLOW;
206
207     len -= 2;
208     data += 2;
209   }
210
211   *headerlen = totallen - len;
212   return GZIP_OK;
213 }
214
215 CURLcode
216 Curl_unencode_gzip_write(struct SessionHandle *data,
217                          struct Curl_transfer_keeper *k,
218                          ssize_t nread)
219 {
220   int status;                   /* zlib status */
221   CURLcode result = CURLE_OK;   /* Curl_client_write status */
222   char decomp[DSIZ];            /* Put the decompressed data here. */
223   z_stream *z = &k->z;          /* zlib state structure */
224
225   /* Initialize zlib? */
226   if (!k->zlib_init) {
227     z->zalloc = (alloc_func)Z_NULL;
228     z->zfree = (free_func)Z_NULL;
229     z->opaque = 0;              /* of dubious use 08/27/02 jhrg */
230     z->next_in = NULL;
231     z->avail_in = 0;
232     if (inflateInit2(z, -MAX_WBITS) != Z_OK)
233       return process_zlib_error(data, z);
234     k->zlib_init = 1;   /* Initial call state */
235   }
236
237   /* This next mess is to get around the potential case where there isn't
238   enough data passed in to skip over the gzip header.  If that happens,
239   we malloc a block and copy what we have then wait for the next call.  If
240   there still isn't enough (this is definitely a worst-case scenario), we
241   make the block bigger, copy the next part in and keep waiting. */
242
243   /* Skip over gzip header? */
244   if (k->zlib_init == 1) {
245     /* Initial call state */
246     ssize_t hlen;
247
248     switch (check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
249     case GZIP_OK:
250       z->next_in = (Bytef *)k->str + hlen;
251       z->avail_in = nread - hlen;
252       k->zlib_init = 3; /* Inflating stream state */
253       break;
254
255     case GZIP_UNDERFLOW:
256       /* We need more data so we can find the end of the gzip header.
257       It's possible that the memory block we malloc here will never be
258       freed if the transfer abruptly aborts after this point.  Since it's
259       unlikely that circumstances will be right for this code path to be
260       followed in the first place, and it's even more unlikely for a transfer
261       to fail immediately afterwards, it should seldom be a problem. */
262       z->avail_in = nread;
263       z->next_in = malloc(z->avail_in);
264       if (z->next_in == NULL) {
265         return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
266       }
267       memcpy(z->next_in, k->str, z->avail_in);
268       k->zlib_init = 2;   /* Need more gzip header data state */
269       /* We don't have any data to inflate yet */
270       return CURLE_OK;
271
272     case GZIP_BAD:
273     default:
274       return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
275     }
276
277   }
278   else if (k->zlib_init == 2) {
279     /* Need more gzip header data state */
280     ssize_t hlen;
281     unsigned char *oldblock = z->next_in;
282
283     z->avail_in += nread;
284     z->next_in = realloc(z->next_in, z->avail_in);
285     if (z->next_in == NULL) {
286       free(oldblock);
287       return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
288     }
289     /* Append the new block of data to the previous one */
290     memcpy(z->next_in + z->avail_in - nread, k->str, nread);
291
292     switch (check_gzip_header(z->next_in, z->avail_in, &hlen)) {
293     case GZIP_OK:
294       /* This is the zlib stream data */
295       free(z->next_in);
296       /* Don't point into the malloced block since we just freed it */
297       z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
298       z->avail_in = z->avail_in - hlen;
299       k->zlib_init = 3;   /* Inflating stream state */
300       break;
301
302     case GZIP_UNDERFLOW:
303       /* We still don't have any data to inflate! */
304       return CURLE_OK;
305
306     case GZIP_BAD:
307     default:
308       free(z->next_in);
309       return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
310     }
311
312   }
313   else {
314     /* Inflating stream state */
315     z->next_in = (Bytef *)k->str;
316     z->avail_in = nread;
317   }
318
319   if (z->avail_in == 0) {
320     /* We don't have any data to inflate; wait until next time */
321     return CURLE_OK;
322   }
323
324   /* because the buffer size is fixed, iteratively decompress
325      and transfer to the client via client_write. */
326   for (;;) {
327     /* (re)set buffer for decompressed output for every iteration */
328     z->next_out = (Bytef *)&decomp[0];
329     z->avail_out = DSIZ;
330
331     status = inflate(z, Z_SYNC_FLUSH);
332     if (status == Z_OK || status == Z_STREAM_END) {
333       if(DSIZ - z->avail_out) {
334         result = Curl_client_write(data, CLIENTWRITE_BODY, decomp,
335                                    DSIZ - z->avail_out);
336         /* if !CURLE_OK, clean up, return */
337         if (result)
338           return exit_zlib(z, &k->zlib_init, result);
339       }
340
341       /* Done?; clean up, return */
342       /* We should really check the gzip CRC here */
343       if (status == Z_STREAM_END) {
344         if (inflateEnd(z) == Z_OK)
345           return exit_zlib(z, &k->zlib_init, result);
346         else
347           return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
348       }
349
350       /* Done with these bytes, exit */
351       if (status == Z_OK && z->avail_in == 0 && z->avail_out > 0)
352         return result;
353     }
354     else {                      /* Error; exit loop, handle below */
355       return exit_zlib(z, &k->zlib_init, process_zlib_error(data, z));
356     }
357   }
358 }
359 #endif /* HAVE_LIBZ */