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