1 /* jpeg-data.c
2  *
3  * Copyright (c) 2001 Lutz Mueller <lutz@users.sourceforge.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA  02110-1301  USA.
19  */
20 
21 #include "config.h"
22 #include "jpeg-data.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 
29 /* This refers to the exif-i18n.h file from the "exif" package and is
30  * NOT to be confused with the libexif/i18n.h file.
31  */
32 #include "exif/exif-i18n.h"
33 
34 /* realloc that cleans up on memory failure and returns to caller */
35 #define CLEANUP_REALLOC(p,s) { \
36 	unsigned char *cleanup_ptr = realloc((p),(s)); \
37 	if (!cleanup_ptr) { free(p); (p) = NULL; return; } \
38 	(p) = cleanup_ptr; \
39 }
40 
41 struct _JPEGDataPrivate
42 {
43 	unsigned int ref_count;
44 
45 	ExifLog *log;
46 };
47 
48 JPEGData *
jpeg_data_new(void)49 jpeg_data_new (void)
50 {
51 	JPEGData *data;
52 
53 	data = malloc (sizeof (JPEGData));
54 	if (!data)
55 		return (NULL);
56 	memset (data, 0, sizeof (JPEGData));
57 	data->priv = malloc (sizeof (JPEGDataPrivate));
58 	if (!data->priv) {
59 		free (data);
60 		return (NULL);
61 	}
62 	memset (data->priv, 0, sizeof (JPEGDataPrivate));
63 	data->priv->ref_count = 1;
64 
65 	return (data);
66 }
67 
68 void
jpeg_data_append_section(JPEGData * data)69 jpeg_data_append_section (JPEGData *data)
70 {
71 	JPEGSection *s;
72 
73 	if (!data) return;
74 
75 	if (!data->count)
76 		s = malloc (sizeof (JPEGSection));
77 	else
78 		s = realloc (data->sections,
79 			     sizeof (JPEGSection) * (data->count + 1));
80 	if (!s) {
81 		EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data",
82 				sizeof (JPEGSection) * (data->count + 1));
83 		return;
84 	}
85 	memset(s + data->count, 0, sizeof (JPEGSection));
86 	data->sections = s;
87 	data->count++;
88 }
89 
90 /*! jpeg_data_save_file returns 1 on success, 0 on failure */
91 int
jpeg_data_save_file(JPEGData * data,const char * path)92 jpeg_data_save_file (JPEGData *data, const char *path)
93 {
94 	FILE *f;
95 	unsigned char *d = NULL;
96 	unsigned int size = 0, written;
97 
98 	jpeg_data_save_data (data, &d, &size);
99 	if (!d)
100 		return 0;
101 
102 	f = fopen (path, "wb");
103 	if (!f) {
104 		free (d);
105 		return 0;
106 	}
107 	written = fwrite (d, 1, size, f);
108 	free (d);
109 	if (fclose (f) == EOF)
110 		return 0;
111 	if (written == size)  {
112 		return 1;
113 	}
114 	return 0;
115 }
116 
117 void
jpeg_data_save_data(JPEGData * data,unsigned char ** d,unsigned int * ds)118 jpeg_data_save_data (JPEGData *data, unsigned char **d, unsigned int *ds)
119 {
120 	unsigned int i, eds = 0;
121 	JPEGSection s;
122 	unsigned char *ed = NULL;
123 
124 	if (!data)
125 		return;
126 	if (!d)
127 		return;
128 	if (!ds)
129 		return;
130 
131 	for (*ds = i = 0; i < data->count; i++) {
132 		s = data->sections[i];
133 
134 		/* Write the marker */
135 		CLEANUP_REALLOC (*d, sizeof (char) * (*ds + 2));
136 		(*d)[*ds + 0] = 0xff;
137 		(*d)[*ds + 1] = s.marker;
138 		*ds += 2;
139 
140 		switch (s.marker) {
141 		case JPEG_MARKER_SOI:
142 		case JPEG_MARKER_EOI:
143 			break;
144 		case JPEG_MARKER_APP1:
145 			ed = NULL;
146 			exif_data_save_data (s.content.app1, &ed, &eds);
147 			if (!ed) break;
148 			CLEANUP_REALLOC (*d, sizeof (char) * (*ds + 2));
149 			(*d)[*ds + 0] = (eds + 2) >> 8;
150 			(*d)[*ds + 1] = (eds + 2) >> 0;
151 			*ds += 2;
152 			CLEANUP_REALLOC (*d, sizeof (char) * (*ds + eds));
153 			memcpy (*d + *ds, ed, eds);
154 			*ds += eds;
155 			free (ed);
156 			break;
157 		default:
158 			CLEANUP_REALLOC (*d, sizeof (char) *
159 					(*ds + s.content.generic.size + 2));
160 			(*d)[*ds + 0] = (s.content.generic.size + 2) >> 8;
161 			(*d)[*ds + 1] = (s.content.generic.size + 2) >> 0;
162 			*ds += 2;
163 			memcpy (*d + *ds, s.content.generic.data,
164 				s.content.generic.size);
165 			*ds += s.content.generic.size;
166 
167 			/* In case of SOS, we need to write the data. */
168 			if (s.marker == JPEG_MARKER_SOS) {
169 				CLEANUP_REALLOC (*d, *ds + data->size);
170 				memcpy (*d + *ds, data->data, data->size);
171 				*ds += data->size;
172 			}
173 			break;
174 		}
175 	}
176 }
177 
178 JPEGData *
jpeg_data_new_from_data(const unsigned char * d,unsigned int size)179 jpeg_data_new_from_data (const unsigned char *d,
180 			 unsigned int size)
181 {
182 	JPEGData *data;
183 
184 	data = jpeg_data_new ();
185 	jpeg_data_load_data (data, d, size);
186 	return (data);
187 }
188 
189 void
jpeg_data_load_data(JPEGData * data,const unsigned char * d,unsigned int size)190 jpeg_data_load_data (JPEGData *data, const unsigned char *d,
191 		     unsigned int size)
192 {
193 	unsigned int i, o, len;
194 	JPEGSection *s;
195 	JPEGMarker marker;
196 
197 	if (!data) return;
198 	if (!d) return;
199 	if (!size) return;
200 
201 	for (o = 0; o < size;) {
202 
203 		/*
204 		 * JPEG sections start with 0xff. The first byte that is
205 		 * not 0xff is a marker (hopefully).
206 		 */
207 		for (i = 0; i < MIN(7, size - o); i++)
208 			if (d[o + i] != 0xff)
209 				break;
210 		if (o+i == 0) /* ignore if its the first byte, otherwise 0xff is at -1 */
211 			continue;
212 		if ((i >= size - o) || !JPEG_IS_MARKER (d[o + i])) {
213 			exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
214 					_("Data does not follow JPEG specification."));
215 			return;
216 		}
217 		marker = d[o + i];
218 
219 		/* Append this section */
220 		jpeg_data_append_section (data);
221 		if (!data->count) return;
222 		s = &data->sections[data->count - 1];
223 		s->marker = marker;
224 		o += i + 1;
225 
226 		switch (s->marker) {
227 		case JPEG_MARKER_SOI:
228 		case JPEG_MARKER_EOI:
229 			break;
230 		default:
231 
232 			/* Read the length of the section */
233 			if (2 > size - o) { o = size; break; }
234 			len = ((d[o] << 8) | d[o + 1]) - 2;
235 			if (len > size) { o = size; break; }
236 			o += 2;
237 			if (len > size - o) { o = size; break; }
238 
239 			switch (s->marker) {
240 			case JPEG_MARKER_APP1:
241 				s->content.app1 = exif_data_new_from_data (
242 							d + o - 4, len + 4);
243 				break;
244 			default:
245 				s->content.generic.data =
246 						malloc (sizeof (char) * len);
247 				if (!s->content.generic.data) {
248 					EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", sizeof (char) * len);
249 					return;
250 				}
251 				s->content.generic.size = len;
252 				memcpy (s->content.generic.data, &d[o], len);
253 
254 				/* In case of SOS, image data will follow. */
255 				if (s->marker == JPEG_MARKER_SOS) {
256 					data->size = size - o - len;
257 					if (data->size >= 2) {
258 						/* -2 means 'take all but the last 2 bytes which are
259 						   hoped to be JPEG_MARKER_EOI */
260 						data->size -= 2;
261 						if (d[o + len + data->size] != 0xFF) {
262 							/* A truncated file (i.e. w/o JPEG_MARKER_EOI at the end).
263 							   Instead of trying to use the last two bytes as marker,
264 							   touching memory beyond allocated memory and posssibly saving
265 							   back screwed file, we rather take the rest of the file. */
266 							data->size += 2;
267 						}
268 					}
269 					data->data = malloc (
270 						sizeof (char) * data->size);
271 					if (!data->data) {
272 						EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", sizeof (char) * data->size);
273 						data->size = 0;
274 						return;
275 					}
276 					memcpy (data->data, d + o + len,
277 						data->size);
278 					o += data->size;
279 				}
280 				break;
281 			}
282 			o += len;
283 			break;
284 		}
285 	}
286 }
287 
288 JPEGData *
jpeg_data_new_from_file(const char * path)289 jpeg_data_new_from_file (const char *path)
290 {
291 	JPEGData *data;
292 
293 	data = jpeg_data_new ();
294 	jpeg_data_load_file (data, path);
295 	return (data);
296 }
297 
298 void
jpeg_data_load_file(JPEGData * data,const char * path)299 jpeg_data_load_file (JPEGData *data, const char *path)
300 {
301 	FILE *f;
302 	unsigned char *d;
303 	long size;
304 
305 	if (!data) return;
306 	if (!path) return;
307 
308 	f = fopen (path, "rb");
309 	if (!f) {
310 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
311 				_("Could not open '%s' (%s)!"), path, strerror (errno));
312 		return;
313 	}
314 
315 	/* For now, we read the data into memory. Patches welcome... */
316 	if (fseek (f, 0, SEEK_END) < 0) {
317 		fclose (f);
318 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
319 				_("Could not determine size of '%s' (%s)."), path, strerror (errno));
320 		return;
321 	}
322 	size = ftell (f);
323 	if (size < 0) {
324 		fclose (f);
325 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
326 				_("Could not determine size of '%s' (%s)."), path, strerror (errno));
327 		return;
328 	}
329 	if (fseek (f, 0, SEEK_SET) < 0) {
330 		fclose (f);
331 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
332 				_("Could not determine size of '%s' (%s)."), path, strerror (errno));
333 		return;
334 	}
335 
336 	d = malloc (size);
337 	if (!d) {
338 		fclose (f);
339 		EXIF_LOG_NO_MEMORY (data->priv->log, "jpeg-data", size);
340 		return;
341 	}
342 	if (fread (d, 1, size, f) != (size_t) size) {
343 		free (d);
344 		fclose (f);
345 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
346 				_("Could not read '%s' (%s)."), path, strerror (errno));
347 		return;
348 	}
349 	if (fclose (f) == EOF)
350 		exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
351 				_("Could not read '%s' (%s)."), path, strerror (errno));
352 
353 	jpeg_data_load_data (data, d, size);
354 	free (d);
355 }
356 
357 void
jpeg_data_ref(JPEGData * data)358 jpeg_data_ref (JPEGData *data)
359 {
360 	if (!data)
361 		return;
362 
363 	data->priv->ref_count++;
364 }
365 
366 void
jpeg_data_unref(JPEGData * data)367 jpeg_data_unref (JPEGData *data)
368 {
369 	if (!data)
370 		return;
371 
372 	if (data->priv) {
373 		data->priv->ref_count--;
374 		if (!data->priv->ref_count)
375 			jpeg_data_free (data);
376 	}
377 }
378 
379 void
jpeg_data_free(JPEGData * data)380 jpeg_data_free (JPEGData *data)
381 {
382 	unsigned int i;
383 	JPEGSection s;
384 
385 	if (!data)
386 		return;
387 
388 	if (data->count) {
389 		for (i = 0; i < data->count; i++) {
390 			s = data->sections[i];
391 			switch (s.marker) {
392 			case JPEG_MARKER_SOI:
393 			case JPEG_MARKER_EOI:
394 				break;
395 			case JPEG_MARKER_APP1:
396 				exif_data_unref (s.content.app1);
397 				break;
398 			default:
399 				free (s.content.generic.data);
400 				break;
401 			}
402 		}
403 		free (data->sections);
404 	}
405 
406 	if (data->data)
407 		free (data->data);
408 
409 	if (data->priv) {
410 		if (data->priv->log) {
411 			exif_log_unref (data->priv->log);
412 			data->priv->log = NULL;
413 		}
414 		free (data->priv);
415 	}
416 
417 	free (data);
418 }
419 
420 void
jpeg_data_dump(JPEGData * data)421 jpeg_data_dump (JPEGData *data)
422 {
423 	unsigned int i;
424 	JPEGContent content;
425 	JPEGMarker marker;
426 
427 	if (!data)
428 		return;
429 
430 	printf ("Dumping JPEG data (%i bytes of data)...\n", data->size);
431 	for (i = 0; i < data->count; i++) {
432 		marker = data->sections[i].marker;
433 		content = data->sections[i].content;
434 		printf ("Section %i (marker 0x%x - %s):\n", i, marker,
435 			jpeg_marker_get_name (marker));
436 		printf ("  Description: %s\n",
437 			jpeg_marker_get_description (marker));
438 		switch (marker) {
439                 case JPEG_MARKER_SOI:
440                 case JPEG_MARKER_EOI:
441 			break;
442                 case JPEG_MARKER_APP1:
443 			exif_data_dump (content.app1);
444 			break;
445                 default:
446 			printf ("  Size: %i\n", content.generic.size);
447                         printf ("  Unknown content.\n");
448                         break;
449                 }
450         }
451 }
452 
453 static JPEGSection *
jpeg_data_get_section(JPEGData * data,JPEGMarker marker)454 jpeg_data_get_section (JPEGData *data, JPEGMarker marker)
455 {
456 	unsigned int i;
457 
458 	if (!data)
459 		return (NULL);
460 
461 	for (i = 0; i < data->count; i++)
462 		if (data->sections[i].marker == marker)
463 			return (&data->sections[i]);
464 	return (NULL);
465 }
466 
467 ExifData *
jpeg_data_get_exif_data(JPEGData * data)468 jpeg_data_get_exif_data (JPEGData *data)
469 {
470 	JPEGSection *section;
471 
472 	if (!data)
473 		return NULL;
474 
475 	section = jpeg_data_get_section (data, JPEG_MARKER_APP1);
476 	if (section) {
477 		exif_data_ref (section->content.app1);
478 		return (section->content.app1);
479 	}
480 
481 	return (NULL);
482 }
483 
484 void
jpeg_data_set_exif_data(JPEGData * data,ExifData * exif_data)485 jpeg_data_set_exif_data (JPEGData *data, ExifData *exif_data)
486 {
487 	JPEGSection *section;
488 
489 	if (!data) return;
490 
491 	section = jpeg_data_get_section (data, JPEG_MARKER_APP1);
492 	if (!section) {
493 		jpeg_data_append_section (data);
494 		if (data->count < 2) return;
495 		memmove (&data->sections[2], &data->sections[1],
496 			 sizeof (JPEGSection) * (data->count - 2));
497 		section = &data->sections[1];
498 	} else {
499 		exif_data_unref (section->content.app1);
500 	}
501 	section->marker = JPEG_MARKER_APP1;
502 	section->content.app1 = exif_data;
503 	exif_data_ref (exif_data);
504 }
505 
506 void
jpeg_data_log(JPEGData * data,ExifLog * log)507 jpeg_data_log (JPEGData *data, ExifLog *log)
508 {
509 	if (!data || !data->priv) return;
510 	if (data->priv->log) exif_log_unref (data->priv->log);
511 	data->priv->log = log;
512 	exif_log_ref (log);
513 }
514