RT#128481: fix handling of GIF files with no images
[imager.git] / imexif.c
index 9741f7a..4bdf386 100644 (file)
--- a/imexif.c
+++ b/imexif.c
@@ -1,6 +1,9 @@
+#include "imager.h"
 #include "imexif.h"
 #include <stdlib.h>
 #include <float.h>
+#include <string.h>
+#include <stdio.h>
 
 /*
 =head1 NAME
@@ -9,7 +12,7 @@ imexif.c - EXIF support for Imager
 
 =head1 SYNOPSIS
 
-  if (i_int_decode_exif(im, app1data, app1datasize)) {
+  if (im_decode_exif(im, app1data, app1datasize)) {
     // exif block seen
   }
 
@@ -229,8 +232,10 @@ static int tiff_init(imtiff *tiff, unsigned char *base, size_t length);
 static int tiff_load_ifd(imtiff *tiff, unsigned long offset);
 static void tiff_final(imtiff *tiff);
 static void tiff_clear_ifd(imtiff *tiff);
+#if 0 /* currently unused, but that may change */
 static int tiff_get_bytes(imtiff *tiff, unsigned char *to, size_t offset, 
                          size_t count);
+#endif
 static int tiff_get_tag_double(imtiff *, int index, double *result);
 static int tiff_get_tag_int(imtiff *, int index, int *result);
 static unsigned tiff_get16(imtiff *, unsigned long offset);
@@ -262,12 +267,12 @@ intended to be called from outside of Imager.
 
 =over
 
-=item i_int_decode_exit
+=item im_decode_exif
 
-i_int_decode_exif(im, data_base, data_size);
+im_decode_exif(im, data_base, data_size);
 
-The data from data_base for data_size bytes will be scanned for EXIF
-data.
+The data from C<data_base> for C<data_size> bytes will be scanned for
+EXIF data.
 
 Any data found will be used to set tags in the supplied image.
 
@@ -275,23 +280,16 @@ The intent is that invalid EXIF data will simply fail to set tags, and
 write to the log.  In no case should this code exit when supplied
 invalid data.
 
-Returns true if an Exif header was seen.
+Returns true if an EXIF header was seen.
 
+=cut
 */
 
 int
