1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the copyright holder nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 #define CONTAINER_IS_BIG_ENDIAN
32 //#define ENABLE_CONTAINERS_LOG_FORMAT
33 //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
34 #define CONTAINER_HELPER_LOG_INDENT(a) 0
35 #include "containers/core/containers_private.h"
36 #include "containers/core/containers_io_helpers.h"
37 #include "containers/core/containers_utils.h"
38 #include "containers/core/containers_logging.h"
39 
40 #include "id3_metadata_strings.h"
41 
42 /******************************************************************************
43 Defines
44 ******************************************************************************/
45 #define ID3_SYNC_SAFE(x) ((((x >> 24) & 0x7f) << 21) | (((x >> 16) & 0x7f) << 14) | \
46                           (((x >>  8) & 0x7f) <<  7) | (((x >>  0) & 0x7f) <<  0))
47 
48 /******************************************************************************
49 Type definitions
50 ******************************************************************************/
51 
52 /******************************************************************************
53 Function prototypes
54 ******************************************************************************/
55 VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * );
56 
57 /******************************************************************************
58 Local Functions
59 ******************************************************************************/
id3_metadata_append(VC_CONTAINER_T * p_ctx,VC_CONTAINER_METADATA_KEY_T key,unsigned int size)60 static VC_CONTAINER_METADATA_T *id3_metadata_append( VC_CONTAINER_T *p_ctx,
61                                                      VC_CONTAINER_METADATA_KEY_T key,
62                                                      unsigned int size )
63 {
64    VC_CONTAINER_METADATA_T *meta, **p_meta;
65    unsigned int i;
66 
67    for (i = 0; i != p_ctx->meta_num; ++i)
68    {
69       if (key == p_ctx->meta[i]->key) break;
70    }
71 
72    /* Avoid duplicate entries for now */
73    if (i < p_ctx->meta_num) return NULL;
74 
75    /* Sanity check size, truncate if necessary */
76    size = MIN(size, 512);
77 
78    /* Allocate a new metadata entry */
79    if((meta = malloc(sizeof(VC_CONTAINER_METADATA_T) + size)) == NULL)
80       return NULL;
81 
82    /* We need to grow the array holding the metadata entries somehow, ideally,
83       we'd like to use a linked structure of some sort but realloc is probably
84       okay in this case */
85    if((p_meta = realloc(p_ctx->meta, sizeof(VC_CONTAINER_METADATA_T *) * (p_ctx->meta_num + 1))) == NULL)
86    {
87       free(meta);
88       return NULL;
89    }
90 
91    p_ctx->meta = p_meta;
92    memset(meta, 0, sizeof(VC_CONTAINER_METADATA_T) + size);
93    p_ctx->meta[p_ctx->meta_num] = meta;
94    meta->key = key;
95    meta->value = (char *)&meta[1];
96    meta->size = size;
97    p_ctx->meta_num++;
98 
99    return meta;
100 }
101 
102 /*****************************************************************************/
id3_read_metadata_entry(VC_CONTAINER_T * p_ctx,VC_CONTAINER_METADATA_KEY_T key,unsigned int len)103 static VC_CONTAINER_METADATA_T *id3_read_metadata_entry( VC_CONTAINER_T *p_ctx,
104    VC_CONTAINER_METADATA_KEY_T key, unsigned int len )
105 {
106    VC_CONTAINER_METADATA_T *meta;
107 
108    if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL)
109    {
110       unsigned int size = meta->size - 1;
111       READ_BYTES(p_ctx, meta->value, size);
112 
113       if (len > size)
114       {
115          LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
116          SKIP_BYTES(p_ctx, len - size);
117       }
118    }
119    else
120    {
121       SKIP_BYTES(p_ctx, len);
122    }
123 
124    return meta;
125 }
126 
127 /*****************************************************************************/
id3_read_metadata_entry_ex(VC_CONTAINER_T * p_ctx,VC_CONTAINER_METADATA_KEY_T key,unsigned int len,const char * encoding)128 static VC_CONTAINER_METADATA_T *id3_read_metadata_entry_ex( VC_CONTAINER_T *p_ctx,
129    VC_CONTAINER_METADATA_KEY_T key, unsigned int len, const char *encoding )
130 {
131    VC_CONTAINER_METADATA_T *meta;
132 
133    if ((meta = id3_metadata_append(p_ctx, key, encoding ? len + 2 : len + 1)) != NULL)
134    {
135       unsigned int size;
136 
137       if (encoding)
138       {
139          size = meta->size - 2;
140          READ_STRING_UTF16(p_ctx, meta->value, size, "ID3v2 data");
141       }
142       else
143       {
144          size = meta->size - 1;
145          READ_STRING(p_ctx, meta->value, size, "ID3v2 data");
146       }
147 
148       if (len > size)
149       {
150          LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
151          SKIP_BYTES(p_ctx, len - size);
152       }
153    }
154 
155    return meta;
156 }
157 
158 /*****************************************************************************/
id3_add_metadata_entry(VC_CONTAINER_T * p_ctx,VC_CONTAINER_METADATA_KEY_T key,const char * value)159 static VC_CONTAINER_METADATA_T *id3_add_metadata_entry( VC_CONTAINER_T *p_ctx,
160    VC_CONTAINER_METADATA_KEY_T key, const char *value )
161 {
162    VC_CONTAINER_METADATA_T *meta;
163    unsigned int len = strlen(value);
164 
165    if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL)
166    {
167       unsigned int size = meta->size - 1;
168 
169       if (len > size)
170       {
171          LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
172       }
173 
174       strncpy(meta->value, value, size);
175    }
176 
177    return meta;
178 }
179 
180 /*****************************************************************************/
id3_read_id3v2_frame(VC_CONTAINER_T * p_ctx,VC_CONTAINER_FOURCC_T frame_id,uint32_t frame_size)181 static VC_CONTAINER_STATUS_T id3_read_id3v2_frame( VC_CONTAINER_T *p_ctx,
182    VC_CONTAINER_FOURCC_T frame_id, uint32_t frame_size )
183 {
184    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
185    VC_CONTAINER_METADATA_KEY_T key;
186    VC_CONTAINER_METADATA_T *meta = NULL;
187    uint8_t encoding;
188    const char *charset = NULL;
189 
190    if(frame_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
191 
192    switch (frame_id)
193    {
194       case VC_FOURCC('T','A','L','B'): key = VC_CONTAINER_METADATA_KEY_ALBUM; break;
195       case VC_FOURCC('T','I','T','2'): key = VC_CONTAINER_METADATA_KEY_TITLE; break;
196       case VC_FOURCC('T','R','C','K'): key = VC_CONTAINER_METADATA_KEY_TRACK; break;
197       case VC_FOURCC('T','P','E','1'): key = VC_CONTAINER_METADATA_KEY_ARTIST; break;
198       case VC_FOURCC('T','C','O','N'): key = VC_CONTAINER_METADATA_KEY_GENRE; break;
199       default: key = VC_CONTAINER_METADATA_KEY_UNKNOWN; break;
200    }
201 
202    if (key == VC_CONTAINER_METADATA_KEY_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
203 
204    encoding = READ_U8(p_ctx, "ID3v2 text encoding byte");
205    frame_size -= 1;
206 
207    switch(encoding)
208    {
209       case 0: /* ISO-8859-1 */
210       case 3: /* UTF-8 */
211          break;
212       case 1: /* UTF-16 with BOM */
213          if(frame_size < 2) return VC_CONTAINER_ERROR_CORRUPTED;
214          SKIP_U16(p_ctx, "ID3v2 text encoding BOM"); /* FIXME: Check BOM, 0xFFFE vs 0xFEFFF */
215          frame_size -= 2;
216          charset = "UTF16-LE";
217          break;
218       case 2: /* UTF-16BE */
219          charset = "UTF16-BE";
220          break;
221       default:
222          LOG_DEBUG(p_ctx, "skipping frame, text encoding %x not supported", encoding);
223          SKIP_BYTES(p_ctx, frame_size);
224          return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
225    }
226 
227    if ((meta = id3_read_metadata_entry_ex(p_ctx, key, frame_size, charset)) != NULL)
228    {
229       if (charset)
230       {
231          utf8_from_charset(charset, meta->value, meta->size, meta->value, meta->size);
232       }
233 
234       meta->encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; /* Okay for ISO-8859-1 as well? */
235 
236       status = VC_CONTAINER_SUCCESS;
237    }
238    else
239    {
240       SKIP_BYTES(p_ctx, frame_size);
241    }
242 
243    return status;
244 }
245 
246 /*****************************************************************************/
id3_read_id3v2_tag(VC_CONTAINER_T * p_ctx)247 static VC_CONTAINER_STATUS_T id3_read_id3v2_tag( VC_CONTAINER_T *p_ctx )
248 {
249    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
250    uint8_t maj_version, flags;
251    uint32_t tag_size, size = 0;
252    uint8_t peek_buf[10];
253 
254    SKIP_STRING(p_ctx, 3, "ID3v2 identifier");
255    maj_version = READ_U8(p_ctx, "ID3v2 version (major)");
256    SKIP_U8(p_ctx, "ID3v2 version (minor)");
257    flags = READ_U8(p_ctx, "ID3v2 flags");
258    tag_size = READ_U32(p_ctx, "ID3v2 syncsafe tag size");
259    tag_size = ID3_SYNC_SAFE(tag_size);
260    LOG_DEBUG(p_ctx, "ID3v2 tag size: %d", tag_size);
261 
262    /* Check that we support this major version */
263    if (!(maj_version == 4 || maj_version == 3 || maj_version == 2))
264       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
265 
266    /* We can't currently handle unsynchronisation */
267    if ((flags >> 7) & 1)
268    {
269       LOG_DEBUG(p_ctx, "skipping unsynchronised tag, not supported");
270       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
271    }
272 
273    /* FIXME: check for version 2.2 and extract iTunes gapless playback information */
274    if (maj_version == 2) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
275 
276    if ((flags >> 6) & 1)
277    {
278       /* Skip extended header, we don't support it */
279       uint32_t ext_hdr_size;
280       LOG_DEBUG(p_ctx, "skipping ID3v2 extended header, not supported");
281       ext_hdr_size = READ_U32(p_ctx, "ID3v2 syncsafe extended header size");
282       ext_hdr_size = ID3_SYNC_SAFE(ext_hdr_size);
283       LOG_DEBUG(p_ctx, "ID3v2 extended header size: %d", ext_hdr_size);
284       SKIP_BYTES(p_ctx, MIN(tag_size, ext_hdr_size));
285       size += ext_hdr_size;
286    }
287 
288    while (PEEK_BYTES(p_ctx, peek_buf, 10) == 10 && size < tag_size)
289    {
290       VC_CONTAINER_FOURCC_T frame_id;
291       uint32_t frame_size;
292       uint8_t format_flags;
293 
294       frame_id = READ_FOURCC(p_ctx, "Frame ID");
295       frame_size = READ_U32(p_ctx, "Frame Size");
296 
297       if (maj_version >= 4)
298       {
299          frame_size = ID3_SYNC_SAFE(frame_size);
300          LOG_DEBUG(p_ctx, "ID3v2 actual frame size: %d", frame_size);
301       }
302 
303       SKIP_U8(p_ctx, "ID3v2 status message flags");
304       format_flags = READ_U8(p_ctx, "ID3v2 format description flags");
305 
306       size += 10;
307 
308       if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS || !frame_id)
309          break;
310 
311       /* Early exit if we detect an invalid tag size */
312       if (size + frame_size > tag_size)
313       {
314          status = VC_CONTAINER_ERROR_FORMAT_INVALID;
315          break;
316       }
317 
318       /* We can't currently handle unsynchronised frames */
319       if ((format_flags >> 1) & 1)
320       {
321          LOG_DEBUG(p_ctx, "skipping unsynchronised frame, not supported");
322          SKIP_BYTES(p_ctx, frame_size);
323          continue;
324       }
325 
326       if ((status = id3_read_id3v2_frame(p_ctx, frame_id, frame_size)) != VC_CONTAINER_SUCCESS)
327       {
328          LOG_DEBUG(p_ctx, "skipping unsupported frame");
329          SKIP_BYTES(p_ctx, frame_size);
330       }
331 
332       size += frame_size;
333    }
334 
335    /* Try to skip to end of tag in case we bailed out early */
336    if (size < tag_size) SKIP_BYTES(p_ctx, tag_size - size);
337 
338    return status;
339 }
340 
341 /*****************************************************************************/
id3_read_id3v1_tag(VC_CONTAINER_T * p_ctx)342 static VC_CONTAINER_STATUS_T id3_read_id3v1_tag( VC_CONTAINER_T *p_ctx )
343 {
344    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
345    uint8_t track, genre;
346    char track_num[4] = {0};
347 
348    SKIP_STRING(p_ctx, 3, "ID3v1 identifier");
349    /* ID3v1 title */
350    id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TITLE, 30);
351    /* ID3v1 artist */
352    id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ARTIST, 30);
353    /* ID3v1 album */
354    id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ALBUM, 30);
355    /* ID3v1 year */
356    id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_YEAR, 4);
357    SKIP_STRING(p_ctx, 28, "ID3v1 comment");
358    if (READ_U8(p_ctx, "ID3v1 zero-byte") == 0)
359    {
360       track = READ_U8(p_ctx, "ID3v1 track");
361       snprintf(track_num, sizeof(track_num) - 1, "%02d", track);
362       id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TRACK, track_num);
363    }
364    else
365    {
366       SKIP_BYTES(p_ctx, 1);
367    }
368    genre = READ_U8(p_ctx, "ID3v1 genre");
369    if (genre < countof(id3_genres))
370    {
371       id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_GENRE, id3_genres[genre]);
372    }
373 
374    status = STREAM_STATUS(p_ctx);
375 
376    return status;
377 }
378 
379 /*****************************************************************************
380 Functions exported as part of the Container Module API
381  *****************************************************************************/
382 
383 /*****************************************************************************/
id3_metadata_reader_close(VC_CONTAINER_T * p_ctx)384 static VC_CONTAINER_STATUS_T id3_metadata_reader_close( VC_CONTAINER_T *p_ctx )
385 {
386    VC_CONTAINER_PARAM_UNUSED(p_ctx);
387    return VC_CONTAINER_SUCCESS;
388 }
389 
390 /*****************************************************************************/
id3_metadata_reader_open(VC_CONTAINER_T * p_ctx)391 VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T *p_ctx )
392 {
393    VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
394    uint8_t peek_buf[10];
395    int64_t data_offset;
396 
397    if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10)
398      return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
399 
400    /* Initial ID3v2 tag(s), variable size */
401    while ((peek_buf[0] == 'I') && (peek_buf[1] == 'D') && (peek_buf[2] == '3'))
402    {
403       if ((status = id3_read_id3v2_tag(p_ctx)) != VC_CONTAINER_SUCCESS)
404       {
405          LOG_DEBUG(p_ctx, "error reading ID3v2 tag (%i)", status);
406       }
407 
408       if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) break;
409    }
410 
411    data_offset = STREAM_POSITION(p_ctx);
412 
413    /* ID3v1 tag, 128 bytes at the end of a file */
414    if (p_ctx->priv->io->size >= INT64_C(128) && STREAM_SEEKABLE(p_ctx))
415    {
416       SEEK(p_ctx, p_ctx->priv->io->size - INT64_C(128));
417       if (PEEK_BYTES(p_ctx, peek_buf, 3) != 3) goto end;
418 
419       if ((peek_buf[0] == 'T') && (peek_buf[1] == 'A') && (peek_buf[2] == 'G'))
420       {
421          if ((status = id3_read_id3v1_tag(p_ctx)) != VC_CONTAINER_SUCCESS)
422          {
423             LOG_DEBUG(p_ctx, "error reading ID3v1 tag (%i)", status);
424          }
425       }
426    }
427 
428 end:
429    /* Restore position to start of data */
430    if (STREAM_POSITION(p_ctx) != data_offset)
431       SEEK(p_ctx, data_offset);
432 
433    p_ctx->priv->pf_close = id3_metadata_reader_close;
434 
435    if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
436 
437    return VC_CONTAINER_SUCCESS;
438 
439 error:
440    LOG_DEBUG(p_ctx, "error opening stream (%i)", status);
441    return status;
442 }
443 
444 /********************************************************************************
445  Entrypoint function
446  ********************************************************************************/
447 
448 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
449 # pragma weak reader_open id3_metadata_reader_open
450 #endif
451