Commit 7fe0a63d authored by Alexey Rusakov's avatar Alexey Rusakov

Merge commit 'release-0.15.3' into alt-git

parents 55ebbbcd 7a690c6b
......@@ -599,7 +599,6 @@ noinst_PROGRAMS = \
test/run_input \
test/run_decoder \
test/read_tags \
test/run_encoder \
test/run_output \
test/read_mixer \
test/software_volume
......@@ -662,6 +661,8 @@ test_read_tags_SOURCES = test/read_tags.c \
$(TAG_SRC) \
$(DECODER_SRC)
if ENABLE_ENCODER
noinst_PROGRAMS += test/run_encoder
test_run_encoder_SOURCES = test/run_encoder.c \
src/conf.c src/buffer2array.c \
src/utils.c \
......@@ -671,6 +672,7 @@ test_run_encoder_SOURCES = test/run_encoder.c \
test_run_encoder_LDADD = $(MPD_LIBS) \
$(ENCODER_LIBS) \
$(GLIB_LIBS)
endif
test_software_volume_SOURCES = test/software_volume.c \
src/audio_parser.c \
......
ver 0.15.3 (2009/08/29)
* decoders:
- vorbis: faster tag scanning with ov_test_callback()
* output:
- fix stuttering due to uninitialized variable
* update: don't re-read unchanged container files
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
* output:
- httpd: include sys/types.h (fixes Mac OS X)
* commands:
- don't resume playback when stopping during pause
* database: fixed NULL pointer dereference after charset change
* log: fix double free() bug during shutdown
ver 0.15 (2009/06/23)
* input:
- parse Icy-Metadata
......
AC_PREREQ(2.60)
AC_INIT(mpd, 0.15, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.15.3, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
AM_CONFIG_HEADER(config.h)
......@@ -397,9 +397,9 @@ AC_ARG_ENABLE(audiofile,
enable_audiofile=yes)
AC_ARG_ENABLE(ffmpeg,
AS_HELP_STRING([--disable-ffmpeg],
[enable FFMPEG support (default: enable)]),,
enable_ffmpeg=yes)
AS_HELP_STRING([--enable-ffmpeg],
[enable FFMPEG support]),,
enable_ffmpeg=auto)
AC_ARG_ENABLE(flac,
AS_HELP_STRING([--disable-flac],
......@@ -591,8 +591,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,
......@@ -914,10 +913,8 @@ fi
AM_CONDITIONAL(HAVE_AUDIOFILE, test x$enable_audiofile = xyes)
if test x$enable_ffmpeg = xyes; then
PKG_CHECK_MODULES(FFMPEG, [libavformat libavcodec libavutil],,
enable_ffmpeg=no)
fi
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat libavcodec libavutil],
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
if test x$enable_ffmpeg = xyes; then
old_LIBS=$LIBS
......
......@@ -35,8 +35,8 @@ You must recreate the database after changing this option.
The default is "yes".
.TP
.B follow_inside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing outside the music dir, potentially
adding duplicates to the database.
Control if MPD will follow symbolic links pointing inside the music dir,
potentially adding duplicates to the database.
You must recreate the database after changing this option.
The default is "yes".
.TP
......
......@@ -134,7 +134,7 @@
<listitem>
<para>
<returnvalue>database</returnvalue>: the song database
has been updated
has been modified after <command>update</command>.
</para>
</listitem>
<listitem>
......@@ -272,6 +272,24 @@
</listitem>
<listitem>
<para>
<varname>nextsong</varname>:
<footnoteref linkend="since_0_15"/>
<returnvalue> playlist song number of the next
song to be played
</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>nextsongid</varname>:
<footnoteref linkend="since_0_15"/>
<returnvalue>playlist songid of the next song
to be played
</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>time</varname>:
<returnvalue>total time elapsed (of current
playing/paused song)</returnvalue>
......
......@@ -11,6 +11,30 @@
This document is work in progress. Most of it may be incomplete
yet. Please help!
</para>
<para>
MPD (Music Player Daemon) is, as the name suggests, a server
software allowing you to remotely play your music, handle
playlists, deliver music (HTTP STREAMS with various
sub-protocols) and organizze playlists.
</para>
<para>
It has been written with minimal resource usage and stability in
mind! Infact, it runs fine on a Pentium 75, allowing you to use
your cheap old PC to create a stereo system!
</para>
<para>
MPD supports also Gapless playback, buffered audio output, and
crossfading!
</para>
<para>
The separate client and server design allows users to choose a
user interface that best suites their tastes independently of
the underlying daemon, which actually plays music!
</para>
</chapter>
<chapter>
......@@ -26,10 +50,16 @@
<title>Installing on Debian/Ubuntu</title>
<para>
Install the package <filename>mpd</filename>:
Install the package <filename>mpd</filename> via APT:
</para>
<programlisting>apt-get install mpd</programlisting>
<para>
When installed this way, MPD by default looks for music in
/var/lib/mpd/music/; this may not be correct. Look at your
/etc/mpd.conf file...
</para>
</section>
<section>
......@@ -41,8 +71,8 @@
page</ulink> and unpack it:
</para>
<programlisting>tar xjf mpd-0.14.2.tar.bz
cd mpd-0.14.2</programlisting>
<programlisting>tar xjf mpd-version.tar.bz
cd mpd-version</programlisting>
<para>
Make sure that all the required libraries and build tools are
......@@ -678,6 +708,28 @@ cd mpd-0.14.2</programlisting>
The <varname>pipe</varname> plugin starts a program and
writes raw PCM data into its standard input.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>command</varname>
<parameter>CMD</parameter>
</entry>
<entry>
This command is invoked with the shell.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
......@@ -687,6 +739,40 @@ cd mpd-0.14.2</programlisting>
The <varname>pulse</varname> plugin connects to a PulseAudio
server.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>server</varname>
<parameter>HOSTNAME</parameter>
</entry>
<entry>
Sets the host name of the PulseAudio server. By
default, MPD connects to the local PulseAudio
server.
</entry>
</row>
<row>
<entry>
<varname>sink</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Specifies the name of the PulseAudio sink MPD should
play on.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
......@@ -696,6 +782,129 @@ cd mpd-0.14.2</programlisting>
The <varname>shout</varname> plugin connects to a ShoutCast
or IceCast server. It forwards tags to this server.
</para>
<para>
You must set a <varname>format</varname>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>host</varname>
<parameter>HOSTNAME</parameter>
</entry>
<entry>
Sets the host name of the Shoutcast/Icecast server.
</entry>
</row>
<row>
<entry>
<varname>port</varname>
<parameter>PORTNUMBER</parameter>
</entry>
<entry>
Connect to this port number on the specified host.
</entry>
</row>
<row>
<entry>
<varname>timeout</varname>
<parameter>SECONDS</parameter>
</entry>
<entry>
Set the timeout for the shout connection in seconds.
Defaults to 2 seconds.
</entry>
</row>
<row>
<entry>
<varname>mount</varname>
<parameter>URI</parameter>
</entry>
<entry>
Mounts the MPD stream in the specified URI.
</entry>
</row>
<row>
<entry>
<varname>user</varname>
<parameter>USERNAME</parameter>
</entry>
<entry>
Sets the user name for submitting the stream to the
server. Default is "source".
</entry>
</row>
<row>
<entry>
<varname>password</varname>
<parameter>PWD</parameter>
</entry>
<entry>
Sets the password for submitting the stream to the
server.
</entry>
</row>
<row>
<entry>
<varname>name</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Sets the name of the stream.
</entry>
</row>
<row>
<entry>
<varname>genre</varname>
<parameter>GENRE</parameter>
</entry>
<entry>
Sets the genre of the stream (optional).
</entry>
</row>
<row>
<entry>
<varname>description</varname>
<parameter>DESCRIPTION</parameter>
</entry>
<entry>
Sets a short description of the stream (optional).
</entry>
</row>
<row>
<entry>
<varname>public</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
Specifies whether the stream should be "public".
Default is "no".
</entry>
</row>
<row>
<entry>
<varname>encoder</varname>
<parameter>PLUGIN</parameter>
</entry>
<entry>
Sets the name of the encoder plugin. Default is
"vorbis". "vorbis" and "lame" are valid encoder
plugins (provided that you enabled them at compile
time).
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
......
......@@ -318,10 +318,11 @@ db_load(GError **error)
if (old_charset != NULL
&& strcmp(new_charset, old_charset)) {
fclose(fp);
g_message("Existing database has charset \"%s\" "
"instead of \"%s\"; "
"discarding database file",
new_charset, old_charset);
g_set_error(error, db_quark(), 0,
"Existing database has charset "
"\"%s\" instead of \"%s\"; "
"discarding database file",
new_charset, old_charset);
return false;
}
} else {
......
......@@ -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;
......@@ -371,13 +377,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);
......
......@@ -394,6 +394,7 @@ flac_decode_internal(struct decoder * decoder,
if (!(flac_dec = flac_new()))
return;
flac_data_init(&data, decoder, input_stream);
data.tag = tag_new();
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(flac_dec, FLAC__METADATA_TYPE_VORBIS_COMMENT))
......@@ -422,8 +423,6 @@ flac_decode_internal(struct decoder * decoder,
}
}
data.tag = tag_new();
if (!flac_process_metadata(flac_dec)) {
err = "problem reading metadata";
goto fail;
......
......@@ -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 {
......
......@@ -383,7 +383,7 @@ vorbis_tag_dup(const char *file)
return NULL;
}
if (ov_open(fp, &vf, NULL, 0) < 0) {
if (ov_test_callbacks(fp, &vf, NULL, 0, OV_CALLBACKS_STREAMONLY) < 0) {
fclose(fp);
return NULL;
}
......
......@@ -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);
......
......@@ -259,12 +259,8 @@ void log_init(bool verbose, bool use_stdout)
log_init_syslog();
#endif
} else {
char *path = parsePath(param->value);
g_free(param->value);
if (path == NULL)
g_error("error parsing \"%s\" at line %i\n",
CONF_LOG_FILE, param->line);
const char *path = config_get_path(CONF_LOG_FILE);
assert(path != NULL);
log_init_file(path, param->line);
}
......
......@@ -28,15 +28,12 @@
#include <assert.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_OSX
#include <sys/types.h>
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "httpd_output"
......
......@@ -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);
}
......
......@@ -439,8 +439,6 @@ audio_output_all_cancel(void)
{
unsigned int i;
audio_output_all_update();
/* send the cancel() command to all audio outputs */
for (i = 0; i < num_audio_outputs; ++i) {
......
......@@ -76,6 +76,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;
}
......
......@@ -109,6 +109,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
ao->plugin = plugin;
ao->enabled = config_get_block_bool(param, "enabled", true);
ao->open = false;
ao->pause = false;
ao->fail_timer = NULL;
pcm_convert_init(&ao->convert_state);
......
......@@ -81,6 +81,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").
......
......@@ -165,6 +165,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 {
......@@ -174,6 +175,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)
......@@ -246,7 +249,11 @@ static gpointer audio_output_task(gpointer arg)
case AO_COMMAND_PAUSE:
ao_pause(ao);
break;
/* don't "break" here: this might cause
ao_play() to be called when command==CLOSE
ends the paused state - "continue" checks
the new command first */
continue;
case AO_COMMAND_CANCEL:
ao->chunk = NULL;
......
......@@ -49,6 +49,7 @@ song_alloc(const char *url, struct directory *parent)
song->tag = NULL;
memcpy(song->url, url, urllen + 1);
song->parent = parent;
song->mtime = 0;
return song;
}
......
......@@ -22,6 +22,7 @@
#include <glib.h>
#include <assert.h>
#include <stdio.h>
struct tag *
......@@ -86,15 +87,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;
......@@ -112,7 +116,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--;
}
......
......@@ -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);
......@@ -498,7 +502,8 @@ update_regular_file(struct directory *directory,
{
struct song* song = songvec_find(&directory->songs, name);
if (plugin->container_scan != NULL)
if (!(song != NULL && st->st_mtime == song->mtime) &&
plugin->container_scan != NULL)
{
if (update_container_file(directory, name, st, plugin))
{
......@@ -767,7 +772,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 +780,8 @@ static void * update_task(void *_path)
updateDirectory(directory, &st);
}
g_free(_path);
if (modified || !db_exists())
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