From 0bcada8dc817d526b7dc3e7a8086454dab50469e Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Mon, 31 May 2004 01:24:03 +0000 Subject: [PATCH] Add support for Exif decoding (initial framework). Still lots of work to do here :) svn path=/trunk/; revision=11035 --- packet-image-jfif.c | 262 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 243 insertions(+), 19 deletions(-) diff --git a/packet-image-jfif.c b/packet-image-jfif.c index c0e05d4e73..ea16c1bd94 100644 --- a/packet-image-jfif.c +++ b/packet-image-jfif.c @@ -3,7 +3,7 @@ * Routines for JFIF image/jpeg media dissection * Copyright 2004, Olivier Biot. * - * $Id: packet-image-jfif.c,v 1.4 2004/03/08 22:03:58 obiot Exp $ + * $Id: packet-image-jfif.c,v 1.5 2004/05/31 01:24:03 obiot Exp $ * * Refer to the AUTHORS file or the AUTHORS section in the man page * for contacting the author(s) of this file. @@ -17,6 +17,9 @@ * The JFIF specifications are found at several locations, such as: * http://www.jpeg.org/public/jfif.pdf * http://www.w3.org/Graphics/JPEG/itu-t81.pdf + * + * The Exif specifications are found at several locations, such as: + * http://www.exif.org/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -239,6 +242,74 @@ static const value_string vals_extension_code[] = { { 0x00, NULL } }; +static const value_string vals_exif_tags[] = { + /* + * Tags related to image data structure: + */ + { 0x0100, "ImageWidth" }, + { 0x0101, "ImageLength" }, + { 0x0102, "BitsPerSample" }, + { 0x0103, "Compression" }, + { 0x0106, "PhotometricInterpretation" }, + { 0x0112, "Orientation" }, + { 0x0115, "SamplesPerPixel" }, + { 0x011C, "PlanarConfiguration" }, + { 0x0212, "YCbCrSubSampling" }, + { 0x0213, "YCbCrPositioning" }, + { 0x011A, "XResolution" }, + { 0x011B, "YResolution" }, + { 0x0128, "ResolutionUnit" }, + /* + * Tags relating to recording offset: + */ + { 0x0111, "StripOffsets" }, + { 0x0116, "RowsPerStrip" }, + { 0x0117, "StripByteCounts" }, + { 0x0201, "JPEGInterchangeFormat" }, + { 0x0202, "JPEGInterchangeFormatLength" }, + /* + * Tags relating to image data characteristics: + */ + { 0x012D, "TransferFunction" }, + { 0x013E, "WhitePoint" }, + { 0x013F, "PrimaryChromaticities" }, + { 0x0211, "YCbCrCoefficients" }, + { 0x0214, "ReferenceBlackWhite" }, + /* + * Other tags: + */ + { 0x0132, "DateTime" }, + { 0x010E, "ImageDescription" }, + { 0x010F, "Make" }, + { 0x0110, "Model" }, + { 0x0131, "Software" }, + { 0x013B, "Artist" }, + { 0x8296, "Copyright" }, + /* + * Exif-specific IFD: + */ + { 0x8769, "Exif IFD Pointer"}, + { 0x8825, "GPS IFD Pointer"}, + { 0xA005, "Interoperability IFD Pointer"}, + + { 0x0000, NULL } +}; + +static const value_string vals_exif_types[] = { + { 0x0001, "BYTE" }, + { 0x0002, "ASCII" }, + { 0x0003, "SHORT" }, + { 0x0004, "LONG" }, + { 0x0005, "RATIONAL" }, + /* 0x0006 */ + { 0x0007, "UNDEFINED" }, + /* 0x0008 */ + { 0x0009, "SLONG" }, + { 0x000A, "SRATIONAL" }, + + { 0x0000, NULL } +}; + /* Initialize the protocol and registered fields */ static int proto_jfif = -1; @@ -511,6 +582,170 @@ process_app0_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len, return; } +/* Process an APP1 block. + * + * XXX - This code only works on US-ASCII systems!!! + */ +static void +process_app1_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len, + guint16 marker, const char *marker_name) +{ + proto_item *ti = NULL; + proto_tree *subtree = NULL; + proto_tree *subtree_details = NULL; + char *str; + gint str_size; + guint32 offset, tiff_start; + + if (!tree) + return; + + ti = proto_tree_add_item(tree, hf_marker_segment, + tvb, 0, -1, FALSE); + subtree = proto_item_add_subtree(ti, ett_marker_segment); + + proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker); + proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE); + + proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE); + + str = tvb_get_stringz(tvb, 4, &str_size); + ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE); + offset = tiff_start = 4 + str_size; + if (strcmp(str, "Exif") == 0) { + /* + * Endianness + */ + gboolean is_little_endian; + guint16 val_16; + guint32 val_32; + guint16 num_fields; + + offset++; /* Skip a byte supposed to be 0x00 */ + + val_16 = tvb_get_ntohs(tvb, offset); + if (val_16 == 0x4949) { + is_little_endian = TRUE; + proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: little endian"); + } else if (val_16 == 0x4D4D) { + is_little_endian = FALSE; + proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: big endian"); + } else { + /* Error: invalid endianness encoding */ + proto_tree_add_text(subtree, tvb, offset, 2, + "Incorrect endianness encoding - skipping the remainder of this application marker"); + return; + } + offset += 2; + /* + * Fixed value 42 = 0x002a + */ + offset += 2; + /* + * Offset to IFD + */ + if (is_little_endian) { + val_16 = tvb_get_letohs(tvb, offset); + } else { + val_16 = tvb_get_ntohs(tvb, offset); + } + proto_tree_add_text(subtree, tvb, offset, 4, + "Start offset of IFD starting from the TIFF header start: %u bytes", val_16); + offset += 4; + /* + * Skip the following portion + */ + proto_tree_add_text(subtree, tvb, offset, val_16 + tiff_start - offset, + "Skipped data between end of TIFF header and start of IFD (%u bytes)", + val_16 + tiff_start - offset); + offset = val_16 + tiff_start + 1; + /* + * Process the "0th" IFD + */ + if (is_little_endian) { + num_fields = tvb_get_letohs(tvb, offset); + } else { + num_fields = tvb_get_ntohs(tvb, offset); + } + proto_tree_add_text(subtree, tvb, offset, 2, "Number of fields in this IFD: %u", num_fields); + offset += 2; + while (num_fields-- > 0) { + guint16 tag, type; + guint32 count, off; + + if (is_little_endian) { + tag = tvb_get_letohs(tvb, offset); + type = tvb_get_letohs(tvb, offset + 2); + count = tvb_get_letohl(tvb, offset + 4); + off = tvb_get_letohl(tvb, offset + 8); + } else { + tag = tvb_get_ntohs(tvb, offset); + type = tvb_get_ntohs(tvb, offset + 2); + count = tvb_get_ntohl(tvb, offset + 4); + off = tvb_get_ntohl(tvb, offset + 8); + } + /* TODO - refine this */ + proto_tree_add_text(subtree, tvb, offset, 2, + "Exif Tag: 0x%04X (%s), Type: %u (%s), Count: %u, " + "Value offset from start of TIFF header: %u", + tag, val_to_str(tag, vals_exif_tags, "Unknown Exif tag"), + type, val_to_str(type, vals_exif_types, "Unknown Exif type"), + count, off); + offset += 12; + } + /* + * Offset to the "1st" IFD + */ + if (is_little_endian) { + val_32 = tvb_get_letohl(tvb, offset); + } else { + val_32 = tvb_get_ntohl(tvb, offset); + } + proto_tree_add_text(subtree, tvb, offset, 4, + "Offset to next IFD from start of TIFF header: %u bytes", val_32); + /* TODO - Continue parsing the "1th" IFD */ + offset += 4; + proto_tree_add_text(subtree, tvb, offset, -1, "Remainder of APP1 marker skipped"); + } else { + proto_item_append_text(ti, " (Unknown identifier)"); + } +} + +/* Process an APP2 block. + * + * XXX - This code only works on US-ASCII systems!!! + */ +static void +process_app2_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len, + guint16 marker, const char *marker_name) +{ + proto_item *ti = NULL; + proto_tree *subtree = NULL; + proto_tree *subtree_details = NULL; + char *str; + gint str_size; + + if (!tree) + return; + + ti = proto_tree_add_item(tree, hf_marker_segment, + tvb, 0, -1, FALSE); + subtree = proto_item_add_subtree(ti, ett_marker_segment); + + proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker); + proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE); + + proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE); + + str = tvb_get_stringz(tvb, 4, &str_size); + ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE); + if (strcmp(str, "FPXR") == 0) { + proto_tree_add_text(tree, tvb, 0, -1, "Exif FlashPix APP2 application marker"); + } else { + proto_item_append_text(ti, " (Unknown identifier)"); + } +} + static void dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) { @@ -542,24 +777,7 @@ dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) if (tree) proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE); - marker = tvb_get_ntohs(tvb, 2); - if (marker != MARKER_APP0) { - if (tree) { - proto_tree_add_text(subtree, tvb, 2, 2, ErrorInvalidJFIF); - return; - } - } - - if (! tree) - return; - - offset = 4; - /* The APP0 segment has a length field */ - len = tvb_get_ntohs(tvb, offset); - tmp_tvb = tvb_new_subset(tvb, offset - 2, 2 + len, 2 + len); - process_app0_segment(subtree, tmp_tvb, len, marker, - match_strval(marker, vals_marker)); - offset += len; + offset = 2; /* * Process the remaining markers and marker segments @@ -577,6 +795,12 @@ dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) case MARKER_APP0: process_app0_segment(subtree, tmp_tvb, len, marker, str); break; + case MARKER_APP1: + process_app1_segment(subtree, tmp_tvb, len, marker, str); + break; + case MARKER_APP2: + process_app2_segment(subtree, tmp_tvb, len, marker, str); + break; case MARKER_SOF0: case MARKER_SOF1: case MARKER_SOF2: