Fixed _to_uint method.
[lms] / lightmediascanner / src / plugins / id3 / id3.c
1 /**
2  * Copyright (C) 2008 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 Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * id3 file parser.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #define _XOPEN_SOURCE 600
32 #include <lightmediascanner_plugin.h>
33 #include <lightmediascanner_db.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <ctype.h>
42
43 #define ID3V2_HEADER_SIZE       10
44 #define ID3V2_FOOTER_SIZE       10
45 #define ID3V2_FRAME_HEADER_SIZE 10
46
47 #define ID3V1_NUM_GENRES 148
48
49 static const char *id3v1_genres[ID3V1_NUM_GENRES] = {
50     "Blues",
51     "Classic Rock",
52     "Country",
53     "Dance",
54     "Disco",
55     "Funk",
56     "Grunge",
57     "Hip-Hop",
58     "Jazz",
59     "Metal",
60     "New Age",
61     "Oldies",
62     "Other",
63     "Pop",
64     "R&B",
65     "Rap",
66     "Reggae",
67     "Rock",
68     "Techno",
69     "Industrial",
70     "Alternative",
71     "Ska",
72     "Death Metal",
73     "Pranks",
74     "Soundtrack",
75     "Euro-Techno",
76     "Ambient",
77     "Trip-Hop",
78     "Vocal",
79     "Jazz+Funk",
80     "Fusion",
81     "Trance",
82     "Classical",
83     "Instrumental",
84     "Acid",
85     "House",
86     "Game",
87     "Sound Clip",
88     "Gospel",
89     "Noise",
90     "Alternative Rock",
91     "Bass",
92     "Soul",
93     "Punk",
94     "Space",
95     "Meditative",
96     "Instrumental Pop",
97     "Instrumental Rock",
98     "Ethnic",
99     "Gothic",
100     "Darkwave",
101     "Techno-Industrial",
102     "Electronic",
103     "Pop-Folk",
104     "Eurodance",
105     "Dream",
106     "Southern Rock",
107     "Comedy",
108     "Cult",
109     "Gangsta",
110     "Top 40",
111     "Christian Rap",
112     "Pop/Funk",
113     "Jungle",
114     "Native American",
115     "Cabaret",
116     "New Wave",
117     "Psychedelic",
118     "Rave",
119     "Showtunes",
120     "Trailer",
121     "Lo-Fi",
122     "Tribal",
123     "Acid Punk",
124     "Acid Jazz",
125     "Polka",
126     "Retro",
127     "Musical",
128     "Rock & Roll",
129     "Hard Rock",
130     "Folk",
131     "Folk/Rock",
132     "National Folk",
133     "Swing",
134     "Fusion",
135     "Bebob",
136     "Latin",
137     "Revival",
138     "Celtic",
139     "Bluegrass",
140     "Avantgarde",
141     "Gothic Rock",
142     "Progressive Rock",
143     "Psychedelic Rock",
144     "Symphonic Rock",
145     "Slow Rock",
146     "Big Band",
147     "Chorus",
148     "Easy Listening",
149     "Acoustic",
150     "Humour",
151     "Speech",
152     "Chanson",
153     "Opera",
154     "Chamber Music",
155     "Sonata",
156     "Symphony",
157     "Booty Bass",
158     "Primus",
159     "Porn Groove",
160     "Satire",
161     "Slow Jam",
162     "Club",
163     "Tango",
164     "Samba",
165     "Folklore",
166     "Ballad",
167     "Power Ballad",
168     "Rhythmic Soul",
169     "Freestyle",
170     "Duet",
171     "Punk Rock",
172     "Drum Solo",
173     "A Cappella",
174     "Euro-House",
175     "Dance Hall",
176     "Goa",
177     "Drum & Bass",
178     "Club-House",
179     "Hardcore",
180     "Terror",
181     "Indie",
182     "BritPop",
183     "Negerpunk",
184     "Polsk Punk",
185     "Beat",
186     "Christian Gangsta Rap",
187     "Heavy Metal",
188     "Black Metal",
189     "Crossover",
190     "Contemporary Christian",
191     "Christian Rock",
192     "Merengue",
193     "Salsa",
194     "Thrash Metal",
195     "Anime",
196     "Jpop",
197     "Synthpop"
198 };
199
200 struct id3v2_frame_header {
201     char frame_id[4];
202     unsigned int frame_size;
203     int compression;
204     int data_length_indicator;
205 };
206
207 struct id3v1_tag {
208     char title[30];
209     char artist[30];
210     char album[30];
211     char year[4];
212     char comments[30];
213     char genre;
214 } __attribute__((packed));
215
216 struct plugin {
217     struct lms_plugin plugin;
218     lms_db_audio_t *audio_db;
219 };
220
221 static const char _name[] = "id3";
222 static const struct lms_string_size _exts[] = {
223     LMS_STATIC_STRING_SIZE(".mp3"),
224     LMS_STATIC_STRING_SIZE(".aac")
225 };
226
227 static unsigned int
228 _to_uint(const char *data, int data_size)
229 {
230     unsigned int sum = 0;
231     unsigned int last, i;
232
233     last = data_size > 4 ? 3 : data_size - 1;
234
235     for (i = 0; i <= last; i++)
236         sum |= ((unsigned char) data[i]) << ((last - i) * 8);
237
238     return sum;
239 }
240
241 static inline int
242 _is_id3v2_second_synch_byte(unsigned char byte)
243 {
244     if (byte == 0xff)
245         return 0;
246     if ((byte & 0xE0) == 0xE0)
247         return 1;
248     return 0;
249 }
250
251 static long
252 _find_id3v2(int fd)
253 {
254     long buffer_offset = 0;
255     char buffer[3], *p;
256     int buffer_size = sizeof(buffer);
257     const char pattern[] = "ID3";
258     ssize_t nread;
259
260     /* These variables are used to keep track of a partial match that happens at
261      * the end of a buffer. */
262     int previous_partial_match = -1;
263     int previous_partial_synch_match = 0;
264     int first_synch_byte;
265
266     /* Start the search at the beginning of the file. */
267     lseek(fd, 0, SEEK_SET);
268
269     if ((nread = read(fd, &buffer, buffer_size)) != buffer_size)
270         return -1;
271
272     /* check if pattern is in the beggining of the file */
273     if (memcmp(buffer, pattern, 3) == 0)
274         return 0;
275
276     /* This loop is the crux of the find method.  There are three cases that we
277      * want to account for:
278      * (1) The previously searched buffer contained a partial match of the search
279      * pattern and we want to see if the next one starts with the remainder of
280      * that pattern.
281      *
282      * (2) The search pattern is wholly contained within the current buffer.
283      *
284      * (3) The current buffer ends with a partial match of the pattern.  We will
285      * note this for use in the next iteration, where we will check for the rest
286      * of the pattern. */
287     while (1) {
288         /* (1) previous partial match */
289         if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
290             return -1;
291
292         if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
293             const int pattern_offset = buffer_size - previous_partial_match;
294
295             if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
296                 return buffer_offset - buffer_size + previous_partial_match;
297         }
298
299         /* (2) pattern contained in current buffer */
300         p = buffer;
301         while ((p = memchr(p, 'I', buffer_size))) {
302             if (memcmp(p, pattern, 3) == 0)
303                 return buffer_offset + (p - buffer);
304             p += 1;
305         }
306
307         p = memchr(buffer, 255, buffer_size);
308         if (p)
309             first_synch_byte = p - buffer;
310         else
311             first_synch_byte = -1;
312
313         /* Here we have to loop because there could be several of the first
314          * (11111111) byte, and we want to check all such instances until we find
315          * a full match (11111111 111) or hit the end of the buffer. */
316         while (first_synch_byte >= 0) {
317             /* if this *is not* at the end of the buffer */
318             if (first_synch_byte < buffer_size - 1) {
319                 if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
320                     /* We've found the frame synch pattern. */
321                     return -1;
322                 else
323                     /* We found 11111111 at the end of the current buffer indicating a
324                      * partial match of the synch pattern.  The find() below should
325                      * return -1 and break out of the loop. */
326                     previous_partial_synch_match = 1;
327             }
328
329             /* Check in the rest of the buffer. */
330             p = memchr(p + 1, 255, buffer_size);
331             if (p)
332                 first_synch_byte = p - buffer;
333             else
334                 first_synch_byte = -1;
335         }
336
337         /* (3) partial match */
338         if (buffer[nread - 1] == pattern[1])
339             previous_partial_match = nread - 1;
340         else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
341             previous_partial_match = nread - 2;
342         buffer_offset += buffer_size;
343
344         if ((nread = read(fd, &buffer, sizeof(buffer))) == -1)
345             return -1;
346     }
347
348     return -1;
349 }
350
351 static unsigned int
352 _get_id3v2_frame_header_size(unsigned int version)
353 {
354     switch (version) {
355     case 0:
356     case 1:
357     case 2:
358         return 6;
359     case 3:
360     case 4:
361     default:
362         return 10;
363     }
364 }
365
366 static void
367 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
368 {
369     switch (version) {
370     case 0:
371     case 1:
372     case 2:
373         memcpy(fh->frame_id, data, 3);
374         fh->frame_id[3] = 0;
375         fh->frame_size = _to_uint(data + 3, 3);
376         fh->compression = 0;
377         fh->data_length_indicator = 0;
378         break;
379     case 3:
380         memcpy(fh->frame_id, data, 4);
381         fh->frame_size = _to_uint(data + 4, 4);
382         fh->compression = data[9] & 0x40;
383         fh->data_length_indicator = 0;
384         break;
385     case 4:
386     default:
387         memcpy(fh->frame_id, data, 4);
388         fh->frame_size = _to_uint(data + 4, 4);
389         fh->compression = data[9] & 0x4;
390         fh->data_length_indicator = data[9] & 0x1;
391         break;
392     }
393 }
394
395 static inline void
396 _get_id3v2_frame_info(const char *frame_data, unsigned int frame_size, struct lms_string_size *s)
397 {
398     s->str = malloc(sizeof(char) * (frame_size + 1));
399     memcpy(s->str, frame_data, frame_size);
400     s->str[frame_size] = '\0';
401     s->len = frame_size;
402 }
403
404 static void
405 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
406 {
407     unsigned int frame_size;
408
409     /* TODO proper handle text encoding
410      *
411      * Latin1  = 0
412      * UTF16   = 1
413      * UTF16BE = 2
414      * UTF8    = 3
415      * UTF16LE = 4
416      */
417
418     /* skip first byte - text encoding */
419     frame_data += 1;
420     frame_size = fh->frame_size - 1;
421
422     if (memcmp(fh->frame_id, "TIT2", 4) == 0)
423         _get_id3v2_frame_info(frame_data, frame_size, &info->title);
424     else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
425         _get_id3v2_frame_info(frame_data, frame_size, &info->artist);
426     else if (memcmp(fh->frame_id, "TALB", 4) == 0)
427         _get_id3v2_frame_info(frame_data, frame_size, &info->album);
428     else if (memcmp(fh->frame_id, "TCON", 4) == 0) {
429         /* TODO handle multiple genres */
430         int i, is_number;
431
432         _get_id3v2_frame_info(frame_data, frame_size, &info->genre);
433
434         is_number = 1;
435         for (i = 0; i < info->genre.len; ++i) {
436             if (!isdigit(info->genre.str[i]))
437                 is_number = 0;
438         }
439
440         if ((is_number) &&
441             (info->genre.str) &&
442             (info->genre.len > 0)) {
443             int genre = atoi(info->genre.str);
444             if (genre >= 0 && genre < ID3V1_NUM_GENRES) {
445                 free(info->genre.str);
446                 info->genre.str = strdup(id3v1_genres[(int) genre]);
447                 info->genre.len = strlen(info->genre.str);
448             }
449             else {
450                 /* ignore other genres */
451                 free(info->genre.str);
452                 info->genre.str = NULL;
453                 info->genre.len = 0;
454             }
455         }
456     }
457     else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
458         struct lms_string_size trackno;
459         _get_id3v2_frame_info(frame_data, frame_size, &trackno);
460         info->trackno = atoi(trackno.str);
461         free(trackno.str);
462     }
463 }
464
465 static int
466 _parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
467 {
468     char header_data[10], frame_header_data[10];
469     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
470     int extended_header, footer_present;
471     struct id3v2_frame_header fh;
472
473     lseek(fd, id3v2_offset, SEEK_SET);
474
475     /* parse header */
476     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
477         return -1;
478
479     tag_size = _to_uint(header_data + 6, 4);
480     if (tag_size == 0)
481         return -1;
482
483     /* parse frames */
484     major_version = header_data[3];
485
486     frame_data_pos = 0;
487     frame_data_length = tag_size;
488
489     /* check for extended header */
490     extended_header = header_data[5] & 0x20; /* bit 6 */
491     if (extended_header) {
492         /* skip extended header */
493         unsigned int extended_header_size;
494         char extended_header_data[4];
495
496         if (read(fd, extended_header_data, 4) != 4)
497             return -1;
498         extended_header_size = _to_uint(extended_header_data, 4);
499         lseek(fd, extended_header_size - 4, SEEK_CUR);
500         frame_data_pos += extended_header_size;
501         frame_data_length -= extended_header_size;
502     }
503
504     footer_present = header_data[5] & 0x8;   /* bit 4 */
505     if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
506         frame_data_length -= ID3V2_FOOTER_SIZE;
507
508     frame_header_size = _get_id3v2_frame_header_size(major_version);
509     while (frame_data_pos < frame_data_length - frame_header_size) {
510         if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
511             return -1;
512
513         if (frame_header_data[0] == 0)
514             break;
515
516         _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
517
518         if (!fh.compression &&
519             fh.frame_id[0] == 'T' &&
520             memcmp(fh.frame_id, "TXXX", 4) != 0) {
521             char *frame_data;
522
523             if (fh.data_length_indicator)
524                 lseek(fd, 4, SEEK_CUR);
525
526             frame_data = malloc(sizeof(char) * fh.frame_size);
527             if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
528                 free(frame_data);
529                 return -1;
530             }
531
532             _parse_id3v2_frame(&fh, frame_data, info);
533             free(frame_data);
534         }
535         else {
536             if (fh.data_length_indicator)
537                 lseek(fd, fh.frame_size + 4, SEEK_CUR);
538             else
539                 lseek(fd, fh.frame_size, SEEK_CUR);
540         }
541
542         frame_data_pos += fh.frame_size + frame_header_size;
543     }
544
545     return 0;
546 }
547
548 static int
549 _parse_id3v1(int fd, struct lms_audio_info *info)
550 {
551     struct id3v1_tag tag;
552     if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
553         return -1;
554
555     info->title.str = strdup(tag.title);
556     info->title.len = strlen(info->title.str);
557     info->artist.str = strdup(tag.artist);
558     info->artist.len = strlen(info->artist.str);
559     info->album.str = strdup(tag.album);
560     info->album.len = strlen(info->album.str);
561     info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
562     info->genre.len = strlen(info->genre.str);
563     if (tag.comments[28] == 0 && tag.comments[29] != 0)
564         info->trackno = (unsigned char) tag.comments[29];
565
566     return 0;
567 }
568
569 static void *
570 _match(struct plugin *p, const char *path, int len, int base)
571 {
572     int i;
573
574     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
575     if (i < 0)
576       return NULL;
577     else
578       return (void*)(i + 1);
579 }
580
581 static int
582 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
583 {
584     struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
585     int r, fd;
586     long id3v2_offset;
587
588     fd = open(finfo->path, O_RDONLY);
589     if (fd < 0) {
590         perror("open");
591         return -1;
592     }
593
594     id3v2_offset = _find_id3v2(fd);
595     if (id3v2_offset >= 0) {
596 #if 0
597         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
598                 finfo->path, id3v2_offset);
599 #endif
600         if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
601             r = -2;
602             goto done;
603         }
604     }
605     else {
606         char tag[3];
607 #if 0
608         fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
609 #endif
610         /* check for id3v1 tag */
611         if (lseek(fd, -128, SEEK_END) == -1) {
612             r = -3;
613             goto done;
614         }
615
616         if (read(fd, &tag, 3) == -1) {
617             r = -4;
618             goto done;
619         }
620
621         if (memcmp(tag, "TAG", 3) == 0) {
622 #if 0
623             fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
624 #endif
625             if (_parse_id3v1(fd, &info) != 0) {
626                 r = -5;
627                 goto done;
628             }
629         }
630     }
631
632     lms_string_size_strip_and_free(&info.title);
633     lms_string_size_strip_and_free(&info.artist);
634     lms_string_size_strip_and_free(&info.album);
635     lms_string_size_strip_and_free(&info.genre);
636
637     if (!info.title.str) {
638         int ext_idx;
639         ext_idx = ((int)match) - 1;
640         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
641         info.title.str = malloc((info.title.len + 1) * sizeof(char));
642         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
643         info.title.str[info.title.len] = '\0';
644     }
645     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
646
647     if (info.artist.str)
648         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
649     if (info.album.str)
650         lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
651     if (info.genre.str)
652         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
653
654 #if 0
655     fprintf(stderr, "file %s info\n", finfo->path);
656     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
657     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
658     fprintf(stderr, "\talbum='%s'\n", info.album.str);
659     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
660     fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
661 #endif
662
663     info.id = finfo->id;
664     r = lms_db_audio_add(plugin->audio_db, &info);
665
666   done:
667     close(fd);
668
669     if (info.title.str)
670         free(info.title.str);
671     if (info.artist.str)
672         free(info.artist.str);
673     if (info.album.str)
674         free(info.album.str);
675     if (info.genre.str)
676         free(info.genre.str);
677
678     return r;
679 }
680
681 static int
682 _setup(struct plugin *plugin, struct lms_context *ctxt)
683 {
684     plugin->audio_db = lms_db_audio_new(ctxt->db);
685     if (!plugin->audio_db)
686         return -1;
687     return 0;
688 }
689
690 static int
691 _start(struct plugin *plugin, struct lms_context *ctxt)
692 {
693     return lms_db_audio_start(plugin->audio_db);
694 }
695
696 static int
697 _finish(struct plugin *plugin, struct lms_context *ctxt)
698 {
699     if (plugin->audio_db)
700         lms_db_audio_free(plugin->audio_db);
701     return 0;
702 }
703
704 static int
705 _close(struct plugin *plugin)
706 {
707     free(plugin);
708     return 0;
709 }
710
711 API struct lms_plugin *
712 lms_plugin_open(void)
713 {
714     struct plugin *plugin;
715
716     plugin = (struct plugin *)malloc(sizeof(*plugin));
717     plugin->plugin.name = _name;
718     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
719     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
720     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
721     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
722     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
723     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
724
725     return (struct lms_plugin *)plugin;
726 }