vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / translators / jpeg / exif_parser.cpp
blob1b0ae15547bd530547ff81fdf2b80b14950ea156
1 /*
2 * Copyright 2007-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "exif_parser.h"
9 #include <Catalog.h>
10 #include <ctype.h>
11 #include <set>
12 #include <stdio.h>
13 #include <stdlib.h>
15 #include <Catalog.h>
16 #include <Message.h>
18 #include <ReadHelper.h>
20 #undef B_TRANSLATION_CONTEXT
21 #define B_TRANSLATION_CONTEXT "exit_parser"
24 using std::set;
26 enum {
27 TAG_EXIF_OFFSET = 0x8769,
28 TAG_SUB_DIR_OFFSET = 0xa005,
30 TAG_MAKER = 0x10f,
31 TAG_MODEL = 0x110,
32 TAG_ORIENTATION = 0x112,
33 TAG_EXPOSURE_TIME = 0x829a,
34 TAG_ISO = 0x8827,
37 static const convert_tag kDefaultTags[] = {
38 {TAG_MAKER, B_ANY_TYPE, "Maker"},
39 {TAG_MODEL, B_ANY_TYPE, "Model"},
40 {TAG_ORIENTATION, B_INT32_TYPE, "Orientation"},
41 {TAG_EXPOSURE_TIME, B_DOUBLE_TYPE, "ExposureTime"},
42 {TAG_ISO, B_INT32_TYPE, "ISO"},
44 static const size_t kNumDefaultTags = sizeof(kDefaultTags)
45 / sizeof(kDefaultTags[0]);
48 static status_t parse_tiff_directory(TReadHelper& read, set<off_t>& visited,
49 BMessage& target, const convert_tag* tags, size_t tagCount);
52 static status_t
53 add_to_message(TReadHelper& source, BMessage& target, tiff_tag& tag,
54 const char* name, type_code type)
56 type_code defaultType = B_INT32_TYPE;
57 double doubleValue = 0.0;
58 int32 intValue = 0;
60 switch (tag.type) {
61 case TIFF_STRING_TYPE:
63 if (type != B_ANY_TYPE && type != B_STRING_TYPE)
64 return B_BAD_VALUE;
66 char* buffer = (char*)malloc(tag.length);
67 if (buffer == NULL)
68 return B_NO_MEMORY;
70 source(buffer, tag.length);
72 // remove trailing spaces
73 int32 i = tag.length;
74 while ((--i > 0 && isspace(buffer[i])) || !buffer[i]) {
75 buffer[i] = '\0';
78 status_t status = target.AddString(name, buffer);
79 free(buffer);
81 return status;
84 case TIFF_UNDEFINED_TYPE:
86 if (type != B_ANY_TYPE && type != B_STRING_TYPE && type != B_RAW_TYPE)
87 return B_BAD_VALUE;
89 char* buffer = (char*)malloc(tag.length);
90 if (buffer == NULL)
91 return B_NO_MEMORY;
93 source(buffer, tag.length);
95 status_t status;
96 if (type == B_STRING_TYPE)
97 status = target.AddString(name, buffer);
98 else
99 status = target.AddData(name, B_RAW_TYPE, buffer, tag.length);
101 free(buffer);
103 return status;
106 // unsigned
107 case TIFF_UINT8_TYPE:
108 intValue = source.Next<uint8>();
109 break;
110 case TIFF_UINT16_TYPE:
111 defaultType = B_INT32_TYPE;
112 intValue = source.Next<uint16>();
113 break;
114 case TIFF_UINT32_TYPE:
115 defaultType = B_INT32_TYPE;
116 intValue = source.Next<uint32>();
117 break;
118 case TIFF_UFRACTION_TYPE:
120 defaultType = B_DOUBLE_TYPE;
121 double value = source.Next<uint32>();
122 doubleValue = value / source.Next<uint32>();
123 break;
126 // signed
127 case TIFF_INT8_TYPE:
128 intValue = source.Next<int8>();
129 break;
130 case TIFF_INT16_TYPE:
131 intValue = source.Next<int16>();
132 break;
133 case TIFF_INT32_TYPE:
134 intValue = source.Next<int32>();
135 break;
136 case TIFF_FRACTION_TYPE:
138 defaultType = B_DOUBLE_TYPE;
139 double value = source.Next<int32>();
140 doubleValue = value / source.Next<int32>();
141 break;
144 // floating point
145 case TIFF_FLOAT_TYPE:
146 defaultType = B_FLOAT_TYPE;
147 doubleValue = source.Next<float>();
148 break;
149 case TIFF_DOUBLE_TYPE:
150 defaultType = B_DOUBLE_TYPE;
151 doubleValue = source.Next<double>();
152 break;
154 default:
155 return B_BAD_VALUE;
158 if (defaultType == B_INT32_TYPE)
159 doubleValue = intValue;
160 else
161 intValue = int32(doubleValue + 0.5);
163 if (type == B_ANY_TYPE)
164 type = defaultType;
166 switch (type) {
167 case B_INT32_TYPE:
168 return target.AddInt32(name, intValue);
169 case B_FLOAT_TYPE:
170 return target.AddFloat(name, doubleValue);
171 case B_DOUBLE_TYPE:
172 return target.AddDouble(name, doubleValue);
174 default:
175 return B_BAD_VALUE;
180 static const convert_tag*
181 find_convert_tag(uint16 id, const convert_tag* tags, size_t count)
183 for (size_t i = 0; i < count; i++) {
184 if (tags[i].tag == id)
185 return &tags[i];
188 return NULL;
193 Reads a TIFF tag and positions the file stream to its data section
195 void
196 parse_tiff_tag(TReadHelper& read, tiff_tag& tag, off_t& offset)
198 read(tag.tag);
199 read(tag.type);
200 read(tag.length);
202 offset = read.Position() + 4;
204 uint32 length = tag.length;
206 switch (tag.type) {
207 case TIFF_UINT16_TYPE:
208 case TIFF_INT16_TYPE:
209 length *= 2;
210 break;
212 case TIFF_UINT32_TYPE:
213 case TIFF_INT32_TYPE:
214 case TIFF_FLOAT_TYPE:
215 length *= 4;
216 break;
218 case TIFF_UFRACTION_TYPE:
219 case TIFF_FRACTION_TYPE:
220 case TIFF_DOUBLE_TYPE:
221 length *= 8;
222 break;
224 default:
225 break;
228 if (length > 4) {
229 uint32 position;
230 read(position);
232 read.Seek(position, SEEK_SET);
237 static status_t
238 parse_tiff_directory(TReadHelper& read, set<off_t>& visited, off_t offset,
239 BMessage& target, const convert_tag* convertTags, size_t convertTagCount)
241 if (visited.find(offset) != visited.end()) {
242 // The EXIF data is obviously corrupt
243 return B_BAD_DATA;
246 read.Seek(offset, SEEK_SET);
247 visited.insert(offset);
249 uint16 tags;
250 read(tags);
251 if (tags > 512)
252 return B_BAD_DATA;
254 while (tags--) {
255 off_t nextOffset;
256 tiff_tag tag;
257 parse_tiff_tag(read, tag, nextOffset);
259 //printf("TAG %u\n", tag.tag);
261 switch (tag.tag) {
262 case TAG_EXIF_OFFSET:
263 case TAG_SUB_DIR_OFFSET:
265 status_t status = parse_tiff_directory(read, visited, target,
266 convertTags, convertTagCount);
267 if (status < B_OK)
268 return status;
269 break;
272 default:
273 const convert_tag* convertTag = find_convert_tag(tag.tag,
274 convertTags, convertTagCount);
275 if (convertTag != NULL) {
276 add_to_message(read, target, tag, convertTag->name,
277 convertTag->type);
279 break;
282 if (visited.find(nextOffset) != visited.end())
283 return B_BAD_DATA;
285 read.Seek(nextOffset, SEEK_SET);
286 visited.insert(nextOffset);
289 return B_OK;
293 static status_t
294 parse_tiff_directory(TReadHelper& read, set<off_t>& visited, BMessage& target,
295 const convert_tag* tags, size_t tagCount)
297 while (true) {
298 int32 offset;
299 read(offset);
300 if (offset == 0)
301 break;
303 status_t status = parse_tiff_directory(read, visited, offset, target,
304 tags, tagCount);
305 if (status < B_OK)
306 return status;
309 return B_OK;
313 // #pragma mark -
316 /*! Converts the EXIF data that starts in \a source to a BMessage in \a target.
317 If the EXIF data is corrupt, this function will return an appropriate error
318 code. Nevertheless, there might be some data ending up in \a target that
319 was parsed until this point.
321 status_t
322 convert_exif_to_message(BPositionIO& source, BMessage& target,
323 const convert_tag* tags, size_t tagCount)
325 TReadHelper read(source);
327 uint16 endian;
328 read(endian);
329 if (endian != 'MM' && endian != 'II')
330 return B_BAD_TYPE;
332 #if B_HOST_IS_LENDIAN
333 read.SetSwap(endian == 'MM');
334 #else
335 read.SetSwap(endian == 'II');
336 #endif
338 int16 magic;
339 read(magic);
340 if (magic != 42)
341 return B_BAD_TYPE;
343 set<off_t> visitedOffsets;
344 return parse_tiff_directory(read, visitedOffsets, target, tags, tagCount);
348 status_t
349 convert_exif_to_message(BPositionIO& source, BMessage& target)
351 return convert_exif_to_message(source, target, kDefaultTags,
352 kNumDefaultTags);