gmediaserver-devel
[Top][All Lists]
Advanced

[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);

reply via email to

[Prev in Thread] Current Thread [Next in Thread]