-i_int_decode_exif(i_img *im, unsigned char *data, size_t length) {
+im_decode_exif(i_img *im, unsigned char *data, size_t length) {
   imtiff tiff;
   unsigned long exif_ifd_offset = 0;
   unsigned long gps_ifd_offset = 0;
-  /* basic checks - must start with "Exif\0\0" */
-
-  if (length < 6 || memcmp(data, "Exif\0\0", 6) != 0) {
-    return 0;
-  }
-
-  data += 6;
-  length -= 6;
 
   if (!tiff_init(&tiff, data, length)) {
     mm_log((2, "Exif header found, but no valid TIFF header\n"));
@@ -702,7 +700,7 @@ save_exif_ifd_tags(i_img *im, imtiff *tiff) {
       /* find the actual end of the string */
       while (i < entry->size && user_comment[i])
        ++i;
-      i_tags_add(&im->tags, "exif_user_comment", 0, user_comment, i, 0);
+      i_tags_set(&im->tags, "exif_user_comment", user_comment, i);
       myfree(user_comment);
       break;
 
@@ -915,7 +913,7 @@ tiff_load_ifd(imtiff *tiff, unsigned long offset) {
 
   /* rough check count + 1 entry + next offset */
   if (offset + (2+12+4) > tiff->size) {
-    mm_log((2, "offset %uld beyond end off Exif block"));
+    mm_log((2, "offset %lu beyond end off Exif block", offset));
     return 0;
   }
 
@@ -924,7 +922,7 @@ tiff_load_ifd(imtiff *tiff, unsigned long offset) {
   /* check we can fit the whole thing */
   ifd_size = 2 + count * 12 + 4; /* count + count entries + next offset */
   if (offset + ifd_size > tiff->size) {
-    mm_log((2, "offset %uld beyond end off Exif block"));
+    mm_log((2, "offset %lu beyond end off Exif block", offset));
     return 0;
   }
 
@@ -940,6 +938,7 @@ tiff_load_ifd(imtiff *tiff, unsigned long offset) {
       entry->item_size = type_sizes[entry->type];
       entry->size = entry->item_size * entry->count;
       if (entry->size / entry->item_size != entry->count) {
+       myfree(entries);
        mm_log((1, "Integer overflow calculating tag data size processing EXIF block\n"));
        return 0;
       }
@@ -1013,7 +1012,8 @@ tiff_get_tag_double_array(imtiff *tiff, int index, double *result,
   ifd_entry *entry;
   unsigned long offset;
   if (index < 0 || index >= tiff->ifd_size) {
-    i_fatal(3, "tiff_get_tag_double_array() tag index out of range");
+    mm_log((3, "tiff_get_tag_double_array() tag index out of range"));
+    return 0;
   }
   
   entry = tiff->ifd + index;
@@ -1077,7 +1077,8 @@ static int
 tiff_get_tag_double(imtiff *tiff, int index, double *result) {
   ifd_entry *entry;
   if (index < 0 || index >= tiff->ifd_size) {
-    i_fatal(3, "tiff_get_tag_double() index out of range");
+    mm_log((3, "tiff_get_tag_double() index out of range"));
+    return 0;
   }
   
   entry = tiff->ifd + index;
@@ -1108,12 +1109,14 @@ tiff_get_tag_int_array(imtiff *tiff, int index, int *result, int array_index) {
   ifd_entry *entry;
   unsigned long offset;
   if (index < 0 || index >= tiff->ifd_size) {
-    i_fatal(3, "tiff_get_tag_int_array() tag index out of range");
+    mm_log((3, "tiff_get_tag_int_array() tag index out of range"));
+    return 0;
   }
   
   entry = tiff->ifd + index;
   if (array_index < 0 || array_index >= entry->count) {
-    i_fatal(3, "tiff_get_tag_int_array() array index out of range");
+    mm_log((3, "tiff_get_tag_int_array() array index out of range"));
+    return 0;
   }
 
   offset = entry->offset + array_index * entry->item_size;
@@ -1163,7 +1166,8 @@ static int
 tiff_get_tag_int(imtiff *tiff, int index, int *result) {
   ifd_entry *entry;
   if (index < 0 || index >= tiff->ifd_size) {
-    i_fatal(3, "tiff_get_tag_int() index out of range");
+    mm_log((3, "tiff_get_tag_int() index out of range"));
+    return 0;
   }
 
   entry = tiff->ifd + index;
@@ -1204,7 +1208,7 @@ copy_int_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
       int value;
       if (map[i].tag == entry->tag
          && tiff_get_tag_int(tiff, tag_index, &value)) {
-       i_tags_addn(&im->tags, map[i].name, 0, value);
+       i_tags_setn(&im->tags, map[i].name, value);
        break;
       }
     }
@@ -1255,8 +1259,8 @@ copy_string_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
     for (i = 0; i < map_count; ++i) {
       if (map[i].tag == entry->tag) {
        int len = entry->type == ift_ascii ? entry->size - 1 : entry->size;
-       i_tags_add(&im->tags, map[i].name, 0,
-                  (char const *)(tiff->base + entry->offset), len, 0);
+       i_tags_set(&im->tags, map[i].name,
+                  (char const *)(tiff->base + entry->offset), len);
        break;
       }
     }
@@ -1287,32 +1291,58 @@ copy_num_array_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
        if (entry->type == ift_rational || entry->type == ift_srational) {
          double value;
          char workstr[MAX_ARRAY_STRING];
+         size_t len = 0, item_len;
          *workstr = '\0';
          for (j = 0; j < entry->count; ++j) {
            if (!tiff_get_tag_double_array(tiff, tag_index, &value, j)) {
-             i_fatal(3, "unexpected failure from tiff_get_tag_double_array(..., %d, ..., %d)\n", tag_index, j);
+             mm_log((3, "unexpected failure from tiff_get_tag_double_array(..., %d, ..., %d)\n", tag_index, j));
+             return;
+           }
+           if (len >= sizeof(workstr) - 1) {
+             mm_log((3, "Buffer would overflow reading tag %#x\n", entry->tag));
+             return;
            }
-           if (j) 
+           if (j) {
              strcat(workstr, " ");
-           sprintf(workstr + strlen(workstr), "%.6g", value);
+             ++len;
+           }
+#ifdef IMAGER_SNPRINTF
+           item_len = snprintf(workstr + len, sizeof(workstr)-len, "%.6g", value);
+#else
+           item_len = sprintf(workstr + len, "%.6g", value);
+#endif
+           len += item_len;
          }
-         i_tags_add(&im->tags, map[i].name, 0, workstr, -1, 0);
+         i_tags_set(&im->tags, map[i].name, workstr, -1);
        }
        else if (entry->type == ift_short || entry->type == ift_long
                 || entry->type == ift_sshort || entry->type == ift_slong
                 || entry->type == ift_byte) {
          int value;
          char workstr[MAX_ARRAY_STRING];
+         size_t len = 0, item_len;
          *workstr = '\0';
          for (j = 0; j < entry->count; ++j) {
            if (!tiff_get_tag_int_array(tiff, tag_index, &value, j)) {
-             i_fatal(3, "unexpected failure from tiff_get_tag_int_array(..., %d, ..., %d)\n", tag_index, j);
+             mm_log((3, "unexpected failure from tiff_get_tag_int_array(..., %d, ..., %d)\n", tag_index, j));
+             return;
+           }
+           if (len >= sizeof(workstr) - 1) {
+             mm_log((3, "Buffer would overflow reading tag %#x\n", entry->tag));
+             return;
            }
-           if (j) 
+           if (j) {
              strcat(workstr, " ");
-           sprintf(workstr + strlen(workstr), "%d", value);
+             ++len;
+           }
+#ifdef IMAGER_SNPRINTF
+           item_len = snprintf(workstr + len, sizeof(workstr) - len, "%d", value);
+#else
+           item_len = sprintf(workstr + len, "%d", value);
+#endif
+           len += item_len;
          }
-         i_tags_add(&im->tags, map[i].name, 0, workstr, -1, 0);
+         i_tags_set(&im->tags, map[i].name, workstr, -1);
        }
        break;
       }
@@ -1350,7 +1380,7 @@ copy_name_tags(i_img *im, imtiff *tiff, tag_value_map *map, int map_count) {
          }
        }
        if (found) {
-         i_tags_add(&im->tags, map[i].name, 0, found->name, -1, 0);
+         i_tags_set(&im->tags, map[i].name, found->name, -1);
        }
        break;
       }
@@ -1382,8 +1412,11 @@ Retrieve a 16 bit unsigned integer from offset.
 
 static unsigned
 tiff_get16(imtiff *tiff, unsigned long offset) {
-  if (offset + 2 > tiff->size)
-    i_fatal(3, "attempt to get16 at %uld in %uld image", offset, tiff->size);
+  if (offset + 2 > tiff->size) {
+    mm_log((3, "attempt to get16 at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   if (tiff->type == tt_intel) 
     return tiff->base[offset] + 0x100 * tiff->base[offset+1];
@@ -1401,8 +1434,11 @@ Retrieve a 32-bit unsigned integer from offset.
 
 static unsigned
 tiff_get32(imtiff *tiff, unsigned long offset) {
-  if (offset + 4 > tiff->size)
-    i_fatal(3, "attempt to get16 at %uld in %uld image", offset, tiff->size);
+  if (offset + 4 > tiff->size) {
+    mm_log((3, "attempt to get16 at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   if (tiff->type == tt_intel) 
     return tiff->base[offset] + 0x100 * tiff->base[offset+1] 
@@ -1412,6 +1448,8 @@ tiff_get32(imtiff *tiff, unsigned long offset) {
       + 0x10000 * tiff->base[offset+1] + 0x1000000 * tiff->base[offset];
 }
 
+#if 0 /* currently unused, but that may change */
+
 /*
 =item tiff_get_bytes
 
@@ -1434,6 +1472,8 @@ tiff_get_bytes(imtiff *tiff, unsigned char *data, size_t offset,
   return 1;
 }
 
+#endif
+
 /*
 =item tiff_get16s
 
@@ -1446,8 +1486,11 @@ static int
 tiff_get16s(imtiff *tiff, unsigned long offset) {
   int result;
 
-  if (offset + 2 > tiff->size)
-    i_fatal(3, "attempt to get16 at %uld in %uld image", offset, tiff->size);
+  if (offset + 2 > tiff->size) {
+    mm_log((3, "attempt to get16 at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   if (tiff->type == tt_intel) 
     result = tiff->base[offset] + 0x100 * tiff->base[offset+1];
@@ -1472,8 +1515,11 @@ static int
 tiff_get32s(imtiff *tiff, unsigned long offset) {
   unsigned work;
 
-  if (offset + 4 > tiff->size)
-    i_fatal(3, "attempt to get16 at %uld in %uld image", offset, tiff->size);
+  if (offset + 4 > tiff->size) {
+    mm_log((3, "attempt to get16 at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   if (tiff->type == tt_intel) 
     work = tiff->base[offset] + 0x100 * tiff->base[offset+1] 
@@ -1500,8 +1546,11 @@ Retrieve an unsigned rational from offset.
 static double
 tiff_get_rat(imtiff *tiff, unsigned long offset) {
   unsigned long numer, denom;
-  if (offset + 8 > tiff->size)
-    i_fatal(3, "attempt to get_rat at %lu in %lu image", offset, tiff->size);
+  if (offset + 8 > tiff->size) {
+    mm_log((3, "attempt to get_rat at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   numer = tiff_get32(tiff, offset);
   denom = tiff_get32(tiff, offset+4);
@@ -1524,8 +1573,11 @@ Retrieve an signed rational from offset.
 static double
 tiff_get_rats(imtiff *tiff, unsigned long offset) {
   long numer, denom;
-  if (offset + 8 > tiff->size)
-    i_fatal(3, "attempt to get_rat at %lu in %lu image", offset, tiff->size);
+  if (offset + 8 > tiff->size) {
+    mm_log((3, "attempt to get_rat at %lu in %lu image", offset,
+           (unsigned long)tiff->size));
+    return 0;
+  }
 
   numer = tiff_get32s(tiff, offset);
   denom = tiff_get32s(tiff, offset+4);
@@ -1548,7 +1600,7 @@ http://www.exif.org/
 
 =head1 AUTHOR
 
-Tony Cook <tony@imager.perl.org>
+Tony Cook <tonyc@cpan.org>
 
 =head1 REVISION