Commit f401c105 authored by Max Kellermann's avatar Max Kellermann

Merged release 0.15.2 from branch 'v0.15.x'

Conflicts: NEWS configure.ac
parents e28a0e97 5715534b
...@@ -27,6 +27,22 @@ ver 0.16 (20??/??/??) ...@@ -27,6 +27,22 @@ ver 0.16 (20??/??/??)
* obey $(sysconfdir) for default mpd.conf location * 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) ver 0.15.1 (2009/07/15)
* decoders: * decoders:
- flac: fix assertion failure in tag_free() call - flac: fix assertion failure in tag_free() call
......
...@@ -629,8 +629,7 @@ dnl audio output plugins ...@@ -629,8 +629,7 @@ dnl audio output plugins
dnl dnl
AC_ARG_ENABLE(alsa, AC_ARG_ENABLE(alsa,
AS_HELP_STRING([--enable-alsa], AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
[disable ALSA support]),,
enable_alsa=auto) enable_alsa=auto)
AC_ARG_ENABLE(ao, AC_ARG_ENABLE(ao,
......
...@@ -40,53 +40,57 @@ flac_data_init(struct flac_data *data, struct decoder * decoder, ...@@ -40,53 +40,57 @@ flac_data_init(struct flac_data *data, struct decoder * decoder,
data->tag = NULL; data->tag = NULL;
} }
static bool static void
flac_find_float_comment(const FLAC__StreamMetadata *block, flac_find_float_comment(const FLAC__StreamMetadata *block,
const char *cmnt, float *fl) const char *cmnt, float *fl, bool *found_r)
{ {
int offset = int offset;
FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, cmnt); size_t pos;
int len;
if (offset >= 0) { unsigned char tmp, *p;
size_t pos = strlen(cmnt) + 1; /* 1 is for '=' */
int len = block->data.vorbis_comment.comments[offset].length offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
- pos; cmnt);
if (len > 0) { if (offset < 0)
unsigned char tmp; return;
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;
}
}
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 static void
flac_parse_replay_gain(const FLAC__StreamMetadata *block, flac_parse_replay_gain(const FLAC__StreamMetadata *block,
struct flac_data *data) struct flac_data *data)
{ {
bool found; bool found = false;
if (data->replay_gain_info) if (data->replay_gain_info)
replay_gain_info_free(data->replay_gain_info); replay_gain_info_free(data->replay_gain_info);
data->replay_gain_info = replay_gain_info_new(); data->replay_gain_info = replay_gain_info_new();
found = flac_find_float_comment(block, "replaygain_album_gain", flac_find_float_comment(block, "replaygain_album_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain) || &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain,
flac_find_float_comment(block, "replaygain_album_peak", &found);
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak) || flac_find_float_comment(block, "replaygain_album_peak",
flac_find_float_comment(block, "replaygain_track_gain", &data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak,
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain) || &found);
flac_find_float_comment(block, "replaygain_track_peak", flac_find_float_comment(block, "replaygain_track_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak); &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) { if (!found) {
replay_gain_info_free(data->replay_gain_info); replay_gain_info_free(data->replay_gain_info);
...@@ -106,25 +110,27 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, ...@@ -106,25 +110,27 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
size_t char_tnum_length = 0; size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry; const char *comment = (const char*)entry->entry;
if (entry->length > name_length && if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) == 0) { g_ascii_strncasecmp(comment, name, name_length) != 0)
if (char_tnum != NULL) { return NULL;
char_tnum_length = strlen(char_tnum);
if (entry->length > name_length + char_tnum_length + 2 && if (char_tnum != NULL) {
comment[name_length] == '[' && char_tnum_length = strlen(char_tnum);
g_ascii_strncasecmp(comment + name_length + 1, if (entry->length > name_length + char_tnum_length + 2 &&
char_tnum, char_tnum_length) == 0 && comment[name_length] == '[' &&
comment[name_length + char_tnum_length + 1] == ']') g_ascii_strncasecmp(comment + name_length + 1,
name_length = name_length + char_tnum_length + 2; char_tnum, char_tnum_length) == 0 &&
else if (entry->length > name_length + char_tnum_length && comment[name_length + char_tnum_length + 1] == ']')
g_ascii_strncasecmp(comment + name_length, name_length = name_length + char_tnum_length + 2;
char_tnum, char_tnum_length) == 0) else if (entry->length > name_length + char_tnum_length &&
name_length = name_length + char_tnum_length; g_ascii_strncasecmp(comment + name_length,
} char_tnum, char_tnum_length) == 0)
if (comment[name_length] == '=') { name_length = name_length + char_tnum_length;
*length_r = entry->length - name_length - 1; }
return comment + name_length + 1;
} if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
} }
return NULL; return NULL;
...@@ -370,13 +376,15 @@ char* ...@@ -370,13 +376,15 @@ char*
flac_cue_track( const char* pathname, flac_cue_track( const char* pathname,
const unsigned int tnum) 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); success = FLAC__metadata_get_cuesheet(pathname, &cs);
if (!success)
if (cs == NULL)
return NULL; return NULL;
assert(cs != NULL);
if (cs->data.cue_sheet.num_tracks <= 1) if (cs->data.cue_sheet.num_tracks <= 1)
{ {
FLAC__metadata_object_delete(cs); FLAC__metadata_object_delete(cs);
......
...@@ -350,11 +350,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag) ...@@ -350,11 +350,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag)
} }
#endif #endif
#ifdef HAVE_ID3TAG
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
struct tag **mpd_tag, struct tag **mpd_tag,
struct replay_gain_info **replay_gain_info_r) struct replay_gain_info **replay_gain_info_r)
{ {
#ifdef HAVE_ID3TAG
struct id3_tag *id3_tag = NULL; struct id3_tag *id3_tag = NULL;
id3_length_t count; id3_length_t count;
id3_byte_t const *id3_data; id3_byte_t const *id3_data;
...@@ -418,8 +418,34 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, ...@@ -418,8 +418,34 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
id3_tag_delete(id3_tag); id3_tag_delete(id3_tag);
g_free(allocated); 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 #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 static enum mp3_action
decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, 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, ...@@ -433,7 +459,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
return DECODE_BREAK; return DECODE_BREAK;
} }
if (mad_header_decode(&data->frame.header, &data->stream)) { if (mad_header_decode(&data->frame.header, &data->stream)) {
#ifdef HAVE_ID3TAG
if ((data->stream).error == MAD_ERROR_LOSTSYNC && if ((data->stream).error == MAD_ERROR_LOSTSYNC &&
(data->stream).this_frame) { (data->stream).this_frame) {
signed long tagsize = id3_tag_query((data->stream). 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, ...@@ -454,7 +479,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
return DECODE_CONT; return DECODE_CONT;
} }
} }
#endif
if (MAD_RECOVERABLE((data->stream).error)) { if (MAD_RECOVERABLE((data->stream).error)) {
return DECODE_SKIP; return DECODE_SKIP;
} else { } else {
...@@ -493,7 +517,6 @@ decodeNextFrame(struct mp3_data *data) ...@@ -493,7 +517,6 @@ decodeNextFrame(struct mp3_data *data)
return DECODE_BREAK; return DECODE_BREAK;
} }
if (mad_frame_decode(&data->frame, &data->stream)) { if (mad_frame_decode(&data->frame, &data->stream)) {
#ifdef HAVE_ID3TAG
if ((data->stream).error == MAD_ERROR_LOSTSYNC) { if ((data->stream).error == MAD_ERROR_LOSTSYNC) {
signed long tagsize = id3_tag_query((data->stream). signed long tagsize = id3_tag_query((data->stream).
this_frame, this_frame,
...@@ -506,7 +529,6 @@ decodeNextFrame(struct mp3_data *data) ...@@ -506,7 +529,6 @@ decodeNextFrame(struct mp3_data *data)
return DECODE_CONT; return DECODE_CONT;
} }
} }
#endif
if (MAD_RECOVERABLE((data->stream).error)) { if (MAD_RECOVERABLE((data->stream).error)) {
return DECODE_SKIP; return DECODE_SKIP;
} else { } else {
......
...@@ -73,9 +73,14 @@ directory_prune_empty(struct directory *directory) ...@@ -73,9 +73,14 @@ directory_prune_empty(struct directory *directory)
struct dirvec *dv = &directory->children; struct dirvec *dv = &directory->children;
for (i = dv->nr; --i >= 0; ) { for (i = dv->nr; --i >= 0; ) {
directory_prune_empty(dv->base[i]); struct directory *child = dv->base[i];
if (directory_is_empty(dv->base[i]))
dirvec_delete(dv, dv->base[i]); directory_prune_empty(child);
if (directory_is_empty(child)) {
dirvec_delete(dv, child);
directory_free(child);
}
} }
if (!dv->nr) if (!dv->nr)
dirvec_destroy(dv); dirvec_destroy(dv);
......
...@@ -448,8 +448,15 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error) ...@@ -448,8 +448,15 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error)
static bool static bool
my_shout_pause(void *data) my_shout_pause(void *data)
{ {
struct shout_data *sd = (struct shout_data *)data;
static const char silence[1020]; 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); return my_shout_play(data, silence, sizeof(silence), NULL);
} }
......
...@@ -77,6 +77,17 @@ audio_output_open(struct audio_output *ao, ...@@ -77,6 +77,17 @@ audio_output_open(struct audio_output *ao,
audio_format_equals(audio_format, &ao->in_audio_format)) { audio_format_equals(audio_format, &ao->in_audio_format)) {
assert(ao->pipe == mp); 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; return true;
} }
......
...@@ -87,6 +87,12 @@ struct audio_output { ...@@ -87,6 +87,12 @@ struct audio_output {
bool open; 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 * If not NULL, the device has failed, and this timer is used
* to estimate how long it should stay disabled (unless * to estimate how long it should stay disabled (unless
* explicitly reopened with "play"). * explicitly reopened with "play").
......
...@@ -295,6 +295,7 @@ static void ao_pause(struct audio_output *ao) ...@@ -295,6 +295,7 @@ static void ao_pause(struct audio_output *ao)
bool ret; bool ret;
ao_plugin_cancel(ao->plugin, ao->data); ao_plugin_cancel(ao->plugin, ao->data);
ao->pause = true;
ao_command_finished(ao); ao_command_finished(ao);
do { do {
...@@ -304,6 +305,8 @@ static void ao_pause(struct audio_output *ao) ...@@ -304,6 +305,8 @@ static void ao_pause(struct audio_output *ao)
break; break;
} }
} while (ao->command == AO_COMMAND_NONE); } while (ao->command == AO_COMMAND_NONE);
ao->pause = false;
} }
static gpointer audio_output_task(gpointer arg) static gpointer audio_output_task(gpointer arg)
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <glib.h> #include <glib.h>
#include <assert.h>
#include <stdio.h> #include <stdio.h>
static const char *const ape_tag_names[] = { static const char *const ape_tag_names[] = {
...@@ -95,15 +96,18 @@ tag_ape_load(const char *file) ...@@ -95,15 +96,18 @@ tag_ape_load(const char *file)
/* find beginning of ape tag */ /* find beginning of ape tag */
tagLen = GUINT32_FROM_LE(footer.length); 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; goto fail;
if (fseek(fp, size - tagLen, SEEK_SET)) if (fseek(fp, size - tagLen, SEEK_SET))
goto fail; goto fail;
/* read tag into buffer */ /* read tag into buffer */
tagLen -= sizeof(footer); tagLen -= sizeof(footer);
if (tagLen <= 0) assert(tagLen > 10);
goto fail;
buffer = g_malloc(tagLen); buffer = g_malloc(tagLen);
if (fread(buffer, 1, tagLen, fp) != tagLen) if (fread(buffer, 1, tagLen, fp) != tagLen)
goto fail; goto fail;
...@@ -121,7 +125,7 @@ tag_ape_load(const char *file) ...@@ -121,7 +125,7 @@ tag_ape_load(const char *file)
/* get the key */ /* get the key */
key = p; key = p;
while (tagLen - size > 0 && *p != '\0') { while (tagLen > size && *p != '\0') {
p++; p++;
tagLen--; tagLen--;
} }
......
...@@ -430,7 +430,7 @@ update_container_file( struct directory* directory, ...@@ -430,7 +430,7 @@ update_container_file( struct directory* directory,
{ {
char* vtrack = NULL; char* vtrack = NULL;
unsigned int tnum = 0; 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); struct directory* contdir = dirvec_find(&directory->children, name);
// directory exists already // directory exists already
...@@ -446,8 +446,10 @@ update_container_file( struct directory* directory, ...@@ -446,8 +446,10 @@ update_container_file( struct directory* directory,
modified = true; modified = true;
} }
else else {
g_free(pathname);
return true; return true;
}
} }
contdir = make_subdir(directory, name); contdir = make_subdir(directory, name);
...@@ -473,6 +475,8 @@ update_container_file( struct directory* directory, ...@@ -473,6 +475,8 @@ update_container_file( struct directory* directory,
g_free(vtrack); g_free(vtrack);
} }
g_free(pathname);
if (tnum == 1) if (tnum == 1)
{ {
delete_directory(contdir); delete_directory(contdir);
...@@ -767,7 +771,6 @@ static void * update_task(void *_path) ...@@ -767,7 +771,6 @@ static void * update_task(void *_path)
{ {
if (_path != NULL && !isRootDirectory(_path)) { if (_path != NULL && !isRootDirectory(_path)) {
updatePath((char *)_path); updatePath((char *)_path);
g_free(_path);
} else { } else {
struct directory *directory = db_get_root(); struct directory *directory = db_get_root();
struct stat st; struct stat st;
...@@ -776,6 +779,8 @@ static void * update_task(void *_path) ...@@ -776,6 +779,8 @@ static void * update_task(void *_path)
updateDirectory(directory, &st); updateDirectory(directory, &st);
} }
g_free(_path);
if (modified || !db_exists()) if (modified || !db_exists())
db_save(); db_save();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment