1 #include <qpdf/Pl_Flate.hh>
2 #include <zlib.h>
3 #include <string.h>
4 #include <limits.h>
5
6 #include <qpdf/QUtil.hh>
7 #include <qpdf/QIntC.hh>
8
9 int Pl_Flate::compression_level = Z_DEFAULT_COMPRESSION;
10
Members(size_t out_bufsize,action_e action)11 Pl_Flate::Members::Members(size_t out_bufsize,
12 action_e action) :
13 out_bufsize(out_bufsize),
14 action(action),
15 initialized(false),
16 zdata(0)
17 {
18 this->outbuf = PointerHolder<unsigned char>(
19 true, new unsigned char[out_bufsize]);
20 // Indirect through zdata to reach the z_stream so we don't have
21 // to include zlib.h in Pl_Flate.hh. This means people using
22 // shared library versions of qpdf don't have to have zlib
23 // development files available, which particularly helps in a
24 // Windows environment.
25 this->zdata = new z_stream;
26
27 if (out_bufsize > UINT_MAX)
28 {
29 throw std::runtime_error(
30 "Pl_Flate: zlib doesn't support buffer"
31 " sizes larger than unsigned int");
32 }
33
34 z_stream& zstream = *(static_cast<z_stream*>(this->zdata));
35 zstream.zalloc = 0;
36 zstream.zfree = 0;
37 zstream.opaque = 0;
38 zstream.next_in = 0;
39 zstream.avail_in = 0;
40 zstream.next_out = this->outbuf.getPointer();
41 zstream.avail_out = QIntC::to_uint(out_bufsize);
42 }
43
~Members()44 Pl_Flate::Members::~Members()
45 {
46 if (this->initialized)
47 {
48 z_stream& zstream = *(static_cast<z_stream*>(this->zdata));
49 if (action == a_deflate)
50 {
51 deflateEnd(&zstream);
52 }
53 else
54 {
55 inflateEnd(&zstream);
56 }
57 }
58
59 delete static_cast<z_stream*>(this->zdata);
60 this->zdata = 0;
61 }
62
Pl_Flate(char const * identifier,Pipeline * next,action_e action,unsigned int out_bufsize_int)63 Pl_Flate::Pl_Flate(char const* identifier, Pipeline* next,
64 action_e action, unsigned int out_bufsize_int) :
65 Pipeline(identifier, next),
66 m(new Members(QIntC::to_size(out_bufsize_int), action))
67 {
68 }
69
~Pl_Flate()70 Pl_Flate::~Pl_Flate()
71 {
72 }
73
74 void
setWarnCallback(std::function<void (char const *,int)> callback)75 Pl_Flate::setWarnCallback(std::function<void(char const*, int)> callback)
76 {
77 this->m->callback = callback;
78 }
79
80 void
warn(char const * msg,int code)81 Pl_Flate::warn(char const* msg, int code)
82 {
83 if (this->m->callback != nullptr)
84 {
85 this->m->callback(msg, code);
86 }
87 }
88
89 void
write(unsigned char * data,size_t len)90 Pl_Flate::write(unsigned char* data, size_t len)
91 {
92 if (this->m->outbuf.getPointer() == 0)
93 {
94 throw std::logic_error(
95 this->identifier +
96 ": Pl_Flate: write() called after finish() called");
97 }
98
99 // Write in chunks in case len is too big to fit in an int.
100 // Assume int is at least 32 bits.
101 static size_t const max_bytes = 1 << 30;
102 size_t bytes_left = len;
103 unsigned char* buf = data;
104 while (bytes_left > 0)
105 {
106 size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
107 handleData(buf, bytes,
108 (this->m->action == a_inflate ? Z_SYNC_FLUSH : Z_NO_FLUSH));
109 bytes_left -= bytes;
110 buf += bytes;
111 }
112 }
113
114 void
handleData(unsigned char * data,size_t len,int flush)115 Pl_Flate::handleData(unsigned char* data, size_t len, int flush)
116 {
117 if (len > UINT_MAX)
118 {
119 throw std::runtime_error(
120 "Pl_Flate: zlib doesn't support data"
121 " blocks larger than int");
122 }
123 z_stream& zstream = *(static_cast<z_stream*>(this->m->zdata));
124 zstream.next_in = data;
125 zstream.avail_in = QIntC::to_uint(len);
126
127 if (! this->m->initialized)
128 {
129 int err = Z_OK;
130
131 // deflateInit and inflateInit are macros that use old-style
132 // casts.
133 #if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
134 defined(__clang__))
135 # pragma GCC diagnostic push
136 # pragma GCC diagnostic ignored "-Wold-style-cast"
137 #endif
138 if (this->m->action == a_deflate)
139 {
140 err = deflateInit(&zstream, compression_level);
141 }
142 else
143 {
144 err = inflateInit(&zstream);
145 }
146 #if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
147 defined(__clang__))
148 # pragma GCC diagnostic pop
149 #endif
150
151 checkError("Init", err);
152 this->m->initialized = true;
153 }
154
155 int err = Z_OK;
156
157 bool done = false;
158 while (! done)
159 {
160 if (this->m->action == a_deflate)
161 {
162 err = deflate(&zstream, flush);
163 }
164 else
165 {
166 err = inflate(&zstream, flush);
167 }
168 if ((this->m->action == a_inflate) && (err != Z_OK) && zstream.msg &&
169 (strcmp(zstream.msg, "incorrect data check") == 0))
170 {
171 // Other PDF readers ignore this specific error. Combining
172 // this with Z_SYNC_FLUSH enables qpdf to handle some
173 // broken zlib streams without losing data.
174 err = Z_STREAM_END;
175 }
176 switch (err)
177 {
178 case Z_BUF_ERROR:
179 // Probably shouldn't be able to happen, but possible as a
180 // boundary condition: if the last call to inflate exactly
181 // filled the output buffer, it's possible that the next
182 // call to inflate could have nothing to do. There are PDF
183 // files in the wild that have this error (including at
184 // least one in qpdf's test suite). In some cases, we want
185 // to know about this, because it indicates incorrect
186 // compression, so call a callback if provided.
187 this->warn(
188 "input stream is complete but output may still be valid",
189 err);
190 done = true;
191 break;
192
193 case Z_STREAM_END:
194 done = true;
195 // fall through
196
197 case Z_OK:
198 {
199 if ((zstream.avail_in == 0) &&
200 (zstream.avail_out > 0))
201 {
202 // There is nothing left to read, and there was
203 // sufficient buffer space to write everything we
204 // needed, so we're done for now.
205 done = true;
206 }
207 uLong ready =
208 QIntC::to_ulong(this->m->out_bufsize - zstream.avail_out);
209 if (ready > 0)
210 {
211 this->getNext()->write(this->m->outbuf.getPointer(), ready);
212 zstream.next_out = this->m->outbuf.getPointer();
213 zstream.avail_out = QIntC::to_uint(this->m->out_bufsize);
214 }
215 }
216 break;
217
218 default:
219 this->checkError("data", err);
220 break;
221 }
222 }
223 }
224
225 void
finish()226 Pl_Flate::finish()
227 {
228 try
229 {
230 if (this->m->outbuf.getPointer())
231 {
232 if (this->m->initialized)
233 {
234 z_stream& zstream = *(static_cast<z_stream*>(this->m->zdata));
235 unsigned char buf[1];
236 buf[0] = '\0';
237 handleData(buf, 0, Z_FINISH);
238 int err = Z_OK;
239 if (this->m->action == a_deflate)
240 {
241 err = deflateEnd(&zstream);
242 }
243 else
244 {
245 err = inflateEnd(&zstream);
246 }
247 this->m->initialized = false;
248 checkError("End", err);
249 }
250
251 this->m->outbuf = 0;
252 }
253 }
254 catch (std::exception& e)
255 {
256 try
257 {
258 this->getNext()->finish();
259 }
260 catch (...)
261 {
262 // ignore secondary exception
263 }
264 throw std::runtime_error(e.what());
265 }
266 this->getNext()->finish();
267 }
268
269 void
setCompressionLevel(int level)270 Pl_Flate::setCompressionLevel(int level)
271 {
272 compression_level = level;
273 }
274
275 void
checkError(char const * prefix,int error_code)276 Pl_Flate::checkError(char const* prefix, int error_code)
277 {
278 z_stream& zstream = *(static_cast<z_stream*>(this->m->zdata));
279 if (error_code != Z_OK)
280 {
281 char const* action_str =
282 (this->m->action == a_deflate ? "deflate" : "inflate");
283 std::string msg =
284 this->identifier + ": " + action_str + ": " + prefix + ": ";
285
286 if (zstream.msg)
287 {
288 msg += zstream.msg;
289 }
290 else
291 {
292 switch (error_code)
293 {
294 case Z_ERRNO:
295 msg += "zlib system error";
296 break;
297
298 case Z_STREAM_ERROR:
299 msg += "zlib stream error";
300 break;
301
302 case Z_DATA_ERROR:
303 msg += "zlib data error";
304 break;
305
306 case Z_MEM_ERROR:
307 msg += "zlib memory error";
308 break;
309
310 case Z_BUF_ERROR:
311 msg += "zlib buffer error";
312 break;
313
314 case Z_VERSION_ERROR:
315 msg += "zlib version error";
316 break;
317
318 default:
319 msg += std::string("zlib unknown error (") +
320 QUtil::int_to_string(error_code) + ")";
321 break;
322 }
323 }
324
325 throw std::runtime_error(msg);
326 }
327 }
328