[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [gmediaserver-devel] new feature? [patch]
From: |
James E. Flemer |
Subject: |
Re: [gmediaserver-devel] new feature? [patch] |
Date: |
Sat, 05 Jan 2008 06:41:27 -0700 |
User-agent: |
Thunderbird 2.0.0.0 (X11/20070611) |
Guess it's time to re-post my GMS patch set. The attached diff adds a
few things:
1) dynamic re-reading of the exported file structure (based on file/dir
m-time)
2) ICY proxying (to let GMS server shoutcast streams with embedded
meta-data for device that do not accept meta-data)
I think there may still be some bugs in the re-reading code; I haven't
bothered to figure out if my device (Omnifi GMS1) crashes because its
got crappy firmware, or if GMS sends it something bogus. I haven't had
GMS itself crash with these patches in a long time.
Enjoy.
Carsten Wendt wrote:
Hy,
today the gmediaserver determined at start time the size of the file
that is to be distributed. If the size changed during the file
distribution the gmediaserver take not account of this. The gmediaserver
stopped the distribution at reaching the initial file size.
It would be very nice if in such situation the gemediaserver could
recognize that the size changed and still distribute the file until
reaching an "dynamic" file end.
Regards,
Carsten
_______________________________________________
GMediaServer-devel mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/gmediaserver-devel
# $FreeBSD$
--- configure.ac.orig Tue Aug 29 14:17:21 2006
+++ configure.ac Tue Oct 30 18:22:10 2007
@@ -47,6 +47,8 @@
if test "$UPNP_LIBS" = ""; then
AC_LIB_UPNP([], [AC_MSG_ERROR([the upnp library is required to build and run
this program])])
fi
+# - libz (for FreeBSD libmagic)
+AC_CHECK_LIB(z, inflate)
# - libmagic
AC_CHECK_HEADERS([magic.h], [], [AC_MSG_ERROR([libmagic magic.h header file
not found])])
AC_CHECK_LIB([magic], [magic_open], [AC_SUBST([LIBMAGIC], [-lmagic])],
[AC_MSG_ERROR([libmagic library or magic_open function not found])])
# $FreeBSD$
--- lib/Makefile.am.orig Tue Aug 29 14:52:26 2006
+++ lib/Makefile.am Tue Oct 30 18:22:10 2007
@@ -15,7 +15,6 @@
noinst_LIBRARIES = libgnu.a
libgnu_a_SOURCES =
-libgnu_a_LIBADD = @LIBOBJS@
noinst_HEADERS =
EXTRA_DIST =
BUILT_SOURCES =
@@ -209,7 +208,6 @@
## begin gnulib module iconvme
libgnu_a_SOURCES += iconvme.h iconvme.c
-libgnu_a_LIBADD += $(LTLIBICONV)
## end gnulib module iconvme
# $FreeBSD$
--- lib/Makefile.in.orig Tue Aug 29 14:53:57 2006
+++ lib/Makefile.in Tue Oct 30 18:22:10 2007
@@ -300,7 +300,7 @@
vasnprintf.h vasprintf.h version-etc.h version-etc.c \
xalloc-die.c xgethostname.h xgethostname.c xsize.h xstrndup.h \
xstrndup.c xvasprintf.h xvasprintf.c xasprintf.c
-libgnu_a_LIBADD = @LIBOBJS@ $(LTLIBICONV)
+libgnu_a_LIBADD = @LIBOBJS@
noinst_HEADERS =
EXTRA_DIST = alloca_.h dirname.h exitfail.h getdelim.h getline.h \
getndelim2.h getndelim2.c getopt_.h getopt_int.h intprops.h \
# $FreeBSD$
--- lib/iconvme.c.orig Fri Sep 16 07:41:57 2005
+++ lib/iconvme.c Sat Jan 7 11:48:42 2006
@@ -109,7 +109,7 @@
iconv_alloc (iconv_t cd, const char *str)
{
char *dest;
- char *p = (char *) str;
+ const char *p = str;
char *outp;
size_t inbytes_remaining = strlen (p);
/* Guess the maximum length the output string can have. */
# $FreeBSD$
--- src/contentdir.c.orig Thu Aug 31 14:30:09 2006
+++ src/contentdir.c Sun Nov 11 10:52:47 2007
@@ -52,6 +52,7 @@
return &contentdir_service_variables[c];
}
assert(0); /* Shouldn't get here */
+ return 0;
}
void
@@ -97,8 +98,8 @@
if (strcmp(property, "upnp:class") == 0) {
if (has_entry_detail(entry, DETAIL_CHILDREN))
return xstrdup("object.container.storageFolder");
- /*if (has_entry_detail(entry, DETAIL_URL))
- return xstrdup("object.item.audioItem.audioBroadcast");*/
+ if (has_entry_detail(entry, DETAIL_URL))
+ return xstrdup("object.item.audioItem.audioBroadcast");
detail = get_entry_detail(entry, DETAIL_FILE);
switch (detail->data.file.item_class) {
@@ -109,6 +110,7 @@
case ITEM_VIDEO:
return xstrdup("object.item.videoItem.movie");
case ITEM_PLAYLIST:
+ return xstrdup("object.container.storageFolder");
case ITEM_TEXT:
case ITEM_UNKNOWN:
default:
@@ -436,7 +438,7 @@
tmap_put(results, entry, strbuf_free_to_string(result));
}
-static char *
+static const char *
operator_name(int type)
{
switch (type) {
@@ -821,7 +823,7 @@
free(results_str);
upnp_add_response(event, "NumberReturned", int32_str(match_count));
upnp_add_response(event, "TotalMatches", "0");
- upnp_add_response(event, "UpdateID", uint32_str(id == 0 ? update_id : 0));
+ upnp_add_response(event, "UpdateID",
uint32_str(detail->data.children.update_id));
free_sort_criteria(sort_criteria);
free_search_criteria(search_criteria);
unlock_metadata();
# $FreeBSD$
--- src/gmediaserver.h.orig Sun Apr 9 04:31:24 2006
+++ src/gmediaserver.h Sun Nov 11 16:48:51 2007
@@ -52,6 +52,7 @@
typedef struct _URL URL;
typedef enum {
+ DETAIL_NONE, /* null */
DETAIL_CHILDREN, /* mutually exclusive with DETAIL_FILE, URL */
DETAIL_TAG, /* currently only available for DETAIL_FILE */
DETAIL_FILE, /* mutually exclusive with DETAIL_CHILDREN, URL */
@@ -93,6 +94,7 @@
struct _EntryChildren {
uint32_t count;
int32_t *list;
+ uint32_t update_id;
};
struct _EntryTag {
@@ -119,7 +121,7 @@
struct _EntryDetail {
EntryDetail *next;
- /*EntryDetail *previous;*/
+ EntryDetail *previous;
EntryDetailType type;
union {
EntryTag tag;
@@ -187,12 +189,14 @@
size_t bufsize;
size_t bufhead;
size_t buftail;
+ uint32_t icy_metaint;
+ size_t ict_metapos;
};
struct _URL {
/*char *protocol;*/
char *hostname;
- uint16_t port;
+ char *port;
char *path;
};
@@ -218,9 +222,9 @@
/* metadata.c */
extern char *file_types;
extern bool tags_enabled;
+extern bool path_safety_enabled;
bool init_metadata(void);
bool scan_entries(char **pathv, int pathc, int indent_size);
-void clear_entries(void);
void finish_metadata(void);
void lock_metadata(void);
void unlock_metadata(void);
@@ -264,7 +268,7 @@
extern struct UpnpVirtualDirCallbacks virtual_dir_callbacks;
/* webclient.c */
-HTTPResult *http_query(const char *method, const char *url, bool keep_open);
+HTTPResult *http_query(const char *method, const char *url, bool keep_open,
bool want_metadata);
void http_result_free(HTTPResult *result);
ssize_t http_skip(HTTPResult *result, size_t count);
ssize_t http_read(HTTPResult *result, void *buf, size_t count);
# $FreeBSD$
--- src/main.c.orig Thu Aug 31 12:44:25 2006
+++ src/main.c Tue Oct 30 21:11:02 2007
@@ -65,11 +65,11 @@
OPT_EXPIRE_TIME,
OPT_FILE_TYPES,
OPT_PROFILE,
- OPT_HELP,
OPT_VERSION,
+ OPT_PATH_SAFETY,
};
-static const char *short_options = "bv::i:o:p:";
+static const char *short_options = "bhv::i:o:p:";
static struct option long_options[] = {
{ "disable-tags", no_argument, NULL, OPT_DISABLE_TAGS },
{ "fs-charset", required_argument, NULL, OPT_FS_CHARSET },
@@ -87,8 +87,9 @@
{ "verbose", optional_argument, NULL, 'v' },
{ "expire-time", required_argument, NULL, OPT_EXPIRE_TIME },
{ "file-types", required_argument, NULL, OPT_FILE_TYPES },
- { "help", no_argument, NULL, OPT_HELP },
+ { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, OPT_VERSION },
+ { "path-safety", no_argument, NULL, OPT_PATH_SAFETY },
{ NULL, 0, NULL, 0 }
};
@@ -287,7 +288,10 @@
verbosity++;
}
break;
- case OPT_HELP:
+ case OPT_PATH_SAFETY:
+ path_safety_enabled = true;
+ break;
+ case 'h':
printf(_("Usage: %s [OPTIONS]... DIRECTORIES...\n"), argv[0]);
printf(_("Run the UPnP media server.\n\n"));
printf(_(" --friendly-name=NAME set display name for
media server\n"));
@@ -306,7 +310,8 @@
printf(_(" --profile=NAME specify profile (see
below)\n"));
printf(_(" --file-types=TYPE[,..] list of file types to
serve (see docs)\n"));
printf(_(" --expire-time=SECONDS advertisement expire
time (default 100)\n"));
- printf(_(" --help display this help and
exit\n"));
+ printf(_(" --path-safety disalow absolute paths,
and .. paths\n"));
+ printf(_(" -h, --help display this help and
exit\n"));
printf(_(" --version output version
information and exit\n"));
printf(_("\nThe directories arguments specify where audio files are
located.\n"
"See strftime(3) for description of the timestamp
format.\n"));
@@ -451,6 +456,7 @@
else
say(2, _("Received signal %d\n"), sig);
+#if 0
if (sig == SIGUSR1) {
lock_metadata();
clear_entries();
@@ -461,7 +467,9 @@
}
bump_update_id();
unlock_metadata();
- } else if (sig == SIGTERM || sig == SIGINT) {
+ } else
+#endif
+ if (sig == SIGTERM || sig == SIGINT) {
break;
}
}
# $FreeBSD$
--- src/metadata.c.orig Thu Aug 31 13:48:01 2006
+++ src/metadata.c Fri Nov 16 22:10:38 2007
@@ -36,6 +36,7 @@
#include <fcntl.h> /* POSIX */
#include <unistd.h> /* POSIX */
#include <ctype.h> /* C89 */
+#include <libgen.h> /* XPG4.2 */
#include <inttypes.h> /* ? */
#include <iconv.h> /* POSIX */
#include <magic.h> /* libmagic */
@@ -144,8 +145,10 @@
[FILE_JPG] = ITEM_IMAGE,
};
+static Entry *rescan_entry(Entry *entry, bool recurse);
static Entry *scan_entry(const char *fullpath, const char *name, int32_t
parent, int indent_size, InodeList *inl);
-static Entry *scan_playlist_file(const char *fullpath, const char *name,
int32_t parent, FileType type, int indent_size, InodeList *inl, ino_t node);
+static Entry *scan_playlist_file(Entry *entry, FileType type, int indent_size,
InodeList *inl, ino_t node);
+static Entry *scan_playlist_entry(const char *file, const char *name, const
char *playlist_dir, int32_t parent, int indent_size, InodeList *inl);
static Entry *root_entry;
static uint32_t entry_count = 0;
@@ -159,8 +162,9 @@
char *file_types = DEFAULT_FILE_TYPES;
static ithread_mutex_t metadata_mutex;
bool tags_enabled = true;
+bool path_safety_enabled = false;
#ifdef HAVE_ID3LIB
-static ID3Tag *id3;
+static ID3Tag *id3 = NULL;
#endif
EntryDetail *
@@ -169,6 +173,8 @@
EntryDetail *d;
for (d = entry->details; d != NULL; d = d->next) {
+ if (d == d->next)
+ abort();
if (d->type == type)
return d;
}
@@ -188,19 +194,94 @@
EntryDetail *detail;
detail = xmalloc(sizeof(EntryDetail));
+ bzero(detail, sizeof(EntryDetail));
+ if (entry->details)
+ entry->details->previous = detail;
+ detail->previous = NULL;
detail->next = entry->details;
detail->type = type;
entry->details = detail;
+ if (detail == detail->next)
+ abort();
+
return detail;
}
+static void
+free_entry_detail(EntryDetail *d)
+{
+ switch (d->type)
+ {
+ case DETAIL_CHILDREN:
+ if (d->data.children.list)
+ free(d->data.children.list);
+ d->data.children.list = NULL;
+ break;
+
+ case DETAIL_TAG:
+ if (d->data.tag.title)
+ free(d->data.tag.title);
+ if (d->data.tag.artist)
+ free(d->data.tag.artist);
+ if (d->data.tag.album)
+ free(d->data.tag.album);
+ if (d->data.tag.genre)
+ free(d->data.tag.genre);
+ d->data.tag.title = NULL;
+ d->data.tag.artist = NULL;
+ d->data.tag.album = NULL;
+ d->data.tag.genre = NULL;
+ break;
+
+ case DETAIL_FILE:
+ if (d->data.file.filename)
+ free(d->data.file.filename);
+ d->data.file.filename = NULL;
+ break;
+
+ case DETAIL_URL:
+ if (d->data.url.url)
+ free(d->data.url.url);
+ d->data.url.url = NULL;
+ break;
+
+ default:
+ break;
+ }
+ d->next = NULL;
+ d->previous = NULL;
+ d->type = DETAIL_NONE;
+ bzero(d, sizeof(EntryDetail));
+ free(d);
+}
+
+static void
+delete_entry_detail(Entry *entry, EntryDetailType type)
+{
+ EntryDetail *d;
+
+ for (d = entry->details; d != NULL; d = d->next)
+ {
+ if (d->type == type)
+ {
+ if (d->previous)
+ d->previous->next = d->next;
+ if (d->next)
+ d->next->previous = d->previous;
+ free_entry_detail(d);
+ break;
+ }
+ }
+}
+
static Entry *
make_entry(const char *name, int32_t parent, bool directory)
{
Entry *entry;
entry = xmalloc(sizeof(Entry));
+ bzero(entry, sizeof(Entry));
entry->id = entry_count++;
entry->parent = parent;
entry->name = xstrdup(name);
@@ -211,6 +292,7 @@
detail = add_entry_detail(entry, DETAIL_CHILDREN);
detail->data.children.count = 0;
detail->data.children.list = NULL;
+ detail->data.children.update_id = 0;
}
if (entry->id >= entries_size) {
@@ -222,16 +304,52 @@
return entry;
}
+static void
+delete_entry(Entry *entry, bool recurse)
+{
+ EntryDetail *d, *n;
+
+ /* just incase */
+ if (NULL == entry)
+ return;
+
+ for (d = entry->details; d != NULL; ) {
+ n = d->next;
+ if (recurse && d->type == DETAIL_CHILDREN)
+ {
+ int c;
+ for (c = 0; c < d->data.children.count; c++)
+ {
+ Entry *child = entries[d->data.children.list[c]];
+ if (child)
+ delete_entry(child, recurse);
+ }
+ }
+ free_entry_detail(d);
+ d = n;
+ }
+ entry->details = NULL;
+ entries[entry->id] = NULL;
+
+ if (entry->name)
+ free(entry->name);
+ bzero(entry, sizeof(Entry));
+ free(entry);
+}
+
Entry *
get_entry_by_id(uint32_t id)
{
+ Entry *entry;
if (id < 0 || id >= entry_count)
return NULL;
- return entries[id];
+ entry = entries[id];
+ if (entry)
+ entry = rescan_entry(entry, false);
+ return (entries[id] = entry);
}
-
static bool
attempt_read_at(int fd, size_t size, off_t pos, uint8_t *buf, const char
*fullpath)
{
@@ -479,101 +597,103 @@
}
static bool
-add_playlist_child(Entry *parent, size_t *children_size, char *buf, const char
*playlist_dir, int indent_size, InodeList *inl)
-{
- Entry *child;
- char *child_path = buf;
- char *child_name;
-
- if (buf[0] != '/' && strncasecmp(buf, "http://", 7) != 0)
- child_path = concat_filenames(playlist_dir, buf);
- /* XXX: using base_name here may not be entire appropriate -
- * maybe child_path is an url?
- */
-
- child_name = xstrdup(conv_filename(base_name(child_path)));
- child = scan_entry(child_path, child_name, parent->id, indent_size, inl);
- free(child_name);
- if (child != NULL) {
- EntryChildren *children;
-
- children = &(get_entry_detail(parent, DETAIL_CHILDREN)->data.children);
- if (children->list == NULL) {
- children->list = xmalloc((*children_size) * sizeof(int32_t));
- } else if (children->count >= (*children_size)) {
- *children_size *= 2;
- children->list = xrealloc(children->list, (*children_size) *
sizeof(int32_t));
- }
- children->list[children->count++] = child->id;
- }
- if (child_path != buf)
- free(child_path);
-
- return child != NULL;
-}
-
-static bool
fix_playlist_path(char *path)
{
char *p;
/* Deny empty and absolute paths */
- if (path[0] == '\0' || path[0] == '/' || path[0] == '\\')
+ if (path_safety_enabled && (path[0] == '\0' || path[0] == '/' || path[0]
== '\\')) {
+ warn(_("deny empty or absolute path: %s"), path);
return false;
+ }
/* Convert backslashes to slashes. Deny '..' at the same time. */
p = path;
for (; *path; path++) {
if (*path == '\\' || *path == '/') {
*path = '/';
- if (path-p == 2 && p[0] == '.' && p[1] == '.')
+ if (path_safety_enabled && (path-p == 2 && p[0] == '.' && p[1] ==
'.')) {
+ warn(_("deny .. path: %s"), p);
return false;
+ }
p = path+1;
}
}
- if (path-p == 2 && p[0] == '.' && p[1] == '.')
+ if (path_safety_enabled && (path-p == 2 && p[0] == '.' && p[1] == '.')) {
+ warn(_("deny .. path: %s"), p);
return false;
+ }
return true;
}
static Entry *
-scan_playlist_file(const char *fullpath, const char *name, int32_t parent,
FileType type, int indent_size, InodeList *inl, ino_t node)
+scan_playlist_file(Entry *entry, FileType type, int indent_size, InodeList
*inl, ino_t node)
{
+ EntryDetail *child_detail;
+ const char *fullpath = get_entry_detail(entry,
DETAIL_FILE)->data.file.filename;
+
FILE *fh;
size_t alloc_max;
char *buf = NULL;
size_t bufsize = 0;
ssize_t len;
- Entry *entry;
char *playlist_dir;
- size_t children_size = DEFAULT_PLAYLIST_ENTRIES; /* just some basic
initial size */
int ln;
int bad = 0;
char *indent;
InodeList inl_new;
+ Entry *child = NULL;
+ int32_t *children;
+ uint32_t child_count, child_size;
+
indent = xstrdupn(" ", indent_size*2);
if (inode_in_list(node, inl)) {
warn(_("%s: recursive loop detected - ignoring\n"),
quotearg(conv_filename(fullpath)));
free(indent);
- return NULL;
+ delete_entry(entry, true);
+ entry = NULL;
+ return entry;
}
say(4, _("%sScanning playlist %s\n"), indent,
quote(conv_filename(fullpath)));
+ child_detail = get_entry_detail(entry, DETAIL_CHILDREN);
+ child_detail->data.children.update_id++;
+
+ child_count = 0;
+ child_size = child_detail->data.children.count ?
child_detail->data.children.count : DEFAULT_PLAYLIST_ENTRIES;
+ children = xmalloc(sizeof(int32_t) * child_size);
+
+ /* dump the old child list */
+ if (child_detail->data.children.list)
+ {
+ int oc;
+ int32_t *old_children = child_detail->data.children.list;
+ for (oc = 0; oc < child_detail->data.children.count; oc++)
+ {
+ child = entries[old_children[oc]];
+ if (child)
+ delete_entry(child, true);
+ }
+ free(old_children);
+ child_detail->data.children.list = NULL;
+ }
+
fh = fopen(fullpath, "r");
if (fh == NULL) {
warn(_("%s: cannot open file for reading: %s\n"),
quotearg(conv_filename(fullpath)), errstr);
free(indent);
- return NULL;
+ delete_entry(entry, true);
+ entry = NULL;
+ return entry;
}
inl_new.prev = inl;
inl_new.node = node;
playlist_dir = dir_name(fullpath);
- entry = make_entry(name, parent, true);
/* Note: we don't handle playlists with MAC-like newlines (CR only),
* simply because getline or getdelim doesn't handle that easily.
@@ -592,7 +712,8 @@
if (!ferror(fh)) {
for (; (len = getnline(&buf, &bufsize, alloc_max, fh)) >= 0; ln++) {
len = chomp(buf, len);
- if (type == FILE_PLS) {
+ child = NULL;
+ if (type == FILE_PLS) {
if (len >= 7 && strncasecmp(buf, "Title", 5) == 0) {
/* Ignore Title lines. */
} else if (len >= 8 && strncasecmp(buf, "Length", 6) == 0) {
@@ -610,7 +731,8 @@
break;
}
/* Ignore the numbering of files in PLS playlists. */
- if (!add_playlist_child(entry, &children_size, buf+c+1,
playlist_dir, indent_size+1, &inl_new)) {
+ child = scan_playlist_entry(buf+c+1, buf+c+1,
playlist_dir, entry->id, indent_size+1, &inl_new);
+ if (NULL == child) {
bad++;
if (bad > MAX_INVALID_PLS_FILES) {
/* For now, just break out of the loop. */
@@ -634,7 +756,8 @@
warn(_("%s: invalid line %d\n"),
quotearg(conv_filename(fullpath)), ln);
bad++;
} else {
- if (!add_playlist_child(entry, &children_size, buf,
playlist_dir, indent_size+1, &inl_new))
+ child = scan_playlist_entry(buf, buf, playlist_dir,
entry->id, indent_size+1, &inl_new);
+ if (NULL == child)
bad++;
}
if (bad > MAX_INVALID_EXTM3U_FILES) {
@@ -647,7 +770,8 @@
warn(_("%s: invalid line %d\n"),
quotearg(conv_filename(fullpath)), ln);
bad++;
} else {
- if (!add_playlist_child(entry, &children_size, buf,
playlist_dir, indent_size+1, &inl_new))
+ child = scan_playlist_entry(buf, buf, playlist_dir,
entry->id, indent_size+1, &inl_new);
+ if (NULL == child)
bad++;
}
if (bad > MAX_INVALID_M3U_FILES) {
@@ -656,6 +780,15 @@
break;
}
}
+
+ if (child)
+ {
+ if (child_count >= child_size) {
+ child_size *= 2;
+ children = xrealloc(children, child_size * sizeof(int32_t));
+ }
+ children[child_count++] = child->id;
+ }
}
}
@@ -665,13 +798,134 @@
if (ferror(fh))
warn(_("%s: cannot read from file: %s\n"),
quotearg(conv_filename(fullpath)), errstr);
+ child_detail->data.children.count = child_count;
+ if (child_count)
+ child_detail->data.children.list = xmemdup(children, sizeof(int32_t) *
child_count);
+ else
+ child_detail->data.children.list = 0;
+ free(children);
+
fclose(fh); /* Ignore errors because we opened for reading */
free(buf);
free(playlist_dir);
free(indent);
return entry;
}
-
+
+static void
+scan_tag_data(Entry *entry, const char *fullpath, FileType type)
+{
+ if (tags_enabled && (type == FILE_MP3 || type == FILE_MP3_ID3)) {
+ EntryDetail *detail = NULL;
+
+#ifdef HAVE_TAGLIB
+ if (detail == NULL) {
+ TagLib_File *tl_file;
+ TagLib_File_Type tl_type;
+
+ switch (type) {
+ case FILE_OGG:
+ tl_type = TagLib_File_OggVorbis;
+ break;
+ default:
+ tl_type = TagLib_File_MPEG;
+ break;
+ }
+
+ tl_file = taglib_file_new_type(fullpath, tl_type);
+ if (tl_file != NULL) {
+ TagLib_Tag *tl_tag;
+ const TagLib_AudioProperties *tl_props;
+ char *str;
+ unsigned int track;
+
+ tl_tag = taglib_file_tag(tl_file);
+ /* Clear old tag detail, if any. */
+ delete_entry_detail(entry, DETAIL_TAG);
+ /* Save tag detail. */
+ detail = add_entry_detail(entry, DETAIL_TAG);
+ str = taglib_tag_title(tl_tag);
+ detail->data.tag.title = (*str == '\0' ? NULL :
xstrdup(str));
+ str = taglib_tag_album(tl_tag);
+ detail->data.tag.album = (*str == '\0' ? NULL :
xstrdup(str));
+ str = taglib_tag_artist(tl_tag);
+ detail->data.tag.artist = (*str == '\0' ? NULL :
xstrdup(str));
+ str = taglib_tag_genre(tl_tag);
+ detail->data.tag.genre = (*str == '\0' ? NULL :
xstrdup(str));
+ track = taglib_tag_track(tl_tag);
+ detail->data.tag.track_no = track > 0 ? track : -1;
+ tl_props = taglib_file_audioproperties(tl_file);
+ detail->data.tag.duration =
taglib_audioproperties_length(tl_props);
+
+ /*taglib_tag_free_strings();*/
+ taglib_file_free(tl_file);
+ }
+ }
+#endif
+#ifdef HAVE_ID3LIB
+ if (detail == NULL) {
+ /*char *title;*/
+
+ ID3Tag_Clear(id3);
+ ID3Tag_Link(id3, fullpath);
+
+ if (ID3Tag_NumFrames(id3) != 0) {
+ char *track;
+
+ /* Clear old tag detail, if any. */
+ delete_entry_detail(entry, DETAIL_TAG);
+ /* Save tag detail. */
+ detail = add_entry_detail(entry, DETAIL_TAG);
+ detail->data.tag.title = get_id3_string(fullpath, id3,
ID3FID_TITLE);
+ detail->data.tag.album = get_id3_string(fullpath, id3,
ID3FID_ALBUM);
+ detail->data.tag.artist = get_id3_string(fullpath, id3,
ID3FID_LEADARTIST);
+ detail->data.tag.genre = get_id3_string(fullpath, id3,
ID3FID_CONTENTTYPE);
+ track = get_id3_string(fullpath, id3, ID3FID_TRACKNUM);
+ if (track == NULL || !parse_int32(track,
&detail->data.tag.track_no))
+ detail->data.tag.track_no = -1;
+
+ /* get_id3_string(fullpath, id3, ID3FID_TIME); */
+ detail->data.tag.duration = -1;
+ }
+ }
+#endif
+ }
+}
+
+static Entry *
+scan_playlist_entry(const char *file, const char *name, const char
*playlist_dir, int32_t parent, int indent_size, InodeList *inl)
+{
+ Entry *child;
+
+ if (file[0] == '/' || strncasecmp(file, "http://", 7) == 0)
+ {
+ child = scan_entry(file, name, parent, indent_size, inl);
+ }
+ else
+ {
+ char *child_path = concat_filenames(playlist_dir, file);
+ child = scan_entry(child_path, name, parent, indent_size, inl);
+ free(child_path);
+ }
+ return child;
+}
+
+/* qsort() compatible sort function for struct dirent
+ * - alpha sort with directories first
+ */
+static int
+alphadirsort(const void *d1, const void *d2)
+{
+ /* first compare types */
+ int cmp = (*(struct dirent **)d1)->d_type - (*(struct dirent
**)d2)->d_type;
+ /* then compare names */
+ if (0 == cmp)
+ {
+ cmp = strcasecmp((*(struct dirent **)d1)->d_name, (*(struct dirent
**)d2)->d_name);
+ }
+ return cmp;
+}
+
static Entry *
scan_entry(const char *fullpath, const char *name, int32_t parent, int
indent_size, InodeList *inl)
{
@@ -716,7 +970,7 @@
return NULL;
}
- dirent_count = scandir(fullpath, &dirents, NULL, alphasort);
+ dirent_count = scandir(fullpath, &dirents, NULL, &alphadirsort);
if (dirent_count < 0) {
warn(_("%s: cannot scan directory: %s\n"),
quotearg(conv_filename(fullpath)), errstr);
free(indent);
@@ -728,6 +982,14 @@
entry = make_entry(name, parent, true);
children = xmalloc(sizeof(int32_t) * dirent_count);
+ detail = add_entry_detail(entry, DETAIL_FILE);
+ detail->data.file.size = sb.st_size;
+ detail->data.file.mtime = sb.st_mtime;
+ detail->data.file.mode = sb.st_mode;
+ detail->data.file.filename = xstrdup(fullpath);
+ detail->data.file.mime_type = NULL;
+ detail->data.file.item_class = ITEM_UNKNOWN;
+
child_count = 0;
for (c = 0; c < dirent_count; c++) {
if (strcmp(dirents[c]->d_name, ".") != 0 &&
strcmp(dirents[c]->d_name, "..") != 0) {
@@ -782,8 +1044,20 @@
}
if (type == FILE_PLS || type == FILE_M3U || type == FILE_EXTM3U) {
+
+ entry = make_entry(name, parent, true);
+ detail = add_entry_detail(entry, DETAIL_FILE);
+ detail->data.file.size = sb.st_size;
+ detail->data.file.mtime = sb.st_mtime;
+ detail->data.file.mode = sb.st_mode;
+ detail->data.file.filename = xstrdup(fullpath);
+ detail->data.file.mime_type = file_type_mime_types[type];
+ detail->data.file.item_class = file_type_item_classes[type];
+
+ entry = scan_playlist_file(entry, type, indent_size+1, inl,
sb.st_ino);
+
free(indent);
- return scan_playlist_file(fullpath, name, parent, type,
indent_size+1, inl, sb.st_ino);
+ return entry;
}
/*say(4, _("%s Adding as %s\n"), indent, quote(name));*/
@@ -798,81 +1072,9 @@
detail->data.file.item_class = file_type_item_classes[type];
/* XXX: dump size, mtime, ?? */
- if (tags_enabled && (type == FILE_MP3 || type == FILE_MP3_ID3)) {
- detail = NULL;
-
-#ifdef HAVE_TAGLIB
- if (detail == NULL) {
- TagLib_File *tl_file;
- TagLib_File_Type tl_type;
+ scan_tag_data(entry, fullpath, type);
- switch (type) {
- case FILE_OGG:
- tl_type = TagLib_File_OggVorbis;
- break;
- default:
- tl_type = TagLib_File_MPEG;
- break;
- }
-
- tl_file = taglib_file_new_type(fullpath, tl_type);
- if (tl_file != NULL) {
- TagLib_Tag *tl_tag;
- const TagLib_AudioProperties *tl_props;
- char *str;
- unsigned int track;
-
- tl_tag = taglib_file_tag(tl_file);
- detail = add_entry_detail(entry, DETAIL_TAG);
- str = taglib_tag_title(tl_tag);
- detail->data.tag.title = (*str == '\0' ? NULL :
xstrdup(str));
- str = taglib_tag_album(tl_tag);
- detail->data.tag.album = (*str == '\0' ? NULL :
xstrdup(str));
- str = taglib_tag_artist(tl_tag);
- detail->data.tag.artist = (*str == '\0' ? NULL :
xstrdup(str));
- str = taglib_tag_genre(tl_tag);
- detail->data.tag.genre = (*str == '\0' ? NULL :
xstrdup(str));
- track = taglib_tag_track(tl_tag);
- detail->data.tag.track_no = track > 0 ? track : -1;
- tl_props = taglib_file_audioproperties(tl_file);
- detail->data.tag.duration =
taglib_audioproperties_length(tl_props);
-
- /*taglib_tag_free_strings();*/
- taglib_file_free(tl_file);
- }
- }
-#endif
-#ifdef HAVE_ID3LIB
- if (detail == NULL) {
- /*char *title;*/
-
- ID3Tag_Clear(id3);
- ID3Tag_Link(id3, fullpath);
-
- if (ID3Tag_NumFrames(id3) != 0) {
- char *track;
-
- detail = add_entry_detail(entry, DETAIL_TAG);
- /*title = get_id3_string(fullpath, id3, ID3FID_TITLE);
- if (title != NULL && strcmp(title, "") != 0) {
- free(entry->name);
- entry->name = title;
- } else {
- free(title);
- }*/
- detail->data.tag.title = get_id3_string(fullpath, id3,
ID3FID_TITLE);
- detail->data.tag.album = get_id3_string(fullpath, id3,
ID3FID_ALBUM);
- detail->data.tag.artist = get_id3_string(fullpath, id3,
ID3FID_LEADARTIST);
- detail->data.tag.genre = get_id3_string(fullpath, id3,
ID3FID_CONTENTTYPE);
- track = get_id3_string(fullpath, id3, ID3FID_TRACKNUM);
- if (track == NULL || !parse_int32(track,
&detail->data.tag.track_no))
- detail->data.tag.track_no = -1;
-
- /* get_id3_string(fullpath, id3, ID3FID_TIME); */
- detail->data.tag.duration = -1;
- }
- }
-#endif
+ detail = get_entry_detail(entry, DETAIL_TAG);
if (detail != NULL) {
if (detail->data.tag.title != NULL)
say(4, "%s %s: %s\n", indent, _("Title"),
quotearg(detail->data.tag.title));
@@ -887,7 +1089,6 @@
if (detail->data.tag.duration != -1)
say(4, "%s %s: %u:%02u:%02u.%02u\n", indent,
_("Duration"), SPLIT_DURATION(detail->data.tag.duration));
}
- }
free(indent);
return entry;
@@ -965,10 +1166,193 @@
entries_size = DEFAULT_ENTRIES_SIZE;
entries = xmalloc(sizeof(Entry *) * entries_size);
+ bzero(entries, sizeof(Entry *) * entries_size);
return true;
}
+static Entry *
+rescan_dir_entry(Entry *entry, bool recurse)
+{
+ EntryDetail *child_detail;
+ char *fullpath;
+ int32_t *old_children, *new_children;
+ uint32_t old_child_count, new_child_count;
+ struct dirent **dirents;
+ int dirent_count;
+ int d, oc;
+
+ fullpath = get_entry_detail(entry, DETAIL_FILE)->data.file.filename;
+
+ child_detail = get_entry_detail(entry, DETAIL_CHILDREN);
+ old_children = child_detail->data.children.list;
+ old_child_count = child_detail->data.children.count;
+
+ say(4, _("Re-Scanning directory %s\n"), quote(fullpath));
+ dirent_count = scandir(fullpath, &dirents, NULL, &alphadirsort);
+ if (dirent_count < 0) {
+ warn(_("%s: cannot scan directory: %s\n"), quotearg(fullpath), errstr);
+ delete_entry(entry, true);
+ entry = NULL;
+ return entry;
+ }
+
+ new_children = xmalloc(sizeof(int32_t) * dirent_count);
+
+ d = oc = 0;
+ new_child_count = 0;
+ while (d < dirent_count)
+ {
+ if (strcmp(dirents[d]->d_name, ".") != 0 && strcmp(dirents[d]->d_name,
"..") != 0) {
+ EntryDetail *file_detail = NULL;
+ char *child_path;
+ Entry *child = NULL;
+ int cmp = -1;
+
+ for ( ; oc < old_child_count; oc++)
+ {
+ child = entries[old_children[oc]];
+ if (child)
+ {
+ file_detail = get_entry_detail(child, DETAIL_FILE);
+
+ /* see if this is the same file */
+ cmp = dirents[d]->d_type -
IFTODT(file_detail->data.file.mode);
+ if (cmp == 0)
+ {
+ cmp = strcmp(dirents[d]->d_name,
file_detail->data.file.filename);
+ }
+ break;
+ }
+ }
+
+ if (cmp == 0)
+ {
+ /* if the same file advance both indecies */
+ say(4, _(" [same] %s\n"),
quote(file_detail->data.file.filename));
+ if (recurse)
+ child = rescan_entry(child, recurse);
+ new_children[new_child_count++] = child->id;
+ d++; oc++;
+ }
+ else if (cmp < 0)
+ {
+ /* insert new child */
+ child_path = concat_filenames(fullpath, dirents[d]->d_name);
+ say(4, _(" [new] %s\n"), quote(child_path));
+ child = scan_entry(child_path, dirents[d]->d_name, entry->id,
0, NULL);
+ if (child != NULL)
+ {
+ new_children[new_child_count++] = child->id;
+ }
+ free(child_path);
+ d++;
+ }
+ else /* if (cmp > 0) */
+ {
+ /* delete old child(ren) */
+ say(4, _(" [del] %s\n"),
quote(file_detail->data.file.filename));
+ delete_entry(child, true);
+ child = NULL;
+ oc++;
+ }
+ }
+ else
+ {
+ d++;
+ }
+ }
+
+ child_detail->data.children.count = new_child_count;
+ child_detail->data.children.list = xmemdup(new_children, sizeof(int32_t) *
new_child_count);
+ child_detail->data.children.update_id++;
+ free(old_children);
+ free(new_children);
+
+ return entry;
+}
+
+Entry *
+rescan_entry(Entry *entry, bool recurse)
+{
+ struct stat sb;
+ EntryDetail *detail;
+ char *fullpath;
+
+ detail = get_entry_detail(entry, DETAIL_FILE);
+
+ /* Skip over entries without a file details as there is no way
+ * to rescan them. */
+ if (NULL == detail)
+ return entry;
+
+ lock_metadata();
+
+ fullpath = get_entry_detail(entry, DETAIL_FILE)->data.file.filename;
+ say(4, _("Re-Scanning entry %s\n"), quote(fullpath));
+
+ /* stat() the entry and compare the mod time */
+ if (stat(fullpath, &sb) < 0)
+ {
+ /* delete the entry if the entry cannot be stat()ed */
+ say(4, _(" [del] %s\n"), quote(fullpath));
+ delete_entry(entry, true);
+ entry = NULL;
+ unlock_metadata();
+ return entry;
+ }
+
+ /* Return if the mtime is the same. */
+ if (detail->data.file.mtime == sb.st_mtime) {
+ say(4, _(" [same] %s\n"), quote(fullpath));
+ unlock_metadata();
+ return entry;
+ }
+
+ /* update entry if the mod time is different */
+ detail->data.file.size = sb.st_size;
+ detail->data.file.mtime = sb.st_mtime;
+ detail->data.file.mode = sb.st_mode;
+
+ if (S_ISDIR(sb.st_mode)) {
+ say(4, _(" [new] %s (dir)\n"), quote(fullpath));
+ entry = rescan_dir_entry(entry, recurse);
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ FileType type;
+ say(4, _(" [new] %s\n"), quote(fullpath));
+ say(4, _("Checking content type of file %s\n"), quote(fullpath));
+ type = check_file_content_type(fullpath);
+ detail->data.file.mime_type = file_type_mime_types[type];
+ detail->data.file.item_class = file_type_item_classes[type];
+
+ if (type == FILE_UNKNOWN) {
+ say(4, _("Matched no type for %s\n"), quote(fullpath));
+ } else if (type == FILE_M3U) {
+ say(4, _("Assuming type %s for %s\n"), file_type_descs[type],
quote(fullpath));
+ } else {
+ say(4, _("Matched type %s for %s\n"), file_type_descs[type],
quote(fullpath));
+ }
+ if (!string_in_csv(file_types, ',', file_type_names[type]))
+ {
+ /* delete entry if the file is an unsupported type */
+ delete_entry(entry, true);
+ entry = NULL;
+ unlock_metadata();
+ return entry;
+ }
+
+ if (type == FILE_PLS || type == FILE_M3U || type == FILE_EXTM3U)
+ entry = scan_playlist_file(entry, type, 0, NULL, sb.st_ino);
+ else
+ scan_tag_data(entry, fullpath, type);
+ }
+
+ unlock_metadata();
+ return entry;
+}
+
bool
scan_entries(char **pathv, int pathc, int indent_size)
{
@@ -1023,29 +1407,10 @@
uint32_t c;
for (c = 0; c < entry_count; c++) {
- EntryDetail *d = entries[c]->details;
-
- while (d != NULL) {
- EntryDetail *next;
-
- if (d->type == DETAIL_CHILDREN) {
- free(d->data.children.list);
- } else if (d->type == DETAIL_FILE) {
- free(d->data.file.filename);
- } else if (d->type == DETAIL_TAG) {
- free(d->data.tag.title);
- free(d->data.tag.artist);
- free(d->data.tag.album);
- free(d->data.tag.genre);
- }
-
- next = d->next;
- free(d);
- d = next;
- }
-
- free(entries[c]->name);
- free(entries[c]);
+ Entry *entry = entries[c];
+ if (entry)
+ delete_entry(entry, false);
+ entries[c] = NULL;
}
entry_count = 0;
@@ -1062,6 +1427,7 @@
iconv_close(utf16_to_utf8); /* ignore errors (only EINVAL) */
}
#endif
+ bzero(entries, entries_size * sizeof(Entry *));
free(entries);
entries_size = 0;
@@ -1071,11 +1437,15 @@
void
lock_metadata(void)
{
+ say(4, "> locking metadata\n");
ithread_mutex_lock(&metadata_mutex);
}
void
unlock_metadata(void)
{
+ say(4, "< unlocking metadata\n");
ithread_mutex_unlock(&metadata_mutex);
}
+
+// ex:ts=8:
# $FreeBSD$
--- src/strbuf.c.orig Wed Oct 12 03:51:54 2005
+++ src/strbuf.c Sat Jan 7 11:48:42 2006
@@ -35,6 +35,7 @@
#include "vasprintf.h" /* Gnulib */
#include "xalloc.h" /* Gnulib */
#include "minmax.h" /* Gnulib */
+#include "strnlen.h" /* GNUlib */
#include "strbuf.h"
#define DEFAULT_STRBUF_CAPACITY 16
# $FreeBSD$
--- src/url.c.orig Mon Aug 29 09:58:31 2005
+++ src/url.c Sat Jan 7 11:48:42 2006
@@ -37,6 +37,7 @@
{
free(url->hostname);
free(url->path);
+ free(url->port);
free(url);
}
@@ -50,9 +51,9 @@
{
char *next;
char *next2;
- char *hostname;
- uint16_t port;
- char *path;
+ char *hostname = NULL;
+ char *port = NULL;
+ char *path = NULL;
URL *url;
if (strncasecmp(urlstr, "http://", 7) != 0)
@@ -61,30 +62,26 @@
next = strpbrk(urlstr+7, ":/");
if (next == NULL) {
hostname = xstrdup(urlstr+7);
- port = 80;
- path = xstrdup("/");
} else if (*next == ':') {
next2 = strchr(next+1, '/');
if (next2 == NULL) {
- if (!parse_uint16(next+1, &port))
- return NULL;
- path = xstrdup("/");
+ port = xstrdup(next+1);
} else {
- char *tmp = xstrndup(next+1, next2-next-1);
- if (!parse_uint16(tmp, &port)) { /* XXX: should make sure
tmp[0]=='\0' is not accepted */
- free(tmp);
- return NULL;
- }
- free(tmp);
+ port = xstrndup(next+1, next2-next-1);
path = xstrdup(next2);
}
hostname = xstrndup(urlstr+7, next-urlstr-7);
} else {
- port = 80;
hostname = xstrndup(urlstr+7, next-urlstr-7);
path = xstrdup(next);
}
+ if (NULL == port)
+ port = xstrdup("80");
+
+ if (NULL == path)
+ path = xstrdup("/");
+
url = xmalloc(sizeof(URL));
url->port = port;
url->hostname = hostname;
# $FreeBSD$
--- src/webclient.c.orig Fri May 5 07:31:02 2006
+++ src/webclient.c Tue Oct 30 18:22:10 2007
@@ -41,6 +41,7 @@
#include "getaddrinfo.h" /* Gnulib */
#include "gmediaserver.h"
#include "hmap.h"
+#include "intutil.h"
#include "strutil.h"
#define MIN_HTTP_READ 512
@@ -64,12 +65,81 @@
return 1;
}
+static ssize_t
+icy_read(HTTPResult *result, void *buf, size_t count)
+{
+ ssize_t copylen;
+ ssize_t readlen;
+ ssize_t sz_read, sz_left;
+ unsigned char metadata[4081];
+
+ /* Get all data from local buffer if possible. */
+ copylen = result->buftail - result->bufhead;
+ if (count <= copylen) {
+ memcpy(buf, result->buf + result->bufhead, count);
+ result->bufhead += count;
+ return count;
+ }
+ /* Get some data from local buffer if possible. */
+ if (copylen > 0) {
+ memcpy(buf, result->buf + result->bufhead, copylen);
+ result->bufhead = result->buftail = 0;
+ count -= copylen;
+ buf += copylen;
+ }
+
+ /* If we got here, then result->buf is empty. */
+ /* read content data */
+ sz_read = 0;
+ sz_left = result->bufsize;
+ while (sz_left > 0)
+ {
+ readlen = read(result->fd, result->buf + sz_read, sz_left);
+ if (readlen <= 0)
+ return copylen == 0 ? readlen : copylen;
+ sz_read += readlen;
+ sz_left -= readlen;
+ }
+
+ /* skip metadata */
+ readlen = read(result->fd, metadata, 1);
+ if (readlen != 1)
+ {
+ return copylen == 0 ? readlen : copylen;
+ }
+ sz_read = 1;
+ sz_left = 16 * metadata[0];
+ while (sz_left > 0)
+ {
+ readlen = read(result->fd, metadata + sz_read, sz_left);
+ if (readlen <= 0)
+ return copylen == 0 ? readlen : copylen;
+ sz_read += readlen;
+ sz_left -= readlen;
+ }
+
+ if (count < result->bufsize) {
+ memcpy(buf, result->buf, count);
+ result->bufhead = count;
+ result->buftail = result->bufsize;
+ return copylen + count;
+ }
+ memcpy(buf, result->buf, result->bufsize);
+ result->bufhead = result->buftail = 0;
+ return copylen + result->bufsize;
+}
+
ssize_t
http_read(HTTPResult *result, void *buf, size_t count)
{
ssize_t copylen;
ssize_t readlen;
+ /* Pass off the read request to the ICY handler if there is
+ * ICY metadata involved */
+ if (result->icy_server && result->icy_metaint)
+ return icy_read(result, buf, count);
+
/* Get all data from local buffer if possible. */
copylen = result->buftail - result->bufhead;
if (count <= copylen) {
@@ -86,8 +156,8 @@
}
/* If we got here, then result->buf is empty. */
- if (count < MIN_HTTP_READ) {
- readlen = read(result->fd, result->buf, MIN_HTTP_READ);
+ if (count < result->bufsize) {
+ readlen = read(result->fd, result->buf, result->bufsize);
if (readlen <= 0)
return copylen == 0 ? readlen : copylen;
if (count < readlen) {
@@ -169,7 +239,7 @@
}
HTTPResult *
-http_query(const char *method, const char *urlstr, bool keep_open)
+http_query(const char *method, const char *urlstr, bool keep_open, bool
query_metadata)
{
struct addrinfo hints;
struct addrinfo *addrinfo;
@@ -178,7 +248,6 @@
size_t linesize;
ssize_t linelen;
char *line;
- char service[6]; /* will hold an uint16_t string */
HTTPResult *result;
URL *url;
@@ -188,11 +257,10 @@
return NULL;
}
- snprintf(service, sizeof(service), "%" PRIu16, url->port); /* " */
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- c = getaddrinfo(url->hostname, service, &hints, &addrinfo);
+ c = getaddrinfo(url->hostname, url->port, &hints, &addrinfo);
if (c != 0) {
warn(_("%s: cannot look up host address: %s\n"), url->hostname,
gai_strerror(c));
free_url(url);
@@ -211,32 +279,32 @@
free_url(url);
return NULL;
}
- freeaddrinfo(addrinfo);
-
- result = xmalloc(sizeof(HTTPResult));
- result->headers = NULL;
- result->fd = fd;
- result->bufsize = MIN_HTTP_READ;
- result->buf = xmalloc(result->bufsize);
if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0) {
warn(_("%s: cannot connect: %s\n"), url->hostname, errstr);
- http_result_free(result);
+ freeaddrinfo(addrinfo);
free_url(url);
return NULL;
}
+ freeaddrinfo(addrinfo);
- line = xasprintf("%s %s HTTP/1.0\r\nUser-Agent: %s %s\r\n\r\n", method,
url->path, PACKAGE, VERSION);
+ line = xasprintf("%s %s HTTP/1.0\r\nHost: %s:%s\r\nUser-Agent:
%s/%s\r\nIcy-MetaData:%d\r\n\r\n", method, url->path, url->hostname, url->port,
PACKAGE, VERSION, query_metadata);
linelen = strlen(line);
if (full_write(fd, line, linelen) < linelen) {
warn(_("%s: cannot send: %s\n"), url->hostname, errstr);
free(line);
- http_result_free(result);
free_url(url);
return NULL;
}
free(line);
+ result = xmalloc(sizeof(HTTPResult));
+ result->icy_server = false;
+ result->icy_metaint = 0;
+ result->headers = NULL;
+ result->fd = fd;
+ result->bufsize = MIN_HTTP_READ;
+ result->buf = xmalloc(result->bufsize);
result->bufhead = 0;
result->buftail = 0;
@@ -286,7 +354,6 @@
free_url(url);
return NULL;
}
- result->icy_server = true;
}
result->result = (line[c+1]-'0')*100 + (line[c+2]-'0')*10 +
(line[c+3]-'0');
@@ -339,6 +406,16 @@
for (value++; c_isspace(*value); value++);
hmap_put(result->headers, key, key + (value - line));
/*}*/
+
+ if (result->icy_server == true && strcasecmp("icy-metaint", key) == 0)
+ {
+ parse_uint32(key + (value - line), &result->icy_metaint);
+ if (result->icy_metaint)
+ {
+ result->bufsize = result->icy_metaint;
+ result->buf = xrealloc(result->buf, result->bufsize);
+ }
+ }
}
free(line);
# $FreeBSD$
--- src/webserver.c.orig Thu Aug 31 13:18:14 2006
+++ src/webserver.c Tue Oct 30 18:22:10 2007
@@ -130,7 +130,7 @@
time_t modified;
detail = get_entry_detail(entry, DETAIL_URL);
- result = http_query("HEAD", detail->data.url.url, false);
+ result = http_query("HEAD", detail->data.url.url, false, true);
if (result == NULL)
return -1;
@@ -156,7 +156,7 @@
if (value != NULL) {
info->content_type = ixmlCloneDOMString(value);
} else {
- info->content_type =
ixmlCloneDOMString("application/octet-stream");
+ info->content_type = ixmlCloneDOMString("audio/mpeg");
}
value = hmap_get(result->headers, "Last-Modified");
if (value != NULL && parse_http_date(value, &modified)) {
@@ -238,9 +238,13 @@
url = get_entry_detail(entry, DETAIL_URL)->data.url.url;
- result = http_query("GET", url, true);
+ result = http_query("GET", url, true, false);
if (result == NULL)
- return NULL;
+ {
+ result = http_query("GET", url, true, true);
+ if (result == NULL)
+ return NULL;
+ }
if (result->result == -1) {
warn(_("%s: remote server did not support GET command\n"),
quotearg(url));
http_result_free(result);