Line data Source code
1 : /*********************************************************************
2 : *
3 : * File : $Source: /cvsroot/ijbswa/current/deanimate.c,v $
4 : *
5 : * Purpose : Declares functions to manipulate binary images on the
6 : * fly. High-level functions include:
7 : * - Deanimation of GIF images
8 : *
9 : * Copyright : Written by and Copyright (C) 2001 - 2004, 2006 by the
10 : * Privoxy team. https://www.privoxy.org/
11 : *
12 : * Based on the GIF file format specification (see
13 : * http://tronche.com/computer-graphics/gif/gif89a.html)
14 : * and ideas from the Image::DeAnim Perl module by
15 : * Ken MacFarlane, <ksm+cpan@universal.dca.net>
16 : *
17 : * This program is free software; you can redistribute it
18 : * and/or modify it under the terms of the GNU General
19 : * Public License as published by the Free Software
20 : * Foundation; either version 2 of the License, or (at
21 : * your option) any later version.
22 : *
23 : * This program is distributed in the hope that it will
24 : * be useful, but WITHOUT ANY WARRANTY; without even the
25 : * implied warranty of MERCHANTABILITY or FITNESS FOR A
26 : * PARTICULAR PURPOSE. See the GNU General Public
27 : * License for more details.
28 : *
29 : * The GNU General Public License should be included with
30 : * this file. If not, you can view it at
31 : * http://www.gnu.org/copyleft/gpl.html
32 : * or write to the Free Software Foundation, Inc., 59
33 : * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 : *
35 : **********************************************************************/
36 :
37 :
38 : #include "config.h"
39 :
40 : #include <string.h>
41 : #include <fcntl.h>
42 :
43 : #include "project.h"
44 : #include "errlog.h"
45 : #include "deanimate.h"
46 : #include "miscutil.h"
47 :
48 : /*********************************************************************
49 : *
50 : * Function : buf_free
51 : *
52 : * Description : Safely frees a struct binbuffer
53 : *
54 : * Parameters :
55 : * 1 : buf = Pointer to the binbuffer to be freed
56 : *
57 : * Returns : N/A
58 : *
59 : *********************************************************************/
60 990 : void buf_free(struct binbuffer *buf)
61 : {
62 990 : if (buf == NULL) return;
63 :
64 990 : if (buf->buffer != NULL)
65 : {
66 590 : free(buf->buffer);
67 : }
68 :
69 990 : free(buf);
70 :
71 : }
72 :
73 :
74 : /*********************************************************************
75 : *
76 : * Function : buf_extend
77 : *
78 : * Description : Ensure that a given binbuffer can hold a given amount
79 : * of bytes, by reallocating its buffer if necessary.
80 : * Allocate new mem in chunks of 1024 bytes, so we don't
81 : * have to realloc() too often.
82 : *
83 : * Parameters :
84 : * 1 : buf = Pointer to the binbuffer
85 : * 2 : length = Desired minimum size
86 : *
87 : *
88 : * Returns : 0 on success, 1 on failure.
89 : *
90 : *********************************************************************/
91 9279 : static int buf_extend(struct binbuffer *buf, size_t length)
92 : {
93 : char *newbuf;
94 :
95 9279 : if (buf->offset + length > buf->size)
96 : {
97 1005 : buf->size = ((buf->size + length + (size_t)1023) & ~(size_t)1023);
98 1005 : newbuf = (char *)realloc(buf->buffer, buf->size);
99 :
100 1005 : if (newbuf == NULL)
101 : {
102 0 : freez(buf->buffer);
103 0 : return 1;
104 : }
105 : else
106 : {
107 1005 : buf->buffer = newbuf;
108 1005 : return 0;
109 : }
110 : }
111 8274 : return 0;
112 :
113 : }
114 :
115 :
116 : /*********************************************************************
117 : *
118 : * Function : buf_copy
119 : *
120 : * Description : Safely copies a given amount of bytes from one
121 : * struct binbuffer to another, advancing the
122 : * offsets appropriately.
123 : *
124 : * Parameters :
125 : * 1 : src = Pointer to the source binbuffer
126 : * 2 : dst = Pointer to the destination binbuffer
127 : * 3 : length = Number of bytes to be copied
128 : *
129 : * Returns : 0 on success, 1 on failure.
130 : *
131 : *********************************************************************/
132 9420 : static int buf_copy(struct binbuffer *src, struct binbuffer *dst, size_t length)
133 : {
134 : /*
135 : * Sanity check: Make sure the source buffer contains
136 : * data and there's work to be done.
137 : */
138 9420 : if (src->buffer == NULL || src->size == 0 || length == 0)
139 : {
140 0 : return 1;
141 : }
142 :
143 : /*
144 : * Sanity check: Can't copy more data than we have
145 : */
146 9420 : if ((src->offset + length) > src->size)
147 : {
148 143 : return 1;
149 : }
150 :
151 : /*
152 : * Ensure that dst can hold the new data
153 : */
154 9277 : if (buf_extend(dst, length))
155 : {
156 0 : return 1;
157 : }
158 :
159 : /*
160 : * Now that it's safe, memcpy() the desired amount of
161 : * data from src to dst and adjust the offsets
162 : */
163 9277 : memcpy(dst->buffer + dst->offset, src->buffer + src->offset, length);
164 9277 : src->offset += length;
165 9277 : dst->offset += length;
166 :
167 9277 : return 0;
168 :
169 : }
170 :
171 :
172 : /*********************************************************************
173 : *
174 : * Function : buf_getbyte
175 : *
176 : * Description : Safely gets a byte from a given binbuffer at a
177 : * given offset
178 : *
179 : * Parameters :
180 : * 1 : src = Pointer to the source binbuffer
181 : * 2 : offset = Offset to the desired byte
182 : *
183 : * Returns : The byte on success, or 0 on failure
184 : *
185 : *********************************************************************/
186 12090 : static unsigned char buf_getbyte(const struct binbuffer *src, size_t offset)
187 : {
188 12090 : if (src->offset + offset < src->size)
189 : {
190 12060 : return (unsigned char)*(src->buffer + src->offset + offset);
191 : }
192 : else
193 : {
194 30 : return '\0';
195 : }
196 :
197 : }
198 :
199 :
200 : /*********************************************************************
201 : *
202 : * Function : gif_skip_data_block
203 : *
204 : * Description : Safely advances the offset of a given struct binbuffer
205 : * that contains a GIF image and whose offset is
206 : * positioned at the start of a data block, behind
207 : * that block.
208 : *
209 : * Parameters :
210 : * 1 : buf = Pointer to the binbuffer
211 : *
212 : * Returns : 0 on success, or 1 on failure
213 : *
214 : *********************************************************************/
215 245 : static int gif_skip_data_block(struct binbuffer *buf)
216 : {
217 : unsigned char c;
218 :
219 : /*
220 : * Data blocks are sequences of chunks, which are headed
221 : * by a one-byte length field, with the last chunk having
222 : * zero length.
223 : */
224 1925 : while((c = buf_getbyte(buf, 0)) != '\0')
225 : {
226 1825 : buf->offset += (size_t)c + 1;
227 1825 : if (buf->offset >= buf->size - 1)
228 : {
229 145 : return 1;
230 : }
231 : }
232 100 : buf->offset++;
233 :
234 100 : return 0;
235 :
236 : }
237 :
238 :
239 : /*********************************************************************
240 : *
241 : * Function : gif_extract_image
242 : *
243 : * Description : Safely extracts an image data block from a given
244 : * struct binbuffer that contains a GIF image and whose
245 : * offset is positioned at the start of a data block
246 : * into a given destination binbuffer.
247 : *
248 : * Parameters :
249 : * 1 : src = Pointer to the source binbuffer
250 : * 2 : dst = Pointer to the destination binbuffer
251 : *
252 : * Returns : 0 on success, or 1 on failure
253 : *
254 : *********************************************************************/
255 414 : static int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
256 : {
257 : unsigned char c;
258 :
259 : /*
260 : * Remember the colormap flag and copy the image head
261 : */
262 414 : c = buf_getbyte(src, 9);
263 414 : if (buf_copy(src, dst, 10))
264 : {
265 1 : return 1;
266 : }
267 :
268 : /*
269 : * If the image has a local colormap, copy it.
270 : */
271 413 : if (c & 0x80)
272 : {
273 103 : int map_length = 3 * (1 << ((c & 0x07) + 1));
274 103 : if (map_length <= 0)
275 : {
276 0 : log_error(LOG_LEVEL_DEANIMATE,
277 : "colormap length = %d (%c)?", map_length, c);
278 0 : return 1;
279 : }
280 103 : if (buf_copy(src, dst, (size_t)map_length))
281 : {
282 1 : return 1;
283 : }
284 : }
285 412 : if (buf_copy(src, dst, 1)) return 1;
286 :
287 : /*
288 : * Copy the image chunk by chunk.
289 : */
290 7317 : while((c = buf_getbyte(src, 0)) != '\0')
291 : {
292 6951 : if (buf_copy(src, dst, 1 + (size_t) c)) return 1;
293 : }
294 366 : if (buf_copy(src, dst, 1)) return 1;
295 :
296 : /*
297 : * Trim and rewind the dst buffer
298 : */
299 339 : if (NULL == (dst->buffer = (char *)realloc(dst->buffer, dst->offset))) return 1;
300 339 : dst->size = dst->offset;
301 339 : dst->offset = 0;
302 :
303 339 : return(0);
304 :
305 : }
306 :
307 : /*********************************************************************
308 : *
309 : * Function : gif_deanimate
310 : *
311 : * Description : Deanimate a given GIF image, i.e. given a GIF with
312 : * an (optional) image block and an arbitrary number
313 : * of image extension blocks, produce an output GIF with
314 : * only one image block that contains the last image
315 : * (extension) block of the original.
316 : * Also strip Comments, Application extensions, etc.
317 : *
318 : * Parameters :
319 : * 1 : src = Pointer to the source binbuffer
320 : * 2 : dst = Pointer to the destination binbuffer
321 : * 3 : get_first_image = Flag: If set, get the first image
322 : * If unset (default), get the last
323 : *
324 : * Returns : 0 on success, or 1 on failure
325 : *
326 : *********************************************************************/
327 566 : int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_image)
328 : {
329 : unsigned char c;
330 : struct binbuffer *image;
331 :
332 566 : if (NULL == src || NULL == dst)
333 : {
334 0 : return 1;
335 : }
336 566 : if (src->size <= 10)
337 : {
338 1 : return 1;
339 : }
340 :
341 565 : c = buf_getbyte(src, 10);
342 :
343 : /*
344 : * Check & copy GIF header
345 : */
346 565 : if (strncmp(src->buffer, "GIF89a", 6) && strncmp(src->buffer, "GIF87a", 6))
347 : {
348 72 : return 1;
349 : }
350 : else
351 : {
352 493 : if (buf_copy(src, dst, 13))
353 : {
354 1 : return 1;
355 : }
356 : }
357 :
358 : /*
359 : * Look for global colormap and copy if found.
360 : */
361 492 : if (c & 0x80)
362 : {
363 306 : int map_length = 3 * (1 << ((c & 0x07) + 1));
364 306 : if (map_length <= 0)
365 : {
366 0 : log_error(LOG_LEVEL_DEANIMATE,
367 : "colormap length = %d (%c)?", map_length, c);
368 0 : return 1;
369 : }
370 306 : if (buf_copy(src, dst, (size_t)map_length))
371 : {
372 66 : return 1;
373 : }
374 : }
375 :
376 : /*
377 : * Reserve a buffer for the current image block
378 : */
379 426 : image = zalloc_or_die(sizeof(*image));
380 :
381 : /*
382 : * Parse the GIF block by block and copy the relevant
383 : * parts to dst
384 : */
385 865 : while(src->offset < src->size)
386 : {
387 863 : switch(buf_getbyte(src, 0))
388 : {
389 : /*
390 : * End-of-GIF Marker: Append current image if we got
391 : * one and return.
392 : */
393 12 : case 0x3b:
394 12 : if (image->size == 0) goto failed;
395 2 : goto write;
396 :
397 : /*
398 : * Image block: Extract to current image buffer.
399 : */
400 63 : case 0x2c:
401 63 : image->offset = 0;
402 63 : if (gif_extract_image(src, image)) goto failed;
403 11 : if (get_first_image) goto write;
404 11 : continue;
405 :
406 : /*
407 : * Extension block: Look at next byte and decide
408 : */
409 634 : case 0x21:
410 634 : switch (buf_getbyte(src, 1))
411 : {
412 : /*
413 : * Image extension: Copy extension header and image
414 : * to the current image buffer
415 : */
416 373 : case 0xf9:
417 373 : image->offset = 0;
418 373 : if (buf_copy(src, image, 8) || buf_getbyte(src, 0) != 0x2c) goto failed;
419 351 : if (gif_extract_image(src, image)) goto failed;
420 328 : if (get_first_image) goto write;
421 328 : continue;
422 :
423 : /*
424 : * Application extension: Skip
425 : */
426 67 : case 0xff:
427 67 : if ((src->offset += 14) >= src->size || gif_skip_data_block(src)) goto failed;
428 55 : continue;
429 :
430 : /*
431 : * Comment extension: Skip
432 : */
433 150 : case 0xfe:
434 150 : if ((src->offset += 2) >= src->size || gif_skip_data_block(src)) goto failed;
435 24 : continue;
436 :
437 : /*
438 : * Plain text extension: Skip
439 : */
440 32 : case 0x01:
441 32 : if ((src->offset += 15) >= src->size || gif_skip_data_block(src)) goto failed;
442 21 : continue;
443 :
444 : /*
445 : * Ooops, what type of extension is that?
446 : */
447 12 : default:
448 12 : goto failed;
449 :
450 : }
451 :
452 : /*
453 : * Ooops, what type of block is that?
454 : */
455 154 : default:
456 154 : goto failed;
457 :
458 : }
459 : } /* -END- while src */
460 :
461 : /*
462 : * Either we got here by goto, or because the GIF is
463 : * bogus and EOF was reached before an end-of-gif marker
464 : * was found.
465 : */
466 :
467 424 : failed:
468 424 : buf_free(image);
469 424 : return 1;
470 :
471 : /*
472 : * Append the current image to dst and return
473 : */
474 :
475 2 : write:
476 2 : if (buf_copy(image, dst, image->size)) goto failed;
477 2 : if (buf_extend(dst, 1)) goto failed;
478 2 : *(dst->buffer + dst->offset++) = 0x3b;
479 2 : buf_free(image);
480 2 : return 0;
481 :
482 : }
483 :
484 :
485 : /*
486 : Local Variables:
487 : tab-width: 3
488 : end:
489 : */
|