diff options
Diffstat (limited to 'libexif/exif-data.c')
-rw-r--r-- | libexif/exif-data.c | 1276 |
1 files changed, 1276 insertions, 0 deletions
diff --git a/libexif/exif-data.c b/libexif/exif-data.c new file mode 100644 index 0000000..67df4db --- /dev/null +++ b/libexif/exif-data.c @@ -0,0 +1,1276 @@ +/* exif-data.c + * + * Copyright (c) 2001 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 <libexif/exif-mnote-data.h> +#include <libexif/exif-data.h> +#include <libexif/exif-ifd.h> +#include <libexif/exif-mnote-data-priv.h> +#include <libexif/exif-utils.h> +#include <libexif/exif-loader.h> +#include <libexif/exif-log.h> +#include <libexif/i18n.h> +#include <libexif/exif-system.h> + +#include <libexif/canon/exif-mnote-data-canon.h> +#include <libexif/fuji/exif-mnote-data-fuji.h> +#include <libexif/olympus/exif-mnote-data-olympus.h> +#include <libexif/pentax/exif-mnote-data-pentax.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#undef JPEG_MARKER_SOI +#define JPEG_MARKER_SOI 0xd8 +#undef JPEG_MARKER_APP0 +#define JPEG_MARKER_APP0 0xe0 +#undef JPEG_MARKER_APP1 +#define JPEG_MARKER_APP1 0xe1 + +static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + +struct _ExifDataPrivate +{ + ExifByteOrder order; + + ExifMnoteData *md; + + ExifLog *log; + ExifMem *mem; + + unsigned int ref_count; + + /* Temporarily used while loading data */ + unsigned int offset_mnote; + + ExifDataOption options; + ExifDataType data_type; +}; + +static void * +exif_data_alloc (ExifData *data, unsigned int i) +{ + void *d; + + if (!data || !i) + return NULL; + + d = exif_mem_alloc (data->priv->mem, i); + if (d) + return d; + + EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", i); + return NULL; +} + +ExifMnoteData * +exif_data_get_mnote_data (ExifData *d) +{ + return (d && d->priv) ? d->priv->md : NULL; +} + +ExifData * +exif_data_new (void) +{ + ExifMem *mem = exif_mem_new_default (); + ExifData *d = exif_data_new_mem (mem); + + exif_mem_unref (mem); + + return d; +} + +ExifData * +exif_data_new_mem (ExifMem *mem) +{ + ExifData *data; + unsigned int i; + + if (!mem) + return NULL; + + data = exif_mem_alloc (mem, sizeof (ExifData)); + if (!data) + return (NULL); + data->priv = exif_mem_alloc (mem, sizeof (ExifDataPrivate)); + if (!data->priv) { + exif_mem_free (mem, data); + return (NULL); + } + data->priv->ref_count = 1; + + data->priv->mem = mem; + exif_mem_ref (mem); + + for (i = 0; i < EXIF_IFD_COUNT; i++) { + data->ifd[i] = exif_content_new_mem (data->priv->mem); + if (!data->ifd[i]) { + exif_data_free (data); + return (NULL); + } + data->ifd[i]->parent = data; + } + + /* Default options */ +#ifndef NO_VERBOSE_TAG_STRINGS + /* + * When the tag list is compiled away, setting this option prevents + * any tags from being loaded + */ + exif_data_set_option (data, EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS); +#endif + exif_data_set_option (data, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); + + /* Default data type: none */ + exif_data_set_data_type (data, EXIF_DATA_TYPE_COUNT); + + return (data); +} + +ExifData * +exif_data_new_from_data (const unsigned char *data, unsigned int size) +{ + ExifData *edata; + + edata = exif_data_new (); + exif_data_load_data (edata, data, size); + return (edata); +} + +static int +exif_data_load_data_entry (ExifData *data, ExifEntry *entry, + const unsigned char *d, + unsigned int size, unsigned int offset) +{ + unsigned int s, doff; + + entry->tag = exif_get_short (d + offset + 0, data->priv->order); + entry->format = exif_get_short (d + offset + 2, data->priv->order); + entry->components = exif_get_long (d + offset + 4, data->priv->order); + + /* FIXME: should use exif_tag_get_name_in_ifd here but entry->parent + * has not been set yet + */ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Loading entry 0x%x ('%s')...", entry->tag, + exif_tag_get_name (entry->tag)); + + /* {0,1,2,4,8} x { 0x00000000 .. 0xffffffff } + * -> { 0x000000000 .. 0x7fffffff8 } */ + s = exif_format_get_size(entry->format) * entry->components; + if ((s < entry->components) || (s == 0)){ + return 0; + } + + /* + * Size? If bigger than 4 bytes, the actual data is not + * in the entry but somewhere else (offset). + */ + if (s > 4) + doff = exif_get_long (d + offset + 8, data->priv->order); + else + doff = offset + 8; + + /* Sanity checks */ + if ((doff + s < doff) || (doff + s < s) || (doff + s > size)) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Tag data past end of buffer (%u > %u)", doff+s, size); + return 0; + } + + entry->data = exif_data_alloc (data, s); + if (entry->data) { + entry->size = s; + memcpy (entry->data, d + doff, s); + } else { + /* FIXME: What do our callers do if (entry->data == NULL)? */ + EXIF_LOG_NO_MEMORY(data->priv->log, "ExifData", s); + } + + /* If this is the MakerNote, remember the offset */ + if (entry->tag == EXIF_TAG_MAKER_NOTE) { + if (!entry->data) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "MakerNote found with empty data"); + } else if (entry->size > 6) { + exif_log (data->priv->log, + EXIF_LOG_CODE_DEBUG, "ExifData", + "MakerNote found (%02x %02x %02x %02x " + "%02x %02x %02x...).", + entry->data[0], entry->data[1], entry->data[2], + entry->data[3], entry->data[4], entry->data[5], + entry->data[6]); + } + data->priv->offset_mnote = doff; + } + return 1; +} + +static void +exif_data_save_data_entry (ExifData *data, ExifEntry *e, + unsigned char **d, unsigned int *ds, + unsigned int offset) +{ + unsigned int doff, s; + unsigned int ts; + + if (!data || !data->priv) + return; + + /* + * Each entry is 12 bytes long. The memory for the entry has + * already been allocated. + */ + exif_set_short (*d + 6 + offset + 0, + data->priv->order, (ExifShort) e->tag); + exif_set_short (*d + 6 + offset + 2, + data->priv->order, (ExifShort) e->format); + + if (!(data->priv->options & EXIF_DATA_OPTION_DONT_CHANGE_MAKER_NOTE)) { + /* If this is the maker note tag, update it. */ + if ((e->tag == EXIF_TAG_MAKER_NOTE) && data->priv->md) { + /* TODO: this is using the wrong ExifMem to free e->data */ + exif_mem_free (data->priv->mem, e->data); + e->data = NULL; + e->size = 0; + exif_mnote_data_set_offset (data->priv->md, *ds - 6); + exif_mnote_data_save (data->priv->md, &e->data, &e->size); + e->components = e->size; + } + } + + exif_set_long (*d + 6 + offset + 4, + data->priv->order, e->components); + + /* + * Size? If bigger than 4 bytes, the actual data is not in + * the entry but somewhere else. + */ + s = exif_format_get_size (e->format) * e->components; + if (s > 4) { + unsigned char *t; + doff = *ds - 6; + ts = *ds + s; + + /* + * According to the TIFF specification, + * the offset must be an even number. If we need to introduce + * a padding byte, we set it to 0. + */ + if (s & 1) + ts++; + t = exif_mem_realloc (data->priv->mem, *d, ts); + if (!t) { + EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", ts); + return; + } + *d = t; + *ds = ts; + exif_set_long (*d + 6 + offset + 8, data->priv->order, doff); + if (s & 1) + *(*d + *ds - 1) = '\0'; + + } else + doff = offset + 8; + + /* Write the data. Fill unneeded bytes with 0. Do not crash with + * e->data is NULL */ + if (e->data) { + memcpy (*d + 6 + doff, e->data, s); + } else { + memset (*d + 6 + doff, 0, s); + } + if (s < 4) + memset (*d + 6 + doff + s, 0, (4 - s)); +} + +static void +exif_data_load_data_thumbnail (ExifData *data, const unsigned char *d, + unsigned int ds, ExifLong o, ExifLong s) +{ + /* Sanity checks */ + if ((o + s < o) || (o + s < s) || (o + s > ds) || (o > ds)) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Bogus thumbnail offset (%u) or size (%u).", + o, s); + return; + } + + if (data->data) + exif_mem_free (data->priv->mem, data->data); + if (!(data->data = exif_data_alloc (data, s))) { + EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", s); + data->size = 0; + return; + } + data->size = s; + memcpy (data->data, d + o, s); +} + +#undef CHECK_REC +#define CHECK_REC(i) \ +if ((i) == ifd) { \ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, \ + "ExifData", "Recursive entry in IFD " \ + "'%s' detected. Skipping...", \ + exif_ifd_get_name (i)); \ + break; \ +} \ +if (data->ifd[(i)]->count) { \ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, \ + "ExifData", "Attempt to load IFD " \ + "'%s' multiple times detected. " \ + "Skipping...", \ + exif_ifd_get_name (i)); \ + break; \ +} + +/*! Load data for an IFD. + * + * \param[in,out] data #ExifData + * \param[in] ifd IFD to load + * \param[in] d pointer to buffer containing raw IFD data + * \param[in] ds size of raw data in buffer at \c d + * \param[in] offset offset into buffer at \c d at which IFD starts + * \param[in] recursion_depth number of times this function has been + * recursively called without returning + */ +static void +exif_data_load_data_content (ExifData *data, ExifIfd ifd, + const unsigned char *d, + unsigned int ds, unsigned int offset, unsigned int recursion_depth) +{ + ExifLong o, thumbnail_offset = 0, thumbnail_length = 0; + ExifShort n; + ExifEntry *entry; + unsigned int i; + ExifTag tag; + + if (!data || !data->priv) + return; + + /* check for valid ExifIfd enum range */ + if ((((int)ifd) < 0) || ( ((int)ifd) >= EXIF_IFD_COUNT)) + return; + + if (recursion_depth > 30) { + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", + "Deep recursion detected!"); + return; + } + + /* Read the number of entries */ + if ((offset + 2 < offset) || (offset + 2 < 2) || (offset + 2 > ds)) { + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", + "Tag data past end of buffer (%u > %u)", offset+2, ds); + return; + } + n = exif_get_short (d + offset, data->priv->order); + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Loading %hu entries...", n); + offset += 2; + + /* Check if we have enough data. */ + if (offset + 12 * n > ds) { + n = (ds - offset) / 12; + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Short data; only loading %hu entries...", n); + } + + for (i = 0; i < n; i++) { + + tag = exif_get_short (d + offset + 12 * i, data->priv->order); + switch (tag) { + case EXIF_TAG_EXIF_IFD_POINTER: + case EXIF_TAG_GPS_INFO_IFD_POINTER: + case EXIF_TAG_INTEROPERABILITY_IFD_POINTER: + case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: + case EXIF_TAG_JPEG_INTERCHANGE_FORMAT: + o = exif_get_long (d + offset + 12 * i + 8, + data->priv->order); + /* FIXME: IFD_POINTER tags aren't marked as being in a + * specific IFD, so exif_tag_get_name_in_ifd won't work + */ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Sub-IFD entry 0x%x ('%s') at %u.", tag, + exif_tag_get_name(tag), o); + switch (tag) { + case EXIF_TAG_EXIF_IFD_POINTER: + CHECK_REC (EXIF_IFD_EXIF); + exif_data_load_data_content (data, EXIF_IFD_EXIF, d, ds, o, recursion_depth + 1); + break; + case EXIF_TAG_GPS_INFO_IFD_POINTER: + CHECK_REC (EXIF_IFD_GPS); + exif_data_load_data_content (data, EXIF_IFD_GPS, d, ds, o, recursion_depth + 1); + break; + case EXIF_TAG_INTEROPERABILITY_IFD_POINTER: + CHECK_REC (EXIF_IFD_INTEROPERABILITY); + exif_data_load_data_content (data, EXIF_IFD_INTEROPERABILITY, d, ds, o, recursion_depth + 1); + break; + case EXIF_TAG_JPEG_INTERCHANGE_FORMAT: + thumbnail_offset = o; + if (thumbnail_offset && thumbnail_length) + exif_data_load_data_thumbnail (data, d, + ds, thumbnail_offset, + thumbnail_length); + break; + case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: + thumbnail_length = o; + if (thumbnail_offset && thumbnail_length) + exif_data_load_data_thumbnail (data, d, + ds, thumbnail_offset, + thumbnail_length); + break; + default: + return; + } + break; + default: + + /* + * If we don't know the tag, don't fail. It could be that new + * versions of the standard have defined additional tags. Note that + * 0 is a valid tag in the GPS IFD. + */ + if (!exif_tag_get_name_in_ifd (tag, ifd)) { + + /* + * Special case: Tag and format 0. That's against specification + * (at least up to 2.2). But Photoshop writes it anyways. + */ + if (!memcmp (d + offset + 12 * i, "\0\0\0\0", 4)) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Skipping empty entry at position %u in '%s'.", i, + exif_ifd_get_name (ifd)); + break; + } + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Unknown tag 0x%04x (entry %u in '%s'). Please report this tag " + "to <libexif-devel@lists.sourceforge.net>.", tag, i, + exif_ifd_get_name (ifd)); + if (data->priv->options & EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS) + break; + } + entry = exif_entry_new_mem (data->priv->mem); + if (exif_data_load_data_entry (data, entry, d, ds, + offset + 12 * i)) + exif_content_add_entry (data->ifd[ifd], entry); + exif_entry_unref (entry); + break; + } + } +} + +static int +cmp_func (const unsigned char *p1, const unsigned char *p2, ExifByteOrder o) +{ + ExifShort tag1 = exif_get_short (p1, o); + ExifShort tag2 = exif_get_short (p2, o); + + return (tag1 < tag2) ? -1 : (tag1 > tag2) ? 1 : 0; +} + +static int +cmp_func_intel (const void *elem1, const void *elem2) +{ + return cmp_func ((const unsigned char *) elem1, + (const unsigned char *) elem2, EXIF_BYTE_ORDER_INTEL); +} + +static int +cmp_func_motorola (const void *elem1, const void *elem2) +{ + return cmp_func ((const unsigned char *) elem1, + (const unsigned char *) elem2, EXIF_BYTE_ORDER_MOTOROLA); +} + +static void +exif_data_save_data_content (ExifData *data, ExifContent *ifd, + unsigned char **d, unsigned int *ds, + unsigned int offset) +{ + unsigned int j, n_ptr = 0, n_thumb = 0; + ExifIfd i; + unsigned char *t; + unsigned int ts; + + if (!data || !data->priv || !ifd || !d || !ds) + return; + + for (i = 0; i < EXIF_IFD_COUNT; i++) + if (ifd == data->ifd[i]) + break; + if (i == EXIF_IFD_COUNT) + return; /* error */ + + /* + * Check if we need some extra entries for pointers or the thumbnail. + */ + switch (i) { + case EXIF_IFD_0: + + /* + * The pointer to IFD_EXIF is in IFD_0. The pointer to + * IFD_INTEROPERABILITY is in IFD_EXIF. + */ + if (data->ifd[EXIF_IFD_EXIF]->count || + data->ifd[EXIF_IFD_INTEROPERABILITY]->count) + n_ptr++; + + /* The pointer to IFD_GPS is in IFD_0. */ + if (data->ifd[EXIF_IFD_GPS]->count) + n_ptr++; + + break; + case EXIF_IFD_1: + if (data->size) + n_thumb = 2; + break; + case EXIF_IFD_EXIF: + if (data->ifd[EXIF_IFD_INTEROPERABILITY]->count) + n_ptr++; + default: + break; + } + + /* + * Allocate enough memory for all entries + * and the number of entries. + */ + ts = *ds + (2 + (ifd->count + n_ptr + n_thumb) * 12 + 4); + t = exif_mem_realloc (data->priv->mem, *d, ts); + if (!t) { + EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", ts); + return; + } + *d = t; + *ds = ts; + + /* Save the number of entries */ + exif_set_short (*d + 6 + offset, data->priv->order, + (ExifShort) (ifd->count + n_ptr + n_thumb)); + offset += 2; + + /* + * Save each entry. Make sure that no memcpys from NULL pointers are + * performed + */ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Saving %i entries (IFD '%s', offset: %i)...", + ifd->count, exif_ifd_get_name (i), offset); + for (j = 0; j < ifd->count; j++) { + if (ifd->entries[j]) { + exif_data_save_data_entry (data, ifd->entries[j], d, ds, + offset + 12 * j); + } + } + + offset += 12 * ifd->count; + + /* Now save special entries. */ + switch (i) { + case EXIF_IFD_0: + + /* + * The pointer to IFD_EXIF is in IFD_0. + * However, the pointer to IFD_INTEROPERABILITY is in IFD_EXIF, + * therefore, if IFD_INTEROPERABILITY is not empty, we need + * IFD_EXIF even if latter is empty. + */ + if (data->ifd[EXIF_IFD_EXIF]->count || + data->ifd[EXIF_IFD_INTEROPERABILITY]->count) { + exif_set_short (*d + 6 + offset + 0, data->priv->order, + EXIF_TAG_EXIF_IFD_POINTER); + exif_set_short (*d + 6 + offset + 2, data->priv->order, + EXIF_FORMAT_LONG); + exif_set_long (*d + 6 + offset + 4, data->priv->order, + 1); + exif_set_long (*d + 6 + offset + 8, data->priv->order, + *ds - 6); + exif_data_save_data_content (data, + data->ifd[EXIF_IFD_EXIF], d, ds, *ds - 6); + offset += 12; + } + + /* The pointer to IFD_GPS is in IFD_0, too. */ + if (data->ifd[EXIF_IFD_GPS]->count) { + exif_set_short (*d + 6 + offset + 0, data->priv->order, + EXIF_TAG_GPS_INFO_IFD_POINTER); + exif_set_short (*d + 6 + offset + 2, data->priv->order, + EXIF_FORMAT_LONG); + exif_set_long (*d + 6 + offset + 4, data->priv->order, + 1); + exif_set_long (*d + 6 + offset + 8, data->priv->order, + *ds - 6); + exif_data_save_data_content (data, + data->ifd[EXIF_IFD_GPS], d, ds, *ds - 6); + offset += 12; + } + + break; + case EXIF_IFD_EXIF: + + /* + * The pointer to IFD_INTEROPERABILITY is in IFD_EXIF. + * See note above. + */ + if (data->ifd[EXIF_IFD_INTEROPERABILITY]->count) { + exif_set_short (*d + 6 + offset + 0, data->priv->order, + EXIF_TAG_INTEROPERABILITY_IFD_POINTER); + exif_set_short (*d + 6 + offset + 2, data->priv->order, + EXIF_FORMAT_LONG); + exif_set_long (*d + 6 + offset + 4, data->priv->order, + 1); + exif_set_long (*d + 6 + offset + 8, data->priv->order, + *ds - 6); + exif_data_save_data_content (data, + data->ifd[EXIF_IFD_INTEROPERABILITY], d, ds, + *ds - 6); + offset += 12; + } + + break; + case EXIF_IFD_1: + + /* + * Information about the thumbnail (if any) is saved in + * IFD_1. + */ + if (data->size) { + + /* EXIF_TAG_JPEG_INTERCHANGE_FORMAT */ + exif_set_short (*d + 6 + offset + 0, data->priv->order, + EXIF_TAG_JPEG_INTERCHANGE_FORMAT); + exif_set_short (*d + 6 + offset + 2, data->priv->order, + EXIF_FORMAT_LONG); + exif_set_long (*d + 6 + offset + 4, data->priv->order, + 1); + exif_set_long (*d + 6 + offset + 8, data->priv->order, + *ds - 6); + ts = *ds + data->size; + t = exif_mem_realloc (data->priv->mem, *d, ts); + if (!t) { + EXIF_LOG_NO_MEMORY (data->priv->log, "ExifData", + ts); + return; + } + *d = t; + *ds = ts; + memcpy (*d + *ds - data->size, data->data, data->size); + offset += 12; + + /* EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH */ + exif_set_short (*d + 6 + offset + 0, data->priv->order, + EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); + exif_set_short (*d + 6 + offset + 2, data->priv->order, + EXIF_FORMAT_LONG); + exif_set_long (*d + 6 + offset + 4, data->priv->order, + 1); + exif_set_long (*d + 6 + offset + 8, data->priv->order, + data->size); + offset += 12; + } + + break; + default: + break; + } + + /* Sort the directory according to TIFF specification */ + qsort (*d + 6 + offset - (ifd->count + n_ptr + n_thumb) * 12, + (ifd->count + n_ptr + n_thumb), 12, + (data->priv->order == EXIF_BYTE_ORDER_INTEL) ? cmp_func_intel : cmp_func_motorola); + + /* Correctly terminate the directory */ + if (i == EXIF_IFD_0 && (data->ifd[EXIF_IFD_1]->count || + data->size)) { + + /* + * We are saving IFD 0. Tell where IFD 1 starts and save + * IFD 1. + */ + exif_set_long (*d + 6 + offset, data->priv->order, *ds - 6); + exif_data_save_data_content (data, data->ifd[EXIF_IFD_1], d, ds, + *ds - 6); + } else + exif_set_long (*d + 6 + offset, data->priv->order, 0); +} + +typedef enum { + EXIF_DATA_TYPE_MAKER_NOTE_NONE = 0, + EXIF_DATA_TYPE_MAKER_NOTE_CANON = 1, + EXIF_DATA_TYPE_MAKER_NOTE_OLYMPUS = 2, + EXIF_DATA_TYPE_MAKER_NOTE_PENTAX = 3, + EXIF_DATA_TYPE_MAKER_NOTE_NIKON = 4, + EXIF_DATA_TYPE_MAKER_NOTE_CASIO = 5, + EXIF_DATA_TYPE_MAKER_NOTE_FUJI = 6 +} ExifDataTypeMakerNote; + +/*! If MakerNote is recognized, load it. + * + * \param[in,out] data #ExifData + * \param[in] d pointer to raw EXIF data + * \param[in] ds length of data at d + */ +static void +interpret_maker_note(ExifData *data, const unsigned char *d, unsigned int ds) +{ + int mnoteid; + ExifEntry* e = exif_data_get_entry (data, EXIF_TAG_MAKER_NOTE); + if (!e) + return; + + if ((mnoteid = exif_mnote_data_olympus_identify (data, e)) != 0) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, + "ExifData", "Olympus MakerNote variant type %d", mnoteid); + data->priv->md = exif_mnote_data_olympus_new (data->priv->mem); + + } else if ((mnoteid = exif_mnote_data_canon_identify (data, e)) != 0) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, + "ExifData", "Canon MakerNote variant type %d", mnoteid); + data->priv->md = exif_mnote_data_canon_new (data->priv->mem, data->priv->options); + + } else if ((mnoteid = exif_mnote_data_fuji_identify (data, e)) != 0) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, + "ExifData", "Fuji MakerNote variant type %d", mnoteid); + data->priv->md = exif_mnote_data_fuji_new (data->priv->mem); + + /* NOTE: Must do Pentax detection last because some of the + * heuristics are pretty general. */ + } else if ((mnoteid = exif_mnote_data_pentax_identify (data, e)) != 0) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, + "ExifData", "Pentax MakerNote variant type %d", mnoteid); + data->priv->md = exif_mnote_data_pentax_new (data->priv->mem); + } + + /* + * If we are able to interpret the maker note, do so. + */ + if (data->priv->md) { + exif_mnote_data_log (data->priv->md, data->priv->log); + exif_mnote_data_set_byte_order (data->priv->md, + data->priv->order); + exif_mnote_data_set_offset (data->priv->md, + data->priv->offset_mnote); + exif_mnote_data_load (data->priv->md, d, ds); + } +} + +#define LOG_TOO_SMALL \ +exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", \ + _("Size of data too small to allow for EXIF data.")); + +void +exif_data_load_data (ExifData *data, const unsigned char *d_orig, + unsigned int ds) +{ + unsigned int l; + ExifLong offset; + ExifShort n; + const unsigned char *d = d_orig; + unsigned int len, fullds; + + if (!data || !data->priv || !d || !ds) + return; + + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Parsing %i byte(s) EXIF data...\n", ds); + + /* + * It can be that the data starts with the EXIF header. If it does + * not, search the EXIF marker. + */ + if (ds < 6) { + LOG_TOO_SMALL; + return; + } + if (!memcmp (d, ExifHeader, 6)) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Found EXIF header."); + } else { + while (ds >= 3) { + while (ds && (d[0] == 0xff)) { + d++; + ds--; + } + + /* JPEG_MARKER_SOI */ + if (ds && d[0] == JPEG_MARKER_SOI) { + d++; + ds--; + continue; + } + + /* JPEG_MARKER_APP0 */ + if (ds >= 3 && d[0] == JPEG_MARKER_APP0) { + d++; + ds--; + l = (d[0] << 8) | d[1]; + if (l > ds) + return; + d += l; + ds -= l; + continue; + } + + /* JPEG_MARKER_APP1 */ + if (ds && d[0] == JPEG_MARKER_APP1) + break; + + /* Unknown marker or data. Give up. */ + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, + "ExifData", _("EXIF marker not found.")); + return; + } + if (ds < 3) { + LOG_TOO_SMALL; + return; + } + d++; + ds--; + len = (d[0] << 8) | d[1]; + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "We have to deal with %i byte(s) of EXIF data.", + len); + d += 2; + ds -= 2; + } + + /* + * Verify the exif header + * (offset 2, length 6). + */ + if (ds < 6) { + LOG_TOO_SMALL; + return; + } + if (memcmp (d, ExifHeader, 6)) { + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, + "ExifData", _("EXIF header not found.")); + return; + } + + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Found EXIF header."); + + /* Sanity check the data length */ + if (ds < 14) + return; + + /* The JPEG APP1 section can be no longer than 64 KiB (including a + 16-bit length), so cap the data length to protect against overflow + in future offset calculations */ + fullds = ds; + if (ds > 0xfffe) + ds = 0xfffe; + + /* Byte order (offset 6, length 2) */ + if (!memcmp (d + 6, "II", 2)) + data->priv->order = EXIF_BYTE_ORDER_INTEL; + else if (!memcmp (d + 6, "MM", 2)) + data->priv->order = EXIF_BYTE_ORDER_MOTOROLA; + else { + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, + "ExifData", _("Unknown encoding.")); + return; + } + + /* Fixed value */ + if (exif_get_short (d + 8, data->priv->order) != 0x002a) + return; + + /* IFD 0 offset */ + offset = exif_get_long (d + 10, data->priv->order); + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "IFD 0 at %i.", (int) offset); + + /* Sanity check the offset, being careful about overflow */ + if (offset > ds || offset + 6 + 2 > ds) + return; + + /* Parse the actual exif data (usually offset 14 from start) */ + exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0); + + /* IFD 1 offset */ + n = exif_get_short (d + 6 + offset, data->priv->order); + if (offset + 6 + 2 + 12 * n + 4 > ds) + return; + + offset = exif_get_long (d + 6 + offset + 2 + 12 * n, data->priv->order); + if (offset) { + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "IFD 1 at %i.", (int) offset); + + /* Sanity check. */ + if (offset > ds || offset + 6 > ds) { + exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, + "ExifData", "Bogus offset of IFD1."); + } else { + exif_data_load_data_content (data, EXIF_IFD_1, d + 6, ds - 6, offset, 0); + } + } + + /* + * If we got an EXIF_TAG_MAKER_NOTE, try to interpret it. Some + * cameras use pointers in the maker note tag that point to the + * space between IFDs. Here is the only place where we have access + * to that data. + */ + interpret_maker_note(data, d, fullds); + + /* Fixup tags if requested */ + if (data->priv->options & EXIF_DATA_OPTION_FOLLOW_SPECIFICATION) + exif_data_fix (data); +} + +void +exif_data_save_data (ExifData *data, unsigned char **d, unsigned int *ds) +{ + if (ds) + *ds = 0; /* This means something went wrong */ + + if (!data || !d || !ds) + return; + + /* Header */ + *ds = 14; + *d = exif_data_alloc (data, *ds); + if (!*d) { + *ds = 0; + return; + } + memcpy (*d, ExifHeader, 6); + + /* Order (offset 6) */ + if (data->priv->order == EXIF_BYTE_ORDER_INTEL) { + memcpy (*d + 6, "II", 2); + } else { + memcpy (*d + 6, "MM", 2); + } + + /* Fixed value (2 bytes, offset 8) */ + exif_set_short (*d + 8, data->priv->order, 0x002a); + + /* + * IFD 0 offset (4 bytes, offset 10). + * We will start 8 bytes after the + * EXIF header (2 bytes for order, another 2 for the test, and + * 4 bytes for the IFD 0 offset make 8 bytes together). + */ + exif_set_long (*d + 10, data->priv->order, 8); + + /* Now save IFD 0. IFD 1 will be saved automatically. */ + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Saving IFDs..."); + exif_data_save_data_content (data, data->ifd[EXIF_IFD_0], d, ds, + *ds - 6); + exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", + "Saved %i byte(s) EXIF data.", *ds); +} + +ExifData * +exif_data_new_from_file (const char *path) +{ + ExifData *edata; + ExifLoader *loader; + + loader = exif_loader_new (); + exif_loader_write_file (loader, path); + edata = exif_loader_get_data (loader); + exif_loader_unref (loader); + + return (edata); +} + +void +exif_data_ref (ExifData *data) +{ + if (!data) + return; + + data->priv->ref_count++; +} + +void +exif_data_unref (ExifData *data) +{ + if (!data) + return; + + data->priv->ref_count--; + if (!data->priv->ref_count) + exif_data_free (data); +} + +void +exif_data_free (ExifData *data) +{ + unsigned int i; + ExifMem *mem = (data && data->priv) ? data->priv->mem : NULL; + + if (!data) + return; + + for (i = 0; i < EXIF_IFD_COUNT; i++) { + if (data->ifd[i]) { + exif_content_unref (data->ifd[i]); + data->ifd[i] = NULL; + } + } + + if (data->data) { + exif_mem_free (mem, data->data); + data->data = NULL; + } + + if (data->priv) { + if (data->priv->log) { + exif_log_unref (data->priv->log); + data->priv->log = NULL; + } + if (data->priv->md) { + exif_mnote_data_unref (data->priv->md); + data->priv->md = NULL; + } + exif_mem_free (mem, data->priv); + exif_mem_free (mem, data); + } + + exif_mem_unref (mem); +} + +void +exif_data_dump (ExifData *data) +{ + unsigned int i; + + if (!data) + return; + + for (i = 0; i < EXIF_IFD_COUNT; i++) { + if (data->ifd[i] && data->ifd[i]->count) { + printf ("Dumping IFD '%s'...\n", + exif_ifd_get_name (i)); + exif_content_dump (data->ifd[i], 0); + } + } + + if (data->data) { + printf ("%i byte(s) thumbnail data available.", data->size); + if (data->size >= 4) { + printf ("0x%02x 0x%02x ... 0x%02x 0x%02x\n", + data->data[0], data->data[1], + data->data[data->size - 2], + data->data[data->size - 1]); + } + } +} + +ExifByteOrder +exif_data_get_byte_order (ExifData *data) +{ + if (!data) + return (0); + + return (data->priv->order); +} + +void +exif_data_foreach_content (ExifData *data, ExifDataForeachContentFunc func, + void *user_data) +{ + unsigned int i; + + if (!data || !func) + return; + + for (i = 0; i < EXIF_IFD_COUNT; i++) + func (data->ifd[i], user_data); +} + +typedef struct _ByteOrderChangeData ByteOrderChangeData; +struct _ByteOrderChangeData { + ExifByteOrder old, new; +}; + +static void +entry_set_byte_order (ExifEntry *e, void *data) +{ + ByteOrderChangeData *d = data; + + if (!e) + return; + + exif_array_set_byte_order (e->format, e->data, e->components, d->old, d->new); +} + +static void +content_set_byte_order (ExifContent *content, void *data) +{ + exif_content_foreach_entry (content, entry_set_byte_order, data); +} + +void +exif_data_set_byte_order (ExifData *data, ExifByteOrder order) +{ + ByteOrderChangeData d; + + if (!data || (order == data->priv->order)) + return; + + d.old = data->priv->order; + d.new = order; + exif_data_foreach_content (data, content_set_byte_order, &d); + data->priv->order = order; + if (data->priv->md) + exif_mnote_data_set_byte_order (data->priv->md, order); +} + +void +exif_data_log (ExifData *data, ExifLog *log) +{ + unsigned int i; + + if (!data || !data->priv) + return; + exif_log_unref (data->priv->log); + data->priv->log = log; + exif_log_ref (log); + + for (i = 0; i < EXIF_IFD_COUNT; i++) + exif_content_log (data->ifd[i], log); +} + +/* Used internally within libexif */ +ExifLog *exif_data_get_log (ExifData *); +ExifLog * +exif_data_get_log (ExifData *data) +{ + if (!data || !data->priv) + return NULL; + return data->priv->log; +} + +static const struct { + ExifDataOption option; + const char *name; + const char *description; +} exif_data_option[] = { + {EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS, N_("Ignore unknown tags"), + N_("Ignore unknown tags when loading EXIF data.")}, + {EXIF_DATA_OPTION_FOLLOW_SPECIFICATION, N_("Follow specification"), + N_("Add, correct and remove entries to get EXIF data that follows " + "the specification.")}, + {EXIF_DATA_OPTION_DONT_CHANGE_MAKER_NOTE, N_("Do not change maker note"), + N_("When loading and resaving Exif data, save the maker note unmodified." + " Be aware that the maker note can get corrupted.")}, + {0, NULL, NULL} +}; + +const char * +exif_data_option_get_name (ExifDataOption o) +{ + unsigned int i; + + for (i = 0; exif_data_option[i].name; i++) + if (exif_data_option[i].option == o) + break; + return _(exif_data_option[i].name); +} + +const char * +exif_data_option_get_description (ExifDataOption o) +{ + unsigned int i; + + for (i = 0; exif_data_option[i].description; i++) + if (exif_data_option[i].option == o) + break; + return _(exif_data_option[i].description); +} + +void +exif_data_set_option (ExifData *d, ExifDataOption o) +{ + if (!d) + return; + + d->priv->options |= o; +} + +void +exif_data_unset_option (ExifData *d, ExifDataOption o) +{ + if (!d) + return; + + d->priv->options &= ~o; +} + +static void +fix_func (ExifContent *c, void *UNUSED(data)) +{ + switch (exif_content_get_ifd (c)) { + case EXIF_IFD_1: + if (c->parent->data) + exif_content_fix (c); + else if (c->count) { + exif_log (c->parent->priv->log, EXIF_LOG_CODE_DEBUG, "exif-data", + "No thumbnail but entries on thumbnail. These entries have been " + "removed."); + while (c->count) { + unsigned int cnt = c->count; + exif_content_remove_entry (c, c->entries[c->count - 1]); + if (cnt == c->count) { + /* safety net */ + exif_log (c->parent->priv->log, EXIF_LOG_CODE_DEBUG, "exif-data", + "failed to remove last entry from entries."); + c->count--; + } + } + } + break; + default: + exif_content_fix (c); + } +} + +void +exif_data_fix (ExifData *d) +{ + exif_data_foreach_content (d, fix_func, NULL); +} + +void +exif_data_set_data_type (ExifData *d, ExifDataType dt) +{ + if (!d || !d->priv) + return; + + d->priv->data_type = dt; +} + +ExifDataType +exif_data_get_data_type (ExifData *d) +{ + return (d && d->priv) ? d->priv->data_type : EXIF_DATA_TYPE_UNKNOWN; +} |