207764a52621e28f51bdb7ba4ee63788e14c2871
[platform/upstream/lightmediascanner.git] / 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 _GNU_SOURCE
32 #define _XOPEN_SOURCE 600
33 #include <lightmediascanner_plugin.h>
34 #include <lightmediascanner_db.h>
35 #include <lightmediascanner_charset_conv.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <ctype.h>
44
45 #define ID3V2_HEADER_SIZE       10
46 #define ID3V2_FOOTER_SIZE       10
47
48 enum ID3Encodings {
49     ID3_ENCODING_LATIN1 = 0,
50     ID3_ENCODING_UTF16,
51     ID3_ENCODING_UTF16BE,
52     ID3_ENCODING_UTF8,
53     ID3_ENCODING_UTF16LE,
54     ID3_ENCODING_LAST
55 };
56 #define ID3_NUM_ENCODINGS ID3_ENCODING_LAST
57
58
59 static const char *id3v1_genres[] = {
60     "Blues",
61     "Classic Rock",
62     "Country",
63     "Dance",
64     "Disco",
65     "Funk",
66     "Grunge",
67     "Hip-Hop",
68     "Jazz",
69     "Metal",
70     "New Age",
71     "Oldies",
72     "Other",
73     "Pop",
74     "R&B",
75     "Rap",
76     "Reggae",
77     "Rock",
78     "Techno",
79     "Industrial",
80     "Alternative",
81     "Ska",
82     "Death Metal",
83     "Pranks",
84     "Soundtrack",
85     "Euro-Techno",
86     "Ambient",
87     "Trip-Hop",
88     "Vocal",
89     "Jazz+Funk",
90     "Fusion",
91     "Trance",
92     "Classical",
93     "Instrumental",
94     "Acid",
95     "House",
96     "Game",
97     "Sound Clip",
98     "Gospel",
99     "Noise",
100     "Alternative Rock",
101     "Bass",
102     "Soul",
103     "Punk",
104     "Space",
105     "Meditative",
106     "Instrumental Pop",
107     "Instrumental Rock",
108     "Ethnic",
109     "Gothic",
110     "Darkwave",
111     "Techno-Industrial",
112     "Electronic",
113     "Pop-Folk",
114     "Eurodance",
115     "Dream",
116     "Southern Rock",
117     "Comedy",
118     "Cult",
119     "Gangsta",
120     "Top 40",
121     "Christian Rap",
122     "Pop/Funk",
123     "Jungle",
124     "Native American",
125     "Cabaret",
126     "New Wave",
127     "Psychedelic",
128     "Rave",
129     "Showtunes",
130     "Trailer",
131     "Lo-Fi",
132     "Tribal",
133     "Acid Punk",
134     "Acid Jazz",
135     "Polka",
136     "Retro",
137     "Musical",
138     "Rock & Roll",
139     "Hard Rock",
140     "Folk",
141     "Folk/Rock",
142     "National Folk",
143     "Swing",
144     "Fusion",
145     "Bebob",
146     "Latin",
147     "Revival",
148     "Celtic",
149     "Bluegrass",
150     "Avantgarde",
151     "Gothic Rock",
152     "Progressive Rock",
153     "Psychedelic Rock",
154     "Symphonic Rock",
155     "Slow Rock",
156     "Big Band",
157     "Chorus",
158     "Easy Listening",
159     "Acoustic",
160     "Humour",
161     "Speech",
162     "Chanson",
163     "Opera",
164     "Chamber Music",
165     "Sonata",
166     "Symphony",
167     "Booty Bass",
168     "Primus",
169     "Porn Groove",
170     "Satire",
171     "Slow Jam",
172     "Club",
173     "Tango",
174     "Samba",
175     "Folklore",
176     "Ballad",
177     "Power Ballad",
178     "Rhythmic Soul",
179     "Freestyle",
180     "Duet",
181     "Punk Rock",
182     "Drum Solo",
183     "A Cappella",
184     "Euro-House",
185     "Dance Hall",
186     "Goa",
187     "Drum & Bass",
188     "Club-House",
189     "Hardcore",
190     "Terror",
191     "Indie",
192     "BritPop",
193     "Negerpunk",
194     "Polsk Punk",
195     "Beat",
196     "Christian Gangsta Rap",
197     "Heavy Metal",
198     "Black Metal",
199     "Crossover",
200     "Contemporary Christian",
201     "Christian Rock",
202     "Merengue",
203     "Salsa",
204     "Thrash Metal",
205     "Anime",
206     "Jpop",
207     "Synthpop"
208 };
209 #define ID3V1_NUM_GENRES (sizeof(id3v1_genres) / sizeof(char *))
210
211 struct id3_info {
212     struct lms_string_size title;
213     struct lms_string_size artist;
214     struct lms_string_size album;
215     struct lms_string_size genre;
216     unsigned char trackno;
217     int cur_artist_priority;
218 };
219
220 struct id3v2_frame_header {
221     char frame_id[4];
222     unsigned int frame_size;
223     int compression;
224     int data_length_indicator;
225 };
226
227 struct id3v1_tag {
228     char title[30];
229     char artist[30];
230     char album[30];
231     char year[4];
232     char comments[30];
233     char genre;
234 } __attribute__((packed));
235
236 struct plugin {
237     struct lms_plugin plugin;
238     lms_db_audio_t *audio_db;
239     lms_charset_conv_t *cs_convs[ID3_NUM_ENCODINGS];
240 };
241
242 static const char _name[] = "id3";
243 static const struct lms_string_size _exts[] = {
244     LMS_STATIC_STRING_SIZE(".mp3"),
245     LMS_STATIC_STRING_SIZE(".aac")
246 };
247
248 static unsigned int
249 _to_uint(const char *data, int data_size)
250 {
251     unsigned int sum = 0;
252     unsigned int last, i;
253
254     last = data_size > 4 ? 3 : data_size - 1;
255
256     for (i = 0; i <= last; i++)
257         sum |= ((unsigned char) data[i]) << ((last - i) * 8);
258
259     return sum;
260 }
261
262 static inline int
263 _is_id3v2_second_synch_byte(unsigned char byte)
264 {
265     if (byte == 0xff)
266         return 0;
267     if ((byte & 0xE0) == 0xE0)
268         return 1;
269     return 0;
270 }
271
272 static long
273 _find_id3v2(int fd)
274 {
275     long buffer_offset = 0;
276     char buffer[3], *p;
277     int buffer_size = sizeof(buffer);
278     const char pattern[] = "ID3";
279     ssize_t nread;
280
281     /* These variables are used to keep track of a partial match that happens at
282      * the end of a buffer. */
283     int previous_partial_match = -1;
284     int previous_partial_synch_match = 0;
285     int first_synch_byte;
286
287     /* Start the search at the beginning of the file. */
288     lseek(fd, 0, SEEK_SET);
289
290     if ((nread = read(fd, &buffer, buffer_size)) != buffer_size)
291         return -1;
292
293     /* check if pattern is in the beggining of the file */
294     if (memcmp(buffer, pattern, 3) == 0)
295         return 0;
296
297     /* This loop is the crux of the find method.  There are three cases that we
298      * want to account for:
299      * (1) The previously searched buffer contained a partial match of the search
300      * pattern and we want to see if the next one starts with the remainder of
301      * that pattern.
302      *
303      * (2) The search pattern is wholly contained within the current buffer.
304      *
305      * (3) The current buffer ends with a partial match of the pattern.  We will
306      * note this for use in the next iteration, where we will check for the rest
307      * of the pattern. */
308     while (1) {
309         /* (1) previous partial match */
310         if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
311             return -1;
312
313         if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
314             const int pattern_offset = buffer_size - previous_partial_match;
315
316             if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
317                 return buffer_offset - buffer_size + previous_partial_match;
318         }
319
320         /* (2) pattern contained in current buffer */
321         p = buffer;
322         while ((p = memchr(p, 'I', buffer_size - (p - buffer)))) {
323             if (buffer_size - (p - buffer) < 3)
324                 break;
325
326             if (memcmp(p, pattern, 3) == 0)
327                 return buffer_offset + (p - buffer);
328
329             p += 1;
330         }
331
332         p = memchr(buffer, 255, buffer_size);
333         if (p)
334             first_synch_byte = p - buffer;
335         else
336             first_synch_byte = -1;
337
338         /* Here we have to loop because there could be several of the first
339          * (11111111) byte, and we want to check all such instances until we find
340          * a full match (11111111 111) or hit the end of the buffer. */
341         while (first_synch_byte >= 0) {
342             /* if this *is not* at the end of the buffer */
343             if (first_synch_byte < buffer_size - 1) {
344                 if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
345                     /* We've found the frame synch pattern. */
346                     return -1;
347                 else
348                     /* We found 11111111 at the end of the current buffer indicating a
349                      * partial match of the synch pattern.  The find() below should
350                      * return -1 and break out of the loop. */
351                     previous_partial_synch_match = 1;
352             }
353
354             /* Check in the rest of the buffer. */
355             p = memchr(p + 1, 255, buffer_size - (p + 1 - buffer));
356             if (p)
357                 first_synch_byte = p - buffer;
358             else
359                 first_synch_byte = -1;
360         }
361
362         /* (3) partial match */
363         if (buffer[nread - 1] == pattern[1])
364             previous_partial_match = nread - 1;
365         else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
366             previous_partial_match = nread - 2;
367         buffer_offset += buffer_size;
368
369         if ((nread = read(fd, &buffer, sizeof(buffer))) == -1)
370             return -1;
371     }
372
373     return -1;
374 }
375
376 static unsigned int
377 _get_id3v2_frame_header_size(unsigned int version)
378 {
379     switch (version) {
380     case 0:
381     case 1:
382     case 2:
383         return 6;
384     case 3:
385     case 4:
386     default:
387         return 10;
388     }
389 }
390
391 static void
392 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
393 {
394     switch (version) {
395     case 0:
396     case 1:
397     case 2:
398         memcpy(fh->frame_id, data, 3);
399         fh->frame_id[3] = 0;
400         fh->frame_size = _to_uint(data + 3, 3);
401         fh->compression = 0;
402         fh->data_length_indicator = 0;
403         break;
404     case 3:
405         memcpy(fh->frame_id, data, 4);
406         fh->frame_size = _to_uint(data + 4, 4);
407         fh->compression = data[9] & 0x40;
408         fh->data_length_indicator = 0;
409         break;
410     case 4:
411     default:
412         memcpy(fh->frame_id, data, 4);
413         fh->frame_size = _to_uint(data + 4, 4);
414         fh->compression = data[9] & 0x4;
415         fh->data_length_indicator = data[9] & 0x1;
416         break;
417     }
418 }
419
420 static inline void
421 _get_id3v2_frame_info(const char *frame_data, unsigned int frame_size, struct lms_string_size *s, lms_charset_conv_t *cs_conv)
422 {
423     if (frame_size > s->len)
424         s->str = realloc(s->str, sizeof(char) * (frame_size + 1));
425     memcpy(s->str, frame_data, frame_size);
426     s->str[frame_size] = '\0';
427     s->len = frame_size;
428     if (cs_conv)
429         lms_charset_conv(cs_conv, &s->str, &s->len);
430 }
431
432 static int
433 _get_id3v1_genre(int genre, struct lms_string_size *out)
434 {
435     if (genre >= 0 && genre < ID3V1_NUM_GENRES) {
436         out->str = strdup(id3v1_genres[genre]);
437         out->len = strlen(out->str);
438         return 0;
439     }
440     return -1;
441 }
442
443 static inline int
444 _parse_id3v1_genre(const char *str_genre, struct lms_string_size *out)
445 {
446     return _get_id3v1_genre(atoi(str_genre), out);
447 }
448
449 static inline void
450 _get_id3v2_genre(const char *frame_data, unsigned int frame_size, struct lms_string_size *out, lms_charset_conv_t *cs_conv)
451 {
452     int i, is_number;
453     struct lms_string_size genre = {0};
454
455     _get_id3v2_frame_info(frame_data, frame_size, &genre, cs_conv);
456
457     if (!genre.str)
458         return;
459
460     if (out->str)
461         free(out->str);
462
463     is_number = (genre.len != 0 && genre.str[0] != '(');
464     if (is_number) {
465         for (i = 0; i < genre.len; ++i) {
466             if (!isdigit(genre.str[i])) {
467                 is_number = 0;
468                 break;
469             }
470         }
471     }
472
473     if (is_number && _parse_id3v1_genre(genre.str, out) == 0) {
474         /* id3v1 genre found */
475         free(genre.str);
476         return;
477     }
478
479     /* ID3v2.3 "content type" can contain a ID3v1 genre number in parenthesis at
480      * the beginning of the field. If this is all that the field contains, do a
481      * translation from that number to the name and return that.  If there is a
482      * string folloing the ID3v1 genre number, that is considered to be
483      * authoritative and we return that instead. Or finally, the field may
484      * simply be free text, in which case we just return the value. */
485
486     if (genre.len > 1 && genre.str[0] == '(') {
487         char *closing = NULL;
488
489         if (genre.str[genre.len - 1] == ')') {
490             closing = strchr(genre.str, ')');
491             if (closing == genre.str + genre.len - 1) {
492                 /* ) is the last character and only appears once in the string */
493                 /* get the id3v1 genre enclosed by parentheses */
494                 if (_parse_id3v1_genre(genre.str + 1, out) == 0) {
495                     free(genre.str);
496                     return;
497                 }
498             }
499         }
500
501         /* get the string followed by the id3v1 genre */
502         if (!closing)
503             closing = strchr(genre.str, ')');
504
505         if (closing) {
506             out->str = strdup(closing + 1);
507             out->len = genre.len - (closing + 1 - genre.str);
508             free(genre.str);
509             return;
510         }
511     }
512
513     /* pure text */
514     *out = genre;
515 }
516
517 static void
518 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct id3_info *info, lms_charset_conv_t **cs_convs)
519 {
520     lms_charset_conv_t *cs_conv = NULL;
521     unsigned int text_encoding, frame_size;
522     static const int artist_priorities[] = { 3, 4, 2, 1 };
523
524 #if 0
525     fprintf(stderr, "frame id = %.4s frame size = %d text encoding = %d\n", fh->frame_id, fh->frame_size, frame_data[0]);
526 #endif
527
528     /* Latin1  = 0
529      * UTF16   = 1
530      * UTF16BE = 2
531      * UTF8    = 3
532      * UTF16LE = 4
533      */
534     text_encoding = frame_data[0];
535
536     /* skip first byte - text encoding */
537     frame_data += 1;
538     frame_size = fh->frame_size - 1;
539
540     if (text_encoding >= 0 && text_encoding < ID3_NUM_ENCODINGS) {
541         if (text_encoding == ID3_ENCODING_UTF16) {
542             if (memcmp(frame_data, "\xfe\xff", 2) == 0)
543                 text_encoding = ID3_ENCODING_UTF16BE;
544             else
545                 text_encoding = ID3_ENCODING_UTF16LE;
546             frame_data += 2;
547             frame_size -= 2;
548         }
549         cs_conv = cs_convs[text_encoding];
550     }
551
552     /* ID3v2.2 used 3 bytes for the frame id, so let's check it */
553     if (memcmp(fh->frame_id, "TIT2", 4) == 0 ||
554         memcmp(fh->frame_id, "TT2", 3) == 0)
555         _get_id3v2_frame_info(frame_data, frame_size, &info->title, cs_conv);
556     else if (memcmp(fh->frame_id, "TP", 2) == 0) {
557         int index = -1;
558         struct lms_string_size artist = {0};
559
560         if (memcmp(fh->frame_id, "TPE", 3) == 0) {
561             /* this check shouldn't be needed, but let's make sure */
562             if (fh->frame_id[3] >= '1' && fh->frame_id[3] <= '4')
563                 index = fh->frame_id[3] - '1';
564         }
565         else {
566             /* ignore TPA, TPB */
567             if (fh->frame_id[2] >= '1' && fh->frame_id[2] <= '4')
568                 index = fh->frame_id[2] - '1';
569         }
570
571         if (index != -1 &&
572             artist_priorities[index] > info->cur_artist_priority) {
573             info->cur_artist_priority = artist_priorities[index];
574             _get_id3v2_frame_info(frame_data, frame_size, &artist, cs_conv);
575             lms_string_size_strip_and_free(&artist);
576             if (artist.str) {
577                 if (info->artist.str)
578                     free(info->artist.str);
579                 info->artist = artist;
580             }
581         }
582     }
583     /* TALB, TAL */
584     else if (memcmp(fh->frame_id, "TAL", 3) == 0)
585         _get_id3v2_frame_info(frame_data, frame_size, &info->album, cs_conv);
586     /* TCON, TCO */
587     else if (memcmp(fh->frame_id, "TCO", 3) == 0)
588         _get_id3v2_genre(frame_data, frame_size, &info->genre, cs_conv);
589     else if (memcmp(fh->frame_id, "TRCK", 4) == 0 ||
590              memcmp(fh->frame_id, "TRK", 3) == 0) {
591         struct lms_string_size trackno = {0};
592         _get_id3v2_frame_info(frame_data, frame_size, &trackno, cs_conv);
593         info->trackno = atoi(trackno.str);
594         free(trackno.str);
595     }
596 }
597
598 static int
599 _parse_id3v2(int fd, long id3v2_offset, struct id3_info *info, lms_charset_conv_t **cs_convs)
600 {
601     char header_data[10], frame_header_data[10];
602     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
603     int extended_header, footer_present;
604     struct id3v2_frame_header fh;
605     size_t nread;
606
607     lseek(fd, id3v2_offset, SEEK_SET);
608
609     /* parse header */
610     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
611         return -1;
612
613     tag_size = _to_uint(header_data + 6, 4);
614     if (tag_size == 0)
615         return -1;
616
617     /* parse frames */
618     major_version = header_data[3];
619
620     frame_data_pos = 0;
621     frame_data_length = tag_size;
622
623     /* check for extended header */
624     extended_header = header_data[5] & 0x20; /* bit 6 */
625     if (extended_header) {
626         /* skip extended header */
627         unsigned int extended_header_size;
628         char extended_header_data[4];
629
630         if (read(fd, extended_header_data, 4) != 4)
631             return -1;
632         extended_header_size = _to_uint(extended_header_data, 4);
633         lseek(fd, extended_header_size - 4, SEEK_CUR);
634         frame_data_pos += extended_header_size;
635         frame_data_length -= extended_header_size;
636     }
637
638     footer_present = header_data[5] & 0x8;   /* bit 4 */
639     if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
640         frame_data_length -= ID3V2_FOOTER_SIZE;
641
642     frame_header_size = _get_id3v2_frame_header_size(major_version);
643     while (frame_data_pos < frame_data_length - frame_header_size) {
644         nread = read(fd, frame_header_data, frame_header_size);
645         if (nread == 0)
646             break;
647
648         if (nread != frame_header_size)
649             return -1;
650
651         if (frame_header_data[0] == 0)
652             break;
653
654         _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
655         if (!fh.frame_size)
656             break;
657
658         if (!fh.compression &&
659             fh.frame_id[0] == 'T' &&
660             memcmp(fh.frame_id, "TXXX", 4) != 0) {
661             char *frame_data;
662
663             if (fh.data_length_indicator)
664                 lseek(fd, 4, SEEK_CUR);
665
666             frame_data = malloc(sizeof(char) * fh.frame_size);
667             if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
668                 free(frame_data);
669                 return -1;
670             }
671
672             _parse_id3v2_frame(&fh, frame_data, info, cs_convs);
673             free(frame_data);
674         }
675         else {
676             if (fh.data_length_indicator)
677                 lseek(fd, fh.frame_size + 4, SEEK_CUR);
678             else
679                 lseek(fd, fh.frame_size, SEEK_CUR);
680         }
681
682         frame_data_pos += fh.frame_size + frame_header_size;
683     }
684
685     return 0;
686 }
687
688 static int
689 _parse_id3v1(int fd, struct id3_info *info, lms_charset_conv_t *cs_conv)
690 {
691     struct id3v1_tag tag;
692     if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
693         return -1;
694
695     info->title.str = strndup(tag.title, 30);
696     info->title.len = strlen(info->title.str);
697     lms_charset_conv(cs_conv, &info->title.str, &info->title.len);
698     info->artist.str = strndup(tag.artist, 30);
699     info->artist.len = strlen(info->artist.str);
700     lms_charset_conv(cs_conv, &info->artist.str, &info->artist.len);
701     info->album.str = strndup(tag.album, 30);
702     info->album.len = strlen(info->album.str);
703     lms_charset_conv(cs_conv, &info->album.str, &info->album.len);
704     _get_id3v1_genre(tag.genre, &info->genre);
705     if (tag.comments[28] == 0 && tag.comments[29] != 0)
706         info->trackno = (unsigned char) tag.comments[29];
707
708     return 0;
709 }
710
711 static void *
712 _match(struct plugin *p, const char *path, int len, int base)
713 {
714     int i;
715
716     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
717     if (i < 0)
718       return NULL;
719     else
720       return (void*)(i + 1);
721 }
722
723 static int
724 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
725 {
726     struct id3_info info = {{0}, {0}, {0}, {0}, 0, -1};
727     struct lms_audio_info audio_info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
728     int r, fd;
729     long id3v2_offset;
730
731     fd = open(finfo->path, O_RDONLY);
732     if (fd < 0) {
733         perror("open");
734         return -1;
735     }
736
737     id3v2_offset = _find_id3v2(fd);
738     if (id3v2_offset >= 0) {
739 #if 0
740         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
741                 finfo->path, id3v2_offset);
742 #endif
743         if (_parse_id3v2(fd, id3v2_offset, &info, plugin->cs_convs) != 0) {
744             r = -2;
745             goto done;
746         }
747     }
748     else {
749         char tag[3];
750 #if 0
751         fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
752 #endif
753         /* check for id3v1 tag */
754         if (lseek(fd, -128, SEEK_END) == -1) {
755             r = -3;
756             goto done;
757         }
758
759         if (read(fd, &tag, 3) == -1) {
760             r = -4;
761             goto done;
762         }
763
764         if (memcmp(tag, "TAG", 3) == 0) {
765 #if 0
766             fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
767 #endif
768             if (_parse_id3v1(fd, &info, ctxt->cs_conv) != 0) {
769                 r = -5;
770                 goto done;
771             }
772         }
773     }
774
775     lms_string_size_strip_and_free(&info.title);
776     lms_string_size_strip_and_free(&info.artist);
777     lms_string_size_strip_and_free(&info.album);
778     lms_string_size_strip_and_free(&info.genre);
779
780     if (!info.title.str) {
781         int ext_idx;
782         ext_idx = ((int)match) - 1;
783         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
784         info.title.str = malloc((info.title.len + 1) * sizeof(char));
785         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
786         info.title.str[info.title.len] = '\0';
787         lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
788     }
789
790 #if 0
791     fprintf(stderr, "file %s info\n", finfo->path);
792     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
793     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
794     fprintf(stderr, "\talbum='%s'\n", info.album.str);
795     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
796     fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
797 #endif
798
799     audio_info.id = finfo->id;
800     audio_info.title = info.title;
801     audio_info.artist = info.artist;
802     audio_info.album = info.album;
803     audio_info.genre = info.genre;
804     audio_info.trackno = info.trackno;
805     r = lms_db_audio_add(plugin->audio_db, &audio_info);
806
807   done:
808     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
809     close(fd);
810
811     if (info.title.str)
812         free(info.title.str);
813     if (info.artist.str)
814         free(info.artist.str);
815     if (info.album.str)
816         free(info.album.str);
817     if (info.genre.str)
818         free(info.genre.str);
819
820     return r;
821 }
822
823 static int
824 _setup(struct plugin *plugin, struct lms_context *ctxt)
825 {
826     int i;
827     const char *id3_encodings[ID3_NUM_ENCODINGS] = {
828         "Latin1",
829         NULL, /* UTF-16 */
830         "UTF-16BE",
831         NULL, /* UTF-8 */
832         "UTF-16LE",
833     };
834
835     plugin->audio_db = lms_db_audio_new(ctxt->db);
836     if (!plugin->audio_db)
837         return -1;
838
839     for (i = 0; i < ID3_NUM_ENCODINGS; ++i) {
840         /* do not create charset conv for UTF-8 encoding */
841         if (!id3_encodings[i]) {
842             plugin->cs_convs[i] = NULL;
843             continue;
844         }
845         plugin->cs_convs[i] = lms_charset_conv_new_full(0, 0);
846         if (!plugin->cs_convs[i])
847             return -1;
848         lms_charset_conv_add(plugin->cs_convs[i], id3_encodings[i]);
849     }
850
851     return 0;
852 }
853
854 static int
855 _start(struct plugin *plugin, struct lms_context *ctxt)
856 {
857     return lms_db_audio_start(plugin->audio_db);
858 }
859
860 static int
861 _finish(struct plugin *plugin, struct lms_context *ctxt)
862 {
863     int i;
864
865     if (plugin->audio_db)
866         lms_db_audio_free(plugin->audio_db);
867
868     for (i = 0; i < ID3_NUM_ENCODINGS; ++i) {
869         if (plugin->cs_convs[i])
870             lms_charset_conv_free(plugin->cs_convs[i]);
871     }
872
873     return 0;
874 }
875
876 static int
877 _close(struct plugin *plugin)
878 {
879     free(plugin);
880     return 0;
881 }
882
883 API struct lms_plugin *
884 lms_plugin_open(void)
885 {
886     struct plugin *plugin;
887
888     plugin = (struct plugin *)malloc(sizeof(*plugin));
889     plugin->plugin.name = _name;
890     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
891     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
892     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
893     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
894     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
895     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
896
897     return (struct lms_plugin *)plugin;
898 }