• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* exif-content.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  * SPDX-License-Identifier: LGPL-2.0-or-later
21  */
22 
23 #include <config.h>
24 
25 #include <libexif/exif-content.h>
26 #include <libexif/exif-system.h>
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 /* unused constant
33  * static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
34  */
35 
36 const static unsigned char FOCUS_MODE_AUTO[] = {'A', 'u', 't', 'o'};
37 const static unsigned char FOCUS_MODE_AF_MF[] = {'A', 'F', '_', 'M', 'F'};
38 const static unsigned char FOCUS_MODE_AF_C[] = {'A', 'F', '_', 'C'};
39 const static unsigned char FOCUS_MODE_AF_S[] = {'A', 'F', '_', 'S'};
40 
41 struct _ExifContentPrivate
42 {
43 	unsigned int ref_count;
44 
45 	ExifMem *mem;
46 	ExifLog *log;
47 };
48 
49 ExifContent *
exif_content_new(void)50 exif_content_new (void)
51 {
52 	ExifMem *mem = exif_mem_new_default ();
53 	ExifContent *content = exif_content_new_mem (mem);
54 
55 	exif_mem_unref (mem);
56 
57 	return content;
58 }
59 
60 ExifContent *
exif_content_new_mem(ExifMem * mem)61 exif_content_new_mem (ExifMem *mem)
62 {
63 	ExifContent *content;
64 
65 	if (!mem) return NULL;
66 
67 	content = exif_mem_alloc (mem, (ExifLong) sizeof (ExifContent));
68 	if (!content)
69 		return NULL;
70 	content->priv = exif_mem_alloc (mem,
71 				(ExifLong) sizeof (ExifContentPrivate));
72 	if (!content->priv) {
73 		exif_mem_free (mem, content);
74 		return NULL;
75 	}
76 
77 	content->priv->ref_count = 1;
78 
79 	content->priv->mem = mem;
80 	exif_mem_ref (mem);
81 
82 	return content;
83 }
84 
85 void
exif_content_ref(ExifContent * content)86 exif_content_ref (ExifContent *content)
87 {
88 	if (!content)
89 		return;
90 
91 	content->priv->ref_count++;
92 }
93 
94 void
exif_content_unref(ExifContent * content)95 exif_content_unref (ExifContent *content)
96 {
97 	if (!content || !content->priv)
98 		return;
99 
100 	content->priv->ref_count--;
101 	if (!content->priv->ref_count)
102 		exif_content_free (content);
103 }
104 
105 void
exif_content_free(ExifContent * content)106 exif_content_free (ExifContent *content)
107 {
108 	ExifMem *mem = (content && content->priv) ? content->priv->mem : NULL;
109 	unsigned int i;
110 
111 	if (!content) return;
112 
113 	for (i = 0; i < content->count; i++)
114 		exif_entry_unref (content->entries[i]);
115 	exif_mem_free (mem, content->entries);
116 
117 	if (content->priv) {
118 		exif_log_unref (content->priv->log);
119 	}
120 
121 	exif_mem_free (mem, content->priv);
122 	exif_mem_free (mem, content);
123 	exif_mem_unref (mem);
124 }
125 
126 void
exif_content_dump(ExifContent * content,unsigned int indent)127 exif_content_dump (ExifContent *content, unsigned int indent)
128 {
129 	char buf[1024];
130 	unsigned int i, l;
131 
132 	if (!content)
133 		return;
134 
135 	l = MIN(sizeof(buf)-1, 2*indent);
136 	memset(buf, ' ', l);
137 	buf[l] = '\0';
138 
139 	printf ("%sDumping exif content (%u entries)...\n", buf,
140 		content->count);
141 	for (i = 0; i < content->count; i++)
142 		exif_entry_dump (content->entries[i], indent + 1);
143 }
144 
145 void
exif_content_add_entry(ExifContent * c,ExifEntry * entry)146 exif_content_add_entry (ExifContent *c, ExifEntry *entry)
147 {
148 	ExifEntry **entries;
149 	if (!c || !c->priv || !entry || entry->parent) return;
150 
151 	/* One tag can only be added once to an IFD. */
152 	if (exif_content_get_entry (c, entry->tag) && entry->tag != EXIF_TAG_MAKER_NOTE) {
153 		exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "ExifContent",
154 			"An attempt has been made to add "
155 			"the tag '%s' twice to an IFD. This is against "
156 			"specification.", exif_tag_get_name (entry->tag));
157 		return;
158 	}
159 
160 	/* Add the entry */
161 	if (c->entries) {
162 		entries = exif_mem_realloc (c->priv->mem,
163 			c->entries, sizeof (ExifEntry*) * (c->count + 1));
164 	} else {
165 		entries = exif_mem_alloc (c->priv->mem,
166 			sizeof (ExifEntry*) * (c->count + 1));
167 	}
168 
169 	if (!entries) return;
170 	entry->parent = c;
171 	entries[c->count++] = entry;
172 	c->entries = entries;
173 	exif_entry_ref (entry);
174 }
175 
176 void
exif_content_remove_entry(ExifContent * c,ExifEntry * e)177 exif_content_remove_entry (ExifContent *c, ExifEntry *e)
178 {
179 	unsigned int i;
180 	ExifEntry **t, *temp;
181 
182 	if (!c || !c->priv || !e || (e->parent != c)) return;
183 
184 	/* Search the entry */
185 	for (i = 0; i < c->count; i++)
186 			if (c->entries[i] == e)
187 					break;
188 
189 	if (i == c->count)
190 			return;
191 
192 	/* Remove the entry */
193 	temp = c->entries[c->count-1];
194 	if (c->count > 1) {
195 		t = exif_mem_realloc (c->priv->mem, c->entries,
196 					sizeof(ExifEntry*) * (c->count - 1));
197 		if (!t) {
198 			return;
199 		}
200 		c->entries = t;
201 		c->count--;
202 		if (i != c->count) { /* we deallocated the last slot already */
203 			memmove (&t[i], &t[i + 1], sizeof (ExifEntry*) * (c->count - i - 1));
204 			t[c->count-1] = temp;
205 		}
206 	} else {
207 		exif_mem_free (c->priv->mem, c->entries);
208 		c->entries = NULL;
209 		c->count = 0;
210 	}
211 	e->parent = NULL;
212 	exif_entry_unref (e);
213 }
214 
215 ExifEntry *
exif_content_get_entry(ExifContent * content,ExifTag tag)216 exif_content_get_entry (ExifContent *content, ExifTag tag)
217 {
218 	unsigned int i;
219 
220 	if (!content)
221 		return (NULL);
222 
223 	for (i = 0; i < content->count; i++)
224 		if (content->entries[i]->tag == tag)
225 			return (content->entries[i]);
226 	return (NULL);
227 }
228 
229 ExifEntry *
exif_content_get_entry_ext(ExifContent * content,ExifTag tag)230 exif_content_get_entry_ext (ExifContent *content, ExifTag tag)
231 {
232     if (!content) {
233         return (NULL);
234     }
235     for (unsigned int i = 0; i < content->count; i++) {
236         if (content->entries[i]->tag == tag) {
237             unsigned char* data = content->entries[i]->data;
238             if (tag == EXIF_TAG_MAKER_NOTE && data &&
239                 (!memcmp(data, FOCUS_MODE_AUTO, sizeof(FOCUS_MODE_AUTO) / sizeof(FOCUS_MODE_AUTO[0])) ||
240                 !memcmp(data, FOCUS_MODE_AF_C, sizeof(FOCUS_MODE_AF_C) / sizeof(FOCUS_MODE_AF_C[0])) ||
241                 !memcmp(data, FOCUS_MODE_AF_MF, sizeof(FOCUS_MODE_AF_MF) / sizeof(FOCUS_MODE_AF_MF[0])) ||
242                 !memcmp(data, FOCUS_MODE_AF_S, sizeof(FOCUS_MODE_AF_S) / sizeof(FOCUS_MODE_AF_S[0])))) {
243                 return (content->entries[i]);
244             }
245             continue;
246         }
247     }
248     return (NULL);
249 }
250 
251 ExifEntry *
exif_content_get_huawei_makenote_entry(ExifContent * content)252 exif_content_get_huawei_makenote_entry (ExifContent *content)
253 {
254 	if (!content)
255 		return (NULL);
256 
257 	ExifEntry *entry = NULL;
258 	for (unsigned int i = 0; i < content->count; i++) {
259 		entry = content->entries[i];
260 		if (entry->tag == EXIF_TAG_MAKER_NOTE) {
261 			if (entry->data && (entry->size >= 8) &&
262 				(!memcmp(entry->data, "HUAWEI\0\0", 8))) {
263 				return entry;
264 			}
265 		}
266 	}
267 
268 	return (NULL);
269 }
270 
271 void
exif_content_foreach_entry(ExifContent * content,ExifContentForeachEntryFunc func,void * data)272 exif_content_foreach_entry (ExifContent *content,
273 			    ExifContentForeachEntryFunc func, void *data)
274 {
275 	unsigned int i;
276 
277 	if (!content || !func)
278 		return;
279 
280 	for (i = 0; i < content->count; i++)
281 		func (content->entries[i], data);
282 }
283 
284 void
exif_content_log(ExifContent * content,ExifLog * log)285 exif_content_log (ExifContent *content, ExifLog *log)
286 {
287 	if (!content || !content->priv || !log || content->priv->log == log)
288 		return;
289 
290 	if (content->priv->log) exif_log_unref (content->priv->log);
291 	content->priv->log = log;
292 	exif_log_ref (log);
293 }
294 
295 ExifIfd
exif_content_get_ifd(ExifContent * c)296 exif_content_get_ifd (ExifContent *c)
297 {
298 	if (!c || !c->parent) return EXIF_IFD_COUNT;
299 
300 	return
301 		((c)->parent->ifd[EXIF_IFD_EXIF] == (c)) ? EXIF_IFD_EXIF :
302 		((c)->parent->ifd[EXIF_IFD_0] == (c)) ? EXIF_IFD_0 :
303 		((c)->parent->ifd[EXIF_IFD_1] == (c)) ? EXIF_IFD_1 :
304 		((c)->parent->ifd[EXIF_IFD_GPS] == (c)) ? EXIF_IFD_GPS :
305 		((c)->parent->ifd[EXIF_IFD_INTEROPERABILITY] == (c)) ? EXIF_IFD_INTEROPERABILITY :
306 		EXIF_IFD_COUNT;
307 }
308 
309 static void
fix_func(ExifEntry * e,void * UNUSED (data))310 fix_func (ExifEntry *e, void *UNUSED(data))
311 {
312 	exif_entry_fix (e);
313 }
314 
315 /*!
316  * Check if this entry is unknown and if so, delete it.
317  * \note Be careful calling this function in a loop. Deleting an entry from
318  * an ExifContent changes the index of subsequent entries, as well as the
319  * total size of the entries array.
320  */
321 static void
remove_not_recorded(ExifEntry * e,void * UNUSED (data))322 remove_not_recorded (ExifEntry *e, void *UNUSED(data))
323 {
324 	ExifIfd ifd = exif_entry_get_ifd(e) ;
325 	ExifContent *c = e->parent;
326 	ExifDataType dt = exif_data_get_data_type (c->parent);
327 	ExifTag t = e->tag;
328 
329 	if (exif_tag_get_support_level_in_ifd (t, ifd, dt) ==
330 			 EXIF_SUPPORT_LEVEL_NOT_RECORDED) {
331 		exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content",
332 				"Tag 0x%04x is not recorded in IFD '%s' and has therefore been "
333 				"removed.", t, exif_ifd_get_name (ifd));
334 		exif_content_remove_entry (c, e);
335 	}
336 
337 }
338 
339 void
exif_content_fix(ExifContent * c)340 exif_content_fix (ExifContent *c)
341 {
342 	ExifIfd ifd = exif_content_get_ifd (c);
343 	ExifDataType dt;
344 	ExifEntry *e;
345 	unsigned int i, num;
346 
347 	if (!c)
348 		return;
349 
350 	dt = exif_data_get_data_type (c->parent);
351 
352 	/*
353 	 * First of all, fix all existing entries.
354 	 */
355 	exif_content_foreach_entry (c, fix_func, NULL);
356 
357 	/*
358 	 * Go through each tag and if it's not recorded, remove it. If one
359 	 * is removed, exif_content_foreach_entry() will skip the next entry,
360 	 * so if this happens do the loop again from the beginning to ensure
361 	 * they're all checked. This could be avoided if we stop relying on
362 	 * exif_content_foreach_entry but loop intelligently here.
363 	 */
364 	do {
365 		num = c->count;
366 		exif_content_foreach_entry (c, remove_not_recorded, NULL);
367 	} while (num != c->count);
368 
369 	/*
370 	 * Then check for non-existing mandatory tags and create them if needed
371 	 */
372 	num = exif_tag_table_count();
373 	for (i = 0; i < num; ++i) {
374 		const ExifTag t = exif_tag_table_get_tag (i);
375 		if (exif_tag_get_support_level_in_ifd (t, ifd, dt) ==
376 			EXIF_SUPPORT_LEVEL_MANDATORY) {
377 			if (exif_content_get_entry (c, t))
378 				/* This tag already exists */
379 				continue;
380 			exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content",
381 					"Tag '%s' is mandatory in IFD '%s' and has therefore been added.",
382 					exif_tag_get_name_in_ifd (t, ifd), exif_ifd_get_name (ifd));
383 			e = exif_entry_new ();
384 			exif_content_add_entry (c, e);
385 			exif_entry_initialize (e, t);
386 			exif_entry_unref (e);
387 		}
388 	}
389 }
390