exiv2: Fix use-after-free of the image filename/path.
[geeqie.git] / src / exiv2.cc
1 /*
2  * Geeqie
3  * Copyright (C) 2008 - 2012 The Geeqie Team
4  *
5  * Author: Vladimir Nadvornik
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "config.h"
13
14 #ifdef HAVE_EXIV2
15
16 #include <exiv2/image.hpp>
17 #include <exiv2/exif.hpp>
18 #include <iostream>
19
20 // EXIV2_TEST_VERSION is defined in Exiv2 0.15 and newer.
21 #ifndef EXIV2_TEST_VERSION
22 # define EXIV2_TEST_VERSION(major,minor,patch) \
23         ( EXIV2_VERSION >= EXIV2_MAKE_VERSION(major,minor,patch) )
24 #endif
25
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/mman.h>
32
33 #if !EXIV2_TEST_VERSION(0,17,90)
34 #include <exiv2/tiffparser.hpp>
35 #include <exiv2/tiffcomposite.hpp>
36 #include <exiv2/tiffvisitor.hpp>
37 #include <exiv2/tiffimage.hpp>
38 #include <exiv2/cr2image.hpp>
39 #include <exiv2/crwimage.hpp>
40 #if EXIV2_TEST_VERSION(0,16,0)
41 #include <exiv2/orfimage.hpp>
42 #endif
43 #if EXIV2_TEST_VERSION(0,13,0)
44 #include <exiv2/rafimage.hpp>
45 #endif
46 #include <exiv2/futils.hpp>
47 #else
48 #include <exiv2/preview.hpp>
49 #endif
50
51 #if EXIV2_TEST_VERSION(0,17,0)
52 #include <exiv2/convert.hpp>
53 #include <exiv2/xmpsidecar.hpp>
54 #endif
55
56 extern "C" {
57 #include <glib.h>
58
59 #include "main.h"
60 #include "exif.h"
61
62 #include "filefilter.h"
63 #include "ui_fileops.h"
64
65 #include "misc.h"
66 }
67
68 typedef struct _AltKey AltKey;
69
70 struct _AltKey
71 {
72         const gchar *xmp_key;
73         const gchar *exif_key;
74         const gchar *iptc_key;
75 };
76
77 /* this is a list of keys that should be converted, even with the older Exiv2 which does not support it directly */
78 static const AltKey alt_keys[] = {
79         {"Xmp.tiff.Orientation",                "Exif.Image.Orientation",       NULL},
80         {"Xmp.dc.title",                        NULL,                           "Iptc.Application2.ObjectName"          },
81         {"Xmp.photoshop.Urgency",               NULL,                           "Iptc.Application2.Urgency"             },
82         {"Xmp.photoshop.Category",              NULL,                           "Iptc.Application2.Category"            },
83         {"Xmp.photoshop.SupplementalCategory",  NULL,                           "Iptc.Application2.SuppCategory"        },
84         {"Xmp.dc.subject",                      NULL,                           "Iptc.Application2.Keywords"            },
85         {"Xmp.iptc.Location",                   NULL,                           "Iptc.Application2.LocationName"        },
86         {"Xmp.photoshop.Instruction",           NULL,                           "Iptc.Application2.SpecialInstructions" },
87         {"Xmp.photoshop.DateCreated",           NULL,                           "Iptc.Application2.DateCreated"         },
88         {"Xmp.dc.creator",                      NULL,                           "Iptc.Application2.Byline"              },
89         {"Xmp.photoshop.AuthorsPosition",       NULL,                           "Iptc.Application2.BylineTitle"         },
90         {"Xmp.photoshop.City",                  NULL,                           "Iptc.Application2.City"                },
91         {"Xmp.photoshop.State",                 NULL,                           "Iptc.Application2.ProvinceState"       },
92         {"Xmp.iptc.CountryCode",                NULL,                           "Iptc.Application2.CountryCode"         },
93         {"Xmp.photoshop.Country",               NULL,                           "Iptc.Application2.CountryName"         },
94         {"Xmp.photoshop.TransmissionReference", NULL,                           "Iptc.Application2.TransmissionReference"},
95         {"Xmp.photoshop.Headline",              NULL,                           "Iptc.Application2.Headline"            },
96         {"Xmp.photoshop.Credit",                NULL,                           "Iptc.Application2.Credit"              },
97         {"Xmp.photoshop.Source",                NULL,                           "Iptc.Application2.Source"              },
98         {"Xmp.dc.rights",                       NULL,                           "Iptc.Application2.Copyright"           },
99         {"Xmp.dc.description",                  NULL,                           "Iptc.Application2.Caption"             },
100         {"Xmp.photoshop.CaptionWriter",         NULL,                           "Iptc.Application2.Writer"              },
101         {NULL, NULL, NULL}
102         };
103
104 static void _debug_exception(const char* file,
105                              int line,
106                              const char* func,
107                              Exiv2::AnyError& e)
108 {
109         gchar *str = g_locale_from_utf8(e.what(), -1, NULL, NULL, NULL);
110         DEBUG_1("%s:%d:%s:Exiv2: %s", file, line, func, str);
111         g_free(str);
112 }
113
114 #define debug_exception(e) _debug_exception(__FILE__, __LINE__, __func__, e)
115
116 struct _ExifData
117 {
118         Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */
119         Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */
120 #if EXIV2_TEST_VERSION(0,16,0)
121         Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */
122 #endif
123
124         virtual ~_ExifData()
125         {
126         }
127
128         virtual void writeMetadata(gchar *path = NULL)
129         {
130                 g_critical("Unsupported method of writing metadata");
131         }
132
133         virtual ExifData *original()
134         {
135                 return NULL;
136         }
137
138         virtual Exiv2::Image *image() = 0;
139
140         virtual Exiv2::ExifData &exifData() = 0;
141
142         virtual Exiv2::IptcData &iptcData() = 0;
143
144 #if EXIV2_TEST_VERSION(0,16,0)
145         virtual Exiv2::XmpData &xmpData() = 0;
146 #endif
147
148         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0;
149
150         virtual guchar *get_jpeg_color_profile(guint *data_len) = 0;
151 };
152
153 // This allows read-only access to the original metadata
154 struct _ExifDataOriginal : public _ExifData
155 {
156 protected:
157         Exiv2::Image::AutoPtr image_;
158
159         /* the icc profile in jpeg is not technically exif - store it here */
160         unsigned char *cp_data_;
161         guint cp_length_;
162         gboolean valid_;
163         gchar *pathl_;
164
165         Exiv2::ExifData emptyExifData_;
166         Exiv2::IptcData emptyIptcData_;
167 #if EXIV2_TEST_VERSION(0,16,0)
168         Exiv2::XmpData emptyXmpData_;
169 #endif
170
171 public:
172         _ExifDataOriginal(Exiv2::Image::AutoPtr image)
173         {
174                 cp_data_ = NULL;
175                 cp_length_ = 0;
176                 image_ = image;
177                 valid_ = TRUE;
178         }
179
180         _ExifDataOriginal(gchar *path)
181         {
182                 cp_data_ = NULL;
183                 cp_length_ = 0;
184                 valid_ = TRUE;
185
186                 pathl_ = path_from_utf8(path);
187                 try
188                         {
189                         image_ = Exiv2::ImageFactory::open(pathl_);
190 //                      g_assert (image.get() != 0);
191                         image_->readMetadata();
192
193 #if EXIV2_TEST_VERSION(0,16,0)
194                         if (image_->mimeType() == "application/rdf+xml")
195                                 {
196                                 //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
197                                 image_->clearExifData();
198                                 image_->clearIptcData();
199                                 }
200 #endif
201
202 #if EXIV2_TEST_VERSION(0,14,0)
203                         if (image_->mimeType() == "image/jpeg")
204                                 {
205                                 /* try to get jpeg color profile */
206                                 Exiv2::BasicIo &io = image_->io();
207                                 gint open = io.isopen();
208                                 if (!open) io.open();
209                                 if (io.isopen())
210                                         {
211                                         unsigned char *mapped = (unsigned char*)io.mmap();
212                                         if (mapped) exif_jpeg_parse_color(this, mapped, io.size());
213                                         io.munmap();
214                                         }
215                                 if (!open) io.close();
216                                 }
217 #endif
218                         }
219                 catch (Exiv2::AnyError& e)
220                         {
221                         valid_ = FALSE;
222                         }
223         }
224
225         virtual ~_ExifDataOriginal()
226         {
227                 if (cp_data_) g_free(cp_data_);
228                 if (pathl_) g_free(pathl_);
229         }
230
231         virtual Exiv2::Image *image()
232         {
233                 if (!valid_) return NULL;
234                 return image_.get();
235         }
236
237         virtual Exiv2::ExifData &exifData ()
238         {
239                 if (!valid_) return emptyExifData_;
240                 return image_->exifData();
241         }
242
243         virtual Exiv2::IptcData &iptcData ()
244         {
245                 if (!valid_) return emptyIptcData_;
246                 return image_->iptcData();
247         }
248
249 #if EXIV2_TEST_VERSION(0,16,0)
250         virtual Exiv2::XmpData &xmpData ()
251         {
252                 if (!valid_) return emptyXmpData_;
253                 return image_->xmpData();
254         }
255 #endif
256
257         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
258         {
259                 if (cp_data_) g_free(cp_data_);
260                 cp_data_ = cp_data;
261                 cp_length_ = cp_length;
262         }
263
264         virtual guchar *get_jpeg_color_profile(guint *data_len)
265         {
266                 if (cp_data_)
267                 {
268                         if (data_len) *data_len = cp_length_;
269                         return (unsigned char *) g_memdup(cp_data_, cp_length_);
270                 }
271                 return NULL;
272         }
273 };
274
275 extern "C" {
276 static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data);
277 }
278
279 // This allows read-write access to the metadata
280 struct _ExifDataProcessed : public _ExifData
281 {
282 protected:
283         _ExifDataOriginal *imageData_;
284         _ExifDataOriginal *sidecarData_;
285
286         Exiv2::ExifData exifData_;
287         Exiv2::IptcData iptcData_;
288 #if EXIV2_TEST_VERSION(0,16,0)
289         Exiv2::XmpData xmpData_;
290 #endif
291
292 public:
293         _ExifDataProcessed(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp)
294         {
295                 imageData_ = new _ExifDataOriginal(path);
296                 sidecarData_ = NULL;
297 #if EXIV2_TEST_VERSION(0,16,0)
298                 if (sidecar_path)
299                         {
300                         sidecarData_ = new _ExifDataOriginal(sidecar_path);
301                         xmpData_ = sidecarData_->xmpData();
302                         }
303                 else
304                         {
305                         xmpData_ = imageData_->xmpData();
306                         }
307
308 #endif
309                 exifData_ = imageData_->exifData();
310                 iptcData_ = imageData_->iptcData();
311 #if EXIV2_TEST_VERSION(0,17,0)
312                 try
313                         {
314                         syncExifWithXmp(exifData_, xmpData_);
315                         }
316                 catch (...)
317                         {
318                         DEBUG_1("Exiv2: Catching bug\n");
319                         }
320 #endif
321                 if (modified_xmp)
322                         {
323                         g_hash_table_foreach(modified_xmp, _ExifDataProcessed_update_xmp, this);
324                         }
325         }
326
327         virtual ~_ExifDataProcessed()
328         {
329                 if (imageData_) delete imageData_;
330                 if (sidecarData_) delete sidecarData_;
331         }
332
333         virtual ExifData *original()
334         {
335                 return imageData_;
336         }
337
338         virtual void writeMetadata(gchar *path = NULL)
339         {
340                 if (!path)
341                         {
342 #if EXIV2_TEST_VERSION(0,17,0)
343                         if (options->metadata.save_legacy_IPTC)
344                                 copyXmpToIptc(xmpData_, iptcData_);
345                         else
346                                 iptcData_.clear();
347
348                         copyXmpToExif(xmpData_, exifData_);
349 #endif
350                         Exiv2::Image *image = imageData_->image();
351
352                         if (!image) Exiv2::Error(21);
353                         image->setExifData(exifData_);
354                         image->setIptcData(iptcData_);
355 #if EXIV2_TEST_VERSION(0,16,0)
356                         image->setXmpData(xmpData_);
357 #endif
358                         image->writeMetadata();
359                         }
360                 else
361                         {
362 #if EXIV2_TEST_VERSION(0,17,0)
363                         gchar *pathl = path_from_utf8(path);;
364
365                         Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl);
366
367                         g_free(pathl);
368
369                         sidecar->setXmpData(xmpData_);
370                         sidecar->writeMetadata();
371 #else
372                         throw Exiv2::Error(3, "xmp");
373 #endif
374                         }
375         }
376
377         virtual Exiv2::Image *image()
378         {
379                 return imageData_->image();
380         }
381
382         virtual Exiv2::ExifData &exifData ()
383         {
384                 return exifData_;
385         }
386
387         virtual Exiv2::IptcData &iptcData ()
388         {
389                 return iptcData_;
390         }
391
392 #if EXIV2_TEST_VERSION(0,16,0)
393         virtual Exiv2::XmpData &xmpData ()
394         {
395                 return xmpData_;
396         }
397 #endif
398
399         virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length)
400         {
401                 imageData_->add_jpeg_color_profile(cp_data, cp_length);
402         }
403
404         virtual guchar *get_jpeg_color_profile(guint *data_len)
405         {
406                 return imageData_->get_jpeg_color_profile(data_len);
407         }
408 };
409
410
411
412
413 extern "C" {
414
415
416 void exif_init(void)
417 {
418 #ifdef EXV_ENABLE_NLS
419         bind_textdomain_codeset (EXV_PACKAGE, "UTF-8");
420 #endif
421 }
422
423
424
425 static void _ExifDataProcessed_update_xmp(gpointer key, gpointer value, gpointer data)
426 {
427         exif_update_metadata((ExifData *)data, (gchar *)key, (GList *)value);
428 }
429
430 ExifData *exif_read(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp)
431 {
432         DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-");
433         try {
434                 return new _ExifDataProcessed(path, sidecar_path, modified_xmp);
435         }
436         catch (Exiv2::AnyError& e) {
437                 debug_exception(e);
438                 return NULL;
439         }
440
441 }
442
443 gboolean exif_write(ExifData *exif)
444 {
445         try {
446                 exif->writeMetadata();
447                 return TRUE;
448         }
449         catch (Exiv2::AnyError& e) {
450                 debug_exception(e);
451                 return FALSE;
452         }
453 }
454
455 gboolean exif_write_sidecar(ExifData *exif, gchar *path)
456 {
457         try {
458                 exif->writeMetadata(path);
459                 return TRUE;
460         }
461         catch (Exiv2::AnyError& e) {
462                 debug_exception(e);
463                 return FALSE;
464         }
465
466 }
467
468
469 void exif_free(ExifData *exif)
470 {
471         if (!exif) return;
472         g_assert(dynamic_cast<_ExifDataProcessed *>(exif)); // this should not be called on ExifDataOriginal
473         delete exif;
474 }
475
476 ExifData *exif_get_original(ExifData *exif)
477 {
478         return exif->original();
479 }
480
481
482 ExifItem *exif_get_item(ExifData *exif, const gchar *key)
483 {
484         try {
485                 Exiv2::Metadatum *item = NULL;
486                 try {
487                         Exiv2::ExifKey ekey(key);
488                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
489                         if (pos == exif->exifData().end()) return NULL;
490                         item = &*pos;
491                 }
492                 catch (Exiv2::AnyError& e) {
493                         try {
494                                 Exiv2::IptcKey ekey(key);
495                                 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
496                                 if (pos == exif->iptcData().end()) return NULL;
497                                 item = &*pos;
498                         }
499                         catch (Exiv2::AnyError& e) {
500 #if EXIV2_TEST_VERSION(0,16,0)
501                                 Exiv2::XmpKey ekey(key);
502                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
503                                 if (pos == exif->xmpData().end()) return NULL;
504                                 item = &*pos;
505 #endif
506                         }
507                 }
508                 return (ExifItem *)item;
509         }
510         catch (Exiv2::AnyError& e) {
511                 debug_exception(e);
512                 return NULL;
513         }
514 }
515
516 ExifItem *exif_add_item(ExifData *exif, const gchar *key)
517 {
518         try {
519                 Exiv2::Metadatum *item = NULL;
520                 try {
521                         Exiv2::ExifKey ekey(key);
522                         exif->exifData().add(ekey, NULL);
523                         Exiv2::ExifData::iterator pos = exif->exifData().end(); // a hack, there should be a better way to get the currently added item
524                         pos--;
525                         item = &*pos;
526                 }
527                 catch (Exiv2::AnyError& e) {
528                         try {
529                                 Exiv2::IptcKey ekey(key);
530                                 exif->iptcData().add(ekey, NULL);
531                                 Exiv2::IptcData::iterator pos = exif->iptcData().end();
532                                 pos--;
533                                 item = &*pos;
534                         }
535                         catch (Exiv2::AnyError& e) {
536 #if EXIV2_TEST_VERSION(0,16,0)
537                                 Exiv2::XmpKey ekey(key);
538                                 exif->xmpData().add(ekey, NULL);
539                                 Exiv2::XmpData::iterator pos = exif->xmpData().end();
540                                 pos--;
541                                 item = &*pos;
542 #endif
543                         }
544                 }
545                 return (ExifItem *)item;
546         }
547         catch (Exiv2::AnyError& e) {
548                 debug_exception(e);
549                 return NULL;
550         }
551 }
552
553
554 ExifItem *exif_get_first_item(ExifData *exif)
555 {
556         try {
557                 exif->exifIter = exif->exifData().begin();
558                 exif->iptcIter = exif->iptcData().begin();
559 #if EXIV2_TEST_VERSION(0,16,0)
560                 exif->xmpIter = exif->xmpData().begin();
561 #endif
562                 if (exif->exifIter != exif->exifData().end())
563                         {
564                         const Exiv2::Metadatum *item = &*exif->exifIter;
565                         exif->exifIter++;
566                         return (ExifItem *)item;
567                         }
568                 if (exif->iptcIter != exif->iptcData().end())
569                         {
570                         const Exiv2::Metadatum *item = &*exif->iptcIter;
571                         exif->iptcIter++;
572                         return (ExifItem *)item;
573                         }
574 #if EXIV2_TEST_VERSION(0,16,0)
575                 if (exif->xmpIter != exif->xmpData().end())
576                         {
577                         const Exiv2::Metadatum *item = &*exif->xmpIter;
578                         exif->xmpIter++;
579                         return (ExifItem *)item;
580                         }
581 #endif
582                 return NULL;
583
584         }
585         catch (Exiv2::AnyError& e) {
586                 debug_exception(e);
587                 return NULL;
588         }
589 }
590
591 ExifItem *exif_get_next_item(ExifData *exif)
592 {
593         try {
594                 if (exif->exifIter != exif->exifData().end())
595                         {
596                         const Exiv2::Metadatum *item = &*exif->exifIter;
597                         exif->exifIter++;
598                         return (ExifItem *)item;
599                 }
600                 if (exif->iptcIter != exif->iptcData().end())
601                         {
602                         const Exiv2::Metadatum *item = &*exif->iptcIter;
603                         exif->iptcIter++;
604                         return (ExifItem *)item;
605                 }
606 #if EXIV2_TEST_VERSION(0,16,0)
607                 if (exif->xmpIter != exif->xmpData().end())
608                         {
609                         const Exiv2::Metadatum *item = &*exif->xmpIter;
610                         exif->xmpIter++;
611                         return (ExifItem *)item;
612                 }
613 #endif
614                 return NULL;
615         }
616         catch (Exiv2::AnyError& e) {
617                 debug_exception(e);
618                 return NULL;
619         }
620 }
621
622 char *exif_item_get_tag_name(ExifItem *item)
623 {
624         try {
625                 if (!item) return NULL;
626                 return g_strdup(((Exiv2::Metadatum *)item)->key().c_str());
627         }
628         catch (Exiv2::AnyError& e) {
629                 debug_exception(e);
630                 return NULL;
631         }
632 }
633
634 guint exif_item_get_tag_id(ExifItem *item)
635 {
636         try {
637                 if (!item) return 0;
638                 return ((Exiv2::Metadatum *)item)->tag();
639         }
640         catch (Exiv2::AnyError& e) {
641                 debug_exception(e);
642                 return 0;
643         }
644 }
645
646 guint exif_item_get_elements(ExifItem *item)
647 {
648         try {
649                 if (!item) return 0;
650                 return ((Exiv2::Metadatum *)item)->count();
651         }
652         catch (Exiv2::AnyError& e) {
653                 debug_exception(e);
654                 return 0;
655         }
656 }
657
658 char *exif_item_get_data(ExifItem *item, guint *data_len)
659 {
660         try {
661                 if (!item) return 0;
662                 Exiv2::Metadatum *md = (Exiv2::Metadatum *)item;
663                 if (data_len) *data_len = md->size();
664                 char *data = (char *)g_malloc(md->size());
665                 long res = md->copy((Exiv2::byte *)data, Exiv2::littleEndian /* should not matter */);
666                 g_assert(res == md->size());
667                 return data;
668         }
669         catch (Exiv2::AnyError& e) {
670                 debug_exception(e);
671                 return NULL;
672         }
673 }
674
675 char *exif_item_get_description(ExifItem *item)
676 {
677         try {
678                 if (!item) return NULL;
679                 return utf8_validate_or_convert(((Exiv2::Metadatum *)item)->tagLabel().c_str());
680         }
681         catch (std::exception& e) {
682 //              debug_exception(e);
683                 return NULL;
684         }
685 }
686
687 /*
688 invalidTypeId, unsignedByte, asciiString, unsignedShort,
689   unsignedLong, unsignedRational, signedByte, undefined,
690   signedShort, signedLong, signedRational, string,
691   date, time, comment, directory,
692   xmpText, xmpAlt, xmpBag, xmpSeq,
693   langAlt, lastTypeId
694 */
695
696 static guint format_id_trans_tbl [] = {
697         EXIF_FORMAT_UNKNOWN,
698         EXIF_FORMAT_BYTE_UNSIGNED,
699         EXIF_FORMAT_STRING,
700         EXIF_FORMAT_SHORT_UNSIGNED,
701         EXIF_FORMAT_LONG_UNSIGNED,
702         EXIF_FORMAT_RATIONAL_UNSIGNED,
703         EXIF_FORMAT_BYTE,
704         EXIF_FORMAT_UNDEFINED,
705         EXIF_FORMAT_SHORT,
706         EXIF_FORMAT_LONG,
707         EXIF_FORMAT_RATIONAL,
708         EXIF_FORMAT_STRING,
709         EXIF_FORMAT_STRING,
710         EXIF_FORMAT_STRING,
711         EXIF_FORMAT_UNDEFINED,
712         EXIF_FORMAT_STRING,
713         EXIF_FORMAT_STRING,
714         EXIF_FORMAT_STRING,
715         EXIF_FORMAT_STRING
716         };
717
718
719
720 guint exif_item_get_format_id(ExifItem *item)
721 {
722         try {
723                 if (!item) return EXIF_FORMAT_UNKNOWN;
724                 guint id = ((Exiv2::Metadatum *)item)->typeId();
725                 if (id >= (sizeof(format_id_trans_tbl) / sizeof(format_id_trans_tbl[0])) ) return EXIF_FORMAT_UNKNOWN;
726                 return format_id_trans_tbl[id];
727         }
728         catch (Exiv2::AnyError& e) {
729                 debug_exception(e);
730                 return EXIF_FORMAT_UNKNOWN;
731         }
732 }
733
734 const char *exif_item_get_format_name(ExifItem *item, gboolean brief)
735 {
736         try {
737                 if (!item) return NULL;
738                 return ((Exiv2::Metadatum *)item)->typeName();
739         }
740         catch (Exiv2::AnyError& e) {
741                 debug_exception(e);
742                 return NULL;
743         }
744 }
745
746
747 gchar *exif_item_get_data_as_text(ExifItem *item)
748 {
749         try {
750                 if (!item) return NULL;
751                 Exiv2::Metadatum *metadatum = (Exiv2::Metadatum *)item;
752 #if EXIV2_TEST_VERSION(0,17,0)
753                 return utf8_validate_or_convert(metadatum->print().c_str());
754 #else
755                 std::stringstream str;
756                 Exiv2::Exifdatum *exifdatum;
757                 Exiv2::Iptcdatum *iptcdatum;
758 #if EXIV2_TEST_VERSION(0,16,0)
759                 Exiv2::Xmpdatum *xmpdatum;
760 #endif
761                 if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(metadatum)))
762                         str << *exifdatum;
763                 else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(metadatum)))
764                         str << *iptcdatum;
765 #if EXIV2_TEST_VERSION(0,16,0)
766                 else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(metadatum)))
767                         str << *xmpdatum;
768 #endif
769
770                 return utf8_validate_or_convert(str.str().c_str());
771 #endif
772         }
773         catch (Exiv2::AnyError& e) {
774                 return NULL;
775         }
776 }
777
778 gchar *exif_item_get_string(ExifItem *item, int idx)
779 {
780         try {
781                 if (!item) return NULL;
782                 Exiv2::Metadatum *em = (Exiv2::Metadatum *)item;
783 #if EXIV2_TEST_VERSION(0,16,0)
784                 std::string str = em->toString(idx);
785 #else
786                 std::string str = em->toString(); // FIXME
787 #endif
788                 if (idx == 0 && str == "") str = em->toString();
789                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
790                         {
791                         std::string::size_type pos = str.find_first_of(' ');
792                         if (pos != std::string::npos) str = str.substr(pos+1);
793                         }
794
795                 return utf8_validate_or_convert(str.c_str());
796         }
797         catch (Exiv2::AnyError& e) {
798                 return NULL;
799         }
800 }
801
802
803 gint exif_item_get_integer(ExifItem *item, gint *value)
804 {
805         try {
806                 if (!item || exif_item_get_elements(item) == 0) return 0;
807                 *value = ((Exiv2::Metadatum *)item)->toLong();
808                 return 1;
809         }
810         catch (Exiv2::AnyError& e) {
811                 debug_exception(e);
812                 return 0;
813         }
814 }
815
816 ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n)
817 {
818         try {
819                 if (!item) return NULL;
820                 if (n >= exif_item_get_elements(item)) return NULL;
821                 Exiv2::Rational v = ((Exiv2::Metadatum *)item)->toRational(n);
822                 static ExifRational ret;
823                 ret.num = v.first;
824                 ret.den = v.second;
825                 if (sign) *sign = (((Exiv2::Metadatum *)item)->typeId() == Exiv2::signedRational);
826                 return &ret;
827         }
828         catch (Exiv2::AnyError& e) {
829                 debug_exception(e);
830                 return NULL;
831         }
832 }
833
834 gchar *exif_get_tag_description_by_key(const gchar *key)
835 {
836         try {
837                 Exiv2::ExifKey ekey(key);
838                 return utf8_validate_or_convert(ekey.tagLabel().c_str());
839         }
840         catch (Exiv2::AnyError& e) {
841                 try {
842                         Exiv2::IptcKey ikey(key);
843                         return utf8_validate_or_convert(ikey.tagLabel().c_str());
844                 }
845                 catch (Exiv2::AnyError& e) {
846                         try {
847 #if EXIV2_TEST_VERSION(0,16,0)
848                                 Exiv2::XmpKey xkey(key);
849                                 return utf8_validate_or_convert(xkey.tagLabel().c_str());
850 #endif
851                         }
852                         catch (Exiv2::AnyError& e) {
853                                 debug_exception(e);
854                                 return NULL;
855                         }
856                 }
857         }
858         return NULL;
859 }
860
861 static const AltKey *find_alt_key(const gchar *xmp_key)
862 {
863         gint i = 0;
864
865         while (alt_keys[i].xmp_key)
866                 {
867                 if (strcmp(xmp_key, alt_keys[i].xmp_key) == 0) return &alt_keys[i];
868                 i++;
869                 }
870         return NULL;
871 }
872
873 static gint exif_update_metadata_simple(ExifData *exif, const gchar *key, const GList *values)
874 {
875         try {
876                 const GList *work = values;
877
878                 try {
879                         Exiv2::ExifKey ekey(key);
880
881                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
882                         while (pos != exif->exifData().end())
883                                 {
884                                 exif->exifData().erase(pos);
885                                 pos = exif->exifData().findKey(ekey);
886                                 }
887
888                         while (work)
889                                 {
890                                 exif->exifData()[key] = (gchar *)work->data;
891                                 work = work->next;
892                                 }
893                 }
894                 catch (Exiv2::AnyError& e) {
895 #if EXIV2_TEST_VERSION(0,16,0)
896                         try
897 #endif
898                         {
899                                 Exiv2::IptcKey ekey(key);
900                                 Exiv2::IptcData::iterator pos = exif->iptcData().findKey(ekey);
901                                 while (pos != exif->iptcData().end())
902                                         {
903                                         exif->iptcData().erase(pos);
904                                         pos = exif->iptcData().findKey(ekey);
905                                         }
906
907                                 while (work)
908                                         {
909                                         exif->iptcData()[key] = (gchar *)work->data;
910                                         work = work->next;
911                                         }
912                         }
913 #if EXIV2_TEST_VERSION(0,16,0)
914                         catch (Exiv2::AnyError& e) {
915                                 Exiv2::XmpKey ekey(key);
916                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
917                                 while (pos != exif->xmpData().end())
918                                         {
919                                         exif->xmpData().erase(pos);
920                                         pos = exif->xmpData().findKey(ekey);
921                                         }
922
923                                 while (work)
924                                         {
925                                         exif->xmpData()[key] = (gchar *)work->data;
926                                         work = work->next;
927                                         }
928                         }
929 #endif
930                 }
931                 return 1;
932         }
933         catch (Exiv2::AnyError& e) {
934                 debug_exception(e);
935                 return 0;
936         }
937 }
938
939 gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
940 {
941         gint ret = exif_update_metadata_simple(exif, key, values);
942
943         if (
944 #if !EXIV2_TEST_VERSION(0,17,0)
945             TRUE || /* no conversion support */
946 #endif
947             !values || /* deleting item */
948             !ret  /* writing to the explicitely given xmp tag failed */
949             )
950                 {
951                 /* deleted xmp metadatum can't be converted, we have to delete also the corresponding legacy tag */
952                 /* if we can't write xmp, update at least the legacy tag */
953                 const AltKey *alt_key = find_alt_key(key);
954                 if (alt_key && alt_key->iptc_key)
955                         ret = exif_update_metadata_simple(exif, alt_key->iptc_key, values);
956
957                 if (alt_key && alt_key->exif_key)
958                         ret = exif_update_metadata_simple(exif, alt_key->exif_key, values);
959                 }
960         return ret;
961 }
962
963
964 static GList *exif_add_value_to_glist(GList *list, Exiv2::Metadatum &item, MetadataFormat format, const Exiv2::ExifData *metadata)
965 {
966 #if EXIV2_TEST_VERSION(0,16,0)
967         Exiv2::TypeId id = item.typeId();
968         if (format == METADATA_FORMATTED ||
969             id == Exiv2::asciiString ||
970             id == Exiv2::undefined ||
971             id == Exiv2::string ||
972             id == Exiv2::date ||
973             id == Exiv2::time ||
974             id == Exiv2::xmpText ||
975             id == Exiv2::langAlt ||
976             id == Exiv2::comment
977             )
978                 {
979 #endif
980                 /* read as a single entry */
981                 std::string str;
982
983                 if (format == METADATA_FORMATTED)
984                         {
985 #if EXIV2_TEST_VERSION(0,17,0)
986                         str = item.print(
987 #if EXIV2_TEST_VERSION(0,18,0)
988                                         metadata
989 #endif
990                                         );
991 #else
992                         std::stringstream stream;
993                         Exiv2::Exifdatum *exifdatum;
994                         Exiv2::Iptcdatum *iptcdatum;
995 #if EXIV2_TEST_VERSION(0,16,0)
996                         Exiv2::Xmpdatum *xmpdatum;
997 #endif
998                         if ((exifdatum = dynamic_cast<Exiv2::Exifdatum *>(&item)))
999                                 stream << *exifdatum;
1000                         else if ((iptcdatum = dynamic_cast<Exiv2::Iptcdatum *>(&item)))
1001                                 stream << *iptcdatum;
1002 #if EXIV2_TEST_VERSION(0,16,0)
1003                         else if ((xmpdatum = dynamic_cast<Exiv2::Xmpdatum *>(&item)))
1004                                 stream << *xmpdatum;
1005 #endif
1006                         str = stream.str();
1007 #endif
1008                         if (str.length() > 1024)
1009                                 {
1010                                 /* truncate very long strings, they cause problems in gui */
1011                                 str.erase(1024);
1012                                 str.append("...");
1013                                 }
1014                         }
1015                 else
1016                         {
1017                         str = item.toString();
1018                         }
1019                 if (str.length() > 5 && str.substr(0, 5) == "lang=")
1020                         {
1021                         std::string::size_type pos = str.find_first_of(' ');
1022                         if (pos != std::string::npos) str = str.substr(pos+1);
1023                         }
1024                 list = g_list_append(list, utf8_validate_or_convert(str.c_str()));
1025 #if EXIV2_TEST_VERSION(0,16,0)
1026                 }
1027         else
1028                 {
1029                 /* read as a list */
1030                 gint i;
1031                 for (i = 0; i < item.count(); i++)
1032                         list = g_list_append(list, utf8_validate_or_convert(item.toString(i).c_str()));
1033                 }
1034 #endif
1035         return list;
1036 }
1037
1038 static GList *exif_get_metadata_simple(ExifData *exif, const gchar *key, MetadataFormat format)
1039 {
1040         GList *list = NULL;
1041         try {
1042                 try {
1043                         Exiv2::ExifKey ekey(key);
1044                         Exiv2::ExifData::iterator pos = exif->exifData().findKey(ekey);
1045                         if (pos != exif->exifData().end())
1046                                 list = exif_add_value_to_glist(list, *pos, format, &exif->exifData());
1047
1048                 }
1049                 catch (Exiv2::AnyError& e) {
1050                         try {
1051                                 Exiv2::IptcKey ekey(key);
1052                                 Exiv2::IptcData::iterator pos = exif->iptcData().begin();
1053                                 while (pos != exif->iptcData().end())
1054                                         {
1055                                         if (pos->key() == key)
1056                                                 list = exif_add_value_to_glist(list, *pos, format, NULL);
1057                                         ++pos;
1058                                         }
1059
1060                         }
1061                         catch (Exiv2::AnyError& e) {
1062 #if EXIV2_TEST_VERSION(0,16,0)
1063                                 Exiv2::XmpKey ekey(key);
1064                                 Exiv2::XmpData::iterator pos = exif->xmpData().findKey(ekey);
1065                                 if (pos != exif->xmpData().end())
1066                                         list = exif_add_value_to_glist(list, *pos, format, NULL);
1067 #endif
1068                         }
1069                 }
1070         }
1071         catch (Exiv2::AnyError& e) {
1072                 debug_exception(e);
1073         }
1074         return list;
1075 }
1076
1077 GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format)
1078 {
1079         GList *list = NULL;
1080
1081         if (!key) return NULL;
1082
1083         if (format == METADATA_FORMATTED)
1084                 {
1085                 gchar *text;
1086                 gint key_valid;
1087                 text = exif_get_formatted_by_key(exif, key, &key_valid);
1088                 if (key_valid) return g_list_append(NULL, text);
1089                 }
1090
1091         list = exif_get_metadata_simple(exif, key, format);
1092
1093         /* the following code can be ifdefed out as soon as Exiv2 supports it */
1094         if (!list)
1095                 {
1096                 const AltKey *alt_key = find_alt_key(key);
1097                 if (alt_key && alt_key->iptc_key)
1098                         list = exif_get_metadata_simple(exif, alt_key->iptc_key, format);
1099
1100 #if !EXIV2_TEST_VERSION(0,17,0)
1101                 /* with older Exiv2 versions exif is not synced */
1102                 if (!list && alt_key && alt_key->exif_key)
1103                         list = exif_get_metadata_simple(exif, alt_key->exif_key, format);
1104 #endif
1105                 }
1106         return list;
1107 }
1108
1109
1110 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length)
1111 {
1112         exif->add_jpeg_color_profile(cp_data, cp_length);
1113 }
1114
1115 guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
1116 {
1117         guchar *ret = exif->get_jpeg_color_profile(data_len);
1118         if (ret) return ret;
1119
1120         ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile");
1121         if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED)
1122                 ret = (guchar *)exif_item_get_data(prof_item, data_len);
1123         return ret;
1124 }
1125
1126 #if EXIV2_TEST_VERSION(0,17,90)
1127
1128 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1129 {
1130         if (!exif) return NULL;
1131
1132         if (!exif->image()) return NULL;
1133
1134         /* given image pathname, first do simple (and fast) file extension test */
1135         gboolean is_raw = filter_file_class(exif->image()->io().path().c_str(), FORMAT_CLASS_RAWIMAGE);
1136
1137         if (!is_raw && requested_width == 0) return NULL;
1138
1139         try {
1140
1141                 Exiv2::PreviewManager pm(*exif->image());
1142
1143                 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties();
1144
1145                 if (!list.empty())
1146                         {
1147                         Exiv2::PreviewPropertiesList::iterator pos;
1148                         Exiv2::PreviewPropertiesList::iterator last = --list.end();
1149
1150                         if (requested_width == 0)
1151                                 {
1152                                 pos = last; // the largest
1153                                 }
1154                         else
1155                                 {
1156                                 pos = list.begin();
1157                                 while (pos != last)
1158                                         {
1159                                         if (pos->width_ >= (uint32_t)requested_width &&
1160                                             pos->height_ >= (uint32_t)requested_height) break;
1161                                         ++pos;
1162                                         }
1163
1164                                 // we are not interested in smaller thumbnails in normal image formats - we can use full image instead
1165                                 if (!is_raw)
1166                                         {
1167                                         if (pos->width_ < (uint32_t)requested_width || pos->height_ < (uint32_t)requested_height) return NULL;
1168                                         }
1169                                 }
1170
1171                         Exiv2::PreviewImage image = pm.getPreviewImage(*pos);
1172
1173                         Exiv2::DataBuf buf = image.copy();
1174                         std::pair<Exiv2::byte*, long> p = buf.release();
1175
1176                         *data_len = p.second;
1177                         return p.first;
1178                         }
1179                 return NULL;
1180         }
1181         catch (Exiv2::AnyError& e) {
1182                 debug_exception(e);
1183                 return NULL;
1184         }
1185 }
1186
1187 void exif_free_preview(guchar *buf)
1188 {
1189         delete[] (Exiv2::byte*)buf;
1190 }
1191 #endif
1192
1193 }
1194 #if !EXIV2_TEST_VERSION(0,17,90)
1195
1196 /* This is a dirty hack to support raw file preview, bassed on
1197 tiffparse.cpp from Exiv2 examples */
1198
1199 class RawFile {
1200         public:
1201
1202         RawFile(Exiv2::BasicIo &io);
1203         ~RawFile();
1204
1205         const Exiv2::Value *find(uint16_t tag, uint16_t group);
1206
1207         unsigned long preview_offset();
1208
1209         private:
1210         int type;
1211         Exiv2::TiffComponent::AutoPtr rootDir;
1212         Exiv2::BasicIo &io_;
1213         const Exiv2::byte *map_data;
1214         size_t map_len;
1215         unsigned long offset;
1216 };
1217
1218 typedef struct _UnmapData UnmapData;
1219 struct _UnmapData
1220 {
1221         guchar *ptr;
1222         guchar *map_data;
1223         size_t map_len;
1224 };
1225
1226 static GList *exif_unmap_list = 0;
1227
1228 extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
1229 {
1230         unsigned long offset;
1231
1232         if (!exif) return NULL;
1233         if (!exif->image()) return NULL;
1234
1235         const char* path = exif->image()->io().path().c_str();
1236
1237         /* given image pathname, first do simple (and fast) file extension test */
1238         if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL;
1239
1240         try {
1241                 struct stat st;
1242                 guchar *map_data;
1243                 size_t map_len;
1244                 UnmapData *ud;
1245                 int fd;
1246
1247                 RawFile rf(exif->image()->io());
1248                 offset = rf.preview_offset();
1249                 DEBUG_1("%s: offset %lu", path, offset);
1250
1251                 fd = open(path, O_RDONLY);
1252                 if (fd == -1)
1253                         {
1254                         return NULL;
1255                         }
1256
1257                 if (fstat(fd, &st) == -1)
1258                         {
1259                         close(fd);
1260                         return NULL;
1261                         }
1262                 map_len = st.st_size;
1263                 map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1264                 close(fd);
1265                 if (map_data == MAP_FAILED)
1266                         {
1267                         return NULL;
1268                         }
1269                 *data_len = map_len - offset;
1270                 ud = g_new(UnmapData, 1);
1271                 ud->ptr = map_data + offset;
1272                 ud->map_data = map_data;
1273                 ud->map_len = map_len;
1274
1275                 exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
1276                 return ud->ptr;
1277
1278         }
1279         catch (Exiv2::AnyError& e) {
1280                 debug_exception(e);
1281         }
1282         return NULL;
1283
1284 }
1285
1286 void exif_free_preview(guchar *buf)
1287 {
1288         GList *work = exif_unmap_list;
1289
1290         while (work)
1291                 {
1292                 UnmapData *ud = (UnmapData *)work->data;
1293                 if (ud->ptr == buf)
1294                         {
1295                         munmap(ud->map_data, ud->map_len);
1296                         exif_unmap_list = g_list_remove_link(exif_unmap_list, work);
1297                         g_free(ud);
1298                         return;
1299                         }
1300                 work = work->next;
1301                 }
1302         g_assert_not_reached();
1303 }
1304
1305 using namespace Exiv2;
1306
1307 RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0)
1308 {
1309 /*
1310         struct stat st;
1311         if (fstat(fd, &st) == -1)
1312                 {
1313                 throw Error(14);
1314                 }
1315         map_len = st.st_size;
1316         map_data = (Exiv2::byte *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
1317         if (map_data == MAP_FAILED)
1318                 {
1319                 throw Error(14);
1320                 }
1321 */
1322         if (io.open() != 0) {
1323             throw Error(9, io.path(), strError());
1324         }
1325
1326         map_data = io.mmap();
1327         map_len = io.size();
1328
1329
1330         type = Exiv2::ImageFactory::getType(map_data, map_len);
1331
1332 #if EXIV2_TEST_VERSION(0,16,0)
1333         TiffHeaderBase *tiffHeader = NULL;
1334 #else
1335         TiffHeade2 *tiffHeader = NULL;
1336 #endif
1337         Cr2Header *cr2Header = NULL;
1338
1339         switch (type) {
1340                 case Exiv2::ImageType::tiff:
1341                         tiffHeader = new TiffHeade2();
1342                         break;
1343                 case Exiv2::ImageType::cr2:
1344                         cr2Header = new Cr2Header();
1345                         break;
1346 #if EXIV2_TEST_VERSION(0,16,0)
1347                 case Exiv2::ImageType::orf:
1348                         tiffHeader = new OrfHeader();
1349                         break;
1350 #endif
1351 #if EXIV2_TEST_VERSION(0,13,0)
1352                 case Exiv2::ImageType::raf:
1353                         if (map_len < 84 + 4) throw Error(14);
1354                         offset = getULong(map_data + 84, bigEndian);
1355                         return;
1356 #endif
1357                 case Exiv2::ImageType::crw:
1358                         {
1359                         // Parse the image, starting with a CIFF header component
1360                         Exiv2::CiffHeader::AutoPtr parseTree(new Exiv2::CiffHeader);
1361                         parseTree->read(map_data, map_len);
1362                         CiffComponent *entry = parseTree->findComponent(0x2007, 0);
1363                         if (entry) offset =  entry->pData() - map_data;
1364                         return;
1365                         }
1366
1367                 default:
1368                         throw Error(3, "RAW");
1369         }
1370
1371         // process tiff-like formats
1372
1373         TiffCompFactoryFct createFct = TiffCreator::create;
1374
1375         rootDir = createFct(Tag::root, Group::none);
1376         if (0 == rootDir.get()) {
1377                 throw Error(1, "No root element defined in TIFF structure");
1378         }
1379
1380         if (tiffHeader)
1381                 {
1382                 if (!tiffHeader->read(map_data, map_len)) throw Error(3, "TIFF");
1383 #if EXIV2_TEST_VERSION(0,16,0)
1384                 rootDir->setStart(map_data + tiffHeader->offset());
1385 #else
1386                 rootDir->setStart(map_data + tiffHeader->ifdOffset());
1387 #endif
1388                 }
1389
1390         if (cr2Header)
1391                 {
1392                 rootDir->setStart(map_data + cr2Header->offset());
1393                 }
1394
1395         TiffRwState::AutoPtr state(new TiffRwState(tiffHeader ? tiffHeader->byteOrder() : littleEndian, 0, createFct));
1396
1397         TiffReader reader(map_data,
1398                           map_len,
1399                           rootDir.get(),
1400                           state);
1401
1402         rootDir->accept(reader);
1403
1404         if (tiffHeader)
1405                 delete tiffHeader;
1406         if (cr2Header)
1407                 delete cr2Header;
1408 }
1409
1410 RawFile::~RawFile(void)
1411 {
1412         io_.munmap();
1413         io_.close();
1414 }
1415
1416 const Value * RawFile::find(uint16_t tag, uint16_t group)
1417 {
1418         TiffFinder finder(tag, group);
1419         rootDir->accept(finder);
1420         TiffEntryBase* te = dynamic_cast<TiffEntryBase*>(finder.result());
1421         if (te)
1422                 {
1423                 DEBUG_1("(tag: %04x %04x) ", tag, group);
1424                 return te->pValue();
1425                 }
1426         else
1427                 return NULL;
1428 }
1429
1430 unsigned long RawFile::preview_offset(void)
1431 {
1432         const Value *val;
1433         if (offset) return offset;
1434
1435         if (type == Exiv2::ImageType::cr2)
1436                 {
1437                 val = find(0x111, Group::ifd0);
1438                 if (val) return val->toLong();
1439
1440                 return 0;
1441                 }
1442
1443         val = find(0x201, Group::sub0_0);
1444         if (val) return val->toLong();
1445
1446         val = find(0x201, Group::ifd0);
1447         if (val) return val->toLong();
1448
1449         val = find(0x201, Group::ignr); // for PEF files, originally it was probably ifd2
1450         if (val) return val->toLong();
1451
1452         val = find(0x111, Group::sub0_1); // dng
1453         if (val) return val->toLong();
1454
1455         return 0;
1456 }
1457
1458
1459 #endif
1460
1461
1462 #endif
1463 /* HAVE_EXIV2 */
1464 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */