fd088925041c11fbe786471d7c60d89d8e4476c0
[privoxy.git] / deanimate.c
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-2021 by the
10  *                Privoxy team. https://www.privoxy.org/
11  *
12  *                Based on the GIF file format specification (see
13  *                https://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 void buf_free(struct binbuffer *buf)
61 {
62    if (buf == NULL) return;
63
64    if (buf->buffer != NULL)
65    {
66       free(buf->buffer);
67    }
68
69    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 static int buf_extend(struct binbuffer *buf, size_t length)
92 {
93    char *newbuf;
94
95    if (buf->offset + length > buf->size)
96    {
97       buf->size = ((buf->size + length + (size_t)1023) & ~(size_t)1023);
98       newbuf = (char *)realloc(buf->buffer, buf->size);
99
100       if (newbuf == NULL)
101       {
102          freez(buf->buffer);
103          return 1;
104       }
105       else
106       {
107          buf->buffer = newbuf;
108          return 0;
109       }
110    }
111    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 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    if (src->buffer == NULL || src->size == 0 || length == 0)
139    {
140       return 1;
141    }
142
143    /*
144     * Sanity check: Can't copy more data than we have
145     */
146    if (src->offset + length > src->size)
147    {
148       return 1;
149    }
150
151    /*
152     * Ensure that dst can hold the new data
153     */
154    if (buf_extend(dst, length))
155    {
156       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    memcpy(dst->buffer + dst->offset, src->buffer + src->offset, length);
164    src->offset += length;
165    dst->offset += length;
166
167    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 static unsigned char buf_getbyte(const struct binbuffer *src, size_t offset)
187 {
188    if (src->offset + offset < src->size)
189    {
190       return (unsigned char)*(src->buffer + src->offset + offset);
191    }
192    else
193    {
194       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 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    while((c = buf_getbyte(buf, 0)) != '\0')
225    {
226       buf->offset += (size_t)c + 1;
227       if (buf->offset >= buf->size - 1)
228       {
229          return 1;
230       }
231    }
232    buf->offset++;
233
234    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 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    c = buf_getbyte(src, 9);
263    if (buf_copy(src, dst, 10))
264    {
265       return 1;
266    }
267
268    /*
269     * If the image has a local colormap, copy it.
270     */
271    if (c & 0x80)
272    {
273       int map_length = 3 * (1 << ((c & 0x07) + 1));
274       if (map_length <= 0)
275       {
276          log_error(LOG_LEVEL_DEANIMATE,
277             "colormap length = %d (%c)?", map_length, c);
278          return 1;
279       }
280       if (buf_copy(src, dst, (size_t)map_length))
281       {
282          return 1;
283       }
284    }
285    if (buf_copy(src, dst, 1)) return 1;
286
287    /*
288     * Copy the image chunk by chunk.
289     */
290    while((c = buf_getbyte(src, 0)) != '\0')
291    {
292       if (buf_copy(src, dst, 1 + (size_t) c)) return 1;
293    }
294    if (buf_copy(src, dst, 1)) return 1;
295
296    /*
297     * Trim and rewind the dst buffer
298     */
299    if (NULL == (dst->buffer = (char *)realloc(dst->buffer, dst->offset))) return 1;
300    dst->size = dst->offset;
301    dst->offset = 0;
302
303    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 int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_image)
328 {
329    unsigned char c;
330    struct binbuffer *image;
331    int image_buffered = 0;
332
333    if (NULL == src || NULL == dst)
334    {
335       return 1;
336    }
337    if (src->size <= 10)
338    {
339       return 1;
340    }
341
342    c = buf_getbyte(src, 10);
343
344    /*
345     * Check & copy GIF header
346     */
347    if (strncmp(src->buffer, "GIF89a", 6) && strncmp(src->buffer, "GIF87a", 6))
348    {
349       return 1;
350    }
351    else
352    {
353       if (buf_copy(src, dst, 13))
354       {
355          return 1;
356       }
357    }
358
359    /*
360     * Look for global colormap and  copy if found.
361     */
362    if (c & 0x80)
363    {
364       int map_length = 3 * (1 << ((c & 0x07) + 1));
365       if (map_length <= 0)
366       {
367          log_error(LOG_LEVEL_DEANIMATE,
368             "colormap length = %d (%c)?", map_length, c);
369          return 1;
370       }
371       if (buf_copy(src, dst, (size_t)map_length))
372       {
373          return 1;
374       }
375    }
376
377    /*
378     * Reserve a buffer for the current image block
379     */
380    image = zalloc_or_die(sizeof(*image));
381
382    /*
383     * Parse the GIF block by block and copy the relevant
384     * parts to dst
385     */
386    while(src->offset < src->size)
387    {
388       switch(buf_getbyte(src, 0))
389       {
390          /*
391           * End-of-GIF Marker: Append current image if we got
392           * one and return.
393           */
394       case 0x3b:
395          if (image->size == 0) goto failed;
396          goto write;
397
398          /*
399           * Image block: Extract to current image buffer.
400           */
401       case 0x2c:
402          if (image_buffered == 1)
403          {
404             /* Discard previous image. */
405             image->offset = 0;
406             image_buffered = 0;
407          }
408          if (gif_extract_image(src, image)) goto failed;
409          image_buffered = 1;
410          if (get_first_image) goto write;
411          continue;
412
413          /*
414           * Extension block: Look at next byte and decide
415           */
416       case 0x21:
417          switch (buf_getbyte(src, 1))
418          {
419             /*
420              * Image extension: Copy extension header
421              * and continue looking for new blocks.
422              */
423          case 0xf9:
424             if (image_buffered == 1)
425             {
426                image->offset = 0;
427                image_buffered = 0;
428             }
429             if (buf_copy(src, image, 8)) goto failed;
430             continue;
431
432             /*
433              * Application extension: Skip
434              */
435          case 0xff:
436             if ((src->offset += 14) >= src->size || gif_skip_data_block(src)) goto failed;
437             continue;
438
439             /*
440              * Comment extension: Skip
441              */
442          case 0xfe:
443             if ((src->offset += 2) >= src->size || gif_skip_data_block(src)) goto failed;
444             continue;
445
446             /*
447              * Plain text extension: Skip
448              */
449          case 0x01:
450             if ((src->offset += 15) >= src->size || gif_skip_data_block(src)) goto failed;
451             continue;
452
453             /*
454              * Ooops, what type of extension is that?
455              */
456          default:
457             goto failed;
458
459          }
460
461          /*
462           * Ooops, what type of block is that?
463           */
464       default:
465          goto failed;
466
467       }
468    } /* -END- while src */
469
470    /*
471     * Either we got here by goto, or because the GIF is
472     * bogus and EOF was reached before an end-of-gif marker
473     * was found.
474     */
475
476 failed:
477    buf_free(image);
478    return 1;
479
480    /*
481     * Append the current image to dst and return
482     */
483
484 write:
485    if (buf_copy(image, dst, image->size)) goto failed;
486    if (buf_extend(dst, 1)) goto failed;
487    *(dst->buffer + dst->offset++) = 0x3b;
488    buf_free(image);
489    return 0;
490
491 }
492
493
494 /*
495   Local Variables:
496   tab-width: 3
497   end:
498 */