tag: id3v2: Rewrite parsing of text tags to handle multiple NULL terminated strings...
[platform/upstream/gstreamer.git] / gst-libs / gst / tag / id3v2.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
3  * Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <gst/tag/tag.h>
27
28 #include "id3tags.h"
29
30 GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
31 #define GST_CAT_DEFAULT (id3demux_debug)
32
33 #define HANDLE_INVALID_SYNCSAFE
34 static ID3TagsResult
35 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size);
36
37 guint
38 read_synch_uint (guint8 * data, guint size)
39 {
40   gint i;
41   guint result = 0;
42   gint invalid = 0;
43
44   g_assert (size <= 4);
45
46   size--;
47   for (i = 0; i <= size; i++) {
48     invalid |= data[i] & 0x80;
49     result |= (data[i] & 0x7f) << ((size - i) * 7);
50   }
51
52 #ifdef HANDLE_INVALID_SYNCSAFE
53   if (invalid) {
54     GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
55         "- using the actual value instead");
56     result = 0;
57     for (i = 0; i <= size; i++) {
58       result |= data[i] << ((size - i) * 8);
59     }
60   }
61 #endif
62   return result;
63 }
64
65 ID3TagsResult
66 id3demux_read_id3v1_tag (GstBuffer * buffer, guint * id3v1_size,
67     GstTagList ** tags)
68 {
69   GstTagList *new_tags;
70
71   guint8 *data;
72
73   g_return_val_if_fail (buffer != NULL, ID3TAGS_V1_BAD_SIZE);
74
75   data = GST_BUFFER_DATA (buffer);
76
77   if (GST_BUFFER_SIZE (buffer) != ID3V1_TAG_SIZE)
78     return ID3TAGS_V1_BAD_SIZE;
79
80   /* Check that buffer starts with 'TAG' */
81   if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G') {
82     if (id3v1_size)
83       *id3v1_size = 0;
84     GST_DEBUG ("No ID3v1 tag in data");
85     return ID3TAGS_READ_TAG;
86   }
87
88   g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
89
90   new_tags = gst_tag_list_new_from_id3v1 (GST_BUFFER_DATA (buffer));
91   if (new_tags == NULL)
92     return ID3TAGS_BROKEN_TAG;
93
94   if (*tags) {
95     GstTagList *merged;
96
97     merged = gst_tag_list_merge (*tags, new_tags, GST_TAG_MERGE_REPLACE);
98     gst_tag_list_free (*tags);
99     gst_tag_list_free (new_tags);
100     *tags = merged;
101   } else
102     *tags = new_tags;
103
104   return ID3TAGS_READ_TAG;
105 }
106
107 ID3TagsResult
108 id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size,
109     GstTagList ** tags)
110 {
111   guint8 *data;
112   guint read_size;
113   ID3TagsWorking work;
114   guint8 flags;
115   ID3TagsResult result;
116   guint16 version;
117
118   g_return_val_if_fail (buffer != NULL, ID3TAGS_MORE_DATA);
119
120   if (GST_BUFFER_SIZE (buffer) < ID3V2_MARK_SIZE)
121     return ID3TAGS_MORE_DATA;   /* Need more data to decide with */
122
123   data = GST_BUFFER_DATA (buffer);
124
125   /* Check for 'ID3' string at start of buffer */
126   if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
127     if (id3v2_size)
128       *id3v2_size = 0;
129     GST_DEBUG ("No ID3v2 tag in data");
130     return ID3TAGS_READ_TAG;
131   }
132
133   /* OK, get enough data to read the entire header */
134   if (GST_BUFFER_SIZE (buffer) < ID3V2_HDR_SIZE)
135     return ID3TAGS_MORE_DATA;   /* Need more data to decide with */
136
137   /* Read the version */
138   version = GST_READ_UINT16_BE (data + 3);
139
140   /* Read the flags */
141   flags = data[5];
142
143   /* Read the size from the header */
144   read_size = read_synch_uint (data + 6, 4);
145   if (read_size == 0) {
146     /* Tag has no frames attached. Ignore it, but skip the header */
147     if (id3v2_size)
148       *id3v2_size = ID3V2_HDR_SIZE;
149     return ID3TAGS_BROKEN_TAG;
150   }
151   read_size += ID3V2_HDR_SIZE;
152
153   /* Expand the read size to include a footer if there is one */
154   if (flags & ID3V2_HDR_FLAG_FOOTER) {
155     read_size += 10;
156   }
157
158   if (id3v2_size)
159     *id3v2_size = read_size;
160
161   /* Validate the version. At the moment, we only support up to 2.4.0 */
162   if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0) {
163     GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
164         "but decoder only supports 2.%d.%d. Ignoring as per spec.",
165         version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
166     return ID3TAGS_READ_TAG;
167   }
168   GST_DEBUG ("ID3v2 tag with revision 2.%d.%d\n", version >> 8, version & 0xff);
169
170   if (GST_BUFFER_SIZE (buffer) < read_size)
171     return ID3TAGS_MORE_DATA;   /* Need more data to decode with */
172
173   g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
174
175   memset (&work, 0, sizeof (ID3TagsWorking));
176   work.buffer = buffer;
177   work.hdr.version = version;
178   work.hdr.size = read_size;
179   work.hdr.flags = flags;
180   work.hdr.frame_data = GST_BUFFER_DATA (buffer) + ID3V2_HDR_SIZE;
181   if (flags & ID3V2_HDR_FLAG_FOOTER)
182     work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
183   else
184     work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
185
186   result = id3demux_id3v2_frames_to_tag_list (&work, read_size);
187
188   /* Actually read the tags */
189   if (work.tags != NULL) {
190     if (*tags) {
191       GstTagList *merged;
192
193       merged = gst_tag_list_merge (*tags, work.tags, GST_TAG_MERGE_REPLACE);
194       gst_tag_list_free (*tags);
195       gst_tag_list_free (work.tags);
196       *tags = merged;
197     } else
198       *tags = work.tags;
199   }
200
201   if (work.prev_genre)
202     g_free (work.prev_genre);
203
204   return result;
205 }
206
207 static guint
208 id3demux_id3v2_frame_hdr_size (guint id3v2ver)
209 {
210   /* ID3v2 < 2.3.0 only had 6 byte header */
211   switch (ID3V2_VER_MAJOR (id3v2ver)) {
212     case 0:
213     case 1:
214     case 2:
215       return 6;
216     case 3:
217     case 4:
218     default:
219       return 10;
220   }
221 }
222
223 static const gchar *obsolete_frame_ids[] = {
224   "CRM", "EQU", "LNK", "RVA", "TIM", "TSI",     /* From 2.2 */
225   "EQUA", "RVAD", "TIME", "TRDA", "TSIZ",       /* From 2.3 */
226   NULL
227 };
228
229 const struct ID3v2FrameIDConvert
230 {
231   gchar *orig;
232   gchar *new;
233 } frame_id_conversions[] = {
234   /* 2.3.x frames */
235   {
236   "TDAT", "TDRC"}, {
237   "TORY", "TDOR"}, {
238   "TYER", "TDRC"},
239       /* 2.2.x frames */
240   {
241   "BUF", "RBUF"}, {
242   "CNT", "PCNT"}, {
243   "COM", "COMM"}, {
244   "CRA", "AENC"}, {
245   "ETC", "ETCO"}, {
246   "GEO", "GEOB"}, {
247   "IPL", "TIPL"}, {
248   "MCI", "MCDI"}, {
249   "MLL", "MLLT"}, {
250   "PIC", "APIC"}, {
251   "POP", "POPM"}, {
252   "REV", "RVRB"}, {
253   "SLT", "SYLT"}, {
254   "STC", "SYTC"}, {
255   "TAL", "TALB"}, {
256   "TBP", "TBPM"}, {
257   "TCM", "TCOM"}, {
258   "TCR", "TCOP"}, {
259   "TDA", "TDRC"}, {
260   "TDY", "TDLY"}, {
261   "TEN", "TENC"}, {
262   "TFT", "TFLT"}, {
263   "TKE", "TKEY"}, {
264   "TLA", "TLAN"}, {
265   "TLE", "TLEN"}, {
266   "TMT", "TMED"}, {
267   "TOA", "TOAL"}, {
268   "TOF", "TOFN"}, {
269   "TOL", "TOLY"}, {
270   "TOR", "TDOR"}, {
271   "TOT", "TOAL"}, {
272   "TP1", "TPE1"}, {
273   "TP2", "TPE2"}, {
274   "TP3", "TPE3"}, {
275   "TP4", "TPE4"}, {
276   "TPA", "TPOS"}, {
277   "TPB", "TPUB"}, {
278   "TRC", "TSRC"}, {
279   "TRD", "TDRC"}, {
280   "TRK", "TRCK"}, {
281   "TSS", "TSSE"}, {
282   "TT1", "TIT1"}, {
283   "TT2", "TIT2"}, {
284   "TT3", "TIT3"}, {
285   "TXT", "TOLY"}, {
286   "TXX", "TXXX"}, {
287   "TYE", "TDRC"}, {
288   "UFI", "UFID"}, {
289   "ULT", "USLT"}, {
290   "WAF", "WOAF"}, {
291   "WAR", "WOAR"}, {
292   "WAS", "WOAS"}, {
293   "WCM", "WCOM"}, {
294   "WCP", "WCOP"}, {
295   "WPB", "WPUB"}, {
296   "WXX", "WXXX"}, {
297   NULL, NULL}
298 };
299
300 static gboolean
301 convert_fid_to_v240 (gchar * frame_id)
302 {
303   gint i = 0;
304
305   while (obsolete_frame_ids[i] != NULL) {
306     if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
307       return TRUE;
308     i++;
309   }
310
311   i = 0;
312   while (frame_id_conversions[i].orig != NULL) {
313     if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
314       strcpy (frame_id, frame_id_conversions[i].new);
315       return FALSE;
316     }
317     i++;
318   }
319   return FALSE;
320 }
321
322 static ID3TagsResult
323 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
324 {
325   guint frame_hdr_size;
326   gboolean read_a_frame = FALSE;
327   guint8 *start;
328
329   /* Extended header if present */
330   if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
331     work->hdr.ext_hdr_size = read_synch_uint (work->hdr.frame_data, 4);
332     if (work->hdr.ext_hdr_size < 6 ||
333         (work->hdr.ext_hdr_size) > work->hdr.frame_data_size) {
334       return ID3TAGS_BROKEN_TAG;
335     }
336     work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
337     if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
338       GST_DEBUG
339           ("Tag claims extended header, but doesn't have enough bytes. Broken tag");
340       return ID3TAGS_BROKEN_TAG;
341     }
342
343     work->hdr.ext_flag_data = work->hdr.frame_data + 5;
344     work->hdr.frame_data += work->hdr.ext_hdr_size;
345     work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
346   }
347
348   start = GST_BUFFER_DATA (work->buffer);
349   frame_hdr_size = id3demux_id3v2_frame_hdr_size (work->hdr.version);
350   if (work->hdr.frame_data_size <= frame_hdr_size) {
351     GST_DEBUG ("Tag has no data frames. Broken tag");
352     return ID3TAGS_BROKEN_TAG;  /* Must have at least one frame */
353   }
354
355   work->tags = gst_tag_list_new ();
356   g_return_val_if_fail (work->tags != NULL, ID3TAGS_READ_TAG);
357
358   while (work->hdr.frame_data_size > frame_hdr_size) {
359     guint frame_size = 0;
360     gchar frame_id[5] = "";
361     guint16 frame_flags = 0x0;
362     gboolean obsolete_id = FALSE;
363
364     /* Read the header */
365     switch (ID3V2_VER_MAJOR (work->hdr.version)) {
366       case 0:
367       case 1:
368       case 2:
369         frame_id[0] = work->hdr.frame_data[0];
370         frame_id[1] = work->hdr.frame_data[1];
371         frame_id[2] = work->hdr.frame_data[2];
372         frame_id[3] = 0;
373         frame_id[4] = 0;
374         obsolete_id = convert_fid_to_v240 (frame_id);
375
376         frame_size = read_synch_uint (work->hdr.frame_data + 3, 3);
377         frame_flags = 0;
378         break;
379       case 3:
380       case 4:
381       default:
382         frame_id[0] = work->hdr.frame_data[0];
383         frame_id[1] = work->hdr.frame_data[1];
384         frame_id[2] = work->hdr.frame_data[2];
385         frame_id[3] = work->hdr.frame_data[3];
386         frame_id[4] = 0;
387         frame_size = read_synch_uint (work->hdr.frame_data + 4, 4);
388         frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);
389
390         if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
391           frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
392           obsolete_id = convert_fid_to_v240 (frame_id);
393         }
394         break;
395     }
396
397     work->hdr.frame_data += frame_hdr_size;
398     work->hdr.frame_data_size -= frame_hdr_size;
399
400     if (frame_size > work->hdr.frame_data_size ||
401         frame_size == 0 || strcmp (frame_id, "") == 0)
402       break;                    /* No more frames to read */
403
404 #if 0
405     g_print
406         ("Frame @ %d (0x%02x) id %s size %d, next=%d (0x%02x) obsolete=%d\n",
407         work->hdr.frame_data - start, work->hdr.frame_data - start, frame_id,
408         frame_size, work->hdr.frame_data + frame_hdr_size + frame_size - start,
409         work->hdr.frame_data + frame_hdr_size + frame_size - start,
410         obsolete_id);
411 #endif
412
413     if (!obsolete_id) {
414       /* Now, read, decompress etc the contents of the frame
415        * into a TagList entry */
416       work->cur_frame_size = frame_size;
417       work->frame_id = frame_id;
418       work->frame_flags = frame_flags;
419
420       if (id3demux_id3v2_parse_frame (work)) {
421         read_a_frame = TRUE;
422         GST_LOG ("Extracted frame with id %s", frame_id);
423       }
424     }
425     work->hdr.frame_data += frame_size;
426     work->hdr.frame_data_size -= frame_size;
427   }
428
429   if (!read_a_frame) {
430     GST_DEBUG ("Could not extract any frames from tag. Broken tag");
431     gst_tag_list_free (work->tags);
432     work->tags = NULL;
433     return ID3TAGS_BROKEN_TAG;
434   }
435
436   return ID3TAGS_READ_TAG;
437 }