]> code.citadel.org Git - citadel.git/blob - rss2ctdl/zlib_interface.c
6979d330d8a022d49577168fb642f613d03afaf4
[citadel.git] / rss2ctdl / zlib_interface.c
1 /* Low-level function, decompresses deflate compressed data. Used by gzip_uncompress below. */
2
3 #include "zlib_interface.h"
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <zlib.h>
7 #include <string.h>
8
9 int JG_ZLIB_DEBUG = 0;
10
11 struct gzip_header {
12         unsigned char magic[2];
13         unsigned char method;
14         unsigned char flags;
15         unsigned char mtime[4];
16         unsigned char xfl;
17         unsigned char os;
18 };
19
20 struct gzip_footer {
21         unsigned char crc32[4];
22         unsigned char size[4];
23 };
24
25 int jg_zlib_uncompress(void * const in_buf, int in_size, 
26                                        void **out_buf_ptr, int *out_size,
27                                            int gzip)
28 {
29         char tmpstring[1024];
30         z_stream stream;
31         char *out_buf = NULL;
32         int out_buf_bytes = 0;
33         char tmp_buf[4096];
34         int result;
35         int new_bytes;
36         
37         /* Prepare the stream structure. */
38         stream.zalloc = NULL;
39         stream.zfree = NULL;
40         stream.opaque = NULL;
41         stream.next_in = in_buf;        
42         stream.avail_in = in_size;
43         stream.next_out = tmp_buf;
44         stream.avail_out = sizeof tmp_buf;
45         
46         if (out_size != NULL)
47                 *out_size = 0;
48         
49         if (gzip)
50                 result = inflateInit2(&stream, MAX_WBITS + 32); /* UNTESTED */
51         else
52                 result = inflateInit2(&stream, -MAX_WBITS);
53         
54         if (result != 0) {
55                 if (JG_ZLIB_DEBUG)
56                         fprintf(stderr, "inflateInit2 failed: %d\n", result);
57                 return JG_ZLIB_ERROR_OLDVERSION;
58         }
59         
60         do {
61                 /* Should be Z_FINISH? */
62                 result = inflate(&stream, Z_NO_FLUSH);
63                 switch (result) {
64                         case Z_BUF_ERROR:
65                                 if (stream.avail_in == 0)
66                                         goto DONE; /* zlib bug */
67                         case Z_ERRNO:
68                         case Z_NEED_DICT:
69                         case Z_MEM_ERROR:
70                         case Z_DATA_ERROR:
71                         case Z_VERSION_ERROR:
72                                 inflateEnd(&stream);
73                                 free(out_buf);
74                                 if (JG_ZLIB_DEBUG) {
75                                         snprintf (tmpstring, sizeof(tmpstring), "ERROR: zlib_uncompress: %d %s\n", result, stream.msg);
76                                         fprintf(stderr, tmpstring);
77                                 }
78                                 return JG_ZLIB_ERROR_UNCOMPRESS;
79                 }
80                 if (stream.avail_out < sizeof tmp_buf) {
81                         /* Add the new uncompressed data to our output buffer. */
82                         new_bytes = sizeof tmp_buf - stream.avail_out;
83                         out_buf = realloc(out_buf, out_buf_bytes + new_bytes);
84                         memcpy(out_buf + out_buf_bytes, tmp_buf, new_bytes);
85                         out_buf_bytes += new_bytes;
86                         stream.next_out = tmp_buf;
87                         stream.avail_out = sizeof tmp_buf;
88                 } else {
89                         /* For some reason, inflate() didn't write out a single byte. */
90                         inflateEnd(&stream);
91                         free(out_buf);
92                         if (JG_ZLIB_DEBUG)
93                                 fprintf(stderr, "ERROR: No output during decompression\n");
94                         return JG_ZLIB_ERROR_NODATA;
95                 }
96         } while (result != Z_STREAM_END);
97         
98 DONE:
99         
100         inflateEnd(&stream);
101         
102         /* Null-terminate the output buffer so it can be handled like a string. */
103         out_buf = realloc(out_buf, out_buf_bytes + 1);
104         out_buf[out_buf_bytes] = 0;
105         
106         /* The returned size does NOT include the additionall null byte! */
107         if (out_size != NULL)
108                 *out_size = out_buf_bytes;
109         
110         *out_buf_ptr = out_buf;
111
112         return 0;
113 }
114
115 /* Decompressed gzip,deflate compressed data. This is what the webservers usually send. */
116
117 int jg_gzip_uncompress(void *in_buf, int in_size, 
118                                            void **out_buf_ptr, int *out_size) 
119 {
120         char tmpstring[1024];
121         struct gzip_header *header;
122         char *data_start;
123         int offset = sizeof *header;
124         
125         header = in_buf;
126         
127         if (out_size != NULL)
128                 *out_size = 0;
129         
130         if ((header->magic[0] != 0x1F) || (header->magic[1] != 0x8B)) {
131                 if (JG_ZLIB_DEBUG)
132                         fprintf(stderr, "ERROR: Invalid magic bytes for GZIP data\n");
133                 return JG_ZLIB_ERROR_BAD_MAGIC;
134         }
135         
136         if (header->method != 8) {
137                 if (JG_ZLIB_DEBUG)
138                         fprintf(stderr, "ERROR: Compression method is not deflate\n");
139                 return JG_ZLIB_ERROR_BAD_METHOD;
140         }
141         
142         if (header->flags != 0 && header->flags != 8) {
143                 if (JG_ZLIB_DEBUG) {
144                         snprintf (tmpstring, sizeof(tmpstring), "ERROR: Unsupported flags %d", header->flags);
145                         fprintf(stderr, "ERROR: %s\n", tmpstring);
146                 }
147                 return JG_ZLIB_ERROR_BAD_FLAGS;
148         }
149         
150         if (header->flags & 8) {
151                 /* skip the file name */
152                 while (offset < in_size) {
153                         if (((char *)in_buf)[offset] == 0) {
154                                 offset++;
155                                 break;
156                         }
157                         offset++;
158                 }
159         }
160         
161         data_start = (char *)in_buf + offset;
162
163         return jg_zlib_uncompress(data_start, in_size - offset - 8, 
164                                                           out_buf_ptr, out_size, 0);
165 }