diff options
-rw-r--r-- | NEWS | 16 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | src/decoder/_flac_common.c | 118 | ||||
-rw-r--r-- | src/decoder/mad_plugin.c | 34 | ||||
-rw-r--r-- | src/directory.c | 11 | ||||
-rw-r--r-- | src/output/shout_plugin.c | 7 | ||||
-rw-r--r-- | src/output_control.c | 11 | ||||
-rw-r--r-- | src/output_internal.h | 6 | ||||
-rw-r--r-- | src/output_thread.c | 3 | ||||
-rw-r--r-- | src/tag_ape.c | 12 | ||||
-rw-r--r-- | src/update.c | 11 |
11 files changed, 159 insertions, 73 deletions
@@ -27,6 +27,22 @@ ver 0.16 (20??/??/??) * obey $(sysconfdir) for default mpd.conf location +ver 0.15.2 (2009/08/15) +* tags: + - ape: check the tag size (fixes integer underflow) + - ape: added protection against large memory allocations +* decoders: + - mad: skip ID3 frames when libid3tag is disabled + - flac: parse all replaygain tags + - flac: don't allocate cuesheet twice (memleak) +* output: + - shout: fixed stuck pause bug + - shout: minimize the unpause latency +* update: free empty path string (memleak) +* update: free temporary string in container scan (memleak) +* directory: free empty directories after removing them (memleak) + + ver 0.15.1 (2009/07/15) * decoders: - flac: fix assertion failure in tag_free() call diff --git a/configure.ac b/configure.ac index 600b51df0..67b2f2957 100644 --- a/configure.ac +++ b/configure.ac @@ -629,8 +629,7 @@ dnl audio output plugins dnl AC_ARG_ENABLE(alsa, - AS_HELP_STRING([--enable-alsa], - [disable ALSA support]),, + AS_HELP_STRING([--enable-alsa], [enable ALSA support]),, enable_alsa=auto) AC_ARG_ENABLE(ao, diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c index 7b3453854..09f7269bd 100644 --- a/src/decoder/_flac_common.c +++ b/src/decoder/_flac_common.c @@ -40,53 +40,57 @@ flac_data_init(struct flac_data *data, struct decoder * decoder, data->tag = NULL; } -static bool +static void flac_find_float_comment(const FLAC__StreamMetadata *block, - const char *cmnt, float *fl) + const char *cmnt, float *fl, bool *found_r) { - int offset = - FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, cmnt); - - if (offset >= 0) { - size_t pos = strlen(cmnt) + 1; /* 1 is for '=' */ - int len = block->data.vorbis_comment.comments[offset].length - - pos; - if (len > 0) { - unsigned char tmp; - unsigned char *p = &(block->data.vorbis_comment. - comments[offset].entry[pos]); - tmp = p[len]; - p[len] = '\0'; - *fl = (float)atof((char *)p); - p[len] = tmp; - - return true; - } - } + int offset; + size_t pos; + int len; + unsigned char tmp, *p; + + offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, + cmnt); + if (offset < 0) + return; - return false; + pos = strlen(cmnt) + 1; /* 1 is for '=' */ + len = block->data.vorbis_comment.comments[offset].length - pos; + if (len <= 0) + return; + + p = &block->data.vorbis_comment.comments[offset].entry[pos]; + tmp = p[len]; + p[len] = '\0'; + *fl = (float)atof((char *)p); + p[len] = tmp; + + *found_r = true; } -/* replaygain stuff by AliasMrJones */ static void flac_parse_replay_gain(const FLAC__StreamMetadata *block, struct flac_data *data) { - bool found; + bool found = false; if (data->replay_gain_info) replay_gain_info_free(data->replay_gain_info); data->replay_gain_info = replay_gain_info_new(); - found = flac_find_float_comment(block, "replaygain_album_gain", - &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain) || - flac_find_float_comment(block, "replaygain_album_peak", - &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak) || - flac_find_float_comment(block, "replaygain_track_gain", - &data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain) || - flac_find_float_comment(block, "replaygain_track_peak", - &data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak); + flac_find_float_comment(block, "replaygain_album_gain", + &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain, + &found); + flac_find_float_comment(block, "replaygain_album_peak", + &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak, + &found); + flac_find_float_comment(block, "replaygain_track_gain", + &data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain, + &found); + flac_find_float_comment(block, "replaygain_track_peak", + &data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak, + &found); if (!found) { replay_gain_info_free(data->replay_gain_info); @@ -106,25 +110,27 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, size_t char_tnum_length = 0; const char *comment = (const char*)entry->entry; - if (entry->length > name_length && - g_ascii_strncasecmp(comment, name, name_length) == 0) { - if (char_tnum != NULL) { - char_tnum_length = strlen(char_tnum); - if (entry->length > name_length + char_tnum_length + 2 && - comment[name_length] == '[' && - g_ascii_strncasecmp(comment + name_length + 1, - char_tnum, char_tnum_length) == 0 && - comment[name_length + char_tnum_length + 1] == ']') - name_length = name_length + char_tnum_length + 2; - else if (entry->length > name_length + char_tnum_length && - g_ascii_strncasecmp(comment + name_length, - char_tnum, char_tnum_length) == 0) - name_length = name_length + char_tnum_length; - } - if (comment[name_length] == '=') { - *length_r = entry->length - name_length - 1; - return comment + name_length + 1; - } + if (entry->length <= name_length || + g_ascii_strncasecmp(comment, name, name_length) != 0) + return NULL; + + if (char_tnum != NULL) { + char_tnum_length = strlen(char_tnum); + if (entry->length > name_length + char_tnum_length + 2 && + comment[name_length] == '[' && + g_ascii_strncasecmp(comment + name_length + 1, + char_tnum, char_tnum_length) == 0 && + comment[name_length + char_tnum_length + 1] == ']') + name_length = name_length + char_tnum_length + 2; + else if (entry->length > name_length + char_tnum_length && + g_ascii_strncasecmp(comment + name_length, + char_tnum, char_tnum_length) == 0) + name_length = name_length + char_tnum_length; + } + + if (comment[name_length] == '=') { + *length_r = entry->length - name_length - 1; + return comment + name_length + 1; } return NULL; @@ -370,13 +376,15 @@ char* flac_cue_track( const char* pathname, const unsigned int tnum) { - FLAC__StreamMetadata* cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET); + FLAC__bool success; + FLAC__StreamMetadata* cs; - FLAC__metadata_get_cuesheet(pathname, &cs); - - if (cs == NULL) + success = FLAC__metadata_get_cuesheet(pathname, &cs); + if (!success) return NULL; + assert(cs != NULL); + if (cs->data.cue_sheet.num_tracks <= 1) { FLAC__metadata_object_delete(cs); diff --git a/src/decoder/mad_plugin.c b/src/decoder/mad_plugin.c index 85f4506d2..c5287564f 100644 --- a/src/decoder/mad_plugin.c +++ b/src/decoder/mad_plugin.c @@ -350,11 +350,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag) } #endif -#ifdef HAVE_ID3TAG static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, struct tag **mpd_tag, struct replay_gain_info **replay_gain_info_r) { +#ifdef HAVE_ID3TAG struct id3_tag *id3_tag = NULL; id3_length_t count; id3_byte_t const *id3_data; @@ -418,8 +418,34 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, id3_tag_delete(id3_tag); g_free(allocated); -} +#else /* !HAVE_ID3TAG */ + (void)mpd_tag; + (void)replay_gain_info_r; + + /* This code is enabled when libid3tag is disabled. Instead + of parsing the ID3 frame, it just skips it. */ + + mad_stream_skip(&data->stream, tagsize); #endif +} + +#ifndef HAVE_ID3TAG +/** + * This function emulates libid3tag when it is disabled. Instead of + * doing a real analyzation of the frame, it just checks whether the + * frame begins with the string "ID3". If so, it returns the full + * length. + */ +static signed long +id3_tag_query(const void *p0, size_t length) +{ + const char *p = p0; + + return length > 3 && memcmp(p, "ID3", 3) == 0 + ? length + : 0; +} +#endif /* !HAVE_ID3TAG */ static enum mp3_action decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, @@ -433,7 +459,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, return DECODE_BREAK; } if (mad_header_decode(&data->frame.header, &data->stream)) { -#ifdef HAVE_ID3TAG if ((data->stream).error == MAD_ERROR_LOSTSYNC && (data->stream).this_frame) { signed long tagsize = id3_tag_query((data->stream). @@ -454,7 +479,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, return DECODE_CONT; } } -#endif if (MAD_RECOVERABLE((data->stream).error)) { return DECODE_SKIP; } else { @@ -493,7 +517,6 @@ decodeNextFrame(struct mp3_data *data) return DECODE_BREAK; } if (mad_frame_decode(&data->frame, &data->stream)) { -#ifdef HAVE_ID3TAG if ((data->stream).error == MAD_ERROR_LOSTSYNC) { signed long tagsize = id3_tag_query((data->stream). this_frame, @@ -506,7 +529,6 @@ decodeNextFrame(struct mp3_data *data) return DECODE_CONT; } } -#endif if (MAD_RECOVERABLE((data->stream).error)) { return DECODE_SKIP; } else { diff --git a/src/directory.c b/src/directory.c index 85c24fd04..ef8c038a3 100644 --- a/src/directory.c +++ b/src/directory.c @@ -73,9 +73,14 @@ directory_prune_empty(struct directory *directory) struct dirvec *dv = &directory->children; for (i = dv->nr; --i >= 0; ) { - directory_prune_empty(dv->base[i]); - if (directory_is_empty(dv->base[i])) - dirvec_delete(dv, dv->base[i]); + struct directory *child = dv->base[i]; + + directory_prune_empty(child); + + if (directory_is_empty(child)) { + dirvec_delete(dv, child); + directory_free(child); + } } if (!dv->nr) dirvec_destroy(dv); diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c index 8e091679e..4412d26ff 100644 --- a/src/output/shout_plugin.c +++ b/src/output/shout_plugin.c @@ -448,8 +448,15 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error) static bool my_shout_pause(void *data) { + struct shout_data *sd = (struct shout_data *)data; static const char silence[1020]; + if (shout_delay(sd->shout_conn) > 500) { + /* cap the latency for unpause */ + g_usleep(500000); + return true; + } + return my_shout_play(data, silence, sizeof(silence), NULL); } diff --git a/src/output_control.c b/src/output_control.c index 70c6d2b1a..ef77bf4fa 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -77,6 +77,17 @@ audio_output_open(struct audio_output *ao, audio_format_equals(audio_format, &ao->in_audio_format)) { assert(ao->pipe == mp); + if (ao->pause) { + /* unpause with the CANCEL command; this is a + hack, but suits well for forcing the thread + to leave the ao_pause() thread, and we need + to flush the device buffer anyway */ + + /* we're not using audio_output_cancel() here, + because that function is asynchronous */ + ao_command(ao, AO_COMMAND_CANCEL); + } + return true; } diff --git a/src/output_internal.h b/src/output_internal.h index 6ca179287..4eb77cc49 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -87,6 +87,12 @@ struct audio_output { bool open; /** + * Is the device paused? i.e. the output thread is in the + * ao_pause() loop. + */ + bool pause; + + /** * If not NULL, the device has failed, and this timer is used * to estimate how long it should stay disabled (unless * explicitly reopened with "play"). diff --git a/src/output_thread.c b/src/output_thread.c index c7bd069b1..e1f20e580 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -295,6 +295,7 @@ static void ao_pause(struct audio_output *ao) bool ret; ao_plugin_cancel(ao->plugin, ao->data); + ao->pause = true; ao_command_finished(ao); do { @@ -304,6 +305,8 @@ static void ao_pause(struct audio_output *ao) break; } } while (ao->command == AO_COMMAND_NONE); + + ao->pause = false; } static gpointer audio_output_task(gpointer arg) diff --git a/src/tag_ape.c b/src/tag_ape.c index 4c3f4cf16..e3b848bfc 100644 --- a/src/tag_ape.c +++ b/src/tag_ape.c @@ -22,6 +22,7 @@ #include <glib.h> +#include <assert.h> #include <stdio.h> static const char *const ape_tag_names[] = { @@ -95,15 +96,18 @@ tag_ape_load(const char *file) /* find beginning of ape tag */ tagLen = GUINT32_FROM_LE(footer.length); - if (tagLen < sizeof(footer)) + if (tagLen <= sizeof(footer) + 10) + goto fail; + if (tagLen > 1024 * 1024) + /* refuse to load more than one megabyte of tag data */ goto fail; if (fseek(fp, size - tagLen, SEEK_SET)) goto fail; /* read tag into buffer */ tagLen -= sizeof(footer); - if (tagLen <= 0) - goto fail; + assert(tagLen > 10); + buffer = g_malloc(tagLen); if (fread(buffer, 1, tagLen, fp) != tagLen) goto fail; @@ -121,7 +125,7 @@ tag_ape_load(const char *file) /* get the key */ key = p; - while (tagLen - size > 0 && *p != '\0') { + while (tagLen > size && *p != '\0') { p++; tagLen--; } diff --git a/src/update.c b/src/update.c index b857b4d65..6778a5989 100644 --- a/src/update.c +++ b/src/update.c @@ -430,7 +430,7 @@ update_container_file( struct directory* directory, { char* vtrack = NULL; unsigned int tnum = 0; - const char* pathname = map_directory_child_fs(directory, name); + char* pathname = map_directory_child_fs(directory, name); struct directory* contdir = dirvec_find(&directory->children, name); // directory exists already @@ -446,8 +446,10 @@ update_container_file( struct directory* directory, modified = true; } - else + else { + g_free(pathname); return true; + } } contdir = make_subdir(directory, name); @@ -473,6 +475,8 @@ update_container_file( struct directory* directory, g_free(vtrack); } + g_free(pathname); + if (tnum == 1) { delete_directory(contdir); @@ -767,7 +771,6 @@ static void * update_task(void *_path) { if (_path != NULL && !isRootDirectory(_path)) { updatePath((char *)_path); - g_free(_path); } else { struct directory *directory = db_get_root(); struct stat st; @@ -776,6 +779,8 @@ static void * update_task(void *_path) updateDirectory(directory, &st); } + g_free(_path); + if (modified || !db_exists()) db_save(); |