Line 0
Link Here
|
|
|
1 |
/* |
2 |
* I wrote this and I say you can do whatever you want with it. Period. |
3 |
* However, I'd love to hear from you what you've done. |
4 |
* |
5 |
* Dominic Fandrey <kamikaze@bsdforen.de> |
6 |
*/ |
7 |
|
8 |
/** |
9 |
* \file httpdecode.c |
10 |
* |
11 |
* This file contains the implemention of the prototypes defined in |
12 |
* httpdecode.h. |
13 |
* |
14 |
* @brief |
15 |
* HTTP content decoding implemention. |
16 |
* @see |
17 |
* httpdecode.h |
18 |
* @author |
19 |
* Dominic Fandrey <kamikaze@bsdforen.de> |
20 |
* @version |
21 |
* 0.1.99.2008.07.07 |
22 |
*/ |
23 |
|
24 |
/* LINTLIBRARY */ |
25 |
|
26 |
#include <stdio.h> |
27 |
#include <stdlib.h> |
28 |
#include <string.h> |
29 |
#include <unistd.h> |
30 |
#include <errno.h> |
31 |
#include <zlib.h> |
32 |
#include "httpdecode.h" |
33 |
|
34 |
/* PRIVATE STRUCTS */ |
35 |
|
36 |
/** |
37 |
* @brief |
38 |
* The necessary data to maintain a zlib decoding stream. |
39 |
*/ |
40 |
struct zlibStream { |
41 |
/** |
42 |
* @brief |
43 |
* The original stream with the encoded data. |
44 |
*/ |
45 |
FILE * source; |
46 |
|
47 |
/** |
48 |
* @brief |
49 |
* A read buffer for the encoded stream. |
50 |
*/ |
51 |
char * buffer; |
52 |
|
53 |
/** |
54 |
* This specifies the encoding of the data. The values |
55 |
* ENCODING_GZIP and ENCODING_DEFLATE are possible. |
56 |
* |
57 |
* @brief |
58 |
* The encoding type of the stream. |
59 |
*/ |
60 |
int encoding; |
61 |
|
62 |
/** |
63 |
* @brief |
64 |
* The stream data used by zlib. |
65 |
*/ |
66 |
z_stream stream; |
67 |
|
68 |
/** |
69 |
* The length of the source stream. The value 0 means that the length |
70 |
* is unknown higher values will be used to automatically close the |
71 |
* stream. This prevents overreading and allows the continued use |
72 |
* of the underlying HTTP stream. |
73 |
* |
74 |
* @brief |
75 |
* The length of the encoded source stream. |
76 |
*/ |
77 |
size_t length; |
78 |
|
79 |
/** |
80 |
* @brief |
81 |
* The amount of data that has been read. |
82 |
*/ |
83 |
size_t read; |
84 |
|
85 |
/** |
86 |
* @brief |
87 |
* The size of the buffer for encoded data. |
88 |
*/ |
89 |
size_t bufferSize; |
90 |
|
91 |
/** |
92 |
* @brief |
93 |
* The amount of data available in the buffer. |
94 |
*/ |
95 |
size_t bufferUsed; |
96 |
}; |
97 |
|
98 |
/* PRIVATE PROTOTYPES */ |
99 |
void moveBuffer(struct zlibStream * cookie, char * newBuffer, size_t size); |
100 |
FILE * zlibOpen(struct zlibStream * cookie); |
101 |
size_t zlibRead(struct zlibStream * cookie, char * buffer, size_t length); |
102 |
int zlibClose(struct zlibStream * cookie); |
103 |
/* TODO |
104 |
FILE * compressOpen(struct zlibStream * cookie); |
105 |
size_t compressRead(struct zlibStream * cookie, char * buffer, size_t length); |
106 |
int compressClose(struct zlibStream * cookie); |
107 |
FILE * randomOpen(struct zlibStream * cookie); |
108 |
size_t randomRead(struct zlibStream * cookie, char * buffer, size_t length); |
109 |
int randomSeek(struct zlibStream * cookie, off_t offset, int whence); |
110 |
int randomClose(struct zlibStream * cookie); |
111 |
*/ |
112 |
|
113 |
/* PUBLIC FUNCTIONS */ |
114 |
|
115 |
/** |
116 |
* Opens a given stream for decoding and returns a FILE handle that can be |
117 |
* used with the fread and fclose function. Internally funopen is used |
118 |
* to achieve this. |
119 |
* |
120 |
* In case of failure NULL is returned and errno is set to EINVAL for |
121 |
* invalid parameters and ENOMEM for insufficient memory. |
122 |
* |
123 |
* @brief |
124 |
* Open a FILE stream to read decoded data from. |
125 |
* @param source |
126 |
* The stream to read the encoded data from. |
127 |
* @param encoding |
128 |
* The encoding type of the source stream. |
129 |
* @param length |
130 |
* The length of the source stream. Use 0 if unknown. |
131 |
* @return |
132 |
* Returns a FILE handle to read an encoded stream. |
133 |
* @see |
134 |
* funopen(3) |
135 |
* @see |
136 |
* fread(3) |
137 |
* @see |
138 |
* fclose(3) |
139 |
*/ |
140 |
FILE * httpDecode(FILE * source, int encoding, size_t length) { |
141 |
struct zlibStream * zlibCookie; |
142 |
|
143 |
switch (encoding) { |
144 |
case ENCODING_RAW: |
145 |
return(source); |
146 |
case ENCODING_GZIP: case ENCODING_DEFLATE: |
147 |
zlibCookie = malloc(sizeof(struct zlibStream)); |
148 |
if (zlibCookie == NULL) /* errno == ENOMEM */ |
149 |
return(NULL); |
150 |
zlibCookie->buffer = NULL; |
151 |
zlibCookie->bufferSize = 0; |
152 |
zlibCookie->source = source; |
153 |
zlibCookie->length = length; |
154 |
zlibCookie->read = 0; |
155 |
zlibCookie->encoding = encoding; |
156 |
return(zlibOpen(zlibCookie)); |
157 |
case ENCODING_COMPRESS: |
158 |
return(NULL); |
159 |
} |
160 |
|
161 |
return(NULL); |
162 |
} |
163 |
|
164 |
/** |
165 |
* This function is a wraper around httpDecode that allows random access |
166 |
* by writing the stream into a temporary file. The file is buffered |
167 |
* by a given number of buffers in memory. |
168 |
* Buffers are overwritten in LRU order. |
169 |
* |
170 |
* @brief |
171 |
* A file backed wrapper around httpDecode for random access. |
172 |
* @param source |
173 |
* The stream to read the encoded data from. |
174 |
* @param encoding |
175 |
* The encoding type of the source stream. |
176 |
* @param length |
177 |
* The length of the source stream. Use 0 if unknown. |
178 |
* @param bufferSize |
179 |
* The size of a buffer. |
180 |
* @param |
181 |
* The number of buffers. |
182 |
* @return |
183 |
* Returns a FILE handle to read an encoded stream. |
184 |
*/ |
185 |
/* TODO |
186 |
FILE * httpDecodeRandom(FILE * source, int encoding, size_t length, |
187 |
size_t bufferSize, size_t buffers) { |
188 |
return(source); |
189 |
} |
190 |
*/ |
191 |
|
192 |
/* PRIVATE FUNCTIONS */ |
193 |
|
194 |
/** |
195 |
* This function replaces the read buffer in the cookie with the new buffer. |
196 |
* The old buffer is freed but the contents are saved in the new buffer. |
197 |
* However, no security checks are performed. |
198 |
* That means that newBuffer must at least have the same size as the old one. |
199 |
* |
200 |
* @brief |
201 |
* Replace the current read buffer. |
202 |
* @param cookie |
203 |
* Contains all the data necessary to maintain the stream. |
204 |
* @param newBuffer |
205 |
* The new buffer to use. |
206 |
*/ |
207 |
void moveBuffer(struct zlibStream * cookie, char * newBuffer, size_t size) { |
208 |
memmove(newBuffer, cookie->buffer, cookie->bufferUsed); |
209 |
free(cookie->buffer); |
210 |
cookie->buffer = newBuffer; |
211 |
cookie->bufferSize = size; |
212 |
} |
213 |
|
214 |
/** |
215 |
* This function initializes a zlib stream and creates the file handler |
216 |
* that will later be used to pull data from the stream. |
217 |
* |
218 |
* Upon any kind of failure errno is set to one of the following values: |
219 |
* EINVAL This can either indicate that an unsupported encoding |
220 |
* was given or that this code and the used zlib implemention |
221 |
* are incompatible. |
222 |
* ENOMEM Indicates that the available memory is insuficient for |
223 |
* the decode buffer, zlib or funopen. |
224 |
* |
225 |
* @brief |
226 |
* Open a zlib stream. |
227 |
* @param cookie |
228 |
* Contains all the data necessary to maintain the stream. |
229 |
* @return |
230 |
* A FILE* pointer or NULL in case of failure. |
231 |
*/ |
232 |
FILE * zlibOpen(struct zlibStream * cookie) { |
233 |
int wbits; |
234 |
z_stream * stream = &(cookie->stream); |
235 |
|
236 |
/* Initialize zlib stream data. */ |
237 |
stream->zalloc = Z_NULL; |
238 |
stream->zfree = Z_NULL; |
239 |
stream->opaque = Z_NULL; |
240 |
stream->avail_in = 0; |
241 |
stream->next_in = (Bytef *) cookie->buffer; |
242 |
|
243 |
/* Set window bits for the selected encoding. */ |
244 |
switch(cookie->encoding) { |
245 |
case ENCODING_DEFLATE: |
246 |
wbits = -MAX_WBITS; |
247 |
break; |
248 |
case ENCODING_GZIP: |
249 |
wbits = MAX_WBITS + 16; |
250 |
break; |
251 |
default: |
252 |
errno = EINVAL; |
253 |
return(NULL); |
254 |
} |
255 |
|
256 |
/* Create the decoding buffer. */ |
257 |
cookie->bufferSize = 32768; /* Zlib breaks with a smaller buffer. */ |
258 |
cookie->bufferUsed = 0; |
259 |
cookie->buffer = malloc(cookie->bufferSize); |
260 |
if (cookie->buffer == NULL) |
261 |
return(NULL); /* errno == ENOMEM */ |
262 |
|
263 |
/* Initialize stream for decoding. */ |
264 |
switch(inflateInit2(stream, wbits)) { |
265 |
case Z_OK: |
266 |
errno = 0; |
267 |
break; |
268 |
case Z_MEM_ERROR: |
269 |
errno = ENOMEM; |
270 |
break; |
271 |
case Z_STREAM_ERROR: /* This is not supposed to happen. */ |
272 |
errno = EINVAL; |
273 |
break; |
274 |
} |
275 |
if (errno) { |
276 |
free(cookie->buffer); |
277 |
return(NULL); |
278 |
} |
279 |
|
280 |
/* Create the file stream to return. */ |
281 |
return(funopen(cookie,(int (*)(void *, char *, int)) zlibRead, |
282 |
NULL, NULL, (int (*)(void *)) zlibClose)); |
283 |
} |
284 |
|
285 |
/** |
286 |
* Writes a chunk of decoded data to the given buffer. |
287 |
* |
288 |
* In case of an error (size_t) -1 is returned to indicate to the funopen |
289 |
* wrapper that an error occured. In such a case errno is set to EIO. |
290 |
* |
291 |
* An error does not cause the stream to be closed. |
292 |
* |
293 |
* @brief |
294 |
* Read decoded data from the encoded stream. |
295 |
* @param cookie |
296 |
* Contains all the data necessary to maintain the stream. |
297 |
* @param buffer |
298 |
* The buffer to write the decoded data to. |
299 |
* @param length |
300 |
* The space available in the buffer. |
301 |
* @return |
302 |
* The number of bytes written to the buffer or (size_t) -1 in case of |
303 |
* failure. |
304 |
*/ |
305 |
size_t zlibRead(struct zlibStream * cookie, char * buffer, size_t length) { |
306 |
char * tmpBuffer; |
307 |
size_t growth, maxRead, bufferAvailable, flushed; |
308 |
int zlibStatus; |
309 |
z_stream * stream = &(cookie->stream); |
310 |
|
311 |
/* |
312 |
* Adjust buffer size if the target buffer is larger than 2 times |
313 |
* the source buffer. |
314 |
*/ |
315 |
if ((length >> 1) > cookie->bufferSize) { |
316 |
tmpBuffer = malloc(length >> 1); |
317 |
|
318 |
/* |
319 |
* If creating a new buffer fails pretend never to have |
320 |
* attempted it. |
321 |
*/ |
322 |
if (tmpBuffer == NULL) |
323 |
errno = 0; |
324 |
else |
325 |
/* Move data from the old buffer to the new one. */ |
326 |
moveBuffer(cookie, tmpBuffer, length >> 1); |
327 |
} |
328 |
|
329 |
/* Run until the target buffer has been filled. */ |
330 |
flushed = 0; |
331 |
while (length > 0) { |
332 |
/* If the input buffer is not full, fill it. */ |
333 |
growth = 0; |
334 |
maxRead = cookie->length - cookie->read; |
335 |
bufferAvailable = cookie->bufferSize - cookie->bufferUsed; |
336 |
bufferAvailable = (maxRead < bufferAvailable \ |
337 |
? maxRead : bufferAvailable); |
338 |
if (bufferAvailable > 0) { |
339 |
growth = fread(cookie->buffer, sizeof(char), \ |
340 |
bufferAvailable, cookie->source); |
341 |
/* Forward errors. */ |
342 |
if (ferror(cookie->source)) |
343 |
return((size_t) -1); |
344 |
cookie->bufferUsed += growth; |
345 |
cookie->read += growth; |
346 |
} |
347 |
|
348 |
/* Decode data from the read to the target buffer. */ |
349 |
stream->avail_in = cookie->bufferUsed; |
350 |
stream->next_in = (Bytef *) cookie->buffer; |
351 |
stream->avail_out = length; |
352 |
stream->next_out = (Bytef *) buffer; |
353 |
zlibStatus = inflate(stream, Z_SYNC_FLUSH); |
354 |
|
355 |
/* The amount of data just written to the target buffer. */ |
356 |
growth = length - stream->avail_out; |
357 |
|
358 |
/* Adjust the read buffer. */ |
359 |
memmove(cookie->buffer, stream->next_in, \ |
360 |
(size_t) stream->avail_in); |
361 |
cookie->bufferUsed = stream->avail_in; |
362 |
stream->next_in = (Bytef *) cookie->buffer; |
363 |
|
364 |
/* Adjust the target buffer. */ |
365 |
flushed += growth; |
366 |
buffer += growth; |
367 |
length = stream->avail_out; |
368 |
|
369 |
/* Deal with errors. */ |
370 |
switch (zlibStatus) { |
371 |
case Z_OK: |
372 |
break; |
373 |
case Z_STREAM_END: |
374 |
length = 0; |
375 |
break; |
376 |
case Z_BUF_ERROR: |
377 |
/* The read buffer is too small, try to double it. */ |
378 |
tmpBuffer = malloc(cookie->bufferSize << 1); |
379 |
if (!tmpBuffer) /* errno == ENOMEN */ |
380 |
return((size_t) -1); |
381 |
moveBuffer(cookie, tmpBuffer, cookie->bufferSize << 1); |
382 |
break; |
383 |
case Z_NEED_DICT: case Z_DATA_ERROR: case Z_STREAM_ERROR: |
384 |
errno = EIO; |
385 |
return((size_t) -1); |
386 |
case Z_MEM_ERROR: |
387 |
errno = ENOMEM; |
388 |
return((size_t) -1); |
389 |
} |
390 |
} |
391 |
|
392 |
return(flushed); |
393 |
} |
394 |
|
395 |
/** |
396 |
* Closes the decoding stream and frees all buffers. |
397 |
* |
398 |
* @brief |
399 |
* Closes the decoding stream. |
400 |
* @param cookie |
401 |
* Contains all the data necessary to maintain the stream. |
402 |
* @return |
403 |
* Always 0 for success. |
404 |
*/ |
405 |
int zlibClose(struct zlibStream * cookie) { |
406 |
inflateEnd(&(cookie->stream)); |
407 |
free(cookie->buffer); |
408 |
free(cookie); |
409 |
return(0); |
410 |
} |
411 |
|