Add a few more NULL pointer checks.
[platform/upstream/libexif.git] / libexif / exif-loader.c
index 8fb17b1..1171485 100644 (file)
+/* exif-loader.c
+ *
+ * Copyright (c) 2002 Lutz Mueller <lutz@users.sourceforge.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. 
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA.
+ */
+
 #include <config.h>
-#include "exif-loader.h"
 
+#include <libexif/exif-loader.h>
+#include <libexif/exif-utils.h>
+#include <libexif/i18n.h>
+
+#include <sys/types.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 
-#include <libexif/i18n.h>
-
-#include <libjpeg/jpeg-marker.h>
+#undef JPEG_MARKER_DHT
+#define JPEG_MARKER_DHT  0xc4
+#undef JPEG_MARKER_SOI
+#define JPEG_MARKER_SOI  0xd8
+#undef JPEG_MARKER_DQT
+#define JPEG_MARKER_DQT  0xdb
+#undef JPEG_MARKER_APP0
+#define JPEG_MARKER_APP0 0xe0
+#undef JPEG_MARKER_APP1
+#define JPEG_MARKER_APP1 0xe1
+#undef JPEG_MARKER_APP2
+#define JPEG_MARKER_APP2 0xe2
+#undef JPEG_MARKER_APP13
+#define JPEG_MARKER_APP13 0xed
+#undef JPEG_MARKER_COM
+#define JPEG_MARKER_COM 0xfe
 
 typedef enum {
        EL_READ = 0,
-       EL_READ_SIZE_HIGH_BYTE,
-       EL_READ_SIZE_LOW_BYTE,
+       EL_READ_SIZE_BYTE_24,
+       EL_READ_SIZE_BYTE_16,
+       EL_READ_SIZE_BYTE_08,
+       EL_READ_SIZE_BYTE_00,
        EL_SKIP_BYTES,
        EL_EXIF_FOUND,
-       EL_FAILED
 } ExifLoaderState;
 
+typedef enum {
+       EL_DATA_FORMAT_UNKNOWN,
+       EL_DATA_FORMAT_EXIF,
+       EL_DATA_FORMAT_JPEG,
+       EL_DATA_FORMAT_FUJI_RAW
+} ExifLoaderDataFormat;
+
+/*! \internal */
 struct _ExifLoader {
        ExifLoaderState state;
+       ExifLoaderDataFormat data_format;
+
+       /*! Small buffer used for detection of format */
+       unsigned char b[12];
+
+       /*! Number of bytes in the small buffer \c b */
+       unsigned char b_len;
 
        unsigned int size;
-       int last_marker;
        unsigned char *buf;
        unsigned int bytes_read;
 
        unsigned int ref_count;
 
        ExifLog *log;
+       ExifMem *mem;
 };
 
+/*! Magic number for EXIF header */
+static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+
 static void *
 exif_loader_alloc (ExifLoader *l, unsigned int i)
 {
        void *d;
 
-       if (!i) return NULL;
+       if (!l || !i) 
+               return NULL;
 
-       /* This is the only call to calloc in this file. */
-       d = calloc (i, 1);
-       if (d) return d;
+       d = exif_mem_alloc (l->mem, i);
+       if (d) 
+               return d;
 
-       if (l) EXIF_LOG_NO_MEMORY (l->log, "ExifLog", i);
+       EXIF_LOG_NO_MEMORY (l->log, "ExifLog", i);
        return NULL;
 }
 
-#undef  MIN
-#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
-
 void
 exif_loader_write_file (ExifLoader *l, const char *path)
 {
@@ -56,7 +110,8 @@ exif_loader_write_file (ExifLoader *l, const char *path)
        int size;
        unsigned char data[1024];
 
-       if (!l) return;
+       if (!l || !path)
+               return;
 
        f = fopen (path, "rb");
        if (!f) {
@@ -66,157 +121,270 @@ exif_loader_write_file (ExifLoader *l, const char *path)
        }
        while (1) {
                size = fread (data, 1, sizeof (data), f);
-               if (size <= 0) break;
-               if (!exif_loader_write (l, data, size)) break;
+               if (size <= 0) 
+                       break;
+               if (!exif_loader_write (l, data, size)) 
+                       break;
        }
        fclose (f);
 }
 
