Improve code reusage: string strip and free functions.
[lms] / lightmediascanner / src / plugins / jpeg / jpeg.c
1 /**
2  * Copyright (C) 2007 by INdT
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * Reads EXIF tags from images.
25  *
26  * @todo: get GPS data.
27  * @todo: check if worth using mmap().
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #define _XOPEN_SOURCE 600
35 #include <lightmediascanner_plugin.h>
36 #include <lightmediascanner_utils.h>
37 #include <lightmediascanner_db.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <time.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <strings.h>
47
48 enum {
49     JPEG_MARKER_SOI = 0xd8,
50     JPEG_MARKER_DQT = 0xdb,
51     JPEG_MARKER_JFIF = 0xe0,
52     JPEG_MARKER_EXIF = 0xe1,
53     JPEG_MARKER_COMM = 0xfe,
54     JPEG_MARKER_SOF0 = 0xc0,
55     JPEG_MARKER_SOF1 = 0xc1,
56     JPEG_MARKER_SOF2 = 0xc2,
57     JPEG_MARKER_SOF9 = 0xc9,
58     JPEG_MARKER_SOF10 = 0xca,
59     JPEG_MARKER_SOS = 0xda
60 };
61
62 /**
63  * Process SOF JPEG, this contains width and height.
64  */
65 static int
66 _jpeg_sof_process(int fd, unsigned short *width, unsigned short *height)
67 {
68     unsigned char buf[6];
69
70     if (read(fd, buf, 6) != 6) {
71         perror("could not read() SOF data");
72         return -1;
73     }
74
75     *height = (buf[1] << 8) | buf[2];
76     *width = (buf[3] << 8) | buf[4];
77
78     return 0;
79 }
80
81 /**
82  * Process COM JPEG, this contains user comment.
83  */
84 static int
85 _jpeg_com_process(int fd, int len, struct lms_string_size *comment)
86 {
87     if (len < 1) {
88         comment->str = NULL;
89         comment->len = 0;
90         return 0;
91     }
92
93     comment->str = malloc(len + 1);
94     if (!comment->str) {
95         perror("malloc");
96         return -1;
97     }
98     if (read(fd, comment->str, len) != len) {
99         perror("read");
100         free(comment->str);
101         comment->str = NULL;
102         comment->len = 0;
103         return -2;
104     }
105     if (comment->str[len - 1] == '\0')
106         len--;
107     else
108         comment->str[len] = '\0';
109     comment->len = len;
110
111     lms_string_size_strip_and_free(comment);
112
113     return 0;
114 }
115
116 /**
117  * Walk JPEG markers in order to get useful information.
118  */
119 static int
120 _jpeg_info_get(int fd, int len, struct lms_image_info *info)
121 {
122     unsigned char buf[4];
123     int found;
124     off_t offset;
125
126     found = info->title.str ? 1 : 0;
127     offset = lseek(fd, len - 2, SEEK_CUR);
128     len = 0;
129     while (found < 2) {
130         offset = lseek(fd, offset + len, SEEK_SET);
131         if (offset == -1) {
132             perror("lseek");
133             return -1;
134         }
135
136         if (read(fd, buf, 4) != 4) {
137             perror("read");
138             return -2;
139         }
140
141         len = ((buf[2] << 8) | buf[3]) - 2;
142
143         if (buf[0] != 0xff) {
144             fprintf(stderr, "ERROR: expected 0xff marker, got %#x\n", buf[0]);
145             return -3;
146         }
147
148         if (buf[1] == JPEG_MARKER_SOF0 ||
149             buf[1] == JPEG_MARKER_SOF1 ||
150             buf[1] == JPEG_MARKER_SOF2 ||
151             buf[1] == JPEG_MARKER_SOF9 ||
152             buf[1] == JPEG_MARKER_SOF10) {
153             if (_jpeg_sof_process(fd, &info->width, &info->height) != 0)
154                 return -4;
155             found++;
156         } else if (buf[1] == JPEG_MARKER_COMM && !info->title.str) {
157             if (_jpeg_com_process(fd, len, &info->title) != 0)
158                 return -5;
159             found++;
160         } else if (buf[1] == JPEG_MARKER_SOS)
161             break;
162
163         len += 4; /* add read size */
164     }
165
166     return 0;
167 }
168
169 /**
170  * Read JPEG file start (0xffd8 marker) and return the next
171  * marker type and its length.
172  */
173 static int
174 _jpeg_data_get(int fd, int *type, int *len)
175 {
176     unsigned char buf[6];
177
178     if (lseek(fd, 0, SEEK_SET) != 0) {
179         perror("lseek");
180         return -1;
181     }
182
183     if (read(fd, buf, 6) != 6) {
184         perror("read");
185         return -2;
186     }
187
188     if (buf[0] != 0xff || buf[1] != JPEG_MARKER_SOI || buf[2] != 0xff) {
189         fprintf(stderr, "ERROR: not JPEG file (magic=%#x %#x %#x)\n",
190                 buf[0], buf[1], buf[2]);
191         return -3;
192     }
193
194     *type = buf[3];
195     *len = (buf[4] << 8) | buf[5];
196
197     return 0;
198 }
199
200 #define LE_4BYTE(a) ((a)[0] | ((a)[1] << 8) | ((a)[2] << 16) | ((a)[3] << 24))
201 #define BE_4BYTE(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
202
203 #define LE_2BYTE(a) ((a)[0] | ((a)[1] << 8))
204 #define BE_2BYTE(a) (((a)[0] << 8) | (a)[1])
205
206 #define E_2BTYE(little_endian, a) ((little_endian) ? LE_2BYTE(a) : BE_2BYTE(a))
207 #define E_4BTYE(little_endian, a) ((little_endian) ? LE_4BYTE(a) : BE_4BYTE(a))
208
209 enum {
210     EXIF_TYPE_BYTE = 1, /* 8 bit unsigned */
211     EXIF_TYPE_ASCII = 2, /* 8 bit byte with 7-bit ASCII code, NULL terminated */
212     EXIF_TYPE_SHORT = 3, /* 2-byte unsigned integer */
213     EXIF_TYPE_LONG = 4, /* 4-byte unsigned integer */
214     EXIF_TYPE_RATIONAL = 5, /* 2 4-byte unsigned integer, 1st = numerator */
215     EXIF_TYPE_UNDEFINED = 7, /* 8-bit byte */
216     EXIF_TYPE_SLONG = 9, /* 4-byte signed integer (2'complement) */
217     EXIF_TYPE_SRATIONAL = 10 /* 2 4-byte signed integer, 1st = numerator */
218 };
219
220 enum {
221     EXIF_TAG_ORIENTATION = 0x0112,
222     EXIF_TAG_ARTIST = 0x013b,
223     EXIF_TAG_USER_COMMENT = 0x9286,
224     EXIF_TAG_IMAGE_DESCRIPTION = 0x010e,
225     EXIF_TAG_DATE_TIME = 0x0132,
226     EXIF_TAG_DATE_TIME_ORIGINAL = 0x9003,
227     EXIF_TAG_DATE_TIME_DIGITIZED = 0x9004,
228     EXIF_TAG_EXIF_IFD_POINTER = 0x8769
229 };
230
231
232 struct exif_ifd {
233     unsigned short tag;
234     unsigned short type;
235     unsigned int count;
236     unsigned int offset;
237 };
238
239 /**
240  * Read IFD from stream.
241  */
242 static int
243 _exif_ifd_get(int fd, int little_endian, struct exif_ifd *ifd)
244 {
245     unsigned char buf[12];
246
247     if (read(fd, buf, 12) != 12) {
248         perror("read");
249         return -1;
250     }
251
252     if (little_endian) {
253         ifd->tag = LE_2BYTE(buf);
254         ifd->type = LE_2BYTE(buf + 2);
255         ifd->count = LE_4BYTE(buf + 4);
256         ifd->offset = LE_4BYTE(buf + 8);
257     } else {
258         ifd->tag = BE_2BYTE(buf);
259         ifd->type = BE_2BYTE(buf + 2);
260         ifd->count = BE_4BYTE(buf + 4);
261         ifd->offset = BE_4BYTE(buf + 8);
262     }
263     return 0;
264 }
265
266 /**
267  * Get non-exif data based on Exif tag offset.
268  *
269  * This will setup the file description position and call _jpeg_info_get().
270  */
271 static int
272 _exif_extra_get(int fd, int abs_offset, int len, struct lms_image_info *info)
273 {
274     if (lseek(fd, abs_offset, SEEK_SET) == -1) {
275         perror("lseek");
276         return -1;
277     }
278
279     if (_jpeg_info_get(fd, len, info) != 0) {
280         fprintf(stderr, "ERROR: could not get image size.\n");
281         return -2;
282     }
283     return 0;
284 }
285
286 static int
287 _exif_text_encoding_get(int fd, unsigned int count, int offset, struct lms_string_size *s)
288 {
289     if (count <= 8)
290         return -1;
291
292     count -= 8; /* XXX don't just ignore character code, handle it. */
293     offset += 8;
294
295     if (lseek(fd, offset, SEEK_SET) == -1) {
296         perror("lseek");
297         return -2;
298     }
299
300     s->str = malloc(count + 1);
301
302     if (read(fd, s->str, count) != count) {
303         perror("read");
304         free(s->str);
305         s->str = NULL;
306         s->len = 0;
307         return -3;
308     }
309     s->str[count] = '\0';
310     s->len = count;
311
312     lms_string_size_strip_and_free(s);
313
314     return 0;
315 }
316
317 static int
318 _exif_text_ascii_get(int fd, unsigned int count, int offset, struct lms_string_size *s)
319 {
320     if (count < 1) {
321         s->str = NULL;
322         s->len = 0;
323         return 0;
324     }
325
326     if (lseek(fd, offset, SEEK_SET) == -1) {
327         perror("lseek");
328         return -1;
329     }
330
331     s->str = malloc(count);
332
333     if (read(fd, s->str, count) != count) {
334         perror("read");
335         free(s->str);
336         s->str = NULL;
337         s->len = 0;
338         return -1;
339     }
340     s->str[count - 1] = '\0';
341     s->len = count - 1;
342
343     lms_string_size_strip_and_free(s);
344
345     return 0;
346 }
347
348 static unsigned int
349 _exif_datetime_get(int fd, int offset)
350 {
351     char buf[20];
352     struct tm tm = {0};
353
354     if (lseek(fd, offset, SEEK_SET) == -1) {
355         perror("lseek");
356         return 0;
357     }
358
359     if (read(fd, buf, 20) != 20) {
360         perror("read");
361         return 0;
362     }
363
364     buf[19] = '\0';
365     if (strptime(buf, "%Y:%m:%d %H:%M:%S", &tm)) {
366         return mktime(&tm);
367     }
368     return 0;
369 }
370
371 static int _exif_private_ifd_get(int fd, int base_offset, int offset, int little_endian, struct lms_image_info *info);
372
373 /**
374  * Process IFD contents.
375  */
376 static int
377 _exif_ifd_process(int fd, int count, int ifd_offset, int tiff_base, int little_endian, struct lms_image_info *info)
378 {
379     int i, torig, tdig, tlast;
380
381     torig = tdig = tlast = 0;
382
383     for (i = 0; i < count; i++) {
384         struct exif_ifd ifd;
385
386         lseek(fd, tiff_base + ifd_offset + i * 12, SEEK_SET);
387         if (_exif_ifd_get(fd, little_endian, &ifd) != 0) {
388             fprintf(stderr, "ERROR: could not read Exif IFD.\n");
389             return -8;
390         }
391
392         switch (ifd.tag) {
393         case EXIF_TAG_ORIENTATION:
394             info->orientation = ifd.offset >> 16;
395             break;
396         case EXIF_TAG_ARTIST:
397             if (!info->artist.str)
398                 _exif_text_ascii_get(fd, ifd.count, tiff_base + ifd.offset,
399                                      &info->artist);
400             break;
401         case EXIF_TAG_USER_COMMENT:
402             if (!info->title.str)
403                 _exif_text_encoding_get(fd, ifd.count, tiff_base + ifd.offset,
404                                         &info->title);
405             break;
406         case EXIF_TAG_IMAGE_DESCRIPTION:
407             if (!info->title.str)
408                 _exif_text_ascii_get(fd, ifd.count, tiff_base + ifd.offset,
409                                      &info->title);
410             break;
411         case EXIF_TAG_DATE_TIME:
412             if (torig == 0 && info->date == 0)
413                 tlast = _exif_datetime_get(fd, tiff_base + ifd.offset);
414             break;
415         case EXIF_TAG_DATE_TIME_ORIGINAL:
416             if (torig == 0 && info->date == 0)
417                 torig = _exif_datetime_get(fd, tiff_base + ifd.offset);
418             break;
419         case EXIF_TAG_DATE_TIME_DIGITIZED:
420             if (torig == 0 && info->date == 0)
421                 tdig = _exif_datetime_get(fd, tiff_base + ifd.offset);
422             break;
423         case EXIF_TAG_EXIF_IFD_POINTER:
424             if (ifd.count == 1 && ifd.type == EXIF_TYPE_LONG)
425                 _exif_private_ifd_get(fd, ifd.offset, tiff_base,
426                                       little_endian, info);
427             break;
428         default:
429             /* ignore */
430             break;
431         }
432     }
433
434     if (info->date == 0) {
435         if (torig)
436             info->date = torig;
437         else if (tdig)
438             info->date = tdig;
439         else
440             info->date = tlast;
441     }
442
443     return 0;
444 }
445
446 /**
447  * Process Exif IFD (Exif Private Tag), with more specific info.
448  */
449 static int
450 _exif_private_ifd_get(int fd, int ifd_offset, int tiff_base, int little_endian, struct lms_image_info *info)
451 {
452     char buf[2];
453     unsigned int count;
454
455     if (lseek(fd, tiff_base + ifd_offset, SEEK_SET) == -1) {
456         perror("lseek");
457         return -1;
458     }
459
460     if (read(fd, buf, 2) != 2) {
461         perror("read");
462         return -1;
463     }
464
465     count = E_2BTYE(little_endian, buf);
466     return _exif_ifd_process(fd, count, ifd_offset + 2, tiff_base,
467                              little_endian, info);
468 }
469
470 /**
471  * Process file as it being Exif, will extract Exif as well as other
472  * JPEG markers (comment, size).
473  */
474 static int
475 _exif_data_get(int fd, int len, struct lms_image_info *info)
476 {
477     const unsigned char exif_hdr[6] = "Exif\0";
478     unsigned char buf[8];
479     unsigned int little_endian, offset, count;
480     off_t abs_offset, tiff_base;
481
482     abs_offset = lseek(fd, 0, SEEK_CUR);
483     if (abs_offset == -1) {
484         perror("lseek");
485         return -1;
486     }
487
488     if (read(fd, buf, 6) != 6) {
489         perror("read");
490         return -2;
491     }
492
493     memset(info, 0, sizeof(*info));
494     info->orientation = 1;
495
496     if (memcmp(buf, exif_hdr, 6) != 0)
497         return _exif_extra_get(fd, abs_offset, len, info);
498
499     if (read(fd, buf, 8) != 8) {
500         perror("read");
501         return -4;
502     }
503
504     if (buf[0] == 'I' && buf[1] == 'I') {
505         little_endian = 1;
506         offset = LE_4BYTE(buf + 4);
507     } else if (buf[0] == 'M' && buf[1] == 'M') {
508         little_endian = 0;
509         offset = BE_4BYTE(buf + 4);
510     } else {
511         fprintf(stderr, "ERROR: undefined byte sex \"%2.2s\".\n", buf);
512         return -5;
513     }
514
515     offset -= 8;
516     if (offset > 0 && lseek(fd, offset, SEEK_CUR) == -1) {
517         perror("lseek");
518         return -6;
519     }
520
521     tiff_base = abs_offset + 6; /* offsets are relative to TIFF base */
522
523     if (read(fd, buf, 2) != 2) {
524         perror("read");
525         return -7;
526     }
527     count = E_2BTYE(little_endian, buf);
528
529     _exif_ifd_process(fd, count, 8 + 2, tiff_base,
530                       little_endian, info);
531
532     return _exif_extra_get(fd, abs_offset, len, info);
533 }
534
535 /**
536  * Process file as it being JFIF
537  */
538 static int
539 _jfif_data_get(int fd, int len, struct lms_image_info *info)
540 {
541     unsigned char buf[4];
542     int new_len;
543
544     memset(info, 0, sizeof(*info));
545     info->orientation = 1;
546
547     /* JFIF provides no useful information, try to find out Exif */
548     if (lseek(fd, len - 2, SEEK_CUR) == -1) {
549         perror("lseek");
550         return -1;
551     }
552
553     if (read(fd, buf, 4) != 4) {
554         perror("read");
555         return -2;
556     }
557
558     new_len = ((buf[2] << 8) | buf[3]);
559     if (buf[0] != 0xff) {
560         fprintf(stderr, "ERROR: expected 0xff marker, got %#x\n", buf[0]);
561         return -3;
562     }
563
564     if (buf[1] == JPEG_MARKER_EXIF)
565         return _exif_data_get(fd, new_len, info);
566     else {
567         /* rollback to avoid losing initial frame */
568         if (lseek(fd, - len - 2, SEEK_CUR) == -1) {
569             perror("lseek");
570             return -1;
571         }
572         return _jpeg_info_get(fd, len, info);
573     }
574 }
575
576 static const char _name[] = "jpeg";
577 static const struct lms_string_size _exts[] = {
578     LMS_STATIC_STRING_SIZE(".jpg"),
579     LMS_STATIC_STRING_SIZE(".jpeg"),
580     LMS_STATIC_STRING_SIZE(".jpe")
581 };
582
583 struct plugin {
584     struct lms_plugin plugin;
585     lms_db_image_t *img_db;
586 };
587
588 static void *
589 _match(struct plugin *p, const char *path, int len, int base)
590 {
591     int i;
592
593     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
594     if (i < 0)
595         return NULL;
596     else
597         return (void*)(i + 1);
598 }
599
600 static int
601 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
602 {
603     struct lms_image_info info = {0};
604     int fd, type, len, r;
605
606     fd = open(finfo->path, O_RDONLY);
607     if (fd < 0) {
608         perror("open");
609         return -1;
610     }
611
612     if (_jpeg_data_get(fd, &type, &len) != 0) {
613         r = -2;
614         goto done;
615     }
616
617     if (type == JPEG_MARKER_EXIF) {
618         if (_exif_data_get(fd, len, &info) != 0) {
619             fprintf(stderr, "ERROR: could not get EXIF info (%s).\n",
620                     finfo->path);
621             r = -3;
622             goto done;
623         }
624     } else if (type == JPEG_MARKER_JFIF || type == JPEG_MARKER_DQT) {
625         if (_jfif_data_get(fd, len, &info) != 0) {
626             fprintf(stderr, "ERROR: could not get JPEG size (%s).\n",
627                     finfo->path);
628             r = -4;
629             goto done;
630         }
631     } else {
632         fprintf(stderr, "ERROR: unsupported JPEG marker %#x (%s)\n", type,
633                 finfo->path);
634         r = -6;
635         goto done;
636     }
637
638     if (info.date == 0)
639         info.date = finfo->mtime;
640
641     if (!info.title.str) {
642       int ext_idx;
643
644       ext_idx = ((int)match) - 1;
645       info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
646       info.title.str = malloc((info.title.len + 1) * sizeof(char));
647       memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
648       info.title.str[info.title.len] = '\0';
649     }
650
651     if (info.title.str)
652       lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
653     if (info.artist.str)
654       lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
655
656     info.id = finfo->id;
657     r = lms_db_image_add(plugin->img_db, &info);
658
659   done:
660     if (info.title.str)
661         free(info.title.str);
662     if (info.artist.str)
663         free(info.artist.str);
664
665     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
666     close(fd);
667
668     return r;
669 }
670
671 static int
672 _setup(struct plugin *plugin, struct lms_context *ctxt)
673 {
674     plugin->img_db = lms_db_image_new(ctxt->db);
675     if (!plugin->img_db)
676         return -1;
677
678     return 0;
679 }
680
681 static int
682 _start(struct plugin *plugin, struct lms_context *ctxt)
683 {
684     return lms_db_image_start(plugin->img_db);
685 }
686
687 static int
688 _finish(struct plugin *plugin, struct lms_context *ctxt)
689 {
690     if (plugin->img_db)
691         return lms_db_image_free(plugin->img_db);
692
693     return 0;
694 }
695
696
697 static int
698 _close(struct plugin *plugin)
699 {
700     free(plugin);
701     return 0;
702 }
703
704 API struct lms_plugin *
705 lms_plugin_open(void)
706 {
707     struct plugin *plugin;
708
709     plugin = malloc(sizeof(*plugin));
710     plugin->plugin.name = _name;
711     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
712     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
713     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
714     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
715     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
716     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
717
718     return (struct lms_plugin *)plugin;
719 }