+static unsigned int
+exif_loader_copy (ExifLoader *eld, unsigned char *buf, unsigned int len)
+{
+       if (!eld || (len && !buf) || (eld->bytes_read >= eld->size)) 
+               return 0;
+
+       /* If needed, allocate the buffer. */
+       if (!eld->buf) 
+               eld->buf = exif_loader_alloc (eld, eld->size);
+       if (!eld->buf) 
+               return 0;
+
+       /* Copy memory */
+       len = MIN (len, eld->size - eld->bytes_read);
+       memcpy (eld->buf + eld->bytes_read, buf, len);
+       eld->bytes_read += len;
+
+       return (eld->bytes_read >= eld->size) ? 0 : 1;
+}
+
 unsigned char
 exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
 {
-       int i, len_remain;
+       unsigned int i;
+
+       if (!eld || (len && !buf)) 
+               return 0;
+
+       switch (eld->state) {
+       case EL_EXIF_FOUND:
+               return exif_loader_copy (eld, buf, len);
+       case EL_SKIP_BYTES:
+               if (eld->size > len) { 
+                       eld->size -= len; 
+                       return 1; 
+               }
+               len -= eld->size;
+               buf += eld->size;
+               eld->size = 0;
+               eld->b_len = 0;
+               switch (eld->data_format) {
+               case EL_DATA_FORMAT_FUJI_RAW:
+                       eld->state = EL_READ_SIZE_BYTE_24;
+                       break;
+               default:
+                       eld->state = EL_READ;
+                       break;
+               }
+               break;
 
-       if (!eld || !buf || !len) return 0;
-       if (eld->state == EL_FAILED) return 0;
-       if (eld->size && eld->bytes_read == eld->size) return 0;
+       case EL_READ:
+       default:
+               break;
+       }
 
+       if (!len)
+               return 1;
        exif_log (eld->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
                  "Scanning %i byte(s) of data...", len);
 
-       for (i = 0; (i < len) && (eld->state != EL_EXIF_FOUND) &&
-                                (eld->state != EL_FAILED); i++) 
+       /*
+        * First fill the small buffer. Only continue if the buffer
+        * is filled. Note that EXIF data contains at least 12 bytes.
+        */
+       i = MIN (len, sizeof (eld->b) - eld->b_len);
+       if (i) {
+               memcpy (&eld->b[eld->b_len], buf, i);
+               eld->b_len += i;
+               if (eld->b_len < sizeof (eld->b)) 
+                       return 1;
+               buf += i;
+               len -= i;
+       }
+
+       switch (eld->data_format) {
+       case EL_DATA_FORMAT_UNKNOWN:
+
+               /* Check the small buffer against known formats. */
+               if (!memcmp (eld->b, "FUJIFILM", 8)) {
+
+                       /* Skip to byte 84. There is another offset there. */
+                       eld->data_format = EL_DATA_FORMAT_FUJI_RAW;
+                       eld->size = 84;
+                       eld->state = EL_SKIP_BYTES;
+                       eld->size = 84;
+
+               } else if (!memcmp (eld->b + 2, ExifHeader, sizeof (ExifHeader))) {
+
+                       /* Read the size (2 bytes). */
+                       eld->data_format = EL_DATA_FORMAT_EXIF;
+                       eld->state = EL_READ_SIZE_BYTE_08;
+               }
+       default:
+               break;
+       }
+
+       for (i = 0; i < sizeof (eld->b); i++)
                switch (eld->state) {
+               case EL_EXIF_FOUND:
+                       if (!exif_loader_copy (eld, eld->b + i,
+                                       sizeof (eld->b) - i)) 
+                               return 0;
+                       return exif_loader_copy (eld, buf, len);
                case EL_SKIP_BYTES:
                        eld->size--;
-                       if (eld->size == 0) {
+                       if (!eld->size) 
                                eld->state = EL_READ;
-                       }
-                       break;
-                       
-               case EL_READ_SIZE_HIGH_BYTE:
-                       eld->size = buf [i] << 8;
-                       eld->state = EL_READ_SIZE_LOW_BYTE;
                        break;
-                       
-               case EL_READ_SIZE_LOW_BYTE:
-                       eld->size |= buf [i];
 
-                       switch (eld->last_marker) {
-                       case JPEG_MARKER_APP0:
-                       case JPEG_MARKER_APP13:
-                       case JPEG_MARKER_COM:
+               case EL_READ_SIZE_BYTE_24:
+                       eld->size |= eld->b[i] << 24;
+                       eld->state = EL_READ_SIZE_BYTE_16;
+                       break;
+               case EL_READ_SIZE_BYTE_16:
+                       eld->size |= eld->b[i] << 16;
+                       eld->state = EL_READ_SIZE_BYTE_08;
+                       break;
+               case EL_READ_SIZE_BYTE_08:
+                       eld->size |= eld->b[i] << 8;
+                       eld->state = EL_READ_SIZE_BYTE_00;
+                       break;
+               case EL_READ_SIZE_BYTE_00:
+                       eld->size |= eld->b[i] << 0;
+                       switch (eld->data_format) {
+                       case EL_DATA_FORMAT_JPEG:
                                eld->state = EL_SKIP_BYTES;
                                eld->size -= 2;
                                break;
-
-                       case JPEG_MARKER_APP1:
-                               eld->state = EL_EXIF_FOUND;
+                       case EL_DATA_FORMAT_FUJI_RAW:
+                               eld->data_format = EL_DATA_FORMAT_EXIF;
+                               eld->state = EL_SKIP_BYTES;
+                               eld->size -= 86;
                                break;
-
-                       case 0:
-                               /*
-                                * Assume that we are reading EXIF data. 
-                                * This should probably be verified by reading
-                                * some bytes ahead.
-                                */
+                       case EL_DATA_FORMAT_EXIF:
                                eld->state = EL_EXIF_FOUND;
                                break;
                        default:
-                               return 0;
+                               break;
                        }
-
-                       eld->last_marker = 0;
                        break;
 
                default:
-                       if (buf[i] != 0xff) {
-                               if (buf[i] == JPEG_MARKER_APP0 ||
-                                   buf[i] == JPEG_MARKER_APP1 ||
-                                   buf[i] == JPEG_MARKER_APP13 ||
-                                   buf[i] == JPEG_MARKER_COM) {
-                                       eld->state = EL_READ_SIZE_HIGH_BYTE;
-                                       eld->last_marker = buf [i];
-
-                               } else if (buf [i] == JPEG_MARKER_SOI) {
-                                       /* Nothing */
+                       switch (eld->b[i]) {
+                       case JPEG_MARKER_APP1:
+                         if (!memcmp (eld->b + i + 3, ExifHeader, MIN((ssize_t)(sizeof(ExifHeader)), MAX(0, ((ssize_t)(sizeof(eld->b))) - ((ssize_t)i) - 3)))) {
+                                       eld->data_format = EL_DATA_FORMAT_EXIF;
                                } else {
-                                       /* Assume that we are reading EXIF
-                                        * data. This should probably be
-                                        * verified by reading some bytes
-                                        * ahead.
-                                        */
-                                       eld->last_marker = JPEG_MARKER_APP1;
-                                       eld->state = EL_READ_SIZE_HIGH_BYTE;
-                                       i--;
+                                       eld->data_format = EL_DATA_FORMAT_JPEG; /* Probably JFIF - keep searching for APP1 EXIF*/
                                }
+                               eld->size = 0;
+                               eld->state = EL_READ_SIZE_BYTE_08;
+                               break;
+                       case JPEG_MARKER_DHT:
+                       case JPEG_MARKER_DQT:
+                       case JPEG_MARKER_APP0:
+                       case JPEG_MARKER_APP2:
+                       case JPEG_MARKER_APP13:
+                       case JPEG_MARKER_COM:
+                               eld->data_format = EL_DATA_FORMAT_JPEG;
+                               eld->size = 0;
+                               eld->state = EL_READ_SIZE_BYTE_08;
+                               break;
+                       case 0xff:
+                       case JPEG_MARKER_SOI:
+                               break;
+                       default:
+                               exif_log (eld->log,
+                                       EXIF_LOG_CODE_CORRUPT_DATA,
+                                       "ExifLoader", _("The data supplied "
+                                               "does not seem to contain "
+                                               "EXIF data."));
+                               exif_loader_reset (eld);
+                               return 0;
                        }
                }
 
-       len_remain = len - i;
-       if (!len_remain) return 1;
-
-       if (eld->state == EL_EXIF_FOUND && len_remain > 0) {
-               if (eld->buf == NULL) {
-                       eld->buf = exif_loader_alloc (eld, eld->size);
-                       if (!eld->buf) return 0;
-                       eld->bytes_read = 0;
-               }
+       /*
+        * If we reach this point, the buffer has not been big enough
+        * to read all data we need. Fill it with new data.
+        */
+       eld->b_len = 0;
+       return exif_loader_write (eld, buf, len);
+}
 
-               if (eld->bytes_read < eld->size) {
-                       int cp_len;
+ExifLoader *
+exif_loader_new (void)
+{
+       ExifMem *mem = exif_mem_new_default ();
+       ExifLoader *l = exif_loader_new_mem (mem);
 
-                       /* the number of bytes we need to copy */
-                       cp_len = MIN (eld->size - eld->bytes_read, len_remain);
-                       
-                       if ((cp_len + eld->bytes_read) > eld->size) return 1;
+       exif_mem_unref (mem);
 
-                       /* Copy memory */
-                       memcpy (eld->buf + eld->bytes_read, &buf[i], cp_len);
-                       eld->bytes_read += cp_len;
-               }
-       }
-
-       return 1;
+       return l;
 }
 
 ExifLoader *
-exif_loader_new (void)
+exif_loader_new_mem (ExifMem *mem)
 {
        ExifLoader *loader;
+
+       if (!mem) 
+               return NULL;
        
-       loader = exif_loader_alloc (NULL, sizeof (ExifLoader));
-       if (!loader) return NULL;
+       loader = exif_mem_alloc (mem, sizeof (ExifLoader));
+       if (!loader) 
+               return NULL;
        loader->ref_count = 1;
 
+       loader->mem = mem;
+       exif_mem_ref (mem);
+
        return loader;
 }
 
 void
 exif_loader_ref (ExifLoader *loader)
 {
-       if (loader) loader->ref_count++;
+       if (loader) 
+               loader->ref_count++;
 }
 
+static void
+exif_loader_free (ExifLoader *loader)
+{
+       ExifMem *mem;
+
+       if (!loader) 
+               return;
+
+       mem = loader->mem;
+       exif_loader_reset (loader);
+       exif_log_unref (loader->log);
+       exif_mem_free (mem, loader);
+       exif_mem_unref (mem);
+}
+       
 void
 exif_loader_unref (ExifLoader *loader)
 {
-       if (!loader) return;
-       if (!--loader->ref_count) {
-               exif_loader_reset (loader);
-               free (loader);
-       }
+       if (!loader) 
+               return;
+       if (!--loader->ref_count)
+               exif_loader_free (loader);
 }
 
 void
 exif_loader_reset (ExifLoader *loader)
 {
-       if (!loader) return;
-       free (loader->buf); loader->buf = NULL;
+       if (!loader) 
+               return;
+       exif_mem_free (loader->mem, loader->buf); loader->buf = NULL;
        loader->size = 0;
        loader->bytes_read = 0;
-       loader->last_marker = 0;
        loader->state = 0;
+       loader->b_len = 0;
+       loader->data_format = EL_DATA_FORMAT_UNKNOWN;
 }
 
 ExifData *
@@ -224,9 +392,11 @@ exif_loader_get_data (ExifLoader *loader)
 {
        ExifData *ed;
 
-       if (!loader) return NULL;
+       if (!loader || (loader->data_format == EL_DATA_FORMAT_UNKNOWN) ||
+           !loader->bytes_read)
+               return NULL;
 
-       ed = exif_data_new ();
+       ed = exif_data_new_mem (loader->mem);
        exif_data_log (ed, loader->log);
        exif_data_load_data (ed, loader->buf, loader->bytes_read);
 
@@ -234,9 +404,33 @@ exif_loader_get_data (ExifLoader *loader)
 }
 
 void
+exif_loader_get_buf (ExifLoader *loader, const unsigned char **buf,
+                                                 unsigned int *buf_size)
+{
+       const unsigned char* b = NULL;
+       unsigned int s = 0;
+
+       if (loader) {
+               if (loader->data_format == EL_DATA_FORMAT_UNKNOWN) {
+                       exif_log (loader->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
+                                         "Loader format unknown");
+               } else {
+                       b = loader->buf;
+                       s = loader->bytes_read;
+               }
+       }
+
+       if (buf)
+               *buf = b;
+       if (buf_size)
+               *buf_size = s;
+}
+
+void
 exif_loader_log (ExifLoader *loader, ExifLog *log)
 {
-       if (!loader) return;
+       if (!loader) 
+               return;
        exif_log_unref (loader->log);
        loader->log = log;
        exif_log_ref (log);