changeset 125:e413158cae13

Add ushare project files.
author naoyan@johnstown.minaminoshima.org
date Sun, 03 Oct 2010 11:35:19 +0900
parents 9c7bc6c0327e
children 5dcaf3785ebe
files Makefile configure src/Makefile src/buffer.c src/buffer.h src/cds.c src/cds.h src/cfgparser.c src/cfgparser.h src/cms.c src/cms.h src/content.c src/content.h src/ctrl_telnet.c src/ctrl_telnet.h src/gettext.h src/http.c src/http.h src/metadata.c src/metadata.h src/mime.c src/mime.h src/minmax.h src/msr.c src/msr.h src/osdep.c src/osdep.h src/presentation.c src/presentation.h src/recpt1.c src/recpt1.h src/redblack.c src/redblack.h src/services.c src/services.h src/trace.c src/trace.h src/ushare.c src/ushare.h src/util_iconv.c src/util_iconv.h src/version.h
diffstat 42 files changed, 9230 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,51 @@
+ifeq (,$(wildcard config.mak))
+$(error "config.mak is not present, run configure !")
+endif
+include config.mak
+
+DISTFILE = recpt1-$(VERSION).tar.bz2
+
+EXTRADIST = configure \
+
+SUBDIRS = src \
+
+all:
+	for subdir in $(SUBDIRS); do \
+	  $(MAKE) -C $$subdir $@; \
+	done
+
+clean:
+	for subdir in $(SUBDIRS); do \
+	  $(MAKE) -C $$subdir $@; \
+	done
+
+distclean: clean
+	for subdir in $(SUBDIRS); do \
+	  $(MAKE) -C $$subdir $@; \
+	done
+	-$(RM) -f config.log
+	-$(RM) -f config.mak
+	-$(RM) -f config.h
+
+
+install:
+	for subdir in $(SUBDIRS); do \
+	  $(MAKE) -C $$subdir $@; \
+	done
+
+.PHONY: clean distclean install
+
+dist:
+	-$(RM) $(DISTFILE)
+	dist=$(shell pwd)/recpt1-$(VERSION) && \
+	for subdir in . $(SUBDIRS); do \
+		mkdir -p "$$dist/$$subdir"; \
+		$(MAKE) -C $$subdir dist-all DIST="$$dist/$$subdir"; \
+	done && \
+	tar cjf $(DISTFILE) recpt1-$(VERSION)
+	-$(RM) -rf recpt1-$(VERSION)
+
+dist-all:
+	cp $(SRCS) Makefile $(DIST)
+
+.PHONY: dist dist-all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,770 @@
+#!/bin/sh
+#
+# recpt1 configure script - (c) 2007 Benjamin Zores
+#
+#  (fully inspirated from ffmpeg configure script, thanks to Fabrice Bellard)
+#
+
+# make sure we are running under a compatible shell
+unset foo
+(: ${foo%%bar}) 2>/dev/null && ! (: ${foo?}) 2>/dev/null
+if test "$?" != 0; then
+    if test "x$USHARE_CONFIGURE_EXEC" = x; then
+        USHARE_CONFIGURE_EXEC=1
+        export USHARE_CONFIGURE_EXEC
+        exec bash "$0" "$@"
+        exec ksh "$0" "$@"
+        exec /usr/xpg4/bin/sh "$0" "$@"
+    fi
+    echo "No compatible shell script interpreter found."
+    exit 1
+fi
+
+show_help(){
+  echo "Usage: configure [options]"
+  echo "Options: [defaults in brackets after descriptions]"
+  echo
+  echo "Standard options:"
+  echo "  --help                      print this message"
+  echo "  --log[=FILE|yes|no]         log tests and output to FILE [config.log]"
+  echo "  --prefix=PREFIX             install in PREFIX [$PREFIX]"
+  echo "  --bindir=DIR                install binaries in DIR [PREFIX/bin]"
+  echo "  --sysconfdir=DIR            configuration files DIR [PREFIX/etc]"
+  echo "  --localedir=DIR             use locales from DIR [PREFIX/share/locale]"
+  echo ""
+  echo "Extended options:"
+  echo "  --enable-dlna               enable DLNA support through libldna"
+  echo "  --disable-dlna              disable DLNA support"
+  echo "  --disable-nls               do not use Native Language Support"
+  echo "  --enable-b25                enable b25 support"
+  echo ""
+  echo "Search paths:"
+  echo "  --with-libupnp-dir=DIR      check for libupnp installed in DIR"
+  echo "  --with-libdlna-dir=DIR      check for libdlna installed in DIR"
+  echo "  --with-b25-dir=DIR          check for libarib25 installed in DIR"
+  echo ""
+  echo "Advanced options (experts only):"
+  echo "  --enable-debug              enable debugging symbols"
+  echo "  --disable-debug             disable debugging symbols"
+  echo "  --disable-strip             disable stripping of executables at installation"
+  echo "  --disable-optimize          disable compiler optimization"
+  echo "  --cross-prefix=PREFIX       use PREFIX for compilation tools [$cross_prefix]"
+  echo "  --cross-compile             assume a cross-compiler is used"
+  exit 1
+}
+
+log(){
+    echo "$@" >>$logfile
+}
+
+log_file(){
+    log BEGIN $1
+    cat -n $1 >>$logfile
+    log END $1
+}
+
+echolog(){
+    log "$@"
+    echo "$@"
+}
+
+clean(){
+    rm -f $TMPC $TMPO $TMPE $TMPS
+}
+
+die(){
+    echolog "$@"
+    if enabled logging; then
+      echo "See file \"$logfile\" produced by configure for more details."
+    else
+      echo "Rerun configure with logging enabled (do not use --log=no) for more details."
+    fi
+    clean
+    exit 1
+}
+
+enabled(){
+    eval test "x\$$1" = "xyes"
+}
+
+flags_saved(){
+    (: ${SAVE_CFLAGS?}) 2>/dev/null
+}
+
+save_flags(){
+    flags_saved && return
+    SAVE_CFLAGS="$CFLAGS"
+    SAVE_LDFLAGS="$LDFLAGS"
+    SAVE_extralibs="$extralibs"
+}
+
+restore_flags(){
+    CFLAGS="$SAVE_CFLAGS"
+    LDFLAGS="$SAVE_LDFLAGS"
+    extralibs="$SAVE_extralibs"
+    unset SAVE_CFLAGS
+    unset SAVE_LDFLAGS
+    unset SAVE_extralibs
+}
+
+temp_cflags(){
+    temp_append CFLAGS "$@"
+}
+
+temp_ldflags(){
+    temp_append LDFLAGS "$@"
+}
+
+temp_extralibs(){
+    temp_append extralibs "$@"
+}
+
+temp_append(){
+    local var
+    var=$1
+    shift
+    save_flags
+    append_var "$var" "$@"
+}
+
+append_var(){
+    local var f
+    var=$1
+    shift
+    for f in $@; do
+      if eval echo \$$var | grep -qv -e "$f"; then
+        eval "$var=\"\$$var $f\""
+      fi
+    done
+}
+
+append(){
+    local var
+    var=$1
+    shift
+    flags_saved && append_var "SAVE_$var" "$@"
+    append_var "$var" "$@"
+}
+
+add_cflags(){
+    append CFLAGS "$@"
+}
+
+add_ldflags(){
+    append LDFLAGS "$@"
+}
+
+add_extralibs(){
+    append extralibs "$@"
+}
+
+add_clog(){
+    echo "#define $1 $2" >> $CONFIG_H
+}
+
+add_clog_str(){
+    echo "#define $1 \"$2\"" >> $CONFIG_H
+}
+
+check_cmd(){
+    log "$@"
+    "$@" >>$logfile 2>&1
+}
+
+check_cc(){
+    log check_cc "$@"
+    cat >$TMPC
+    log_file $TMPC
+    check_cmd $cc $CFLAGS "$@" -c -o $TMPO $TMPC
+}
+
+check_cpp(){
+    log check_cpp "$@"
+    cat >$TMPC
+    log_file $TMPC
+    check_cmd $cc $CFLAGS "$@" -E -o $TMPO $TMPC
+}
+
+check_ld(){
+    log check_ld "$@"
+    check_cc || return
+    check_cmd $cc $LDFLAGS "$@" -o $TMPE $TMPO $extralibs
+}
+
+check_exec(){
+    check_ld "$@" && { enabled cross_compile || $TMPE >>$logfile 2>&1; }
+}
+
+check_cflags(){
+    log check_cflags "$@"
+    check_cc "$@" <<EOF && add_cflags "$@"
+int x;
+EOF
+}
+
+check_ldflags(){
+    log check_ldflags "$@"
+    check_ld "$@" <<EOF && add_ldflags "$@"
+int main(){
+    return 0;
+}
+EOF
+}
+
+check_header(){
+    local header
+    log check_header "$@"
+    header=$1
+    shift
+    check_cpp "$@" <<EOF
+#include <$header>
+int x;
+EOF
+}
+
+check_func(){
+    local func
+    log check_func "$@"
+    func=$1
+    shift
+    check_ld "$@" <<EOF
+extern int $func();
+int main(){
+    $func();
+    return 0;
+}
+EOF
+}
+
+check_lib(){
+    local header func err
+    log check_lib "$@"
+    header="$1"
+    func="$2"
+    shift 2
+    temp_extralibs "$@"
+    check_header $header && check_func $func && add_extralibs "$@"
+    err=$?
+    restore_flags
+    return $err
+}
+
+check_lib_version() {
+    check_cmd pkg-config "$1" --atleast-version="$2"
+    err=$?
+    return $err
+}
+
+append_config(){
+    echo "$@" >> $CONFIGFILE
+}
+
+expand_var(){
+    v="$1"
+    while true; do
+      eval t="$v"
+      test "$t" = "$v" && break
+      v="$t"
+    done
+    echo "$v"
+}
+
+# set temporary file name
+if test ! -z "$TMPDIR" ; then
+    TMPDIR1="${TMPDIR}"
+elif test ! -z "$TEMPDIR" ; then
+    TMPDIR1="${TEMPDIR}"
+else
+    TMPDIR1="/tmp"
+fi
+
+TMPC="${TMPDIR1}/recpt1-${RANDOM}-$$-${RANDOM}.c"
+TMPO="${TMPDIR1}/recpt1-${RANDOM}-$$-${RANDOM}.o"
+TMPE="${TMPDIR1}/recpt1-${RANDOM}-$$-${RANDOM}"
+TMPS="${TMPDIR1}/recpt1-${RANDOM}-$$-${RANDOM}.S"
+
+CONFIGFILE="config.mak"
+CONFIG_H="config.h"
+
+#################################################
+#   set default parameters
+#################################################
+logging="yes"
+logfile="config.log"
+PREFIX="/usr/local"
+bindir='${PREFIX}/bin'
+sysconfdir='${PREFIX}/etc'
+localedir='${PREFIX}/share/locale'
+#dlna="no"
+dlna="yes"
+nls="yes"
+b25="no"
+cc="gcc"
+make="make"
+strip="strip"
+cpu=`uname -m`
+optimize="yes"
+debug="no"
+dostrip="yes"
+extralibs=""
+installstrip="-s"
+cross_compile="no"
+INSTALL="/usr/bin/install -c"
+VERSION="1.1a"
+system_name=`uname -s 2>&1`
+
+#################################################
+#   set cpu variable and specific cpu flags
+#################################################
+case "$cpu" in
+  i386|i486|i586|i686|i86pc|BePC)
+    cpu="x86"
+  ;;
+  x86_64|amd64)
+    cpu="x86"
+    canon_arch="`$cc -dumpmachine | sed -e 's,\([^-]*\)-.*,\1,'`"
+    if [ x"$canon_arch" = x"x86_64" -o x"$canon_arch" = x"amd64" ]; then
+      if [ -z "`echo $CFLAGS | grep -- -m32`"  ]; then
+        cpu="x86_64"
+      fi
+    fi
+  ;;
+# armv4l is a subset of armv5tel
+  arm|armv4l|armv5tel)
+    cpu="armv4l"
+  ;;
+  alpha)
+    cpu="alpha"
+  ;;
+  "Power Macintosh"|ppc|ppc64|powerpc)
+    cpu="powerpc"
+  ;;
+  mips|mipsel|IP*)
+    cpu="mips"
+  ;;
+  sun4u|sparc64)
+    cpu="sparc64"
+  ;;
+  sparc)
+    cpu="sparc"
+  ;;
+  sh4)
+    cpu="sh4"
+  ;;
+  parisc|parisc64)
+    cpu="parisc"
+  ;;
+  s390|s390x)
+    cpu="s390"
+  ;;
+  m68k)
+    cpu="m68k"
+  ;;
+  ia64)
+    cpu="ia64"
+  ;;
+  bfin)
+    cpu="bfin"
+  ;;
+  *)
+    cpu="unknown"
+  ;;
+esac
+
+# OS test booleans functions
+issystem() {
+  test "`echo $system_name | tr A-Z a-z`" = "`echo $1 | tr A-Z a-z`"
+}
+
+linux()   { issystem "Linux" || issystem "uClinux" ; return "$?" ; }
+sunos()   { issystem "SunOS"   ; return "$?" ; }
+hpux()    { issystem "HP-UX"   ; return "$?" ; }
+irix()    { issystem "IRIX"    ; return "$?" ; }
+aix()     { issystem "AIX"     ; return "$?" ; }
+cygwin()  { issystem "CYGWIN"  ; return "$?" ; }
+freebsd() { issystem "FreeBSD" || issystem "GNU/kFreeBSD"; return "$?" ; }
+netbsd()  { issystem "NetBSD"  ; return "$?" ; }
+bsdos()   { issystem "BSD/OS"  ; return "$?" ; }
+openbsd() { issystem "OpenBSD" ; return "$?" ; }
+bsd()     { freebsd || netbsd || bsdos || openbsd ; return "$?" ; }
+qnx()     { issystem "QNX"     ; return "$?" ; }
+darwin()  { issystem "Darwin"  ; return "$?" ; }
+gnu()     { issystem "GNU"     ; return "$?" ; }
+mingw32() { issystem "MINGW32" ; return "$?" ; }
+morphos() { issystem "MorphOS" ; return "$?" ; }
+amigaos() { issystem "AmigaOS" ; return "$?" ; }
+win32()   { cygwin || mingw32  ; return "$?" ; }
+beos()    { issystem "BEOS"    ; return "$?" ; }
+
+#################################################
+#   check options
+#################################################
+for opt do
+  optval="${opt#*=}"
+  case "$opt" in
+  --log)
+  ;;
+  --log=*) logging="$optval"
+  ;;
+  --prefix=*) PREFIX="$optval"; force_prefix=yes
+  ;;
+  --bindir=*) bindir="$optval";
+  ;;
+  --sysconfdir=*) sysconfdir="$optval";
+  ;;
+  --localedir=*) localedir="$optval";
+  ;;
+  --with-libupnp-dir=*) libupnpdir="$optval";
+  ;;
+  --with-libdlna-dir=*) libdlnadir="$optval";
+  ;;
+  --with-b25-dir=*) libb25dir="$optval";
+  ;;
+  --disable-nls) nls="no"
+  ;;
+  --enable-dlna) dlna="yes"
+  ;;
+  --disable-dlna) dlna="no"
+  ;;
+  --enable-b25) b25="yes"
+  ;;
+  --disable-b25) b25="no"
+  ;;
+  --enable-debug) debug="yes"
+  ;;
+  --disable-debug) debug="no"
+  ;;
+  --disable-strip) dostrip="no"
+  ;;
+  --disable-optimize) optimize="no"
+  ;;
+  --cross-prefix=*) cross_prefix="$optval"
+  ;;
+  --cross-compile) cross_compile="yes"
+  ;;
+  --help) show_help
+  ;;
+  *)
+  echo "Unknown option \"$opt\"."
+  echo "See $0 --help for available options."
+  exit 1
+  ;;
+  esac
+done
+
+if [ -n "$cross_prefix" ]; then
+  cross_compile="yes"
+  cc="${cross_prefix}${cc}"
+  strip="${cross_prefix}${strip}"
+else
+  [ -n "$CC" ] && cc="$CC"
+  [ -n "$STRIP" ] && strip="$STRIP"
+fi
+[ -n "$MAKE" ] && make="$MAKE"
+
+#################################################
+#   create logging file
+#################################################
+if test "$logging" != no; then
+  enabled logging || logfile="$logging"
+  echo "# $0 $@" >$logfile
+  set >>$logfile
+else
+  logfile=/dev/null
+fi
+
+#################################################
+#   compiler sanity check
+#################################################
+echolog "Checking for compiler available..."
+check_exec <<EOF
+int main(){
+    return 0;
+}
+EOF
+if test "$?" != 0; then
+  echo "$cc is unable to create an executable file."
+  if test -z "$cross_prefix" -a "$cross_compile" = no; then
+    echo "If $cc is a cross-compiler, use the --cross-compile option."
+  fi
+  die "C compiler test failed."
+fi
+
+#################################################
+#   check for target specific flags
+#################################################
+# check for SIMD availability
+
+# AltiVec flags: The FSF version of GCC differs from the Apple version
+if test $cpu = "powerpc"; then
+  if test $altivec = "yes"; then
+    if test -n "`$cc -v 2>&1 | grep version | grep Apple`"; then
+      add_cflags "-faltivec"
+    else
+      add_cflags "-maltivec -mabi=altivec"
+    fi
+  fi
+fi
+
+check_header altivec.h && _altivec_h=yes || _altivec_h=no
+
+# check if our compiler supports Motorola AltiVec C API
+if enabled altivec; then
+  if enabled _altivec_h; then
+    inc_altivec_h="#include <altivec.h>"
+  else
+    inc_altivec_h=
+  fi
+  check_cc <<EOF || altivec=no
+$inc_altivec_h
+int main(void) {
+    vector signed int v1, v2, v3;
+    v1 = vec_add(v2,v3);
+    return 0;
+}
+EOF
+fi
+
+# mmi only available on mips
+if [ "$mmi" = "default" ]; then
+  if [ "$cpu" = "mips" ]; then
+    mmi="yes"
+  else
+    mmi="no"
+  fi
+fi
+
+# check if our compiler supports mmi
+enabled mmi && check_cc <<EOF || mmi="no"
+int main(void) {
+    __asm__ ("lq \$2, 0(\$2)");
+    return 0;
+}
+EOF
+
+# test gcc version to see if vector builtins can be used
+# currently only used on i386 for MMX builtins
+check_cc -msse <<EOF && builtin_vector=yes || builtin_vector=no
+#include <xmmintrin.h>
+int main(void) {
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+return 0;
+#else
+#error no vector builtins
+#endif
+}
+EOF
+
+# test for mm3dnow.h
+test "$cpu" = "x86_64" && march=k8 || march=athlon
+check_cc -march=$march <<EOF && mm3dnow=yes || mm3dnow=no
+#include <mm3dnow.h>
+int main(void) {
+__m64 b1;
+b1 = _m_pswapd(b1);
+_m_femms();
+return 0;
+}
+EOF
+
+# ---
+# big/little-endian test
+if test "$cross_compile" = "no"; then
+  check_ld <<EOF || die "endian test failed" && $TMPE && bigendian="yes"
+#include <inttypes.h>
+int main(int argc, char ** argv){
+        volatile uint32_t i=0x01234567;
+        return (*((uint8_t*)(&i))) == 0x67;
+}
+EOF
+else
+# programs cannot be launched if cross compiling, so make a static guess
+  if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then
+    bigendian="yes"
+  fi
+fi
+
+# add some useful compiler flags if supported
+add_cflags -I..
+add_cflags -I../driver
+check_cflags -W
+check_cflags -Wall
+check_cflags -D_LARGEFILE_SOURCE
+check_cflags -D_FILE_OFFSET_BITS=64
+check_cflags -D_REENTRANT
+linux && add_cflags -D_GNU_SOURCE
+
+#################################################
+#   check for debug symbols
+#################################################
+if enabled debug; then
+  add_cflags -g3
+  add_cflags -DHAVE_DEBUG
+  dostrip=no
+fi
+
+if enabled optimize; then
+  if test -n "`$cc -v 2>&1 | grep xlc`"; then
+    add_cflags  "-O5"
+    add_ldflags "-O5"
+  else
+    add_cflags "-O3"
+  fi
+fi
+
+#################################################
+#   check for locales (optional)
+#################################################
+echolog "Checking for locales ..."
+check_header locale.h && add_cflags -DHAVE_LOCALE_H
+check_lib locale.h setlocale "" && add_cflags -DHAVE_SETLOCALE
+
+#################################################
+#   check for ifaddr (optional)
+#################################################
+echolog "Checking for ifaddrs ..."
+check_lib ifaddrs.h getifaddrs "" && add_cflags -DHAVE_IFADDRS_H
+
+#################################################
+#   check for langinfo (optional)
+#################################################
+echolog "Checking for langinfo ..."
+check_header langinfo.h && add_cflags -DHAVE_LANGINFO_H
+check_lib langinfo.h nl_langinfo "" && add_cflags -DHAVE_LANGINFO_CODESET
+
+#################################################
+#   check for iconv (optional)
+#################################################
+echolog "Checking for iconv ..."
+check_lib iconv.h iconv "" && add_cflags -DHAVE_ICONV
+
+#################################################
+#   check for libupnp and friends (mandatory)
+#################################################
+if [ -n "$libupnpdir" ]; then
+  check_cflags -I$libupnpdir/include
+  check_ldflags -L$libupnpdir/lib
+fi
+
+echolog "Checking for libixml ..."
+check_lib upnp/ixml.h ixmlRelaxParser -lixml || die "Error, can't find libixml !"
+
+echolog "Checking for libthreadutil ..."
+check_lib upnp/ThreadPool.h ThreadPoolAdd "-lthreadutil -lpthread" || die "Error, can't find libthreadutil !"
+add_extralibs -lpthread
+
+libupnp_min_version="1.4.2"
+echolog "Checking for libupnp >= $libupnp_min_version ..."
+check_lib upnp/upnp.h UpnpSetMaxContentLength -lupnp || die "Error, can't find libupnp !"
+check_lib_version libupnp $libupnp_min_version || die "Error, libupnp < $libupnp_min_version !"
+add_cflags `pkg-config libupnp --cflags`
+add_extralibs `pkg-config libupnp --libs`
+
+#################################################
+#   check for libdlna (mandatory if enabled)
+#################################################
+if test "$dlna" = "yes"; then
+  libdlna_min_version="0.2.1"
+  echolog "Checking for libdlna >= $libdlna_min_version ..."
+  if [ -n "$libdlnadir" ]; then
+    check_cflags -I$libdlnadir/include
+    check_ldflags -L$libdlnadir/lib
+  fi
+  check_lib dlna.h dlna_register_all_media_profiles -ldlna || die "Error, can't find libdlna (install it or use --disable-dlna) !"
+#  check_lib_version libdlna $libdlna_min_version || die "Error, libdlna < $libdlna_min_version !"
+  add_cflags -DHAVE_DLNA
+  add_cflags `pkg-config libdlna --cflags`
+  add_extralibs `pkg-config libdlna --libs`
+fi
+
+#################################################
+#   check for libarib25 (mandatory if enabled)
+#################################################
+if test "$b25" = "yes"; then
+  b25_min_version="0.1.8"
+  echolog "Checking for b25 >= $b25_min_version ..."
+  if [ -n "$libb25dir" ]; then
+    check_cflags -I$libb25dir/include/arib25
+    check_ldflags -L$libb25dir/lib
+  fi
+  check_lib arib_std_b25.h create_arib_std_b25 -larib25 -lpcsclite || die "Error, can't find libarib25 (install it or use --disable-b25) !"
+#  check_lib_version libarib25 $libb25_min_version || die "Error, libb25 < $libb25_min_version !"
+  add_cflags -DHAVE_LIBARIB25
+  add_cflags `pkg-config libarib25 pcsclite --cflags`
+  add_extralibs `pkg-config libarib25 pcsclite --libs`
+fi
+
+#################################################
+#   logging result
+#################################################
+echolog ""
+echolog "recpt1: configure is OK"
+echolog "  version            $VERSION"
+echolog "  using libupnp      `pkg-config libupnp --modversion`"
+test $dlna = yes && echolog "  using libdlna      `pkg-config libdlna --modversion`"
+test $b25 = yes && echolog "  using libb25      `pkg-config libb25 --modversion`"
+echolog "configuration:"
+echolog "  install prefix     $PREFIX"
+echolog "  configuration dir  $sysconfdir"
+echolog "  locales dir        $localedir"
+echolog "  NLS support        $nls"
+echolog "  DLNA support       $dlna"
+echolog "  B25 support        $b25"
+echolog "  C compiler         $cc"
+echolog "  STRIP              $strip"
+echolog "  make               $make"
+echolog "  CPU                $cpu ($tune)"
+echolog "  debug symbols      $debug"
+echolog "  strip symbols      $dostrip"
+echolog "  optimize           $optimize"
+echolog ""
+echolog "  CFLAGS             $CFLAGS"
+echolog "  LDFLAGS            $LDFLAGS"
+echolog "  extralibs          $extralibs"
+echolog ""
+
+#################################################
+#   save configs attributes
+#################################################
+echolog "Creating $CONFIGFILE ..."
+
+echo "# Automatically generated by configure - do not modify!" > $CONFIGFILE
+
+append_config "VERSION=$VERSION"
+
+append_config "PREFIX=$PREFIX"
+append_config "prefix=\$(DESTDIR)\$(PREFIX)"
+append_config "bindir=\$(DESTDIR)$bindir"
+append_config "sysconfdir=\$(DESTDIR)$sysconfdir"
+append_config "localedir=\$(DESTDIR)$localedir"
+
+append_config "MAKE=$make"
+append_config "CC=$cc"
+append_config "LN=ln"
+if enabled dostrip; then
+  append_config "STRIP=$strip"
+  append_config "INSTALLSTRIP=$installstrip"
+else
+  append_config "STRIP=echo ignoring strip"
+  append_config "INSTALLSTRIP="
+fi
+append_config "EXTRALIBS=$extralibs"
+
+append_config "OPTFLAGS=$CFLAGS"
+append_config "LDFLAGS=$LDFLAGS"
+append_config "INSTALL=$INSTALL"
+
+append_config "DEBUG=$debug"
+
+
+echolog "Creating $CONFIG_H ..."
+echo "/* Automatically generated by configure - do not modify! */" > $CONFIG_H
+add_clog_str VERSION "$VERSION"
+add_clog_str PACKAGE "recpt1"
+add_clog_str PACKAGE_NAME "recpt1"
+add_clog_str SYSCONFDIR `expand_var "${sysconfdir}"`
+add_clog_str LOCALEDIR `expand_var "${localedir}"`
+test $nls = yes && add_clog CONFIG_NLS 1
+
+clean
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Makefile	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,98 @@
+ifeq (,$(wildcard ../config.mak))
+$(error "../config.mak is not present, run configure !")
+endif
+include ../config.mak
+
+PROG = recpt1
+
+EXTRADIST = ushare.1 \
+	cds.h \
+	cms.h \
+	msr.h \
+	http.h \
+	presentation.h \
+	metadata.h \
+	mime.h \
+	services.h \
+	buffer.h \
+	util_iconv.h \
+	content.h \
+	cfgparser.h \
+	trace.h \
+	redblack.h \
+	osdep.h \
+	ctrl_telnet.h \
+	ushare.h \
+	gettext.h \
+	minmax.h \
+	decoder.h \
+	mkpath.h \
+	pt1_dev.h \
+	recpt1.h \
+	tssplitter_lite.h \
+	upnp_device.h \
+	upnp_main.h \
+	version.h \
+
+SRCS = \
+        cds.c \
+        cms.c \
+        msr.c \
+        http.c \
+        presentation.c \
+        metadata.c \
+        mime.c \
+        services.c \
+        buffer.c \
+        util_iconv.c \
+        content.c \
+        cfgparser.c \
+        trace.c \
+        redblack.c \
+        osdep.c \
+        ctrl_telnet.c \
+        ushare.c \
+	decoder.c \
+	mkpath.c \
+	recpt1.c \
+	tssplitter_lite.c
+
+OBJS = $(SRCS:.c=.o)
+
+.SUFFIXES: .c .o
+
+all: depend $(PROG)
+
+.c.o:
+	$(CC) -c $(CFLAGS) -g $(OPTFLAGS) -o $@ $<
+
+$(PROG): $(OBJS)
+	$(CC) $(OBJS) $(LDFLAGS) $(EXTRALIBS) -o $@
+
+clean:
+	-$(RM) -f *.o $(PROG)
+	-$(RM) -f .depend
+
+distclean:
+
+install: $(PROG)
+	$(INSTALL) -d $(bindir)
+	$(INSTALL) $(PROG) $(bindir)
+	$(STRIP) $(INSTALLSTRIP) $(bindir)/$(PROG)
+
+depend:
+	$(CC) -I.. -MM $(CFLAGS) $(SRCS) 1>.depend
+
+.PHONY: clean distclean install depend
+
+dist-all:
+	cp $(EXTRADIST) $(SRCS) Makefile $(DIST)
+
+.PHONY: dist-all
+
+#
+# include dependency files if they exist
+#
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buffer.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,106 @@
+/* buffer.c - String buffer manipulation tools.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "buffer.h"
+#include "minmax.h"
+
+#define BUFFER_DEFAULT_CAPACITY 32768
+
+struct buffer_t *
+buffer_new (void)
+{
+  struct buffer_t *buffer = NULL;
+
+  buffer = (struct buffer_t *) malloc (sizeof (struct buffer_t));
+  if (!buffer)
+    return NULL;
+
+  buffer->buf = NULL;
+  buffer->len = 0;
+  buffer->capacity = 0;
+
+  return buffer;
+}
+
+void
+buffer_append (struct buffer_t *buffer, const char *str)
+{
+  size_t len;
+
+  if (!buffer || !str)
+    return;
+
+  if (!buffer->buf)
+  {
+    buffer->capacity = BUFFER_DEFAULT_CAPACITY;
+    buffer->buf = (char *) malloc (buffer->capacity * sizeof (char));
+    memset (buffer->buf, '\0', buffer->capacity);
+  }
+
+  len = buffer->len + strlen (str);
+  if (len >= buffer->capacity)
+  {
+    buffer->capacity = MAX (len + 1, 2 * buffer->capacity);
+    buffer->buf = realloc (buffer->buf, buffer->capacity);
+  }
+
+  strcat (buffer->buf, str);
+  buffer->len += strlen (str);
+}
+
+void
+buffer_appendf (struct buffer_t *buffer, const char *format, ...)
+{
+  char str[BUFFER_DEFAULT_CAPACITY];
+  int size;
+  va_list va;
+
+  if (!buffer || !format)
+    return;
+
+  va_start (va, format);
+  size = vsnprintf (str, BUFFER_DEFAULT_CAPACITY, format, va);
+  if (size >= BUFFER_DEFAULT_CAPACITY)
+  {
+    char* dynstr = (char *) malloc (size + 1);
+    vsnprintf (dynstr, size + 1, format, va);
+    buffer_append (buffer, dynstr);
+    free (dynstr);
+  }
+  else
+    buffer_append (buffer, str);
+  va_end (va);
+}
+
+void
+buffer_free (struct buffer_t *buffer)
+{
+  if (!buffer)
+    return;
+
+  if (buffer->buf)
+    free (buffer->buf);
+  free (buffer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/buffer.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,38 @@
+/* buffer.h - String buffer manipulation tools header.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _STRING_BUFFER_H_
+#define _STRING_BUFFER_H_
+
+struct buffer_t {
+  char *buf;
+  size_t len;
+  size_t capacity;
+};
+
+struct buffer_t *buffer_new (void)
+    __attribute__ ((malloc));
+void buffer_free (struct buffer_t *buffer);
+
+void buffer_append (struct buffer_t *buffer, const char *str);
+void buffer_appendf (struct buffer_t *buffer, const char *format, ...)
+    __attribute__ ((format (printf , 2, 3)));
+
+#endif /* _STRING_BUFFER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cds.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,872 @@
+/*
+ * cds.c : GeeXboX uShare Content Directory Service
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "ushare.h"
+#include "services.h"
+#include "ushare.h"
+#include "services.h"
+#include "metadata.h"
+#include "mime.h"
+#include "buffer.h"
+#include "minmax.h"
+
+/* Represent the CDS GetSearchCapabilities action. */
+#define SERVICE_CDS_ACTION_SEARCH_CAPS "GetSearchCapabilities"
+
+/* Represent the CDS GetSortCapabilities action. */
+#define SERVICE_CDS_ACTION_SORT_CAPS "GetSortCapabilities"
+
+/* Represent the CDS GetSystemUpdateID action. */
+#define SERVICE_CDS_ACTION_UPDATE_ID "GetSystemUpdateID"
+
+/* Represent the CDS Browse action. */
+#define SERVICE_CDS_ACTION_BROWSE "Browse"
+
+/* Represent the CDS Search action. */
+#define SERVICE_CDS_ACTION_SEARCH "Search"
+
+/* Represent the CDS SearchCaps argument. */
+#define SERVICE_CDS_ARG_SEARCH_CAPS "SearchCaps"
+
+/* Represent the CDS SortCaps argument. */
+#define SERVICE_CDS_ARG_SORT_CAPS "SortCaps"
+
+/* Represent the CDS UpdateId argument. */
+#define SERVICE_CDS_ARG_UPDATE_ID "Id"
+
+/* Represent the CDS StartingIndex argument. */
+#define SERVICE_CDS_ARG_START_INDEX "StartingIndex"
+
+/* Represent the CDS RequestedCount argument. */
+#define SERVICE_CDS_ARG_REQUEST_COUNT "RequestedCount"
+
+/* Represent the CDS ObjectID argument. */
+#define SERVICE_CDS_ARG_OBJECT_ID "ObjectID"
+
+/* Represent the CDS Filter argument. */
+#define SERVICE_CDS_ARG_FILTER "Filter"
+
+/* Represent the CDS BrowseFlag argument. */
+#define SERVICE_CDS_ARG_BROWSE_FLAG "BrowseFlag"
+
+/* Represent the CDS SortCriteria argument. */
+#define SERVICE_CDS_ARG_SORT_CRIT "SortCriteria"
+
+/* Represent the CDS SearchCriteria argument. */
+#define SERVICE_CDS_ARG_SEARCH_CRIT "SearchCriteria"
+
+/* Represent the CDS Root Object ID argument. */
+#define SERVICE_CDS_ROOT_OBJECT_ID "0"
+
+/* Represent the CDS DIDL Message Metadata Browse flag argument. */
+#define SERVICE_CDS_BROWSE_METADATA "BrowseMetadata"
+
+/* Represent the CDS DIDL Message DirectChildren Browse flag argument. */
+#define SERVICE_CDS_BROWSE_CHILDREN "BrowseDirectChildren"
+
+/* Represent the CDS DIDL Message Result argument. */
+#define SERVICE_CDS_DIDL_RESULT "Result"
+
+/* Represent the CDS DIDL Message NumberReturned argument. */
+#define SERVICE_CDS_DIDL_NUM_RETURNED "NumberReturned"
+
+/* Represent the CDS DIDL Message TotalMatches argument. */
+#define SERVICE_CDS_DIDL_TOTAL_MATCH "TotalMatches"
+
+/* Represent the CDS DIDL Message UpdateID argument. */
+#define SERVICE_CDS_DIDL_UPDATE_ID "UpdateID"
+
+/* DIDL parameters */
+/* Represent the CDS DIDL Message Header Namespace. */
+#define DIDL_NAMESPACE \
+    "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " \
+    "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " \
+    "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
+
+/* Represent the CDS DIDL Message Header Tag. */
+#define DIDL_LITE "DIDL-Lite"
+
+/* Represent the CDS DIDL Message Item value. */
+#define DIDL_ITEM "item"
+
+/* Represent the CDS DIDL Message Item ID value. */
+#define DIDL_ITEM_ID "id"
+
+/* Represent the CDS DIDL Message Item Parent ID value. */
+#define DIDL_ITEM_PARENT_ID "parentID"
+
+/* Represent the CDS DIDL Message Item Restricted value. */
+#define DIDL_ITEM_RESTRICTED "restricted"
+
+/* Represent the CDS DIDL Message Item UPnP Class value. */
+#define DIDL_ITEM_CLASS "upnp:class"
+
+/* Represent the CDS DIDL Message Item Title value. */
+#define DIDL_ITEM_TITLE "dc:title"
+
+/* Represent the CDS DIDL Message Item Resource value. */
+#define DIDL_RES "res"
+
+/* Represent the CDS DIDL Message Item Protocol Info value. */
+#define DIDL_RES_INFO "protocolInfo"
+
+/* Represent the CDS DIDL Message Item Resource Size value. */
+#define DIDL_RES_SIZE "size"
+
+/* Represent the CDS DIDL Message Container value. */
+#define DIDL_CONTAINER "container"
+
+/* Represent the CDS DIDL Message Container ID value. */
+#define DIDL_CONTAINER_ID "id"
+
+/* Represent the CDS DIDL Message Container Parent ID value. */
+#define DIDL_CONTAINER_PARENT_ID "parentID"
+
+/* Represent the CDS DIDL Message Container number of children value. */
+#define DIDL_CONTAINER_CHILDS "childCount"
+
+/* Represent the CDS DIDL Message Container Restricted value. */
+#define DIDL_CONTAINER_RESTRICTED "restricted"
+
+/* Represent the CDS DIDL Message Container Searchable value. */
+#define DIDL_CONTAINER_SEARCH "searchable"
+
+/* Represent the CDS DIDL Message Container UPnP Class value. */
+#define DIDL_CONTAINER_CLASS "upnp:class"
+
+/* Represent the CDS DIDL Message Container Title value. */
+#define DIDL_CONTAINER_TITLE "dc:title"
+
+/* Represent the "upnp:class" reserved keyword for Search action */
+#define SEARCH_CLASS_MATCH_KEYWORD "(upnp:class = \""
+
+/* Represent the "upnp:class derived from" reserved keyword */
+#define SEARCH_CLASS_DERIVED_KEYWORD "(upnp:class derivedfrom \""
+
+/* Represent the "res@protocolInfo contains" reserved keyword */
+#define SEARCH_PROTOCOL_CONTAINS_KEYWORD "(res@protocolInfo contains \""
+
+/* Represent the Search default keyword */
+#define SEARCH_OBJECT_KEYWORD "object"
+
+/* Represent the Search 'AND' connector keyword */
+#define SEARCH_AND ") and ("
+
+static bool
+filter_has_val (const char *filter, const char *val)
+{
+  char *x = NULL, *token = NULL;
+  char *m_buffer = NULL, *buffer;
+  int len = strlen (val);
+  bool ret = false;
+
+  if (!strcmp (filter, "*"))
+    return true;
+
+  x = strdup (filter);
+  if (x)
+  {
+    m_buffer = (char*) malloc (strlen (x));
+    if (m_buffer)
+    {
+      buffer = m_buffer;
+      token = strtok_r (x, ",", &buffer);
+      while (token)
+      {
+        if (*val == '@')
+          token = strchr (token, '@');
+        if (token && !strncmp (token, val, len))
+        {
+          ret = true;
+          break;
+        }
+        token = strtok_r (NULL, ",", &buffer);
+      }
+      free (m_buffer);
+    }
+    free (x);
+  }
+  return ret;
+}
+
+/* UPnP ContentDirectory Service actions */
+static bool
+cds_get_search_capabilities (struct action_event_t *event)
+{
+  upnp_add_response (event, SERVICE_CDS_ARG_SEARCH_CAPS, "");
+
+  return event->status;
+}
+
+static bool
+cds_get_sort_capabilities (struct action_event_t *event)
+{
+  upnp_add_response (event, SERVICE_CDS_ARG_SORT_CAPS, "");
+
+  return event->status;
+}
+
+static bool
+cds_get_system_update_id (struct action_event_t *event)
+{
+  upnp_add_response (event, SERVICE_CDS_ARG_UPDATE_ID,
+                     SERVICE_CDS_ROOT_OBJECT_ID);
+
+  return event->status;
+}
+
+static void
+didl_add_header (struct buffer_t *out)
+{
+  buffer_appendf (out, "<%s %s>", DIDL_LITE, DIDL_NAMESPACE);
+}
+
+static void
+didl_add_footer (struct buffer_t *out)
+{
+  buffer_appendf (out, "</%s>", DIDL_LITE);
+}
+
+static void
+didl_add_tag (struct buffer_t *out, char *tag, char *value)
+{
+  if (value)
+    buffer_appendf (out, "<%s>%s</%s>", tag, value, tag);
+}
+
+static void
+didl_add_param (struct buffer_t *out, char *param, char *value)
+{
+  if (value)
+    buffer_appendf (out, " %s=\"%s\"", param, value);
+}
+
+static void
+didl_add_value (struct buffer_t *out, char *param, off_t value)
+{
+  buffer_appendf (out, " %s=\"%lld\"", param, value);
+}
+
+static void
+didl_add_item (struct buffer_t *out, int item_id,
+               int parent_id, char *restricted, char *class, char *title,
+               char *protocol_info, off_t size, char *url, char *filter)
+{
+  buffer_appendf (out, "<%s", DIDL_ITEM);
+  didl_add_value (out, DIDL_ITEM_ID, item_id);
+  didl_add_value (out, DIDL_ITEM_PARENT_ID, parent_id);
+  didl_add_param (out, DIDL_ITEM_RESTRICTED, restricted);
+  buffer_append (out, ">");
+
+  didl_add_tag (out, DIDL_ITEM_CLASS, class);
+  didl_add_tag (out, DIDL_ITEM_TITLE, title);
+
+  if (filter_has_val (filter, DIDL_RES))
+  {
+    buffer_appendf (out, "<%s", DIDL_RES);
+    // protocolInfo is required :
+    didl_add_param (out, DIDL_RES_INFO, protocol_info);
+    if (filter_has_val (filter, "@"DIDL_RES_SIZE))
+      didl_add_value (out, DIDL_RES_SIZE, size);
+    buffer_append (out, ">");
+    if (url)
+    {
+      extern struct ushare_t *ut;
+      buffer_appendf (out, "http://%s:%d%s/%s",
+                      UpnpGetServerIpAddress (), ut->port, VIRTUAL_DIR, url);
+    }
+    buffer_appendf (out, "</%s>", DIDL_RES);
+  }
+  buffer_appendf (out, "</%s>", DIDL_ITEM);
+}
+
+static void
+didl_add_container (struct buffer_t *out, int id, int parent_id,
+                    int child_count, char *restricted, char *searchable,
+                    char *title, char *class)
+{
+  buffer_appendf (out, "<%s", DIDL_CONTAINER);
+
+  didl_add_value (out, DIDL_CONTAINER_ID, id);
+  didl_add_value (out, DIDL_CONTAINER_PARENT_ID, parent_id);
+  if (child_count >= 0)
+    didl_add_value (out, DIDL_CONTAINER_CHILDS, child_count);
+  didl_add_param (out, DIDL_CONTAINER_RESTRICTED, restricted);
+  didl_add_param (out, DIDL_CONTAINER_SEARCH, searchable);
+  buffer_append (out, ">");
+
+  didl_add_tag (out, DIDL_CONTAINER_CLASS, class);
+  didl_add_tag (out, DIDL_CONTAINER_TITLE, title);
+
+  buffer_appendf (out, "</%s>", DIDL_CONTAINER);
+}
+
+static int
+cds_browse_metadata (struct action_event_t *event, struct buffer_t *out,
+                     int index, int count, struct upnp_entry_t *entry,
+                     char *filter)
+{
+  int result_count = 0, c = 0;
+
+  if (!entry)
+    return -1;
+
+  if (entry->child_count == -1) /* item : file */
+  {
+#ifdef HAVE_DLNA
+    extern struct ushare_t *ut;
+#endif /* HAVE_DLNA */
+    
+    char *protocol =
+#ifdef HAVE_DLNA
+      entry->dlna_profile ?
+      dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                                DLNA_ORG_PLAY_SPEED_NORMAL,
+                                DLNA_ORG_CONVERSION_NONE,
+                                DLNA_ORG_OPERATION_RANGE,
+                                ut->dlna_flags, entry->dlna_profile) :
+#endif /* HAVE_DLNA */
+      mime_get_protocol (entry->mime_type);
+    
+    didl_add_header (out);
+#ifdef HAVE_DLNA
+    entry->dlna_profile ?
+      didl_add_item (out, entry->id, entry->parent
+                     ? entry->parent->id : -1, "false",
+                     dlna_profile_upnp_object_item (entry->dlna_profile),
+                     entry->title,
+                     protocol, entry->size,
+                     entry->url, filter) :
+#endif /* HAVE_DLNA */
+      didl_add_item (out, entry->id, entry->parent
+                     ? entry->parent->id : -1, "false",
+                     entry->mime_type->mime_class, entry->title,
+                     protocol, entry->size,
+                     entry->url, filter);
+    
+    didl_add_footer (out);
+    free (protocol);
+    
+    for (c = index; c < MIN (index + count, entry->child_count); c++)
+      result_count++;
+  }
+  else  /* container : directory */
+  {
+    didl_add_header (out);
+    didl_add_container (out, entry->id, entry->parent
+                        ? entry->parent->id : -1, entry->child_count,
+                        "true", "true", entry->title,
+                        entry->mime_type->mime_class);
+    didl_add_footer (out);
+
+    result_count = 1;
+  }
+
+  upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
+  upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, "1");
+  upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, "1");
+
+  return result_count;
+}
+
+static int
+cds_browse_directchildren (struct action_event_t *event,
+                           struct buffer_t *out, int index,
+                           int count, struct upnp_entry_t *entry, char *filter)
+{
+  struct upnp_entry_t **childs;
+  int s, result_count = 0;
+  char tmp[32];
+
+  if (entry->child_count == -1) /* item : file */
+    return -1;
+
+  didl_add_header (out);
+
+  /* go to the child pointed out by index */
+  childs = entry->childs;
+  for (s = 0; s < index; s++)
+    if (*childs)
+      childs++;
+
+  /* UPnP CDS compliance : If starting index = 0 and requested count = 0
+     then all children must be returned */
+  if (index == 0 && count == 0)
+    count = entry->child_count;
+
+  for (; *childs; childs++)
+  {
+    if (count == 0 || result_count < count)
+      /* only fetch the requested count number or all entries if count = 0 */
+    {
+      if ((*childs)->child_count >= 0) /* container */
+        didl_add_container (out, (*childs)->id, (*childs)->parent ?
+                            (*childs)->parent->id : -1,
+                            (*childs)->child_count, "true", NULL,
+                            (*childs)->title,
+                            (*childs)->mime_type->mime_class);
+      else /* item */
+      {
+#ifdef HAVE_DLNA
+        extern struct ushare_t *ut;
+#endif /* HAVE_DLNA */
+
+        char *protocol =
+#ifdef HAVE_DLNA
+          (*childs)->dlna_profile ?
+          dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                                    DLNA_ORG_PLAY_SPEED_NORMAL,
+                                    DLNA_ORG_CONVERSION_NONE,
+                                    DLNA_ORG_OPERATION_RANGE,
+                                    ut->dlna_flags, (*childs)->dlna_profile) :
+#endif /* HAVE_DLNA */
+          mime_get_protocol ((*childs)->mime_type);
+
+#ifdef HAVE_DLNA
+        (*childs)->dlna_profile ?
+          didl_add_item (out, (*childs)->id,
+                         (*childs)->parent ? (*childs)->parent->id : -1,
+                         "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
+                         (*childs)->title, protocol,
+                         (*childs)->size, (*childs)->url, filter) :
+#endif /* HAVE_DLNA */
+          didl_add_item (out, (*childs)->id,
+                         (*childs)->parent ? (*childs)->parent->id : -1,
+                         "true", (*childs)->mime_type->mime_class,
+                         (*childs)->title, protocol,
+                         (*childs)->size, (*childs)->url, filter);
+
+        free (protocol);
+      }
+      result_count++;
+    }
+  }
+
+  didl_add_footer (out);
+
+  upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
+  sprintf (tmp, "%d", result_count);
+  upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
+  sprintf (tmp, "%d", entry->child_count);
+  upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
+
+  return result_count;
+}
+
+static bool
+cds_browse (struct action_event_t *event)
+{
+  extern struct ushare_t *ut;
+  struct upnp_entry_t *entry = NULL;
+  int result_count = 0, index, count, id, sort_criteria;
+  char *flag = NULL;
+  char *filter = NULL;
+  struct buffer_t *out = NULL;
+  bool metadata;
+
+  if (!event)
+    return false;
+
+  /* Check for status */
+  if (!event->status)
+    return false;
+
+  /* check if metadatas have been well inited */
+  if (!ut->init)
+    return false;
+
+  /* Retrieve Browse arguments */
+  index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
+  count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
+  id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
+  flag = upnp_get_string (event->request, SERVICE_CDS_ARG_BROWSE_FLAG);
+  filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
+  sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
+
+  if (!flag || !filter)
+    return false;
+
+  /* Check arguments validity */
+  if (!strcmp (flag, SERVICE_CDS_BROWSE_METADATA))
+    {
+      if (index)
+      {
+        free (flag);
+        return false;
+      }
+      metadata = true;
+    }
+  else if (!strcmp (flag, SERVICE_CDS_BROWSE_CHILDREN))
+    metadata = false;
+  else
+  {
+    free (flag);
+    return false;
+  }
+  free (flag);
+
+  entry = upnp_get_entry (ut, id);
+  if (!entry && (id < ut->starting_id))
+    entry = upnp_get_entry (ut, ut->starting_id);
+
+  if (!entry)
+  {
+    free (filter);
+    return false;
+  }
+
+  out = buffer_new ();
+  if (!out)
+  {
+    free (filter);
+    return false;
+  }
+
+  if (metadata)
+    result_count =
+      cds_browse_metadata (event, out, index, count, entry, filter);
+  else
+    result_count =
+      cds_browse_directchildren (event, out, index, count, entry, filter);
+  free (filter);
+
+  if (result_count < 0)
+  {
+    buffer_free (out);
+    return false;
+  }
+
+  buffer_free (out);
+  upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
+                     SERVICE_CDS_ROOT_OBJECT_ID);
+
+  return event->status;
+}
+
+static bool
+matches_search (char *search_criteria, struct upnp_entry_t *entry)
+{
+  char keyword[256] = SEARCH_OBJECT_KEYWORD;
+  bool derived_from = false, protocol_contains = false, result = false;
+  char *quote_closed = NULL, *and_clause = NULL;
+#ifdef HAVE_DLNA
+  extern struct ushare_t *ut;
+#endif /* HAVE_DLNA */
+  char *protocol =
+#ifdef HAVE_DLNA
+    entry->dlna_profile ?
+    dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                              DLNA_ORG_PLAY_SPEED_NORMAL,
+                              DLNA_ORG_CONVERSION_NONE,
+                              DLNA_ORG_OPERATION_RANGE,
+                              ut->dlna_flags, entry->dlna_profile) :
+#endif /* HAVE_DLNA */
+    mime_get_protocol (entry->mime_type);
+  
+  if (!strncmp (search_criteria, SEARCH_CLASS_MATCH_KEYWORD,
+                strlen (SEARCH_CLASS_MATCH_KEYWORD)))
+  {
+    strncpy (keyword, search_criteria
+             + strlen (SEARCH_CLASS_MATCH_KEYWORD), sizeof (keyword));
+    quote_closed = strchr (keyword, '"');
+
+    if (quote_closed)
+      *quote_closed = '\0';
+  }
+  else if (!strncmp (search_criteria, SEARCH_CLASS_DERIVED_KEYWORD,
+                     strlen (SEARCH_CLASS_DERIVED_KEYWORD)))
+  {
+    derived_from = true;
+    strncpy (keyword, search_criteria
+             + strlen (SEARCH_CLASS_DERIVED_KEYWORD), sizeof (keyword));
+    quote_closed = strchr (keyword, '"');
+
+    if (quote_closed)
+      *quote_closed = '\0';
+  }
+  else if (!strncmp (search_criteria, SEARCH_PROTOCOL_CONTAINS_KEYWORD,
+                     strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD)))
+  {
+    protocol_contains = true;
+    strncpy (keyword, search_criteria
+             + strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD), sizeof (keyword));
+    quote_closed = strchr (keyword, '"');
+
+    if (quote_closed)
+      *quote_closed = '\0';
+  }
+
+  if (derived_from && entry->mime_type
+      && !strncmp (entry->mime_type->mime_class, keyword, strlen (keyword)))
+    result = true;
+  else if (protocol_contains && strstr (protocol, keyword))
+    result = true;
+  else if (entry->mime_type &&
+           !strcmp (entry->mime_type->mime_class, keyword))
+    result = true;
+  free (protocol);
+  
+  and_clause = strstr (search_criteria, SEARCH_AND);
+  if (and_clause)
+    return (result &&
+            matches_search (and_clause + strlen (SEARCH_AND) -1, entry));
+
+  return true;
+}
+
+static int
+cds_search_directchildren_recursive (struct buffer_t *out, int count,
+                                     struct upnp_entry_t *entry, char *filter,
+                                     char *search_criteria)
+{
+  struct upnp_entry_t **childs;
+  int result_count = 0;
+
+  if (entry->child_count == -1) /* item : file */
+    return -1;
+
+  /* go to the first child */
+  childs = entry->childs;
+
+  for (; *childs; childs++)
+  {
+    if (count == 0 || result_count < count)
+      /* only fetch the requested count number or all entries if count = 0 */
+    {
+      if ((*childs)->child_count >= 0) /* container */
+      {
+        int new_count;
+        new_count = cds_search_directchildren_recursive
+          (out, (count == 0) ? 0 : (count - result_count),
+           (*childs), filter, search_criteria);
+        result_count += new_count;
+      }
+      else /* item */
+      {
+        if (matches_search (search_criteria, *childs))
+        {
+#ifdef HAVE_DLNA
+          extern struct ushare_t *ut;
+#endif /* HAVE_DLNA */
+          char *protocol =
+#ifdef HAVE_DLNA
+            (*childs)->dlna_profile ?
+            dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                                     DLNA_ORG_PLAY_SPEED_NORMAL,
+                                     DLNA_ORG_CONVERSION_NONE,
+                                     DLNA_ORG_OPERATION_RANGE,
+                                     ut->dlna_flags, (*childs)->dlna_profile):
+#endif /* HAVE_DLNA */
+            mime_get_protocol ((*childs)->mime_type);
+
+#ifdef HAVE_DLNA
+          (*childs)->dlna_profile ?
+            didl_add_item (out, (*childs)->id,
+                           (*childs)->parent ? (*childs)->parent->id : -1,
+                           "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
+                           (*childs)->title, protocol,
+                           (*childs)->size, (*childs)->url, filter) :
+#endif /* HAVE_DLNA */
+            didl_add_item (out, (*childs)->id,
+                           (*childs)->parent ? (*childs)->parent->id : -1,
+                           "true", (*childs)->mime_type->mime_class,
+                           (*childs)->title, protocol,
+                           (*childs)->size, (*childs)->url, filter);
+          free (protocol);
+          result_count++;
+        }
+      }
+    }
+  }
+
+  return result_count;
+}
+
+static int
+cds_search_directchildren (struct action_event_t *event,
+                           struct buffer_t *out, int index,
+                           int count, struct upnp_entry_t *entry,
+                           char *filter, char *search_criteria)
+{
+  struct upnp_entry_t **childs;
+  int s, result_count = 0;
+  char tmp[32];
+
+  index = 0;
+
+  if (entry->child_count == -1) /* item : file */
+    return -1;
+
+  didl_add_header (out);
+
+  /* go to the child pointed out by index */
+  childs = entry->childs;
+  for (s = 0; s < index; s++)
+    if (*childs)
+      childs++;
+
+  /* UPnP CDS compliance : If starting index = 0 and requested count = 0
+     then all children must be returned */
+  if (index == 0 && count == 0)
+    count = entry->child_count;
+
+  for (; *childs; childs++)
+  {
+    if (count == 0 || result_count < count)
+      /* only fetch the requested count number or all entries if count = 0 */
+    {
+      if ((*childs)->child_count >= 0) /* container */
+      {
+        int new_count;
+        new_count = cds_search_directchildren_recursive
+          (out, (count == 0) ? 0 : (count - result_count),
+           (*childs), filter, search_criteria);
+        result_count += new_count;
+      }
+      else /* item */
+      {
+        if (matches_search (search_criteria, *childs))
+        {
+#ifdef HAVE_DLNA
+          extern struct ushare_t *ut;
+#endif /* HAVE_DLNA */
+          char *protocol =
+#ifdef HAVE_DLNA
+            (*childs)->dlna_profile ?
+            dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                                     DLNA_ORG_PLAY_SPEED_NORMAL,
+                                     DLNA_ORG_CONVERSION_NONE,
+                                     DLNA_ORG_OPERATION_RANGE,
+                                     ut->dlna_flags, (*childs)->dlna_profile):
+#endif /* HAVE_DLNA */
+            mime_get_protocol ((*childs)->mime_type);
+
+#ifdef HAVE_DLNA
+          (*childs)->dlna_profile ?
+            didl_add_item (out, (*childs)->id,
+                           (*childs)->parent ? (*childs)->parent->id : -1,
+                           "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
+                           (*childs)->title, protocol,
+                           (*childs)->size, (*childs)->url, filter) :
+#endif /* HAVE_DLNA */
+            didl_add_item (out, (*childs)->id,
+                           (*childs)->parent ? (*childs)->parent->id : -1,
+                           "true", (*childs)->mime_type->mime_class,
+                           (*childs)->title, protocol,
+                           (*childs)->size, (*childs)->url, filter);
+          free (protocol);
+          result_count++;
+        }
+      }
+    }
+  }
+
+  didl_add_footer (out);
+
+  upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
+
+  sprintf (tmp, "%d", result_count);
+  upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
+  sprintf (tmp, "%d", result_count);
+  upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
+
+  return result_count;
+}
+
+static bool
+cds_search (struct action_event_t *event)
+{
+  extern struct ushare_t *ut;
+  struct upnp_entry_t *entry = NULL;
+  int result_count = 0, index, count, id, sort_criteria;
+  char *search_criteria = NULL;
+  char *filter = NULL;
+  struct buffer_t *out = NULL;
+
+  if (!event)
+    return false;
+
+  /* Check for status */
+  if (!event->status)
+    return false;
+
+  /* check if metadatas have been well inited */
+  if (!ut->init)
+    return false;
+
+  /* Retrieve Browse arguments */
+  index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
+  count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
+  id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
+
+  search_criteria = upnp_get_string (event->request,
+                                     SERVICE_CDS_ARG_SEARCH_CRIT);
+  filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
+  sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
+
+  if (!search_criteria || !filter)
+    return false;
+
+  entry = upnp_get_entry (ut, id);
+
+  if (!entry && (id < ut->starting_id))
+    entry = upnp_get_entry (ut, ut->starting_id);
+
+  if (!entry)
+    return false;
+
+  out = buffer_new ();
+  if (!out)
+    return false;
+
+  result_count =
+    cds_search_directchildren (event, out, index, count, entry,
+                               filter, search_criteria);
+
+  if (result_count < 0)
+  {
+    buffer_free (out);
+    return false;
+  }
+
+  buffer_free (out);
+  upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
+                     SERVICE_CDS_ROOT_OBJECT_ID);
+
+  free (search_criteria);
+  free (filter);
+
+  return event->status;
+}
+
+/* List of UPnP ContentDirectory Service actions */
+struct service_action_t cds_service_actions[] = {
+  { SERVICE_CDS_ACTION_SEARCH_CAPS, cds_get_search_capabilities },
+  { SERVICE_CDS_ACTION_SORT_CAPS, cds_get_sort_capabilities },
+  { SERVICE_CDS_ACTION_UPDATE_ID, cds_get_system_update_id },
+  { SERVICE_CDS_ACTION_BROWSE, cds_browse },
+  { SERVICE_CDS_ACTION_SEARCH, cds_search },
+  { NULL, NULL }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cds.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,212 @@
+/*
+ * cds.h : GeeXboX uShare Content Directory Service header.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CDS_H_
+#define CDS_H_
+
+#define CDS_DESCRIPTION \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
+"  <specVersion>" \
+"    <major>1</major>" \
+"    <minor>0</minor>" \
+"  </specVersion>" \
+"  <actionList>" \
+"    <action>" \
+"      <name>Browse</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>ObjectID</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>BrowseFlag</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>" \
+"       </argument>" \
+"        <argument>" \
+"          <name>Filter</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>StartingIndex</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>RequestedCount</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>SortCriteria</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>" \
+"       </argument>" \
+"        <argument>" \
+"          <name>Result</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>NumberReturned</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>TotalMatches</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>UpdateID</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>DestroyObject</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>ObjectID</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>GetSystemUpdateID</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>Id</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>SystemUpdateID</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>GetSearchCapabilities</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>SearchCaps</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>SearchCapabilities</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>GetSortCapabilities</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>SortCaps</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>SortCapabilities</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>UpdateObject</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>ObjectID</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>CurrentTagValue</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>NewTagValue</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"  </actionList>" \
+"  <serviceStateTable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_BrowseFlag</name>" \
+"      <dataType>string</dataType>" \
+"      <allowedValueList>" \
+"        <allowedValue>BrowseMetadata</allowedValue>" \
+"        <allowedValue>BrowseDirectChildren</allowedValue>" \
+"      </allowedValueList>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"yes\">" \
+"      <name>SystemUpdateID</name>" \
+"      <dataType>ui4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_Count</name>" \
+"      <dataType>ui4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_SortCriteria</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>SortCapabilities</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_Index</name>" \
+"      <dataType>ui4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_ObjectID</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_UpdateID</name>" \
+"      <dataType>ui4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_TagValueList</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_Result</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"   <stateVariable sendEvents=\"no\">" \
+"      <name>SearchCapabilities</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_Filter</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"  </serviceStateTable>" \
+"</scpd>"
+
+#define CDS_DESCRIPTION_LEN strlen (CDS_DESCRIPTION)
+
+#define CDS_LOCATION "/web/cds.xml"
+
+#define CDS_SERVICE_ID "urn:upnp-org:serviceId:ContentDirectory"
+#define CDS_SERVICE_TYPE "urn:schemas-upnp-org:service:ContentDirectory:1"
+
+#endif /* CDS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cfgparser.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,450 @@
+/*
+ * cfgparser.c : GeeXboX uShare config file parser.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "config.h"
+#include "gettext.h"
+#include "cfgparser.h"
+#include "ushare.h"
+#include "trace.h"
+#include "osdep.h"
+
+#define USHARE_DIR_DELIM ","
+
+static bool
+ignore_line (const char *line)
+{
+  int i;
+  size_t len;
+
+  /* commented line */
+  if (line[0] == '#' )
+    return true;
+
+  len = strlen (line);
+
+  for (i = 0 ; i < (int) len ; i++ )
+    if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n')
+      return false;
+
+  return true;
+}
+
+static char *
+strdup_trim (const char *s)
+{
+  size_t begin, end;
+  char *r = NULL;
+
+  if (!s)
+    return NULL;
+
+  end = strlen (s) - 1;
+
+  for (begin = 0 ; begin < end ; begin++)
+    if (s[begin] != ' ' && s[begin] != '\t' && s[begin] != '"')
+      break;
+
+  for (; begin < end ; end--)
+    if (s[end] != ' ' && s[end] != '\t' && s[end] != '"' && s[end] != '\n')
+      break;
+
+  r = strndup (s + begin, end - begin + 1);
+
+  return r;
+}
+
+static void
+ushare_set_name (struct ushare_t *ut, const char *name)
+{
+  if (!ut || !name)
+    return;
+
+  if (ut->name)
+  {
+    free (ut->name);
+    ut->name = NULL;
+  }
+
+  ut->name = strdup_trim (name);
+}
+
+static void
+ushare_set_interface (struct ushare_t *ut, const char *iface)
+{
+  if (!ut || !iface)
+    return;
+
+  if (ut->interface)
+  {
+    free (ut->interface);
+    ut->interface = NULL;
+  }
+
+  ut->interface = strdup_trim (iface);
+}
+
+static void
+ushare_add_contentdir (struct ushare_t *ut, const char *dir)
+{
+  if (!ut || !dir)
+    return;
+
+  ut->contentlist = content_add (ut->contentlist, dir);
+}
+
+static void
+ushare_set_cfg_file (struct ushare_t *ut, const char *file)
+{
+  if (!ut || !file)
+    return;
+
+  ut->cfg_file = strdup (file);
+}
+
+static void
+ushare_set_dir (struct ushare_t *ut, const char *dirlist)
+{
+  char *x = NULL, *token = NULL;
+  char *m_buffer = NULL, *buffer;
+
+  if (!ut || !dirlist)
+    return;
+
+  x = strdup_trim (dirlist);
+  if (x)
+  {
+    m_buffer = (char*) malloc (strlen (x) * sizeof (char));
+    if (m_buffer)
+    {
+      buffer = m_buffer;
+      token = strtok_r (x, USHARE_DIR_DELIM, &buffer);
+      while (token)
+      {
+        ushare_add_contentdir (ut, token);
+        token = strtok_r (NULL, USHARE_DIR_DELIM, &buffer);
+      }
+      free (m_buffer);
+    }
+    free (x);
+  }
+}
+
+static void
+ushare_set_port (struct ushare_t *ut, const char *port)
+{
+  if (!ut || !port)
+    return;
+
+  ut->port = atoi (port);
+  if (ut->port < 49152)
+  {
+    fprintf (stderr,
+             _("Warning: port doesn't fit IANA port assignements.\n"));
+
+    fprintf (stderr, _("Warning: Only Dynamic or Private Ports can be used "
+                       "(from 49152 through 65535)\n"));
+    ut->port = 0;
+  }
+}
+
+static void
+ushare_set_telnet_port (struct ushare_t *ut, const char *port)
+{
+  if (!ut || !port)
+    return;
+
+  ut->telnet_port = atoi (port);
+}
+
+static void
+ushare_use_web (struct ushare_t *ut, const char *val)
+{
+  if (!ut || !val)
+    return;
+
+  ut->use_presentation = (!strcmp (val, "yes")) ? true : false;
+}
+
+static void
+ushare_use_telnet (struct ushare_t *ut, const char *val)
+{
+  if (!ut || !val)
+    return;
+
+  ut->use_telnet = (!strcmp (val, "yes")) ? true : false;
+}
+
+static void
+ushare_use_xbox (struct ushare_t *ut, const char *val)
+{
+  if (!ut || !val)
+    return;
+
+  ut->xbox360 = (!strcmp (val, "yes")) ? true : false;
+}
+
+static void
+ushare_use_dlna (struct ushare_t *ut, const char *val)
+{
+  if (!ut || !val)
+    return;
+
+#ifdef HAVE_DLNA
+  ut->dlna_enabled = (!strcmp (val, "yes")) ? true : false;
+#endif /* HAVE_DLNA */
+}
+
+static void
+ushare_set_override_iconv_err (struct ushare_t *ut, const char *arg)
+{
+  if (!ut)
+    return;
+
+  ut->override_iconv_err = false;
+
+  if (!strcasecmp (arg, "yes")
+      || !strcasecmp (arg, "true")
+      || !strcmp (arg, "1"))
+    ut->override_iconv_err = true;
+}
+
+static u_configline_t configline[] = {
+  { USHARE_NAME,                 ushare_set_name                },
+  { USHARE_IFACE,                ushare_set_interface           },
+  { USHARE_PORT,                 ushare_set_port                },
+  { USHARE_TELNET_PORT,          ushare_set_telnet_port         },
+  { USHARE_DIR,                  ushare_set_dir                 },
+  { USHARE_OVERRIDE_ICONV_ERR,   ushare_set_override_iconv_err  },
+  { USHARE_ENABLE_WEB,           ushare_use_web                 },
+  { USHARE_ENABLE_TELNET,        ushare_use_telnet              },
+  { USHARE_ENABLE_XBOX,          ushare_use_xbox                },
+  { USHARE_ENABLE_DLNA,          ushare_use_dlna                },
+  { NULL,                        NULL                           },
+};
+
+static void
+parse_config_line (struct ushare_t *ut, const char *line,
+                   u_configline_t *configline)
+{
+  char *s = NULL;
+
+  s = strchr (line, '=');
+  if (s && s[1] != '\0')
+  {
+    int i;
+    for (i=0 ; configline[i].name ; i++)
+    {
+      if (!strncmp (line, configline[i].name, strlen (configline[i].name)))
+      {
+        configline[i].set_var (ut, s + 1);
+        break;
+      }
+    }
+  }
+}
+
+int
+parse_config_file (struct ushare_t *ut)
+{
+  char filename[PATH_MAX];
+  FILE *conffile;
+  char *line = NULL;
+  size_t size = 0;
+  ssize_t read;
+
+  if (!ut)
+    return -1;
+
+  if (!ut->cfg_file)
+    snprintf (filename, PATH_MAX, "%s/%s", SYSCONFDIR, USHARE_CONFIG_FILE); 
+  else
+    snprintf (filename, PATH_MAX, "%s", ut->cfg_file);
+
+  conffile = fopen (filename, "r");
+  if (!conffile)
+    return -1;
+
+  while ((read = getline (&line, &size, conffile)) != -1)
+  {
+    if (ignore_line (line))
+      continue;
+
+    if (line[read-1] == '\n')
+      line[read-1] = '\0';
+
+    while (line[0] == ' ' || line[0] == '\t')
+      line++;
+
+    parse_config_line (ut, line, configline);
+  }
+
+  fclose (conffile);
+
+  if (line)
+    free (line);
+
+  return 0;
+}
+
+inline static void
+display_usage (void)
+{
+  display_headers ();
+  printf ("\n");
+  printf (_("Usage: ushare [-n name] [-i interface] [-p port] [-c directory] [[-c directory]...]\n"));
+  printf (_("Options:\n"));
+  printf (_(" -n, --name=NAME\tSet UPnP Friendly Name (default is '%s')\n"),
+          DEFAULT_USHARE_NAME);
+  printf (_(" -i, --interface=IFACE\tUse IFACE Network Interface (default is '%s')\n"),
+          DEFAULT_USHARE_IFACE);
+  printf (_(" -f, --cfg=FILE\t\tConfig file to be used\n"));
+  printf (_(" -p, --port=PORT\tForces the HTTP server to run on PORT\n"));
+  printf (_(" -q, --telnet-port=PORT\tForces the TELNET server to run on PORT\n"));
+  printf (_(" -c, --content=DIR\tShare the content of DIR directory\n"));
+  printf (_(" -w, --no-web\t\tDisable the control web page (enabled by default)\n"));
+  printf (_(" -t, --no-telnet\tDisable the TELNET control (enabled by default)\n"));
+  printf (_(" -o, --override-iconv-err\tIf iconv fails parsing name, still add to media contents (hoping the renderer can handle it)\n"));
+  printf (_(" -v, --verbose\t\tSet verbose display\n"));
+  printf (_(" -x, --xbox\t\tUse XboX 360 compliant profile\n"));
+#ifdef HAVE_DLNA
+  printf (_(" -d, --dlna\t\tUse DLNA compliant profile (PlayStation3 needs this)\n"));
+#endif /* HAVE_DLNA */
+  printf (_(" -D, --daemon\t\tRun as a daemon\n"));
+  printf (_(" -V, --version\t\tDisplay the version of uShare and exit\n"));
+  printf (_(" -h, --help\t\tDisplay this help\n"));
+}
+
+int
+parse_command_line (struct ushare_t *ut, int argc, char **argv)
+{
+  int c, index;
+  char short_options[] = "VhvDowtxdn:i:p:q:c:f:";
+  struct option long_options [] = {
+    {"version", no_argument, 0, 'V' },
+    {"help", no_argument, 0, 'h' },
+    {"verbose", no_argument, 0, 'v' },
+    {"daemon", no_argument, 0, 'D' },
+    {"override-iconv-err", no_argument, 0, 'o' },
+    {"name", required_argument, 0, 'n' },
+    {"interface", required_argument, 0, 'i' },
+    {"port", required_argument, 0, 'p' },
+    {"telnet-port", required_argument, 0, 'q' },
+    {"content", required_argument, 0, 'c' },
+    {"no-web", no_argument, 0, 'w' },
+    {"no-telnet", no_argument, 0, 't' },
+    {"xbox", no_argument, 0, 'x' },
+#ifdef HAVE_DLNA
+    {"dlna", no_argument, 0, 'd' },
+#endif /* HAVE_DLNA */
+    {"cfg", required_argument, 0, 'f' },
+    {0, 0, 0, 0 }
+  };
+
+  /* command line argument processing */
+  while (true)
+  {
+    c = getopt_long (argc, argv, short_options, long_options, &index);
+
+    if (c == EOF)
+      break;
+
+    switch (c)
+    {
+    case 0:
+      /* opt = long_options[index].name; */
+      break;
+
+    case '?':
+    case 'h':
+      display_usage ();
+      return -1;
+
+    case 'V':
+      display_headers ();
+      return -1;
+
+    case 'v':
+      ut->verbose = true;
+      break;
+
+    case 'D':
+      ut->daemon = true;
+      break;
+
+    case 'o':
+      ut->override_iconv_err = true;
+      break;
+
+    case 'n':
+      ushare_set_name (ut, optarg);
+      break;
+
+    case 'i':
+      ushare_set_interface (ut, optarg);
+      break;
+
+    case 'p':
+      ushare_set_port (ut, optarg);
+      break;
+
+    case 'q':
+      ushare_set_telnet_port (ut, optarg);
+      break;
+
+    case 'c':
+      ushare_add_contentdir (ut, optarg);
+      break;
+
+    case 'w':
+      ut->use_presentation = false;
+      break;
+
+    case 't':
+      ut->use_telnet = false;
+      break;
+
+    case 'x':
+      ut->xbox360 = true;
+      break;
+
+#ifdef HAVE_DLNA
+    case 'd':
+      ut->dlna_enabled = true;
+      break;
+#endif /* HAVE_DLNA */
+      
+    case 'f':
+      ushare_set_cfg_file (ut, optarg);
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cfgparser.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,56 @@
+/*
+ * cfgparser.c : GeeXboX uShare config file parser headers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CONFIG_PARSER_H_
+#define _CONFIG_PARSER_H_
+
+#include "ushare.h"
+
+#define USHARE_NAME               "USHARE_NAME"
+#define USHARE_IFACE              "USHARE_IFACE"
+#define USHARE_PORT               "USHARE_PORT"
+#define USHARE_TELNET_PORT        "USHARE_TELNET_PORT"
+#define USHARE_DIR                "USHARE_DIR"
+#define USHARE_OVERRIDE_ICONV_ERR "USHARE_OVERRIDE_ICONV_ERR"
+#define USHARE_ENABLE_WEB         "USHARE_ENABLE_WEB"
+#define USHARE_ENABLE_TELNET      "USHARE_ENABLE_TELNET"
+#define USHARE_ENABLE_XBOX        "USHARE_ENABLE_XBOX"
+#define USHARE_ENABLE_DLNA        "USHARE_ENABLE_DLNA"
+
+#define USHARE_CONFIG_FILE        "ushare.conf"
+#define DEFAULT_USHARE_NAME       "uShare"
+
+#if (defined(BSD) || defined(__FreeBSD__))
+#define DEFAULT_USHARE_IFACE      "lnc0"
+#else /* Linux */
+#define DEFAULT_USHARE_IFACE      "eth0"
+#endif
+
+int parse_config_file (struct ushare_t *ut)
+    __attribute__ ((nonnull));
+int parse_command_line (struct ushare_t *ut, int argc, char **argv)
+    __attribute__ ((nonnull (1)));
+
+typedef struct {
+  char *name;
+  void (*set_var) (struct ushare_t*, const char*);
+} u_configline_t;
+
+#endif /* _CONFIG_PARSER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cms.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,180 @@
+/*
+ * cms.c : GeeXboX uShare Connection Management Service.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "ushare.h"
+#include "services.h"
+#include "mime.h"
+
+/* Represent the CMS GetProtocolInfo action. */
+#define SERVICE_CMS_ACTION_PROT_INFO "GetProtocolInfo"
+
+/* Represent the CMS GetCurrentConnectionIDs action. */
+#define SERVICE_CMS_ACTION_CON_ID "GetCurrentConnectionIDs"
+
+/* Represent the CMS GetCurrentConnectionInfo action. */
+#define SERVICE_CMS_ACTION_CON_INFO "GetCurrentConnectionInfo"
+
+/* Represent the CMS SOURCE argument. */
+#define SERVICE_CMS_ARG_SOURCE "Source"
+
+/* Represent the CMS SINK argument. */
+#define SERVICE_CMS_ARG_SINK "Sink"
+
+/* Represent the CMS ConnectionIDs argument. */
+#define SERVICE_CMS_ARG_CONNECTION_IDS "ConnectionIDs"
+
+/* Represent the CMS ConnectionID argument. */
+#define SERVICE_CMS_ARG_CONNECTION_ID "ConnectionID"
+
+/* Represent the CMS RcsID argument. */
+#define SERVICE_CMS_ARG_RCS_ID "RcsID"
+
+/* Represent the CMS AVTransportID argument. */
+#define SERVICE_CMS_ARG_TRANSPORT_ID "AVTransportID"
+
+/* Represent the CMS ProtocolInfo argument. */
+#define SERVICE_CMS_ARG_PROT_INFO "ProtocolInfo"
+
+/* Represent the CMS PeerConnectionManager argument. */
+#define SERVICE_CMS_ARG_PEER_CON_MANAGER "PeerConnectionManager"
+
+/* Represent the CMS PeerConnectionID argument. */
+#define SERVICE_CMS_ARG_PEER_CON_ID "PeerConnectionID"
+
+/* Represent the CMS Direction argument. */
+#define SERVICE_CMS_ARG_DIRECTION "Direction"
+
+/* Represent the CMS Status argument. */
+#define SERVICE_CMS_ARG_STATUS "Status"
+
+/* Represent the CMS default connection ID value. */
+#define SERVICE_CMS_DEFAULT_CON_ID "0"
+
+/* Represent the CMS unknown connection ID value. */
+#define SERVICE_CMS_UNKNOW_ID "-1"
+
+/* Represent the CMS Output value. */
+#define SERVICE_CMS_OUTPUT "Output"
+
+/* Represent the CMS Success Status. */
+#define SERVICE_CMS_STATUS_OK "OK"
+
+static bool
+cms_get_protocol_info (struct action_event_t *event)
+{
+  extern struct mime_type_t MIME_Type_List[];
+  struct mime_type_t *list;
+  char *respText = NULL, *respPtr;
+  size_t respLen = 0, len;
+
+  if (!event)
+    return false;
+
+  // calculating length of response
+  list = MIME_Type_List;
+  while (list->extension)
+  {
+    char *protocol = mime_get_protocol (list);
+    respLen += strlen (protocol) + 1;
+    free (protocol);
+    list++;
+  }
+
+  respText = (char*) malloc (respLen * sizeof (char));
+  if (!respText)
+    return event->status;
+
+  list = MIME_Type_List;
+  respPtr = respText;
+  while (list->extension)
+  {
+    char *protocol = mime_get_protocol (list);
+    len = strlen (protocol);
+    strncpy (respPtr, protocol, len);
+    free (protocol);
+    respPtr += len;
+    list++;
+    if (list->extension)
+      strcpy (respPtr++, ",");
+  }
+  *respPtr = '\0';
+
+  upnp_add_response (event, SERVICE_CMS_ARG_SOURCE, respText);
+  upnp_add_response (event, SERVICE_CMS_ARG_SINK, "");
+
+  free (respText);
+  return event->status;
+}
+
+static bool
+cms_get_current_connection_ids (struct action_event_t *event)
+{
+  if (!event)
+    return false;
+
+  upnp_add_response (event, SERVICE_CMS_ARG_CONNECTION_IDS, "");
+
+  return event->status;
+}
+
+static bool
+cms_get_current_connection_info (struct action_event_t *event)
+{
+  extern struct mime_type_t MIME_Type_List[];
+  struct mime_type_t *list = MIME_Type_List;
+
+  if (!event)
+    return false;
+
+  upnp_add_response (event, SERVICE_CMS_ARG_CONNECTION_ID,
+                     SERVICE_CMS_DEFAULT_CON_ID);
+  upnp_add_response (event, SERVICE_CMS_ARG_RCS_ID, SERVICE_CMS_UNKNOW_ID);
+  upnp_add_response (event, SERVICE_CMS_ARG_TRANSPORT_ID,
+                     SERVICE_CMS_UNKNOW_ID);
+
+  while (list->extension)
+  {
+    char *protocol = mime_get_protocol (list);
+    upnp_add_response (event, SERVICE_CMS_ARG_PROT_INFO, protocol);
+    free (protocol);
+    list++;
+  }
+
+  upnp_add_response (event, SERVICE_CMS_ARG_PEER_CON_MANAGER, "");
+  upnp_add_response (event, SERVICE_CMS_ARG_PEER_CON_ID,
+                     SERVICE_CMS_UNKNOW_ID);
+  upnp_add_response (event, SERVICE_CMS_ARG_DIRECTION, SERVICE_CMS_OUTPUT);
+  upnp_add_response (event, SERVICE_CMS_ARG_STATUS, SERVICE_CMS_STATUS_OK);
+
+  return event->status;
+}
+
+/* List of UPnP ConnectionManager Service actions */
+struct service_action_t cms_service_actions[] = {
+  { SERVICE_CMS_ACTION_PROT_INFO, cms_get_protocol_info },
+  { SERVICE_CMS_ACTION_CON_ID, cms_get_current_connection_ids },
+  { SERVICE_CMS_ACTION_CON_INFO, cms_get_current_connection_info },
+  { NULL, NULL }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cms.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,166 @@
+/*
+ * cms.h : GeeXboX uShare Connection Management Service header.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CMS_H_
+#define CMS_H_
+
+#define CMS_DESCRIPTION \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
+"  <specVersion>" \
+"    <major>1</major>" \
+"    <minor>0</minor>" \
+"  </specVersion>" \
+"  <actionList>" \
+"    <action>" \
+"      <name>GetCurrentConnectionInfo</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>ConnectionID</name>" \
+"          <direction>in</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>RcsID</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>AVTransportID</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>ProtocolInfo</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>PeerConnectionManager</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>PeerConnectionID</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>Direction</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>Status</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>GetProtocolInfo</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>Source</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>SourceProtocolInfo</relatedStateVariable>" \
+"        </argument>" \
+"        <argument>" \
+"          <name>Sink</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>SinkProtocolInfo</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"    <action>" \
+"      <name>GetCurrentConnectionIDs</name>" \
+"      <argumentList>" \
+"        <argument>" \
+"          <name>ConnectionIDs</name>" \
+"          <direction>out</direction>" \
+"          <relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>" \
+"        </argument>" \
+"      </argumentList>" \
+"    </action>" \
+"  </actionList>" \
+"  <serviceStateTable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_ProtocolInfo</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_ConnectionStatus</name>" \
+"     <dataType>string</dataType>" \
+"      <allowedValueList>" \
+"        <allowedValue>OK</allowedValue>" \
+"        <allowedValue>ContentFormatMismatch</allowedValue>" \
+"        <allowedValue>InsufficientBandwidth</allowedValue>" \
+"        <allowedValue>UnreliableChannel</allowedValue>" \
+"        <allowedValue>Unknown</allowedValue>" \
+"      </allowedValueList>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_AVTransportID</name>" \
+"      <dataType>i4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_RcsID</name>" \
+"      <dataType>i4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_ConnectionID</name>" \
+"      <dataType>i4</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_ConnectionManager</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"yes\">" \
+"      <name>SourceProtocolInfo</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"yes\">" \
+"      <name>SinkProtocolInfo</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"no\">" \
+"      <name>A_ARG_TYPE_Direction</name>" \
+"      <dataType>string</dataType>" \
+"      <allowedValueList>" \
+"        <allowedValue>Input</allowedValue>" \
+"        <allowedValue>Output</allowedValue>" \
+"      </allowedValueList>" \
+"    </stateVariable>" \
+"    <stateVariable sendEvents=\"yes\">" \
+"      <name>CurrentConnectionIDs</name>" \
+"      <dataType>string</dataType>" \
+"    </stateVariable>" \
+"  </serviceStateTable>" \
+"</scpd>"
+
+#define CMS_DESCRIPTION_LEN strlen (CMS_DESCRIPTION)
+
+#define CMS_LOCATION "/web/cms.xml"
+
+#define CMS_SERVICE_ID "urn:upnp-org:serviceId:ConnectionManager"
+#define CMS_SERVICE_TYPE "urn:schemas-upnp-org:service:ConnectionManager:1"
+
+#endif /* CMS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/content.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,93 @@
+/*
+ * content.c : GeeXboX uShare content list
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "content.h"
+
+content_list *
+content_add(content_list *list, const char *item)
+{
+  if (!list)
+  {
+    list = (content_list*) malloc (sizeof(content_list));
+    list->content = NULL;
+    list->count = 0;
+  }
+  if (item)
+  {
+    list->count++;
+    list->content = (char**) realloc (list->content, list->count * sizeof(char*));
+    if (!list->content)
+    {
+      perror ("error realloc");
+      exit (2);
+    }
+    list->content[list->count-1] = strdup (item);
+  }
+  return list;
+}
+
+/*
+ * Remove the n'th content (start from 0)
+ */
+content_list *
+content_del(content_list *list, int n)
+{
+  int i;
+
+  if (!list || n >= list->count)
+    return NULL;
+
+  if (n >= list->count)
+    return list;
+
+  if (list->content[n])
+  {
+    free (list->content[n]);
+    for (i = n ; i < list->count - 1 ; i++)
+      list->content[i] = list->content[i+1];
+    list->count--;
+    list->content[list->count] = NULL;
+  }
+
+  return list;
+}
+
+void
+content_free(content_list *list)
+{
+  int i;
+  if (!list)
+    return;
+
+  if (list->content)
+  {
+    for (i=0 ; i < list->count ; i++)
+    {
+      if (list->content[i])
+        free (list->content[i]);
+    }
+    free (list->content);
+  }
+  free (list);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/content.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,36 @@
+/*
+ * content.h : GeeXboX uShare content list header
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CONTENT_H_
+#define _CONTENT_H_
+
+typedef struct content_list {
+  char **content;
+  int count;
+} content_list;
+
+content_list *content_add(content_list *list, const char *item)
+    __attribute__ ((malloc));
+content_list *content_del(content_list *list, int n)
+    __attribute__ ((nonnull));
+void content_free(content_list *list)
+    __attribute__ ((nonnull));
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ctrl_telnet.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,897 @@
+/* ctrltelnet.c - Telnet controler
+ * Copyright (C) 2005-2007 Sven Almgren <sven@tras.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define STR(x) _STR(x)
+#define _STR(x) __STR(x)
+#define __STR(x) #x
+
+#include "config.h"
+#include "ctrl_telnet.h"
+#include "minmax.h"
+#include "trace.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h> /* For select */
+#include <sys/time.h>
+#include <unistd.h> /* For pipe */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdarg.h>
+
+#if (defined(____DISABLE_MUTEX) || 0)
+#define pthread_mutex_lock(x)   printf(">>>> Locking   " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
+#define pthread_mutex_unlock(x) printf("<<<< Unlocking " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
+#endif
+
+/**
+ * @brief Structure holding data between the staring rutine and the thread
+ */
+typedef struct telnet_thread_data_t
+{
+  pthread_t thread;
+
+  /* Litening socket */
+  int listener;
+
+  /* Socket used to terminate loop:
+     0 is reading and 1 is sending, kill by sending to 1 */
+  int killer[2];
+
+  /* Our socket address */
+  struct sockaddr_in local_address;
+
+  /* Shared data buffer that can be used by others... */
+  char shared_buffer[CTRL_TELNET_SHARED_BUFFER_SIZE];
+
+  ctrl_telnet_client *clients;
+} telnet_thread_data;
+
+/**
+ * @brief Struct for registerd commands
+ */
+typedef struct telnet_function_list_t
+{
+  /* Function name, or keyword, if you like */
+  char *name;
+  char *description;
+  ctrl_telnet_command_ptr function;
+
+  struct telnet_function_list_t *next;
+} telnet_function_list;
+
+/* Static yes used to set socketoptions */
+static int yes = 1;
+static telnet_thread_data ttd;
+static telnet_function_list* functions = NULL;
+static pthread_mutex_t functions_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t startstop_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t shared_lock = PTHREAD_MUTEX_INITIALIZER;
+static int started = 0;
+
+/* Threadfunction, core in telnet controler */
+/**
+ * @brief Thread function
+ *
+ * @param data Not used, leave as NULL
+ */
+static void *ctrl_telnet_thread (void *data);
+
+/**
+ * @brief Adds a new client to our list of new ones
+ *
+ * @param client to add
+ */
+static void ctrl_telnet_client_add (ctrl_telnet_client *client);
+
+/**
+ * @brief Removes "client" from our list of clients
+ */
+static void ctrl_telnet_client_remove (ctrl_telnet_client *client);
+
+/**
+ * @brief Updates an fd_set to contain the current set of clients
+ *
+ * @return max fd found in list
+ */
+static int ctrl_telnet_fix_fdset (fd_set* readable);
+
+static void ctrl_telnet_tokenize (char *raw, int *argc, char ***argv);
+
+static int ctrl_telnet_client_recv (ctrl_telnet_client *client);
+static int ctrl_telnet_client_execute (ctrl_telnet_client *client);
+static int ctrl_telnet_client_execute_line (ctrl_telnet_client *client,
+                                            char *line);
+static int ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client,
+                                                 char *line);
+static void ctrl_telnet_register_internals();
+
+/**
+ * @brief Starts a Telnet bound control interface
+ *
+ * @return 0 on success, -1 on error
+ */
+int
+ctrl_telnet_start (int port)
+{
+  /* Start by making us threadsafe... */
+  pthread_mutex_lock (&startstop_lock);
+
+  /* Create listener socket */
+  ttd.listener = socket (PF_INET, SOCK_STREAM, 0);
+  if (ttd.listener == -1)
+  {
+    perror ("socket");
+    pthread_mutex_unlock (&startstop_lock);
+    return -1;
+  }
+
+  /* Clears us from "address already in use" errors */
+  if (setsockopt (ttd.listener, SOL_SOCKET, SO_REUSEADDR,
+                  &yes, sizeof (int)) == -1)
+    perror ("setsockopt");
+
+  ttd.local_address.sin_family = AF_INET;
+  ttd.local_address.sin_addr.s_addr = INADDR_ANY;
+  ttd.local_address.sin_port = htons (port);
+  memset (&ttd.local_address.sin_zero, '\0',
+          sizeof (ttd.local_address.sin_zero));
+
+  if (bind (ttd.listener, (struct sockaddr *) &ttd.local_address,
+            sizeof (ttd.local_address)) == -1)
+  {
+    perror ("bind");
+    pthread_mutex_unlock (&startstop_lock);
+    return -1;
+  }
+
+  if (listen (ttd.listener, CTRL_TELNET_BACKLOG) == -1)
+  {
+    perror ("listen");
+    pthread_mutex_unlock (&startstop_lock);
+    return -1;
+  }
+
+  print_log (ULOG_NORMAL, "Listening on telnet port %u\n", port);
+
+  /* Create killer pipes */
+  if (pipe (ttd.killer))
+  {
+    perror ("Failed to create killer pipe");
+    pthread_mutex_unlock (&startstop_lock);
+    return -1; /* FIXME. Kill all sockets... not critical,, but still */
+  }
+
+  if (pthread_create (&ttd.thread, NULL, ctrl_telnet_thread, NULL))
+  {
+    /* FIXME: Killall sockets... */
+    perror ("Failed to create thread");
+    pthread_mutex_unlock (&startstop_lock);
+    return -1;
+  }
+
+  started = 1;
+  ctrl_telnet_register_internals ();
+  pthread_mutex_unlock (&startstop_lock);
+
+  return 0;
+}
+
+/**
+ * @brief Stops all telnet bound control interfaces
+ */
+void
+ctrl_telnet_stop (void)
+{
+  pthread_mutex_lock (&startstop_lock);
+
+  if (!started)
+  {
+    pthread_mutex_unlock (&startstop_lock);
+    return;
+  }
+
+  /* yes is int, which is bigger then char, so this should be safe */
+  write (ttd.killer[1], &yes, sizeof (char));
+
+  pthread_mutex_unlock (&startstop_lock);
+  pthread_join (ttd.thread, NULL);
+}
+
+/**
+ * @brief Telnet thread function
+ */
+static void *
+ctrl_telnet_thread (void *a __attribute__ ((unused)))
+{
+  /* fd_set with readable clients */
+  fd_set fd_readable;
+
+  /* Pointer to a client object */
+  ctrl_telnet_client *client;
+
+  int fd_max;
+
+  while (1)
+  {
+    /* Get fds */
+    fd_max = ctrl_telnet_fix_fdset (&fd_readable);
+
+    if (select (fd_max + 1, &fd_readable, NULL, NULL, NULL) == -1)
+    {
+      perror ("select");
+      /* FIXME: Close sockets */
+      return NULL;
+    }
+
+    /* Check killer */
+    if (FD_ISSET (ttd.killer[0], &fd_readable))
+    {
+      /* FIXME: TODO: Shut down sockets...  */
+
+      /* Close listener and killer */
+      close (ttd.listener);
+      close (ttd.killer[0]);
+      close (ttd.killer[1]);
+
+      /* Check which fds that had anyhting to say... */
+      client = ttd.clients;
+
+      /* Say goodby to clients */
+      while (client)
+      {
+        ctrl_telnet_client *current = client;
+        ctrl_telnet_client_send (current,
+                                 "\nServer is going down, Bye bye\n");
+        client = client->next;
+        ctrl_telnet_client_remove (current);
+      }
+
+      pthread_mutex_lock (&functions_lock);
+
+      while (functions)
+      {
+        telnet_function_list *head = functions;
+        functions = functions->next;
+
+        free (head->name);
+        if (head->description)
+          free (head->description);
+
+        free (head);
+      }
+
+      pthread_mutex_unlock (&functions_lock);
+
+      return NULL;
+    }
+
+    /* Check for new connection */
+    if (FD_ISSET (ttd.listener, &fd_readable))
+    {
+      socklen_t sl_addr;
+
+      /* Create client object */
+      client = malloc (sizeof (ctrl_telnet_client));
+
+      if (!client)
+      {
+        perror ("Failed to create new client");
+        return NULL;
+      }
+
+      memset (client, '\0', sizeof (ctrl_telnet_client));
+      sl_addr = sizeof (client->remote_address);
+
+      client->socket = accept (ttd.listener,
+                               (struct sockaddr *) &client->remote_address,
+                               &sl_addr);
+      if (client->socket == -1)
+      {
+        perror ("accept");
+        free (client);
+      }
+      else
+      {
+        ctrl_telnet_client_add (client);
+        ctrl_telnet_client_execute_line_safe (client, "banner");
+        ctrl_telnet_client_sendf (client, "For a list of registered commands type \"help\"\n");
+        ctrl_telnet_client_send (client, "\n> ");
+      }
+    }
+
+    /* Check which fds that had anyhting to say... */
+    client = ttd.clients;
+
+    /* Run through all clients and check if there's data avalible
+       with FD_ISSET(current->socket) */
+    while (client)
+    {
+      ctrl_telnet_client *current = client;
+      client = client->next;
+
+      if (FD_ISSET (current->socket, &fd_readable))
+      {
+        if (ctrl_telnet_client_recv (current) <= 0)
+        {
+          ctrl_telnet_client_remove (current);
+          continue;
+        }
+
+        if (current->ready)
+        {
+          ctrl_telnet_client_execute (current);
+
+          if (!current->exiting)
+            ctrl_telnet_client_send (current, "\n> ");
+          else
+            ctrl_telnet_client_remove (current);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * @brief Adds a new client to our list of new ones
+ *
+ * @note This funtion is only called from a single thread,
+ *       as such it won't need to be threadsafe
+ * @param client to add
+ */
+static void
+ctrl_telnet_client_add (ctrl_telnet_client *client)
+{
+  client->next = ttd.clients;
+  ttd.clients = client;
+}
+
+/**
+ * @brief Removes "client" from our list of clients
+ *
+ * @note This funtion is only called from a single thread,
+ *       as such it won't need to be threadsafe
+ * @param client to remove
+ */
+static void
+ctrl_telnet_client_remove (ctrl_telnet_client *client)
+{
+  ctrl_telnet_client *tmp;
+
+  /* Start by dealing with our head */
+  if (client == ttd.clients)
+    ttd.clients = client->next;
+  else
+  {
+    for (tmp = ttd.clients; tmp->next; tmp = tmp->next)
+    {
+      if (tmp->next == client)
+      {
+        tmp->next = tmp->next->next;
+        break;
+      }
+    }
+  }
+
+  close (client->socket);
+
+  free (client);
+}
+
+/**
+ * @brief Clears readable fd_set and adds every client to it,
+ *        returns max fd found
+ *
+ * @param readable fd_set to update
+ * @return Biggest fd
+ */
+static int
+ctrl_telnet_fix_fdset (fd_set *readable)
+{
+  int maxfd;
+  ctrl_telnet_client *client;
+
+  maxfd = MAX (ttd.killer[0], ttd.listener);
+
+  FD_ZERO (readable);
+  FD_SET (ttd.listener, readable);
+  FD_SET (ttd.killer[0], readable);
+
+  client = ttd.clients;
+
+  while (client)
+  {
+    if (client->socket > maxfd)
+      maxfd = client->socket;
+
+    FD_SET (client->socket, readable);
+
+    client = client->next;
+  }
+
+  return maxfd;
+}
+
+static int
+ctrl_telnet_client_recv (ctrl_telnet_client *client)
+{
+  int i;
+  int nbytes;
+  int buffer_free = CTRL_CLIENT_RECV_BUFFER_SIZE - client->buffer_recv_current - 1;
+
+  nbytes = recv (client->socket,
+                 client->buffer_recv + client->buffer_recv_current,
+                 buffer_free, 0);
+  if (nbytes <= 0)
+  {
+    close (client->socket);
+    return nbytes;
+  }
+
+  client->buffer_recv_current += nbytes;
+  client->buffer_recv[client->buffer_recv_current] = '\0';
+
+  for (i = 0; i < client->buffer_recv_current; i++)
+    if (client->buffer_recv[i] == '\n')
+      client->ready = 1;
+
+  return nbytes;
+}
+
+int
+ctrl_telnet_client_send (const ctrl_telnet_client *client, const char *string)
+{
+  const char* cc = string;
+  int len = strlen (cc);
+  int sent = 0;
+  int senttotal = 0;
+
+  while ((cc - string) < len)
+  {
+    /* Use nonblocking just as a precation...
+       and a failed write won't _really_ kill us */
+    sent = send (client->socket, string, len - (cc - string), MSG_DONTWAIT);
+    
+    /* This will mark the socket as dead... just to be safe..
+       and its only a telnet interface... reconnect and do it again */
+    if (sent == -1)
+      return -1;
+
+    senttotal += sent;
+    cc += sent;
+  }
+
+  return senttotal;
+}
+
+int
+ctrl_telnet_client_sendf (const ctrl_telnet_client *client,
+                          const char *format, ...)
+{
+  int retval;
+  va_list ap;
+  int len;
+
+  pthread_mutex_lock (&shared_lock);
+
+  va_start (ap, format);
+  len = vsnprintf (ttd.shared_buffer,
+                   CTRL_TELNET_SHARED_BUFFER_SIZE, format, ap);
+  va_end (ap);
+
+  /* Check if the message fitted inside the buffer, if not,
+     either exit or adjust len to be buffersize, I choose exit for now */
+  if (len >= CTRL_TELNET_SHARED_BUFFER_SIZE)
+  {
+    pthread_mutex_unlock (&shared_lock);
+    /* FIXME: Return error or send what we've got? */
+    return -1; /* Buffer was to small */
+  }
+  
+  /* TODO: Might be good to have the option to specify str length so
+     send doesn't have to recompute it... */
+  retval = ctrl_telnet_client_send (client, ttd.shared_buffer);
+
+  pthread_mutex_unlock (&shared_lock);
+
+  return retval;
+}
+
+int
+ctrl_telnet_client_sendsf (const ctrl_telnet_client *client,
+                           char *buffer, int buffersize,
+                           const char *format, ...)
+{
+  va_list ap;
+  int len;
+
+  va_start (ap, format);
+  len = vsnprintf (buffer, buffersize, format, ap);
+  va_end (ap);
+
+  /* Check if the message fitted inside the buffer, if not,
+     either exit or adjust len to be buffersize, I choose exit for now */
+  if (len >= buffersize)
+    return -1; /* Buffer was to small */
+
+  /* TODO: Might be good to have the option to specify str length
+     so send doesn't have to recompute it... */
+  return ctrl_telnet_client_send (client, buffer);
+}
+
+/* FIXME: Ulgy non optimised version */
+static int
+ctrl_telnet_client_execute (ctrl_telnet_client *client)
+{
+  int i = 0;
+
+  /* Check buffer for complete lines and execute them,,, */
+  for (i = 0; i < client->buffer_recv_current; i++)
+  {
+    if (client->buffer_recv[i] == '\n' || client->buffer_recv[i] == '\r')
+    {
+      /* Replace newline with null (or \r) */
+      client->buffer_recv[i] = '\0';
+
+      /* Send line to execution */
+      ctrl_telnet_client_execute_line_safe (client, client->buffer_recv);
+
+      /* Check if next is either newline or CR, strip that too, if needed */
+      if ((i + 1 < CTRL_CLIENT_RECV_BUFFER_SIZE) &&
+          (client->buffer_recv[i+1]=='\n' || client->buffer_recv[i+1]=='\r'))
+        client->buffer_recv[++i] = '\0';
+
+      /* Remove processed line */
+      memmove (client->buffer_recv, client->buffer_recv + i,
+               client->buffer_recv_current - 1);
+      client->buffer_recv_current -= (i + 1);
+      i = -1;
+    }
+  }
+
+  return 0; /* No syntax error checking yet */
+}
+
+static int
+ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client, char *line)
+{
+  int retval;
+
+  pthread_mutex_lock (&functions_lock);
+  retval = ctrl_telnet_client_execute_line (client, line);
+  pthread_mutex_unlock (&functions_lock);
+
+  return retval;
+}
+
+static int
+ctrl_telnet_client_execute_line (ctrl_telnet_client *client, char *line)
+{
+  int argc = 0;
+  char **argv = NULL;
+  telnet_function_list *node;
+  char *line2 = strdup (line); /* To make it safer */
+  ctrl_telnet_tokenize (line2, &argc, &argv);
+
+  node = functions;
+
+  if (*argv[0] == '\0')
+  {
+    free (argv);
+    free (line2);
+    return 0;
+  }
+
+  while (node)
+  {
+    if (!strcmp (node->name, argv[0]))
+    {
+      node->function (client, argc, argv);
+      break;
+    }
+
+    node = node->next;
+  }
+
+  if (!node)
+    ctrl_telnet_client_sendf (client, "%s: Command not found\n", argv[0]);
+
+  free (argv);
+  free (line2);
+
+  return strlen (line);
+}
+
+void
+ctrl_telnet_register (const char *funcname,
+                      ctrl_telnet_command_ptr funcptr,
+                      const char *description)
+{
+  telnet_function_list *function;
+
+  function = malloc (sizeof (telnet_function_list));
+  function->name = strdup (funcname); /* Mayby use strndup...? */
+  function->description = description ? strdup (description) : NULL;
+  function->function = funcptr;
+
+  pthread_mutex_lock (&functions_lock);
+  function->next = functions;
+  functions = function;
+  pthread_mutex_unlock (&functions_lock);
+}
+
+/* Warning: This WILL edit the input string... use strdup or something
+   if needed, also remember to free() argv as the first array is dynamic */
+/* If *argv != NULL it'll first be free()ed... or realloc,
+   make sure to clear *argv to null on initialization */
+static void
+ctrl_telnet_tokenize (char *raw, int *argc, char ***argv)
+{
+  int i;
+  int has_backslash = 0;
+  int has_quote = 0;
+  char *pc = raw;
+
+  if (!raw || !argc || !argv)
+  {
+    perror ("NULL in " __FILE__ " at line " STR (__LINE__));
+    return;
+  }
+
+  /* (1/3) First run is just to count our arguments... */
+  *argc = (raw[0] == '\0') ? 0 : 1;
+
+  pc = raw;
+  while (*pc)
+  {
+    switch (*pc)
+    {
+    case '\\':
+      if (!has_backslash)
+        has_backslash = 2; /* FULHACK */
+      break;
+    case ' ':
+      if (!has_backslash && !has_quote)
+        (*argc)++;
+      break;
+    case '"':
+      if (!has_backslash)
+        has_quote = !has_quote;
+      
+      break;
+    }
+
+    /* When we get a BS we set it to two, this makes it one,
+       next run it will still be 1, then one after that is zero... FULHACK */
+    if (has_backslash)
+      has_backslash--;
+
+    pc++;
+  }
+
+  /* Create argv */
+  *argv = malloc (sizeof (char **) * ((*argc) + 1));
+
+  /* (2/3) Parse throu one more time, this time filling argv (Pass 2 / 3) */
+  i = 0;
+  pc = raw;
+  has_backslash = 0;
+  has_quote = 0;
+  (*argv)[0] = raw;
+
+  while (*pc)
+  {
+    switch (*pc)
+    {
+    case '\\':
+      if (!has_backslash)
+        has_backslash = 2; /* FULHACK */
+      break;
+    case ' ':
+      if (!has_backslash && !has_quote)
+      {
+        *pc = '\0';
+        (*argv)[++i] = pc+1;
+        pc++;
+        continue;
+      }
+      break;
+    case '"':
+      if (!has_backslash)
+        has_quote = !has_quote;
+      break;
+    }
+
+    /* When we get a BS we set it to two, this makes it one,
+       next run it will still be 1, then one after that is zero... FULHACK */
+    if (has_backslash)
+      has_backslash--;
+
+    pc++;
+  }
+
+  /* Make last element (argc) point to null... */
+  (*argv)[++i] = NULL;
+
+  /* (3/3) Parse arguments to remove escapings and such */
+  for (i = 0; (*argv)[i]; i++)
+  {
+    /* Set up environment */
+    pc = (*argv)[i];
+    has_backslash = 0;
+    has_quote = 0;
+
+    /* Remove leading and ending quotes, if existing */
+    if (*pc == '"')
+    {
+      int len = strlen (pc);
+
+      if (len > 0 && pc[len - 1] == '"')
+        pc[len - 1] = '\0';
+      memmove (pc, pc + 1, len);
+    }
+
+    /* Remove any special characters */
+    while (*pc)
+    {
+      switch (*pc)
+      {
+      case '\\':
+        if (!has_backslash)
+        {
+          has_backslash = 2; /* FULHACK */
+          break;
+        }
+        /* Else: fall through */
+      case ' ':
+      case '"':
+        if (has_backslash)
+        {
+          pc--;
+          memmove (pc, pc + 1, strlen (pc)); /* FIXME: Not cheap */
+        }
+        break;
+      }
+
+      /* When we get a BS we set it to two, this makes it one,
+         next run it will still be 1, then one after that is zero... */
+      if (has_backslash)
+        has_backslash--;
+      
+      pc++;
+    }
+  }
+}
+
+static void
+help (ctrl_telnet_client *client, int argc, char **argv)
+{
+  int hidden = 0;
+  ctrl_telnet_client_execute_line (client, "banner");
+  
+  if (argc < 2)
+  {
+    ctrl_telnet_client_send (client, "\n");
+    ctrl_telnet_client_send (client, "Usage: help TOPIC\n");
+    ctrl_telnet_client_send (client, "Valid topics are\n");
+    ctrl_telnet_client_send
+      (client, "  commands - For a list of registed commands\n");
+    ctrl_telnet_client_send
+      (client, "  syntax - For a description of the interface syntax\n");
+    return;
+  }
+  else
+  {
+    if (!strcmp ("commands", argv[1]))
+    {
+      telnet_function_list *node;
+
+      node = functions;
+      ctrl_telnet_client_send
+        (client, "Registered command (command - description)\n");
+      ctrl_telnet_client_send
+        (client, "=======================================================\n");
+
+      while (node)
+      {
+        /* Make functions without descriptions invisible */
+        if (node->description)
+          ctrl_telnet_client_sendf (client, "  %s - %s\n",
+                                    node->name, node->description);
+        else
+          hidden++;
+
+        node = node->next;
+      }
+
+      if (hidden)
+        ctrl_telnet_client_sendf
+          (client, "There's also %i hidden functions\n", hidden);
+    } /* commands */
+    else if (!strcmp ("syntax", argv[1]))
+    {
+      ctrl_telnet_client_send
+        (client, "Syntax is easy: command parameters\n");
+      ctrl_telnet_client_send
+        (client, "  Each new word is a new argument, unless the space is precided\n");
+      ctrl_telnet_client_send
+        (client, "  a backslash (\\), or if a set of words are surrounded by quotes\n");
+      ctrl_telnet_client_send
+        (client, "  (\"). To get a litteral quote you can escape it as \\\".\n");
+      ctrl_telnet_client_send (client, "\n");
+      ctrl_telnet_client_send (client, "STUB\n");
+    }
+    else
+      ctrl_telnet_client_send (client, "Unknown topic\n");
+  }
+}
+
+static void
+banner (ctrl_telnet_client *client,
+        int argc __attribute__ ((unused)),
+        char **argv __attribute__ ((unused)))
+{
+  ctrl_telnet_client_sendf (client, "%s (%s) (Built %s)\n",
+                            PACKAGE_NAME, VERSION, __DATE__);
+}
+
+static void
+echo (ctrl_telnet_client *client, int argc, char **argv)
+{
+  int i;
+  
+  for (i = 1; i < argc; i++)
+    ctrl_telnet_client_sendf (client, "%s%s", (i > 1 ? " " : ""), argv[i]);
+  ctrl_telnet_client_send (client, "\n");
+}
+
+static void
+echod (ctrl_telnet_client *client, int argc, char **argv)
+{
+  int i;
+
+  ctrl_telnet_client_sendf (client, "Argc: %i\n", argc);
+
+  for (i = 0; i < argc; i++)
+    ctrl_telnet_client_sendf (client, "%i: '%s'\n", i, argv[i]);
+}
+
+static void
+ctrl_telnet_exit (ctrl_telnet_client *client,
+                  int argc __attribute__ ((unused)),
+                  char **argv __attribute__ ((unused)))
+{
+  client->exiting = 1;
+  ctrl_telnet_client_send (client, "Bye bye\n");
+}
+
+static void
+ctrl_telnet_register_internals (void)
+{
+  ctrl_telnet_register ("echo", echo, "Echos all arguments");
+  ctrl_telnet_register ("echod", echod, "Echos all arguments but with each argument on a new line... DEBUG");
+  ctrl_telnet_register ("help", help, "Display help");
+  ctrl_telnet_register ("banner", banner, NULL);
+  ctrl_telnet_register ("exit", ctrl_telnet_exit, "Exits this interface (Or CTRL+D then Enter)");
+  /* CTRL+D... But it has to be fallowd by a new line */
+  ctrl_telnet_register ("\4", ctrl_telnet_exit, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ctrl_telnet.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,75 @@
+/* ctrltelnet.h - Header for the Telnet controler
+ * Copyright (C) 2005-2007 Sven Almgren <sven@tras.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _CTRL_TELNET_H_
+#define _CTRL_TELNET_H_
+
+#define CTRL_TELNET_PORT 1337
+#define CTRL_TELNET_BACKLOG 10
+#define CTRL_TELNET_SHARED_BUFFER_SIZE 256
+#define CTRL_CLIENT_RECV_BUFFER_SIZE 256
+
+#include <netinet/in.h>
+
+/**
+ * @brief Structure doubling as both a connected client data holder
+ *        and as a linked list
+ */
+typedef struct ctrl_telnet_client_t
+{
+  /* Recv buffer used to read single lines from more then one packet ...
+     Not garanteed to be NULL terminated */
+  char buffer_recv[CTRL_CLIENT_RECV_BUFFER_SIZE];
+
+  int buffer_recv_current;
+  int socket;
+  int ready; /* True if this client has a complete line, ready to be parsed */
+  int exiting;
+  struct sockaddr_in remote_address;
+  struct ctrl_telnet_client_t* next;
+} ctrl_telnet_client;
+
+typedef void (* ctrl_telnet_command_ptr) (ctrl_telnet_client *, int, char **);
+
+/**
+ * @brief Starts a Telnet bound control interface
+ *
+ * @return 0 on success, -1 on error
+ */
+int ctrl_telnet_start (int port);
+
+/**
+ * @brief Stops all telnet bound control interfaces
+ */
+void ctrl_telnet_stop (void);
+
+/* FIXME: You can register a function name multiple times,
+   but the last one added is the one getting called... not a problem a.t.m. */
+void ctrl_telnet_register (const char *funcname,
+                           ctrl_telnet_command_ptr funcptr,
+                           const char* description);
+
+int ctrl_telnet_client_send (const ctrl_telnet_client *, const char* string);
+int ctrl_telnet_client_sendf (const ctrl_telnet_client *client,
+                              const char* format, ...);
+int ctrl_telnet_client_sendsf (const ctrl_telnet_client *client,
+                               char* buffer, int buffersize,
+                               const char* format, ...);
+
+#endif /* _CTRL_TELNET_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gettext.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,81 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000-2002, 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+   USA.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+#define _(string) gettext (string)
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#ifdef CONFIG_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+   chokes if dcgettext is defined as a macro.  So include it now, to make
+   later inclusions of <locale.h> a NOP.  We don't include <libintl.h>
+   as well because people using "gettext.h" will not include <libintl.h>,
+   and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+   is OK.  */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Many header files from the libstdc++ coming with g++ 3.3 or newer include
+   <libintl.h>, which chokes if dcgettext is defined as a macro.  So include
+   it now, to make later inclusions of <libintl.h> a NOP.  */
+#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
+# include <cstdlib>
+# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H
+#  include <libintl.h>
+# endif
+#endif
+
+/* Disabled NLS.
+   The casts to 'const char *' serve the purpose of producing warnings
+   for invalid uses of the value returned from these functions.
+   On pre-ANSI systems without 'const', the config.h file is supposed to
+   contain "#define const".  */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+   extraction of messages, but does not call gettext().  The run-time
+   translation is done at a different place in the code.
+   The argument, String, should be a literal string.  Concatenated strings
+   and other string expressions won't work.
+   The macro's expansion is not parenthesized, so that it is suitable as
+   initializer for static 'char[]' or 'const char[]' variables.  */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/http.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,602 @@
+/* -*- tab-width: 4; indent-tabs-mode: nil -*- */
+/* vim: set ts=4 sts=4 sw=4 expandtab number : */
+/*
+ * http.c : GeeXboX uShare Web Server handler.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "services.h"
+#include "cds.h"
+#include "cms.h"
+#include "msr.h"
+#include "metadata.h"
+#include "http.h"
+#include "minmax.h"
+#include "trace.h"
+#include "presentation.h"
+#include "osdep.h"
+#include "mime.h"
+#include "recpt1.h"
+#include "tssplitter_lite.h"
+
+#define PROTOCOL_TYPE_PRE_SZ  11   /* for the str length of "http-get:*:" */
+#define PROTOCOL_TYPE_SUFF_SZ 2    /* for the str length of ":*" */
+
+extern thread_data tdata;
+
+struct web_file_t {
+    char *fullpath;
+    off_t pos;
+    enum {
+        FILE_LOCAL,
+        FILE_MEMORY,
+        FILE_STREAM
+    } type;
+    union {
+        struct {
+            int fd;
+            struct upnp_entry_t *entry;
+        } local;
+        struct {
+            char *contents;
+            off_t len;
+        } memory;
+        struct {
+            int id;
+            STREAM_QUEUE_T *p_queue;
+            ARIB_STD_B25_BUFFER *qbuf;
+            off_t len;
+        } stream;
+    } detail;
+};
+
+    static inline void
+set_info_file (struct File_Info *info, const size_t length,
+        const char *content_type)
+{
+    info->file_length = length;
+    info->last_modified = 0;
+    info->is_directory = 0;
+    info->is_readable = 1;
+    info->content_type = ixmlCloneDOMString (content_type);
+}
+
+//#define STREAM_LOCATION "/web/stream.ts"
+    static int
+http_get_info (const char *filename, struct File_Info *info)
+{
+    extern struct ushare_t *ut;
+    struct upnp_entry_t *entry = NULL;
+    struct stat st;
+    int upnp_id = 0;
+    char *content_type = NULL;
+    char *protocol = NULL;
+    extern thread_data *gp_tdata;
+    thread_data *tdata = gp_tdata;
+
+    if (!filename || !info)
+        return -1;
+
+    log_verbose ("http_get_info, filename : %s\n", filename);
+
+    upnp_id = atoi (strrchr (filename, '/') + 1);
+    entry = upnp_get_entry (ut, upnp_id);
+
+#if 0
+    if ( (!strcmp (filename, STREAM_LOCATION)) ||
+          ( (entry->fullpath != NULL) &&
+            ( !strcmp(entry->fullpath, STREAM_LOCATION))) ) {
+#endif
+    if (!strcmp (filename, STREAM_LOCATION)) {
+        log_verbose ("http_get_info, stream location found.\n");
+        info->is_readable = 1;
+        info->file_length = 100*1024*1024;
+        info->file_length = info->file_length - (info->file_length % LENGTH_PACKET);
+        info->last_modified = time(NULL);
+        info->is_directory = 0;
+        info->content_type = ixmlCloneDOMString ("video/mpeg");
+        return 0;
+    }
+    if (!strcmp (filename, CDS_LOCATION))
+    {
+        set_info_file (info, CDS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (!strcmp (filename, CMS_LOCATION))
+    {
+        set_info_file (info, CMS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (!strcmp (filename, MSR_LOCATION))
+    {
+        set_info_file (info, MSR_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (ut->use_presentation && !strcmp (filename, USHARE_PRESENTATION_PAGE))
+    {
+        if (build_presentation_page (ut) < 0)
+            return -1;
+
+        set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (ut->use_presentation && !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI)))
+    {
+        if (process_cgi (ut, (char *) (filename + strlen (USHARE_CGI) + 1)) < 0)
+            return -1;
+
+        set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (!entry)
+        return -1;
+    log_verbose ("http_get_info, entry found.\n");
+
+    if (!entry->fullpath)
+        return -1;
+
+#if 0
+    if (stat (entry->fullpath, &st) < 0)
+        return -1;
+
+    if (access (entry->fullpath, R_OK) < 0)
+    {
+        if (errno != EACCES) {
+            log_verbose ("http_get_info, access() error.\n");
+            return -1;
+        }
+        info->is_readable = 0;
+    }
+    else
+        info->is_readable = 1;
+
+    /* file exist and can be read */
+    info->file_length = st.st_size;
+    info->last_modified = st.st_mtime;
+    info->is_directory = S_ISDIR (st.st_mode);
+#endif
+
+    info->is_readable = 1;
+    info->file_length = 100*1024*1024;
+    info->file_length = info->file_length - (info->file_length % LENGTH_PACKET);
+    info->last_modified = time(NULL);
+    info->is_directory = 0;
+
+    protocol = 
+#ifdef HAVE_DLNA
+        entry->dlna_profile ?
+        dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
+                DLNA_ORG_PLAY_SPEED_NORMAL,
+                DLNA_ORG_CONVERSION_NONE,
+                DLNA_ORG_OPERATION_RANGE,
+                ut->dlna_flags, entry->dlna_profile) :
+#endif /* HAVE_DLNA */
+        mime_get_protocol (entry->mime_type);
+
+    content_type =
+        strndup ((protocol + PROTOCOL_TYPE_PRE_SZ),
+                strlen (protocol + PROTOCOL_TYPE_PRE_SZ)
+                - PROTOCOL_TYPE_SUFF_SZ);
+    free (protocol);
+
+    if (content_type)
+    {
+        info->content_type = ixmlCloneDOMString (content_type);
+        free (content_type);
+    }
+    else
+        info->content_type = ixmlCloneDOMString ("");
+
+    return 0;
+}
+
+    static UpnpWebFileHandle
+get_file_memory (const char *fullpath, const char *description,
+        const size_t length)
+{
+    struct web_file_t *file;
+
+//    log_verbose ("get_file_memory() description[%s]\n",
+//            description);
+    file = malloc (sizeof (struct web_file_t));
+    file->fullpath = strdup (fullpath);
+    file->pos = 0;
+    file->type = FILE_MEMORY;
+    file->detail.memory.contents = strdup (description);
+    if ( file->detail.memory.contents  == NULL ) {
+        log_verbose ("get_file_memory() null\n");
+    }
+    file->detail.memory.len = length;
+//    log_verbose ("get_file_memory() path[%s] contents[%s]\n",
+//            file->fullpath, file->detail.memory.contents);
+
+    return ((UpnpWebFileHandle) file);
+}
+
+/*
+ * 2. get_file_stream() $B$G$O!"(Bopen()$B;~$NA`:n(B($B%U%!%$%k>pJs$NIU2C(B)$B$NB>!"%U%!%$%k>pJs$H(B queue $B$NI3IU$1$r<B;\(B
+ * $B"((B get_file_memory() $B$+$i$N2~B$E@$KCe4c$7$F5-:\(B
+ *   2.1 tdata->streamer->mutex $B$r(B lock $B$7$F$+$i0J2<$N(Bloop$B=hM}$r9T$&(B(loop$B:GBg?t$O(B16$B$G7h$aBG$A(B)
+ *     2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k$+3NG'(B
+ *       2.1.1.2 NULL$B$G$"$k>l9g(B
+ *         2.1.1.2.1 create_queue $B$r<B;\(B
+ *     2.1.1.3 NULL $B$G$O$J$$>l9g(B
+ *       2.1.1.2.1 $BF@$K$d$k$3$HL5$7(B
+ *   2.2 tdata->streamer->mutex $B$r(B unlock
+ */
+    static UpnpWebFileHandle
+get_file_stream (const char *fullpath, thread_data *tdata)
+{
+#define STREAM_QUEUE (1024)
+    struct web_file_t *file;
+    int i = 0;
+    file = malloc (sizeof (struct web_file_t));
+
+    // 2.1 tdata->streamer->mutex $B$r(B lock $B$7$F$+$i0J2<$N(Bloop$B=hM}$r9T$&(B(loop$B:GBg?t$O(B16$B$G7h$aBG$A(B)
+    pthread_mutex_lock(&tdata->streamer->mutex);
+    for( i=0; i < STREAM_MAX; i++ ) {
+        if ( tdata->streamer->stream_session[i] == NULL ) {
+            // 2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k>l9g(B
+            // 2.1.1.1 $B?75,%9%H%j!<%`MQ$N%-%e!<$r:n@.(B
+            file->detail.stream.id = tdata->streamer->stream_nr;
+            tdata->streamer->stream_session[i] = malloc(sizeof(session));
+            if ( tdata->streamer->stream_session[i] == NULL ) {
+                return NULL;
+            }
+            tdata->streamer->stream_session[i]->is_valid = true;
+            tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE);
+            if ( tdata->streamer->stream_session[i]->p_queue  == NULL ) {
+                log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i);
+                return NULL;
+            }
+            pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL);
+            pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL);
+            pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL);
+            tdata->streamer->stream_nr++;
+            break;
+        } else {
+            // 2.1.2 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$O$J$$(B
+            if ( ! tdata->streamer->stream_session[i]->is_valid ) {
+                // 2.1.2.1 tdata->streamer->stream_session[i] $B$,L$;HMQ>uBV$G$"$k>l9g(B
+                file->detail.stream.id = i;
+                //tdata->streamer->stream_nr++;
+                tdata->streamer->stream_session[i]->is_valid = true;
+                tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE);
+                if ( tdata->streamer->stream_session[i]->p_queue  != NULL ) {
+                    pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL);
+                    pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL);
+                    pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL);
+                } else {
+                    log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i);
+                    return NULL;
+                }
+                break;
+            }
+        }
+    }
+    pthread_mutex_unlock(&tdata->streamer->mutex);
+    if ( i == STREAM_MAX ) {
+        log_verbose ("get_file_stream(): cannot get new file_stream.\n");
+    }
+
+    file->detail.stream.p_queue = tdata->streamer->stream_session[i]->p_queue;
+    file->fullpath = strdup (fullpath);
+    file->pos = 0;
+    file->type = FILE_STREAM;
+    file->detail.stream.len = 100*1024*1024; //$B%U%!%$%k%5%$%:(B($B;DO?2h;~4V(Bx$B%S%C%H%l!<%H(B)
+    file->detail.stream.len = file->detail.stream.len - (file->detail.stream.len % LENGTH_PACKET);
+    file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue);
+    if ( file->detail.stream.qbuf == NULL ) {
+        log_error ("get_file_stream(): stream_dequeue error.\n");
+        return NULL;
+    }
+    log_verbose ("get_file_stream(): finish.\n");
+
+    return ((UpnpWebFileHandle) file);
+}
+
+    static UpnpWebFileHandle
+http_open (const char *filename, enum UpnpOpenFileMode mode)
+{
+    extern struct ushare_t *ut;
+    struct upnp_entry_t *entry = NULL;
+    struct web_file_t *file;
+    int fd, upnp_id = 0;
+    extern thread_data *gp_tdata;
+    thread_data *tdata = gp_tdata;
+
+    if (!filename)
+        return NULL;
+
+    if (mode != UPNP_READ)
+        return NULL;
+
+    if (!strcmp (filename, CDS_LOCATION))
+        return get_file_memory (CDS_LOCATION, CDS_DESCRIPTION, CDS_DESCRIPTION_LEN);
+
+    if (!strcmp (filename, CMS_LOCATION))
+        return get_file_memory (CMS_LOCATION, CMS_DESCRIPTION, CMS_DESCRIPTION_LEN);
+
+    if (!strcmp (filename, MSR_LOCATION))
+        return get_file_memory (MSR_LOCATION, MSR_DESCRIPTION, MSR_DESCRIPTION_LEN);
+
+    if (ut->use_presentation && ( !strcmp (filename, USHARE_PRESENTATION_PAGE)
+                || !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI))))
+        return get_file_memory (USHARE_PRESENTATION_PAGE, ut->presentation->buf,
+                ut->presentation->len);
+
+    upnp_id = atoi (strrchr (filename, '/') + 1);
+    entry = upnp_get_entry (ut, upnp_id);
+    if (!entry)
+        return NULL;
+
+    if (!entry->fullpath)
+        return NULL;
+
+    /*
+     * 1. http_open() $B$G$O(B entry $B$,%9%H%j!<%`:F@8MQ$N$b$N$G$"$k>l9g$K!"(B
+     * get_file_stream()$B$r8F$S=P$7%O%s%I%i$rJV5Q$9$k(B
+     */
+    log_verbose ("Fullpath : %s\n", entry->fullpath);
+    if (!strcmp (entry->fullpath, STREAM_LOCATION))
+        return get_file_stream (STREAM_LOCATION, tdata);
+
+    fd = open (entry->fullpath, O_RDONLY | O_NONBLOCK | O_SYNC | O_NDELAY);
+    if (fd < 0)
+        return NULL;
+
+    file = malloc (sizeof (struct web_file_t));
+    file->fullpath = strdup (entry->fullpath);
+    file->pos = 0;
+    file->type = FILE_LOCAL;
+    file->detail.local.entry = entry;
+    file->detail.local.fd = fd;
+
+    return ((UpnpWebFileHandle) file);
+}
+
+    static int
+http_read (UpnpWebFileHandle fh, char *buf, size_t buflen)
+{
+    struct web_file_t *file = (struct web_file_t *) fh;
+    ssize_t len = -1;
+    extern thread_data *gp_tdata;
+    thread_data *tdata = gp_tdata;
+
+    //log_verbose ("http_read file:[%s]\n", file->fullpath);
+
+    if (!file)
+        return -1;
+
+    switch (file->type)
+    {
+        case FILE_LOCAL:
+            log_verbose ("Read local file.\n");
+            len = read (file->detail.local.fd, buf, buflen);
+            break;
+        case FILE_MEMORY:
+            log_verbose ("Read file from memory.\n");
+            len = (size_t) MIN (buflen, file->detail.memory.len - file->pos);
+            memcpy (buf, file->detail.memory.contents + file->pos, (size_t) len);
+            break;
+        case FILE_STREAM:
+            //log_verbose ("Read file from stream.\n");
+            if ( file->detail.stream.qbuf->size <= file->pos ) {
+                free(file->detail.stream.qbuf->data);
+                file->detail.stream.qbuf->data = NULL;
+                free(file->detail.stream.qbuf);
+                file->detail.stream.qbuf = NULL;
+                file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue);
+                file->pos = 0;
+            }
+            if ( file->detail.stream.qbuf == NULL ) {
+                log_verbose ("http_read stream_dequeue error NULL\n");
+                return -1;
+            }
+            len = (size_t) MIN (buflen, file->detail.stream.qbuf->size - file->pos);
+            memcpy (buf, file->detail.stream.qbuf->data + file->pos, (size_t) len);
+            break;
+        default:
+            log_verbose ("Unknown file type.\n");
+            break;
+    }
+
+    if (len >= 0)
+        file->pos += len;
+
+    //log_verbose ("Read %zd bytes.\n", len);
+
+    return len;
+}
+
+    static int
+http_write (UpnpWebFileHandle fh __attribute__((unused)),
+        char *buf __attribute__((unused)),
+        size_t buflen __attribute__((unused)))
+{
+    log_verbose ("http write\n");
+
+    return 0;
+}
+
+    static int
+http_seek (UpnpWebFileHandle fh, off_t offset, int origin)
+{
+    struct web_file_t *file = (struct web_file_t *) fh;
+    off_t newpos = -1;
+
+    log_verbose ("http_seek\n");
+
+    if (!file)
+        return -1;
+
+    switch (origin)
+    {
+        case SEEK_SET:
+            log_verbose ("Attempting to seek to %lld (was at %lld) in %s\n",
+                    offset, file->pos, file->fullpath);
+            newpos = offset;
+            break;
+        case SEEK_CUR:
+            log_verbose ("Attempting to seek by %lld from %lld in %s\n",
+                    offset, file->pos, file->fullpath);
+            newpos = file->pos + offset;
+            break;
+        case SEEK_END:
+            log_verbose ("Attempting to seek by %lld from end (was at %lld) in %s\n",
+                    offset, file->pos, file->fullpath);
+
+            if (file->type == FILE_LOCAL)
+            {
+                struct stat sb;
+                if (stat (file->fullpath, &sb) < 0)
+                {
+                    log_verbose ("%s: cannot stat: %s\n",
+                            file->fullpath, strerror (errno));
+                    return -1;
+                }
+                newpos = sb.st_size + offset;
+            }
+            else if (file->type == FILE_MEMORY)
+                newpos = file->detail.memory.len + offset;
+            break;
+    }
+
+    switch (file->type)
+    {
+        case FILE_LOCAL:
+            /* Just make sure we cannot seek before start of file. */
+            if (newpos < 0)
+            {
+                log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
+                return -1;
+            }
+
+            /* Don't seek with origin as specified above, as file may have
+               changed in size since our last stat. */
+            if (lseek (file->detail.local.fd, newpos, SEEK_SET) == -1)
+            {
+                log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (errno));
+                return -1;
+            }
+            break;
+        case FILE_MEMORY:
+            if (newpos < 0 || newpos > file->detail.memory.len)
+            {
+                log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
+                return -1;
+            }
+            break;
+        case FILE_STREAM:
+            log_verbose ("%s: cannot seek: %s\n", file->fullpath, "STREAM");
+            newpos = file->pos;
+            break;
+    }
+
+    file->pos = newpos;
+
+    return 0;
+}
+
+    static int
+http_close (UpnpWebFileHandle fh)
+{
+    struct web_file_t *file = (struct web_file_t *) fh;
+    extern thread_data *gp_tdata;
+    thread_data *tdata = gp_tdata;
+    STREAM_QUEUE_T *p_queue;
+    int i;
+    int id = 0;
+    int j;
+
+    if (!file)
+        return -1;
+
+    switch (file->type)
+    {
+        case FILE_LOCAL:
+            close (file->detail.local.fd);
+            break;
+        case FILE_MEMORY:
+            /* no close operation */
+            if (file->detail.memory.contents)
+                free (file->detail.memory.contents);
+            break;
+        case FILE_STREAM:
+            p_queue = file->detail.stream.p_queue;
+            if ( p_queue != NULL) {
+                id = file->detail.stream.id;
+                pthread_mutex_lock(&tdata->streamer->mutex);
+                tdata->streamer->stream_session[id]->is_valid = false;
+                pthread_mutex_unlock(&tdata->streamer->mutex);
+                pthread_mutex_lock(&p_queue->mutex);
+                while ( 0 < p_queue->num_used ) {
+                    free(p_queue->buffer[p_queue->out]->data);
+                    p_queue->buffer[p_queue->out]->data = NULL;
+                    free(p_queue->buffer[p_queue->out]);
+                    p_queue->buffer[p_queue->out] = NULL;
+
+                    p_queue->out++;
+                    p_queue->out %= p_queue->size;
+                    p_queue->num_avail++;
+                    p_queue->num_used--;
+                }
+                pthread_mutex_unlock(&p_queue->mutex);
+                destroy_stream_queue(p_queue);
+                tdata->streamer->stream_session[id]->p_queue = NULL;
+            }
+            break;
+        default:
+            log_verbose ("Unknown file type.\n");
+            break;
+    }
+
+    if (file->fullpath)
+        free (file->fullpath);
+    free (file);
+
+    return 0;
+}
+
+struct UpnpVirtualDirCallbacks virtual_dir_callbacks =
+{
+    http_get_info,
+    http_open,
+    http_read,
+    http_write,
+    http_seek,
+    http_close
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/http.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,30 @@
+/*
+ * http.h : GeeXboX uShare Web Server handler header.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+struct UpnpVirtualDirCallbacks virtual_dir_callbacks;
+
+#endif /* _HTTP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadata.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,624 @@
+/* -*- tab-width: 4; indent-tabs-mode: nil -*- */
+/* vim: set ts=4 sts=4 sw=4 expandtab number : */
+/*
+ * metadata.c : GeeXboX uShare CDS Metadata DB.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "mime.h"
+#include "metadata.h"
+#include "util_iconv.h"
+#include "content.h"
+#include "gettext.h"
+#include "trace.h"
+
+#define TITLE_UNKNOWN "unknown"
+
+#define MAX_URL_SIZE 32
+
+struct upnp_entry_lookup_t {
+  int id;
+  struct upnp_entry_t *entry_ptr;
+};
+
+static char *
+getExtension (const char *filename)
+{
+  char *str = NULL;
+
+  str = strrchr (filename, '.');
+  if (str)
+    str++;
+
+  return str;
+}
+
+static struct mime_type_t *
+getMimeType (const char *extension)
+{
+  extern struct mime_type_t MIME_Type_List[];
+  struct mime_type_t *list;
+
+  if (!extension)
+    return NULL;
+
+  list = MIME_Type_List;
+  while (list->extension)
+  {
+    if (!strcasecmp (list->extension, extension))
+      return list;
+    list++;
+  }
+
+  return NULL;
+}
+
+static bool
+is_valid_extension (const char *extension)
+{
+  if (!extension)
+    return false;
+
+  if (getMimeType (extension))
+    return true;
+
+  return false;
+}
+
+static int
+get_list_length (void *list)
+{
+  void **l = list;
+  int n = 0;
+
+  while (*(l++))
+    n++;
+
+  return n;
+}
+
+static xml_convert_t xml_convert[] = {
+  {'"' , "&quot;"},
+  {'&' , "&amp;"},
+  {'\'', "&apos;"},
+  {'<' , "&lt;"},
+  {'>' , "&gt;"},
+  {'\n', "&#xA;"},
+  {'\r', "&#xD;"},
+  {'\t', "&#x9;"},
+  {0, NULL},
+};
+
+static char *
+get_xmlconvert (int c)
+{
+  int j;
+  for (j = 0; xml_convert[j].xml; j++)
+  {
+    if (c == xml_convert[j].charac)
+      return xml_convert[j].xml;
+  }
+  return NULL;
+}
+
+static char *
+convert_xml (const char *title)
+{
+  char *newtitle, *s, *t, *xml;
+  int nbconvert = 0;
+
+  /* calculate extra size needed */
+  for (t = (char*) title; *t; t++)
+  {
+    xml = get_xmlconvert (*t);
+    if (xml)
+      nbconvert += strlen (xml) - 1;
+  }
+  if (!nbconvert)
+    return NULL;
+
+  newtitle = s = (char*) malloc (strlen (title) + nbconvert + 1);
+
+  for (t = (char*) title; *t; t++)
+  {
+    xml = get_xmlconvert (*t);
+    if (xml)
+    {
+      strcpy (s, xml);
+      s += strlen (xml);
+    }
+    else
+      *s++ = *t;
+  }
+  *s = '\0';
+
+  return newtitle;
+}
+
+static struct mime_type_t Container_MIME_Type =
+  { NULL, "object.container.storageFolder", NULL};
+
+static struct upnp_entry_t *
+upnp_entry_new (struct ushare_t *ut, const char *name, const char *fullpath,
+                struct upnp_entry_t *parent, off_t size, int dir)
+{
+  struct upnp_entry_t *entry = NULL;
+  char *title = NULL, *x = NULL;
+  char url_tmp[MAX_URL_SIZE] = { '\0' };
+  char *title_or_name = NULL;
+
+  if (!name)
+    return NULL;
+
+  entry = (struct upnp_entry_t *) malloc (sizeof (struct upnp_entry_t));
+
+#ifdef HAVE_DLNA
+  // dlna_profile_t $B$rYTB$(B
+  entry->dlna_profile = NULL;
+  entry->url = NULL;
+  if (ut->dlna_enabled && fullpath && !dir)
+  {
+    dlna_profile_t *p = malloc(sizeof(dlna_profile_t));
+    p->id = "MPEG_TS_HD_60_L2_ISO;DLNA.ORG_OP=01;";
+    p->mime = "video/mpeg";
+    p->label = "label";
+    p->class = DLNA_CLASS_AV;
+#if 0
+    dlna_profile_t *p = dlna_guess_media_profile (ut->dlna, fullpath);
+    if (!p)
+    {
+      free (entry);
+      return NULL;
+    }
+#endif
+    entry->dlna_profile = p;
+  }
+#endif /* HAVE_DLNA */
+ 
+  if (ut->xbox360)
+  {
+    if (ut->root_entry)
+      entry->id = ut->starting_id + ut->nr_entries++;
+    else
+      entry->id = 0; /* Creating the root node so don't use the usual IDs */
+  }
+  else
+    entry->id = ut->starting_id + ut->nr_entries++;
+  log_verbose ("upnp_entry_new(), entry->id[%d]\n", entry->id);
+  
+  entry->fullpath = fullpath ? strdup (fullpath) : NULL;
+  entry->parent = parent;
+  entry->child_count =  dir ? 0 : -1;
+  entry->title = NULL;
+
+  entry->childs = (struct upnp_entry_t **)
+    malloc (sizeof (struct upnp_entry_t *));
+  *(entry->childs) = NULL;
+
+  if (!dir) /* item */
+    {
+#if 0
+#ifdef HAVE_DLNA
+      if (ut->dlna_enabled)
+        entry->mime_type = NULL;
+      else
+      {
+#endif /* HAVE_DLNA */
+#endif
+      struct mime_type_t *mime = getMimeType (getExtension (name));
+      if (!mime)
+      {
+        --ut->nr_entries; 
+        upnp_entry_free (ut, entry);
+        log_error ("Invalid Mime type for %s, entry ignored", name);
+        return NULL;
+      }
+      entry->mime_type = mime;
+#if 0
+#ifdef HAVE_DLNA
+      }
+#endif /* HAVE_DLNA */
+#endif
+      
+      if (snprintf (url_tmp, MAX_URL_SIZE, "%d.%s",
+                    entry->id, getExtension (name)) >= MAX_URL_SIZE)
+        log_error ("URL string too long for id %d, truncated!!", entry->id);
+
+      /* Only malloc() what we really need */
+      entry->url = strdup (url_tmp);
+    }
+  else /* container */
+    {
+      entry->mime_type = &Container_MIME_Type;
+      entry->url = NULL;
+    }
+
+  /* Try Iconv'ing the name but if it fails the end device
+     may still be able to handle it */
+  title = iconv_convert (name);
+  if (title)
+    title_or_name = title;
+  else
+  {
+    if (ut->override_iconv_err)
+    {
+      title_or_name = strdup (name);
+      log_error ("Entry invalid name id=%d [%s]\n", entry->id, name);
+    }
+    else
+    {
+      upnp_entry_free (ut, entry);
+      log_error ("Freeing entry invalid name id=%d [%s]\n", entry->id, name);
+      return NULL;
+    }
+  }
+
+  if (!dir)
+  {
+    x = strrchr (title_or_name, '.');
+    if (x)  /* avoid displaying file extension */
+      *x = '\0';
+  }
+  x = convert_xml (title_or_name);
+  if (x)
+  {
+    free (title_or_name);
+    title_or_name = x;
+  }
+  entry->title = title_or_name;
+
+  if (!strcmp (title_or_name, "")) /* DIDL dc:title can't be empty */
+  {
+    free (title_or_name);
+    entry->title = strdup (TITLE_UNKNOWN);
+  }
+
+  entry->size = size;
+  entry->fd = -1;
+
+  if (entry->id && entry->url)
+    log_verbose ("Entry->URL (%d): %s\n", entry->id, entry->url);
+
+  return entry;
+}
+
+/* Seperate recursive free() function in order to avoid freeing off
+ * the parents child list within the freeing of the first child, as
+ * the only entry which is not part of a childs list is the root entry
+ */
+static void
+_upnp_entry_free (struct upnp_entry_t *entry)
+{
+  struct upnp_entry_t **childs;
+
+  if (!entry)
+    return;
+
+  if (entry->fullpath)
+    free (entry->fullpath);
+  if (entry->title)
+    free (entry->title);
+  if (entry->url)
+    free (entry->url);
+#ifdef HAVE_DLNA
+  if (entry->dlna_profile)
+    entry->dlna_profile = NULL;
+#endif /* HAVE_DLNA */
+
+  for (childs = entry->childs; *childs; childs++)
+    _upnp_entry_free (*childs);
+  free (entry->childs);
+}
+
+void
+upnp_entry_free (struct ushare_t *ut, struct upnp_entry_t *entry)
+{
+  if (!ut || !entry)
+    return;
+
+  /* Free all entries (i.e. children) */
+  if (entry == ut->root_entry)
+  {
+    struct upnp_entry_t *entry_found = NULL;
+    struct upnp_entry_lookup_t *lk = NULL;
+    RBLIST *rblist;
+    int i = 0;
+
+    rblist = rbopenlist (ut->rb);
+    lk = (struct upnp_entry_lookup_t *) rbreadlist (rblist);
+
+    while (lk)
+    {
+      entry_found = lk->entry_ptr;
+      if (entry_found)
+      {
+ 	if (entry_found->fullpath)
+ 	  free (entry_found->fullpath);
+ 	if (entry_found->title)
+ 	  free (entry_found->title);
+ 	if (entry_found->url)
+ 	  free (entry_found->url);
+
+	free (entry_found);
+ 	i++;
+      }
+
+      free (lk); /* delete the lookup */
+      lk = (struct upnp_entry_lookup_t *) rbreadlist (rblist);
+    }
+
+    rbcloselist (rblist);
+    rbdestroy (ut->rb);
+    ut->rb = NULL;
+
+    log_verbose ("Freed [%d] entries\n", i);
+  }
+  else
+    _upnp_entry_free (entry);
+
+  free (entry);
+}
+
+static void
+upnp_entry_add_child (struct ushare_t *ut,
+                      struct upnp_entry_t *entry, struct upnp_entry_t *child)
+{
+  struct upnp_entry_lookup_t *entry_lookup_ptr = NULL;
+  struct upnp_entry_t **childs;
+  int n;
+
+  if (!entry || !child)
+    return;
+
+  for (childs = entry->childs; *childs; childs++)
+    if (*childs == child)
+      return;
+
+  n = get_list_length ((void *) entry->childs) + 1;
+  entry->childs = (struct upnp_entry_t **)
+    realloc (entry->childs, (n + 1) * sizeof (*(entry->childs)));
+  entry->childs[n] = NULL;
+  entry->childs[n - 1] = child;
+  entry->child_count++;
+
+  entry_lookup_ptr = (struct upnp_entry_lookup_t *)
+    malloc (sizeof (struct upnp_entry_lookup_t));
+  entry_lookup_ptr->id = child->id;
+  entry_lookup_ptr->entry_ptr = child;
+
+  if (rbsearch ((void *) entry_lookup_ptr, ut->rb) == NULL)
+    log_info (_("Failed to add the RB lookup tree\n"));
+}
+
+struct upnp_entry_t *
+upnp_get_entry (struct ushare_t *ut, int id)
+{
+  struct upnp_entry_lookup_t *res, entry_lookup;
+
+  log_verbose ("Looking for entry id %d\n", id);
+  if (id == 0) /* We do not store the root (id 0) as it is not a child */
+    return ut->root_entry;
+
+  entry_lookup.id = id;
+  res = (struct upnp_entry_lookup_t *)
+    rbfind ((void *) &entry_lookup, ut->rb);
+
+  if (res)
+  {
+    log_verbose ("Found at %p\n",
+                 ((struct upnp_entry_lookup_t *) res)->entry_ptr);
+    return ((struct upnp_entry_lookup_t *) res)->entry_ptr;
+  }
+
+  log_verbose ("Not Found\n");
+
+  return NULL;
+}
+
+static void
+metadata_add_file (struct ushare_t *ut, struct upnp_entry_t *entry,
+                   const char *file, const char *name, struct stat *st_ptr)
+{
+  if (!entry || !file || !name)
+    return;
+
+#ifdef HAVE_DLNA
+  if (ut->dlna_enabled || is_valid_extension (getExtension (file)))
+#else
+  if (is_valid_extension (getExtension (file)))
+#endif
+  {
+    struct upnp_entry_t *child = NULL;
+
+    child = upnp_entry_new (ut, name, file, entry, st_ptr->st_size, false);
+    if (child)
+      upnp_entry_add_child (ut, entry, child);
+  }
+}
+
+static void
+metadata_add_container (struct ushare_t *ut,
+                        struct upnp_entry_t *entry, const char *container)
+{
+  struct dirent **namelist;
+  int n,i;
+
+  if (!entry || !container)
+    return;
+
+  n = scandir (container, &namelist, 0, alphasort);
+  if (n < 0)
+  {
+    perror ("scandir");
+    return;
+  }
+
+  for (i = 0; i < n; i++)
+  {
+    struct stat st;
+    char *fullpath = NULL;
+
+    if (namelist[i]->d_name[0] == '.')
+    {
+      free (namelist[i]);
+      continue;
+    }
+
+    fullpath = (char *)
+      malloc (strlen (container) + strlen (namelist[i]->d_name) + 2);
+    sprintf (fullpath, "%s/%s", container, namelist[i]->d_name);
+
+    log_verbose ("%s\n", fullpath);
+
+    if (stat (fullpath, &st) < 0)
+    {
+      free (namelist[i]);
+      free (fullpath);
+      continue;
+    }
+
+    if (S_ISDIR (st.st_mode))
+    {
+      struct upnp_entry_t *child = NULL;
+
+      child = upnp_entry_new (ut, namelist[i]->d_name,
+                              fullpath, entry, 0, true);
+      if (child)
+      {
+        metadata_add_container (ut, child, fullpath);
+        upnp_entry_add_child (ut, entry, child);
+      }
+    }
+    else
+      metadata_add_file (ut, entry, fullpath, namelist[i]->d_name, &st);
+
+    free (namelist[i]);
+    free (fullpath);
+  }
+  free (namelist);
+}
+
+void
+free_metadata_list (struct ushare_t *ut)
+{
+  ut->init = 0;
+  if (ut->root_entry)
+    upnp_entry_free (ut, ut->root_entry);
+  ut->root_entry = NULL;
+  ut->nr_entries = 0;
+
+  if (ut->rb)
+  {
+    rbdestroy (ut->rb);
+    ut->rb = NULL;
+  }
+
+  ut->rb = rbinit (rb_compare, NULL);
+  if (!ut->rb)
+    log_error (_("Cannot create RB tree for lookups\n"));
+}
+
+void
+build_metadata_list (struct ushare_t *ut)
+{
+  int i;
+  struct stat st;
+  log_info (_("Building Metadata List ...\n"));
+
+  /* build root entry */
+  if (!ut->root_entry)
+    ut->root_entry = upnp_entry_new (ut, "root", NULL, NULL, -1, true);
+
+#if 0
+  entry = upnp_entry_new (ut, "stream.ts", "/web/stream.ts",
+                          ut->root_entry, -1, false);
+    upnp_entry_add_child (ut, ut->root_entry, entry);
+    metadata_add_container (ut, entry, "/web/");
+#endif
+  struct upnp_entry_t *entry = ut->root_entry;
+  st.st_size = 100*1024*1024;
+  metadata_add_file (ut, ut->root_entry, STREAM_LOCATION, STREAM_FILE_NAME, &st);
+  //metadata_add_container (ut, ut->root_entry, "/web/");
+
+#if 0
+  /* add files from content directory */
+  for (i=0 ; i < ut->contentlist->count ; i++)
+  {
+    struct upnp_entry_t *entry = NULL;
+    char *title = NULL;
+    int size = 0;
+
+    log_info (_("Looking for files in content directory : %s\n"),
+              ut->contentlist->content[i]);
+
+    size = strlen (ut->contentlist->content[i]);
+    if (ut->contentlist->content[i][size - 1] == '/')
+      ut->contentlist->content[i][size - 1] = '\0';
+    title = strrchr (ut->contentlist->content[i], '/');
+    if (title)
+      title++;
+    else
+    {
+      /* directly use content directory name if no '/' before basename */
+      title = ut->contentlist->content[i];
+    }
+
+    entry = upnp_entry_new (ut, title, ut->contentlist->content[i],
+                            ut->root_entry, -1, true);
+
+    if (!entry)
+      continue;
+    upnp_entry_add_child (ut, ut->root_entry, entry);
+    metadata_add_container (ut, entry, ut->contentlist->content[i]);
+  }
+#endif
+
+  log_info (_("Found %d files and subdirectories.\n"), ut->nr_entries);
+  ut->init = 1;
+}
+
+int
+rb_compare (const void *pa, const void *pb,
+            const void *config __attribute__ ((unused)))
+{
+  struct upnp_entry_lookup_t *a, *b;
+
+  a = (struct upnp_entry_lookup_t *) pa;
+  b = (struct upnp_entry_lookup_t *) pb;
+
+  if (a->id < b->id)
+    return -1;
+
+  if (a->id > b->id)
+    return 1;
+
+  return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadata.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,60 @@
+/*
+ * metadata.h : GeeXboX uShare CDS Metadata DB header.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _METADATA_H_
+#define _METADATA_H_
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "ushare.h"
+#include "http.h"
+#include "content.h"
+
+struct upnp_entry_t {
+  int id;
+  char *fullpath;
+#ifdef HAVE_DLNA
+  dlna_profile_t *dlna_profile;
+#endif /* HAVE_DLNA */
+  struct upnp_entry_t *parent;
+  int child_count;
+  struct upnp_entry_t **childs;
+  struct mime_type_t *mime_type;
+  char *title;
+  char *url;
+  off_t size;
+  int fd;
+};
+
+typedef struct xml_convert_s {
+  char charac;
+  char *xml;
+} xml_convert_t;
+
+void free_metadata_list (struct ushare_t *ut);
+void build_metadata_list (struct ushare_t *ut);
+struct upnp_entry_t *upnp_get_entry (struct ushare_t *ut, int id);
+void upnp_entry_free (struct ushare_t *ut, struct upnp_entry_t *entry);
+int rb_compare (const void *pa, const void *pb, const void *config);
+
+#endif /* _METADATA_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mime.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,150 @@
+/*
+ * mime.c : GeeXboX uShare media file MIME-type association.
+ * Originally developped for the GeeXboX project.
+ * Ref : http://freedesktop.org/wiki/Standards_2fshared_2dmime_2dinfo_2dspec
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mime.h"
+#include "ushare.h"
+
+#define UPNP_VIDEO "object.item.videoItem"
+#define UPNP_AUDIO "object.item.audioItem.musicTrack"
+#define UPNP_PHOTO "object.item.imageItem.photo"
+#define UPNP_PLAYLIST "object.item.playlistItem"
+#define UPNP_TEXT "object.item.textItem"
+
+const struct mime_type_t MIME_Type_List[] = {
+  /* Video files */
+  { "asf",   UPNP_VIDEO, "http-get:*:video/x-ms-asf:"},
+  { "avc",   UPNP_VIDEO, "http-get:*:video/avi:"},
+  { "avi",   UPNP_VIDEO, "http-get:*:video/avi:"},
+  { "dv",    UPNP_VIDEO, "http-get:*:video/x-dv:"},
+  { "divx",  UPNP_VIDEO, "http-get:*:video/avi:"},
+  { "wmv",   UPNP_VIDEO, "http-get:*:video/x-ms-wmv:"},
+  { "mjpg",  UPNP_VIDEO, "http-get:*:video/x-motion-jpeg:"},
+  { "mjpeg", UPNP_VIDEO, "http-get:*:video/x-motion-jpeg:"},
+  { "mpeg",  UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "mpg",   UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "mpe",   UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "mp2p",  UPNP_VIDEO, "http-get:*:video/mp2p:"},
+  { "vob",   UPNP_VIDEO, "http-get:*:video/mp2p:"},
+  { "mp2t",  UPNP_VIDEO, "http-get:*:video/mp2t:"},
+  { "m1v",   UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "m2v",   UPNP_VIDEO, "http-get:*:video/mpeg2:"},
+  { "mpg2",  UPNP_VIDEO, "http-get:*:video/mpeg2:"},
+  { "mpeg2", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
+  { "m4v",   UPNP_VIDEO, "http-get:*:video/mp4:"},
+  { "m4p",   UPNP_VIDEO, "http-get:*:video/mp4:"},
+  { "mp4ps", UPNP_VIDEO, "http-get:*:video/x-nerodigital-ps:"},
+  { "ts",    UPNP_VIDEO, "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_60_L2_ISO;"},
+  { "ogm",   UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "mkv",   UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "rmvb",  UPNP_VIDEO, "http-get:*:video/mpeg:"},
+  { "mov",   UPNP_VIDEO, "http-get:*:video/quicktime:"},
+  { "hdmov", UPNP_VIDEO, "http-get:*:video/quicktime:"},
+  { "qt",    UPNP_VIDEO, "http-get:*:video/quicktime:"},
+  { "bin",   UPNP_VIDEO, "http-get:*:video/mpeg2:"},
+  { "iso",   UPNP_VIDEO, "http-get:*:video/mpeg2:"},
+
+  /* Audio files */
+  { "3gp",  UPNP_AUDIO, "http-get:*:audio/3gpp:"},
+  { "aac",  UPNP_AUDIO, "http-get:*:audio/x-aac:"},
+  { "ac3",  UPNP_AUDIO, "http-get:*:audio/x-ac3:"},
+  { "aif",  UPNP_AUDIO, "http-get:*:audio/aiff:"},
+  { "aiff", UPNP_AUDIO, "http-get:*:audio/aiff:"},
+  { "at3p", UPNP_AUDIO, "http-get:*:audio/x-atrac3:"},
+  { "au",   UPNP_AUDIO, "http-get:*:audio/basic:"},
+  { "snd",  UPNP_AUDIO, "http-get:*:audio/basic:"},
+  { "dts",  UPNP_AUDIO, "http-get:*:audio/x-dts:"},
+  { "rmi",  UPNP_AUDIO, "http-get:*:audio/midi:"},
+  { "mid",  UPNP_AUDIO, "http-get:*:audio/midi:"},
+  { "mp1",  UPNP_AUDIO, "http-get:*:audio/mp1:"},
+  { "mp2",  UPNP_AUDIO, "http-get:*:audio/mp2:"},
+  { "mp3",  UPNP_AUDIO, "http-get:*:audio/mpeg:"},
+  { "mp4",  UPNP_AUDIO, "http-get:*:audio/mp4:"},
+  { "m4a",  UPNP_AUDIO, "http-get:*:audio/mp4:"},
+  { "ogg",  UPNP_AUDIO, "http-get:*:audio/x-ogg:"},
+  { "wav",  UPNP_AUDIO, "http-get:*:audio/wav:"},
+  { "pcm",  UPNP_AUDIO, "http-get:*:audio/l16:"},
+  { "lpcm", UPNP_AUDIO, "http-get:*:audio/l16:"},
+  { "l16",  UPNP_AUDIO, "http-get:*:audio/l16:"},
+  { "wma",  UPNP_AUDIO, "http-get:*:audio/x-ms-wma:"},
+  { "mka",  UPNP_AUDIO, "http-get:*:audio/mpeg:"},
+  { "ra",   UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
+  { "rm",   UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
+  { "ram",  UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
+  { "flac", UPNP_AUDIO, "http-get:*:audio/x-flac:"},
+
+  /* Images files */
+  { "bmp",  UPNP_PHOTO, "http-get:*:image/bmp:"},
+  { "ico",  UPNP_PHOTO, "http-get:*:image/x-icon:"},
+  { "gif",  UPNP_PHOTO, "http-get:*:image/gif:"},
+  { "jpeg", UPNP_PHOTO, "http-get:*:image/jpeg:"},
+  { "jpg",  UPNP_PHOTO, "http-get:*:image/jpeg:"},
+  { "jpe",  UPNP_PHOTO, "http-get:*:image/jpeg:"},
+  { "pcd",  UPNP_PHOTO, "http-get:*:image/x-ms-bmp:"},
+  { "png",  UPNP_PHOTO, "http-get:*:image/png:"},
+  { "pnm",  UPNP_PHOTO, "http-get:*:image/x-portable-anymap:"},
+  { "ppm",  UPNP_PHOTO, "http-get:*:image/x-portable-pixmap:"},
+  { "qti",  UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
+  { "qtf",  UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
+  { "qtif", UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
+  { "tif",  UPNP_PHOTO, "http-get:*:image/tiff:"},
+  { "tiff", UPNP_PHOTO, "http-get:*:image/tiff:"},
+
+  /* Playlist files */
+  { "pls", UPNP_PLAYLIST, "http-get:*:audio/x-scpls:"},
+  { "m3u", UPNP_PLAYLIST, "http-get:*:audio/mpegurl:"},
+  { "asx", UPNP_PLAYLIST, "http-get:*:video/x-ms-asf:"},
+
+  /* Subtitle Text files */
+  { "srt", UPNP_TEXT, "http-get:*:text/srt:"}, /* SubRip */
+  { "ssa", UPNP_TEXT, "http-get:*:text/ssa:"}, /* SubStation Alpha */
+  { "stl", UPNP_TEXT, "http-get:*:text/srt:"}, /* Spruce */
+  { "psb", UPNP_TEXT, "http-get:*:text/psb:"}, /* PowerDivX */
+  { "pjs", UPNP_TEXT, "http-get:*:text/pjs:"}, /* Phoenix Japanim */
+  { "sub", UPNP_TEXT, "http-get:*:text/sub:"}, /* MicroDVD */
+  { "idx", UPNP_TEXT, "http-get:*:text/idx:"}, /* VOBsub */
+  { "dks", UPNP_TEXT, "http-get:*:text/dks:"}, /* DKS */
+  { "scr", UPNP_TEXT, "http-get:*:text/scr:"}, /* MACsub */
+  { "tts", UPNP_TEXT, "http-get:*:text/tts:"}, /* TurboTitler */
+  { "vsf", UPNP_TEXT, "http-get:*:text/vsf:"}, /* ViPlay */
+  { "zeg", UPNP_TEXT, "http-get:*:text/zeg:"}, /* ZeroG */
+  { "mpl", UPNP_TEXT, "http-get:*:text/mpl:"}, /* MPL */
+
+  /* Miscellaneous text files */
+  { "bup", UPNP_TEXT, "http-get:*:text/bup:"}, /* DVD backup */
+  { "ifo", UPNP_TEXT, "http-get:*:text/ifo:"}, /* DVD information */
+
+  { NULL, NULL, NULL}
+};
+
+char *mime_get_protocol (struct mime_type_t *mime)
+{
+  char protocol[512];
+  
+  if (!mime)
+    return NULL;
+
+  sprintf (protocol, mime->mime_protocol);
+  //strcat (protocol, "*");
+  return strdup (protocol);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mime.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,32 @@
+/*
+ * mime.h : GeeXboX uShare media file MIME-type association header.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _MIME_H_
+#define _MIME_H_
+
+struct mime_type_t {
+  char *extension;
+  char *mime_class;
+  char *mime_protocol;
+};
+
+char *mime_get_protocol (struct mime_type_t *mime);
+
+#endif /* _MIME_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/minmax.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,32 @@
+/*  MIN, MAX macros.
+ *  Copyright (C) 1995, 1998, 2001, 2003, 2005 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _MINMAX_H_
+#define _MINMAX_H_
+
+#include <limits.h>
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#endif /* _MINMAX_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/msr.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,92 @@
+/*
+ * msr.c : GeeXboX uShare Microsoft Registrar Service.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2006 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "ushare.h"
+#include "services.h"
+
+/* Represent the MSR IsAuthorized action. */
+#define SERVICE_MSR_ACTION_IS_AUTHORIZED "IsAuthorized"
+
+/* Represent the MSR RegisterDevice action. */
+#define SERVICE_MSR_ACTION_REGISTER_DEVICE "RegisterDevice"
+
+/* Represent the MSR IsValidated action. */
+#define SERVICE_MSR_ACTION_IS_VALIDATED "IsValidated"
+
+/* Represent the MSR DeviceID argument. */
+#define SERVICE_MSR_ARG_DEVICE_ID "DeviceID"
+
+/* Represent the MSR Result argument. */
+#define SERVICE_MSR_ARG_RESULT "Result"
+
+/* Represent the MSR RegistrationReqMsg argument. */
+#define SERVICE_MSR_ARG_REGISTRATION_REQUEST_MSG "RegistrationReqMsg"
+
+/* Represent the MSR RegistrationRespMsg argument. */
+#define SERVICE_MSR_ARG_REGISTRATION_RESPONSE_MSG "RegistrationRespMsg"
+
+/* Represent the MSR Registered/Activated ID value. */
+#define SERVICE_MSR_STATUS_OK "1"
+
+static bool
+msr_is_authorized (struct action_event_t *event)
+{
+  if (!event)
+    return false;
+
+  /* send a fake authorization to these stupid MS players ;-) */
+  upnp_add_response (event, SERVICE_MSR_ARG_RESULT, SERVICE_MSR_STATUS_OK);
+
+  return event->status;
+}
+
+static bool
+msr_register_device (struct action_event_t *event)
+{
+  if (!event)
+    return false;
+
+  /* dummy action */
+
+  return event->status;
+}
+
+static bool
+msr_is_validated (struct action_event_t *event)
+{
+  if (!event)
+    return false;
+
+  /* send a fake validation to these stupid MS players ;-) */
+  upnp_add_response (event, SERVICE_MSR_ARG_RESULT, SERVICE_MSR_STATUS_OK);
+
+  return event->status;
+}
+
+/* List of UPnP Microsoft Registrar Service actions */
+struct service_action_t msr_service_actions[] = {
+  { SERVICE_MSR_ACTION_IS_AUTHORIZED, msr_is_authorized },
+  { SERVICE_MSR_ACTION_REGISTER_DEVICE, msr_register_device },
+  { SERVICE_MSR_ACTION_IS_VALIDATED, msr_is_validated },
+  { NULL, NULL }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/msr.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,121 @@
+/*
+ * msr.h : GeeXboX uShare Microsoft Registrar Service header.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2006 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MSR_H_
+#define MSR_H_
+
+#define MSR_DESCRIPTION \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
+"<specVersion>" \
+"  <major>1</major>" \
+"  <minor>0</minor>" \
+"</specVersion>" \
+"<actionList>" \
+"  <action>" \
+"    <name>IsAuthorized</name>" \
+"    <argumentList>" \
+"      <argument>" \
+"        <name>DeviceID</name>" \
+"        <direction>in</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>" \
+"      </argument>" \
+"      <argument>" \
+"        <name>Result</name>" \
+"        <direction>out</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
+"      </argument>" \
+"    </argumentList>" \
+"  </action>" \
+"  <action>" \
+"    <name>RegisterDevice</name>" \
+"    <argumentList>" \
+"      <argument>" \
+"        <name>RegistrationReqMsg</name>" \
+"        <direction>in</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_RegistrationReqMsg</relatedStateVariable>" \
+"      </argument>" \
+"      <argument>" \
+"        <name>RegistrationRespMsg</name>" \
+"        <direction>out</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_RegistrationRespMsg</relatedStateVariable>" \
+"      </argument>" \
+"    </argumentList>" \
+"  </action>" \
+"  <action>" \
+"    <name>IsValidated</name>" \
+"    <argumentList>" \
+"      <argument>" \
+"        <name>DeviceID</name>" \
+"        <direction>in</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>" \
+"      </argument>" \
+"      <argument>" \
+"        <name>Result</name>" \
+"        <direction>out</direction>" \
+"        <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
+"      </argument>" \
+"    </argumentList>" \
+"  </action>" \
+"</actionList>" \
+"<serviceStateTable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>A_ARG_TYPE_DeviceID</name>" \
+"    <dataType>string</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>A_ARG_TYPE_Result</name>" \
+"    <dataType>int</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>A_ARG_TYPE_RegistrationReqMsg</name>" \
+"    <dataType>bin.base64</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>A_ARG_TYPE_RegistrationRespMsg</name>" \
+"    <dataType>bin.base64</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>AuthorizationGrantedUpdateID</name>" \
+"    <dataType>ui4</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>AuthorizationDeniedUpdateID</name>" \
+"    <dataType>ui4</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>ValidationSucceededUpdateID</name>" \
+"    <dataType>ui4</dataType>" \
+"  </stateVariable>" \
+"  <stateVariable sendEvents=\"no\">" \
+"    <name>ValidationRevokedUpdateID</name>" \
+"    <dataType>ui4</dataType>" \
+"  </stateVariable>" \
+"</serviceStateTable>" \
+"</scpd>"
+
+#define MSR_DESCRIPTION_LEN strlen (MSR_DESCRIPTION)
+
+#define MSR_LOCATION "/web/msr.xml"
+
+#define MSR_SERVICE_ID "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"
+#define MSR_SERVICE_TYPE "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
+
+#endif /* MSR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/osdep.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,91 @@
+/*
+ * osdep.c : GeeXboX uShare OS independant helpers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "osdep.h"
+
+#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
+char *
+strndup (const char *s, size_t n)
+{
+  size_t len;
+  char *sdup = NULL;
+
+  if (!s)
+    return NULL;
+
+  len = strlen (s);
+  len = n < len ? n : len;
+  sdup = (char *) malloc (len + 1);
+
+  if (sdup)
+  {
+    memcpy (sdup, s, len);
+    sdup[len] = '\0';
+  }
+
+  return sdup;
+}
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+  static char line[256];
+  char *ptr;
+  ssize_t len;
+
+  if (!lineptr || !n)
+    return -1;
+
+  if (ferror (stream))
+    return -1;
+
+  if (feof (stream))
+    return -1;
+
+  fgets (line, 256, stream);
+  ptr = strchr (line, '\n');
+
+  if (ptr)
+    *ptr = '\0';
+
+  len = strlen (line);
+  if ((len + 1) < 256)
+  {
+    ptr = realloc (*lineptr, 256);
+    if (!ptr)
+      return -1;
+
+    *lineptr = ptr;
+    *n = 256;
+  }
+  strcpy (*lineptr, line);
+
+  return len;
+}
+#endif /* (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__)) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/osdep.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,30 @@
+/*
+ * osdep.h : GeeXboX uShare OS independant helpers headers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _OS_DEP_H_
+#define _OS_DEP_H_
+
+#if (defined(BSD) || defined(__FreeBSD__))
+#include <unistd.h>
+char *strndup (const char *s, size_t n);
+ssize_t getline (char **lineptr, size_t *n, FILE *stream);
+#endif
+
+#endif /* _OS_DEP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/presentation.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,215 @@
+/*
+ * presentation.c : GeeXboX uShare UPnP Presentation Page.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+
+#if HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+#endif
+
+#include "config.h"
+#include "metadata.h"
+#include "content.h"
+#include "buffer.h"
+#include "presentation.h"
+#include "gettext.h"
+#include "util_iconv.h"
+
+#define CGI_ACTION "action="
+#define CGI_ACTION_ADD "add"
+#define CGI_ACTION_DEL "del"
+#define CGI_ACTION_REFRESH "refresh"
+#define CGI_PATH "path"
+#define CGI_SHARE "share"
+
+int
+process_cgi (struct ushare_t *ut, char *cgiargs)
+{
+  char *action = NULL;
+  int refresh = 0;
+
+  if (!ut || !cgiargs)
+    return -1;
+
+  if (strncmp (cgiargs, CGI_ACTION, strlen (CGI_ACTION)))
+    return -1;
+
+  action = cgiargs + strlen (CGI_ACTION);
+
+  if (!strncmp (action, CGI_ACTION_ADD, strlen (CGI_ACTION_ADD)))
+  {
+    char *path = NULL;
+    path = action + strlen (CGI_ACTION_ADD) + 1;
+
+    if (path && !strncmp (path, CGI_PATH"=", strlen (CGI_PATH) + 1))
+    {
+      ut->contentlist = content_add (ut->contentlist,
+                                     path + strlen (CGI_PATH) + 1);
+      refresh = 1;
+    }
+  }
+  else if (!strncmp (action, CGI_ACTION_DEL, strlen (CGI_ACTION_DEL)))
+  {
+    char *shares,*share;
+    char *m_buffer = NULL, *buffer;
+    int num, shift=0;
+
+    shares = strdup (action + strlen (CGI_ACTION_DEL) + 1);
+    m_buffer = (char*) malloc (strlen (shares) * sizeof (char));
+    if (m_buffer)
+    {
+      buffer = m_buffer;
+      for (share = strtok_r (shares, "&", &buffer) ; share ;
+           share = strtok_r (NULL, "&", &buffer))
+      {
+        if (sscanf (share, CGI_SHARE"[%d]=on", &num) < 0)
+          continue;
+        ut->contentlist = content_del (ut->contentlist, num - shift++);
+      }
+      free (m_buffer);
+    }
+
+    refresh = 1;
+    free (shares);
+  }
+  else if (!strncmp (action, CGI_ACTION_REFRESH, strlen (CGI_ACTION_REFRESH)))
+    refresh = 1;
+
+  if (refresh && ut->contentlist)
+  {
+    free_metadata_list (ut);
+    build_metadata_list (ut);
+  }
+
+  if (ut->presentation)
+    buffer_free (ut->presentation);
+  ut->presentation = buffer_new ();
+
+  buffer_append (ut->presentation, "<html>");
+  buffer_append (ut->presentation, "<head>");
+  buffer_appendf (ut->presentation, "<title>%s</title>",
+                  _("uShare Information Page"));
+  buffer_append (ut->presentation,
+                 "<meta http-equiv=\"pragma\" content=\"no-cache\"/>");
+  buffer_append (ut->presentation,
+                 "<meta http-equiv=\"expires\" content=\"1970-01-01\"/>");
+  buffer_append (ut->presentation,
+                 "<meta http-equiv=\"refresh\" content=\"0; URL=/web/ushare.html\"/>");
+  buffer_append (ut->presentation, "</head>");
+  buffer_append (ut->presentation, "</html>");
+
+  return 0;
+}
+
+int
+build_presentation_page (struct ushare_t *ut)
+{
+  int i;
+  char *mycodeset = NULL;
+
+  if (!ut)
+    return -1;
+
+  if (ut->presentation)
+    buffer_free (ut->presentation);
+  ut->presentation = buffer_new ();
+
+#if HAVE_LANGINFO_CODESET
+  mycodeset = nl_langinfo (CODESET);
+#endif
+  if (!mycodeset)
+    mycodeset = UTF8;
+
+  buffer_append (ut->presentation, "<html>");
+  buffer_append (ut->presentation, "<head>");
+  buffer_appendf (ut->presentation, "<title>%s</title>",
+                 _("uShare Information Page"));
+  buffer_appendf (ut->presentation,
+                  "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>",
+                  mycodeset);
+  buffer_append (ut->presentation,
+                 "<meta http-equiv=\"pragma\" content=\"no-cache\"/>");
+  buffer_append (ut->presentation,
+                 "<meta http-equiv=\"expires\" content=\"1970-01-01\"/>");
+  buffer_append (ut->presentation, "</head>");
+  buffer_append (ut->presentation, "<body>");
+  buffer_append (ut->presentation, "<h1 align=\"center\">");
+  buffer_appendf (ut->presentation, "<tt>%s</tt><br/>",
+                  _("uShare UPnP A/V Media Server"));
+  buffer_append (ut->presentation, _("Information Page"));
+  buffer_append (ut->presentation, "</h1>");
+  buffer_append (ut->presentation, "<br/>");
+
+  buffer_append (ut->presentation, "<center>");
+  buffer_append (ut->presentation, "<tr width=\"500\">");
+  buffer_appendf (ut->presentation, "<b>%s :</b> %s<br/>",
+                  _("Version"), VERSION);
+  buffer_append (ut->presentation, "</tr>");
+  buffer_appendf (ut->presentation, "<b>%s :</b> %s<br/>",
+                  _("Device UDN"), ut->udn);
+  buffer_appendf (ut->presentation, "<b>%s :</b> %d<br/>",
+                  _("Number of shared files and directories"), ut->nr_entries);
+  buffer_append (ut->presentation, "</center><br/>");
+
+  buffer_appendf (ut->presentation,
+                  "<form method=\"get\" action=\"%s\">", USHARE_CGI);
+  buffer_appendf (ut->presentation,
+                  "<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
+                  CGI_ACTION_DEL);
+  for (i = 0 ; i < ut->contentlist->count ; i++)
+  {
+    buffer_appendf (ut->presentation, "<b>%s #%d :</b>", _("Share"), i + 1);
+    buffer_appendf (ut->presentation,
+                    "<input type=\"checkbox\" name=\""CGI_SHARE"[%d]\"/>", i);
+    buffer_appendf (ut->presentation, "%s<br/>", ut->contentlist->content[i]);
+  }
+  buffer_appendf (ut->presentation,
+                 "<input type=\"submit\" value=\"%s\"/>", _("unShare!"));
+  buffer_append (ut->presentation, "</form>");
+  buffer_append (ut->presentation, "<br/>");
+
+  buffer_appendf (ut->presentation,
+                  "<form method=\"get\" action=\"%s\">", USHARE_CGI);
+  buffer_append (ut->presentation, _("Add a new share :  "));
+  buffer_appendf (ut->presentation,
+                  "<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
+                  CGI_ACTION_ADD);
+  buffer_append (ut->presentation, "<input type=\"text\" name=\""CGI_PATH"\"/>");
+  buffer_appendf (ut->presentation,
+                  "<input type=\"submit\" value=\"%s\"/>", _("Share!"));
+  buffer_append (ut->presentation, "</form>");
+
+  buffer_append (ut->presentation, "<br/>");
+
+  buffer_appendf (ut->presentation,
+                  "<form method=\"get\" action=\"%s\">", USHARE_CGI);
+  buffer_appendf (ut->presentation,
+                  "<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
+                  CGI_ACTION_REFRESH);
+  buffer_appendf (ut->presentation, "<input type=\"submit\" value=\"%s\"/>",
+                  _("Refresh Shares ..."));
+  buffer_append (ut->presentation, "</form>");
+  buffer_append (ut->presentation, "</center>");
+
+  buffer_append (ut->presentation, "</body>");
+  buffer_append (ut->presentation, "</html>");
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/presentation.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,31 @@
+/*
+ * presentation.h : GeeXboX uShare UPnP Presentation Page headers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _PRESENTATION_H_
+#define _PRESENTATION_H_
+
+#define USHARE_PRESENTATION_PAGE "/web/ushare.html"
+#define PRESENTATION_PAGE_CONTENT_TYPE "text/html"
+#define USHARE_CGI "/web/ushare.cgi"
+
+int process_cgi (struct ushare_t *ut, char *cgiargs);
+int build_presentation_page (struct ushare_t *ut);
+
+#endif /* _PRESENTATION_H_ */
--- a/src/recpt1.c	Wed Sep 29 23:18:55 2010 +0900
+++ b/src/recpt1.c	Sun Oct 03 11:35:19 2010 +0900
@@ -204,6 +204,18 @@
     free(p_queue);
 }
 
+void
+destroy_stream_queue(STREAM_QUEUE_T *p_queue)
+{
+    if(!p_queue)
+        return;
+
+    pthread_mutex_destroy(&p_queue->mutex);
+    pthread_cond_destroy(&p_queue->cond_avail);
+    pthread_cond_destroy(&p_queue->cond_used);
+    free(p_queue);
+}
+
 /* enqueue data. this function will block if queue is full. */
 void
 enqueue(QUEUE_T *p_queue, BUFSZ *data)
--- a/src/recpt1.h	Wed Sep 29 23:18:55 2010 +0900
+++ b/src/recpt1.h	Sun Oct 03 11:35:19 2010 +0900
@@ -100,5 +100,6 @@
 QUEUE_T *create_queue(size_t size);
 BUFSZ * dequeue(QUEUE_T *p_queue);
 void destroy_queue(QUEUE_T *p_queue);
+void destroy_stream_queue(STREAM_QUEUE_T *p_queue);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redblack.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,1140 @@
+static char rcsid[]="$Id: redblack.c,v 1.9 2003/10/24 01:31:21 damo Exp $";
+
+/*
+   Redblack balanced tree algorithm
+   Copyright (C) Damian Ivereigh 2000
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version. See the file COPYING for details.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Implement the red/black tree structure. It is designed to emulate
+** the standard tsearch() stuff. i.e. the calling conventions are
+** exactly the same
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "redblack.h"
+
+#define assert(expr)
+
+/* Uncomment this if you would rather use a raw sbrk to get memory
+** (however the memory is never released again (only re-used). Can't
+** see any point in using this these days.
+*/
+/* #define USE_SBRK */
+
+enum nodecolour { BLACK, RED };
+
+struct RB_ENTRY(node)
+{
+	struct RB_ENTRY(node) *left;		/* Left down */
+	struct RB_ENTRY(node) *right;		/* Right down */
+	struct RB_ENTRY(node) *up;		/* Up */
+	enum nodecolour colour;		/* Node colour */
+#ifdef RB_INLINE
+	RB_ENTRY(data_t) key;		/* User's key (and data) */
+#define RB_GET(x,y)		&x->y
+#define RB_SET(x,y,v)		x->y = *(v)
+#else
+	const RB_ENTRY(data_t) *key;	/* Pointer to user's key (and data) */
+#define RB_GET(x,y)		x->y
+#define RB_SET(x,y,v)		x->y = v
+#endif /* RB_INLINE */
+};
+
+/* Dummy (sentinel) node, so that we can make X->left->up = X
+** We then use this instead of NULL to mean the top or bottom
+** end of the rb tree. It is a black node.
+**
+** Initialization of the last field in this initializer is left implicit
+** because it could be of any type.  We count on the compiler to zero it.
+*/
+struct RB_ENTRY(node) RB_ENTRY(_null)={&RB_ENTRY(_null), &RB_ENTRY(_null), &RB_ENTRY(_null), BLACK, &RB_ENTRY(_null)};
+#define RBNULL (&RB_ENTRY(_null))
+
+#if defined(USE_SBRK)
+
+static struct RB_ENTRY(node) *RB_ENTRY(_alloc)();
+static void RB_ENTRY(_free)(struct RB_ENTRY(node) *);
+
+#else
+
+static struct RB_ENTRY(node) *RB_ENTRY(_alloc)() {return (struct RB_ENTRY(node) *) malloc(sizeof(struct RB_ENTRY(node)));}
+static void RB_ENTRY(_free)(struct RB_ENTRY(node) *x) {free(x);}
+
+#endif
+
+/* These functions are always needed */
+static void RB_ENTRY(_left_rotate)(struct RB_ENTRY(node) **, struct RB_ENTRY(node) *);
+static void RB_ENTRY(_right_rotate)(struct RB_ENTRY(node) **, struct RB_ENTRY(node) *);
+static struct RB_ENTRY(node) *RB_ENTRY(_successor)(const struct RB_ENTRY(node) *);
+static struct RB_ENTRY(node) *RB_ENTRY(_predecessor)(const struct RB_ENTRY(node) *);
+static struct RB_ENTRY(node) *RB_ENTRY(_traverse)(int, const RB_ENTRY(data_t) * , struct RB_ENTRY(tree) *);
+
+/* These functions may not be needed */
+#ifndef no_lookup
+static struct RB_ENTRY(node) *RB_ENTRY(_lookup)(int, const RB_ENTRY(data_t) * , struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_destroy
+static void RB_ENTRY(_destroy)(struct RB_ENTRY(node) *);
+#endif
+
+#ifndef no_delete
+static void RB_ENTRY(_delete)(struct RB_ENTRY(node) **, struct RB_ENTRY(node) *);
+static void RB_ENTRY(_delete_fix)(struct RB_ENTRY(node) **, struct RB_ENTRY(node) *);
+#endif
+
+#ifndef no_walk
+static void RB_ENTRY(_walk)(const struct RB_ENTRY(node) *, void (*)(const RB_ENTRY(data_t) *, const VISIT, const int, void *), void *, int);
+#endif
+
+#ifndef no_readlist
+static RBLIST *RB_ENTRY(_openlist)(const struct RB_ENTRY(node) *);
+static const RB_ENTRY(data_t) * RB_ENTRY(_readlist)(RBLIST *);
+static void RB_ENTRY(_closelist)(RBLIST *);
+#endif
+
+/*
+** OK here we go, the balanced tree stuff. The algorithm is the
+** fairly standard red/black taken from "Introduction to Algorithms"
+** by Cormen, Leiserson & Rivest. Maybe one of these days I will
+** fully understand all this stuff.
+**
+** Basically a red/black balanced tree has the following properties:-
+** 1) Every node is either red or black (colour is RED or BLACK)
+** 2) A leaf (RBNULL pointer) is considered black
+** 3) If a node is red then its children are black
+** 4) Every path from a node to a leaf contains the same no
+**    of black nodes
+**
+** 3) & 4) above guarantee that the longest path (alternating
+** red and black nodes) is only twice as long as the shortest
+** path (all black nodes). Thus the tree remains fairly balanced.
+*/
+
+/*
+ * Initialise a tree. Identifies the comparison routine and any config
+ * data that must be sent to it when called.
+ * Returns a pointer to the top of the tree.
+ */
+#ifndef RB_CUSTOMIZE
+RB_STATIC struct RB_ENTRY(tree) *
+rbinit(int (*cmp)(const void *, const void *, const void *), const void *config)
+#else
+RB_STATIC struct RB_ENTRY(tree) *RB_ENTRY(init)(void)
+#endif /* RB_CUSTOMIZE */
+{
+	struct RB_ENTRY(tree) *retval;
+	char c;
+
+	c=rcsid[0]; /* This does nothing but shutup the -Wall */
+
+	if ((retval=(struct RB_ENTRY(tree) *) malloc(sizeof(struct RB_ENTRY(tree))))==NULL)
+		return(NULL);
+
+#ifndef RB_CUSTOMIZE
+	retval->rb_cmp=cmp;
+	retval->rb_config=config;
+#endif /* RB_CUSTOMIZE */
+	retval->rb_root=RBNULL;
+
+	return(retval);
+}
+
+#ifndef no_destroy
+RB_STATIC void
+RB_ENTRY(destroy)(struct RB_ENTRY(tree) *rbinfo)
+{
+	if (rbinfo==NULL)
+		return;
+
+	if (rbinfo->rb_root!=RBNULL)
+		RB_ENTRY(_destroy)(rbinfo->rb_root);
+
+	free(rbinfo);
+}
+#endif /* no_destroy */
+
+#ifndef no_search
+RB_STATIC const RB_ENTRY(data_t) *
+RB_ENTRY(search)(const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x;
+
+	if (rbinfo==NULL)
+		return(NULL);
+
+	x=RB_ENTRY(_traverse)(1, key, rbinfo);
+
+	return((x==RBNULL) ? NULL : RB_GET(x, key));
+}
+#endif /* no_search */
+
+#ifndef no_find
+RB_STATIC const RB_ENTRY(data_t) * 
+RB_ENTRY(find)(const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x;
+
+	if (rbinfo==NULL)
+		return(NULL);
+
+	/* If we have a NULL root (empty tree) then just return */
+	if (rbinfo->rb_root==RBNULL)
+		return(NULL);
+
+	x=RB_ENTRY(_traverse)(0, key, rbinfo);
+
+	return((x==RBNULL) ? NULL : RB_GET(x, key));
+}
+#endif /* no_find */
+
+#ifndef no_delete
+RB_STATIC const RB_ENTRY(data_t) * 
+RB_ENTRY(delete)(const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x;
+	const RB_ENTRY(data_t) * y;
+
+	if (rbinfo==NULL)
+		return(NULL);
+
+	x=RB_ENTRY(_traverse)(0, key, rbinfo);
+
+	if (x==RBNULL)
+	{
+		return(NULL);
+	}
+	else
+	{
+		y=RB_GET(x, key);
+		RB_ENTRY(_delete)(&rbinfo->rb_root, x);
+
+		return(y);
+	}
+}
+#endif /* no_delete */
+
+#ifndef no_walk
+RB_STATIC void
+RB_ENTRY(walk)(const struct RB_ENTRY(tree) *rbinfo, void (*action)(const RB_ENTRY(data_t) *, const VISIT, const int, void *), void *arg)
+{
+	if (rbinfo==NULL)
+		return;
+
+	RB_ENTRY(_walk)(rbinfo->rb_root, action, arg, 0);
+}
+#endif /* no_walk */
+
+#ifndef no_readlist
+RB_STATIC RBLIST *
+RB_ENTRY(openlist)(const struct RB_ENTRY(tree) *rbinfo)
+{
+	if (rbinfo==NULL)
+		return(NULL);
+
+	return(RB_ENTRY(_openlist)(rbinfo->rb_root));
+}
+
+RB_STATIC const RB_ENTRY(data_t) * 
+RB_ENTRY(readlist)(RBLIST *rblistp)
+{
+	if (rblistp==NULL)
+		return(NULL);
+
+	return(RB_ENTRY(_readlist)(rblistp));
+}
+
+RB_STATIC void
+RB_ENTRY(closelist)(RBLIST *rblistp)
+{
+	if (rblistp==NULL)
+		return;
+
+	RB_ENTRY(_closelist)(rblistp);
+}
+#endif /* no_readlist */
+
+#ifndef no_lookup
+RB_STATIC const RB_ENTRY(data_t) * 
+RB_ENTRY(lookup)(int mode, const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x;
+
+	/* If we have a NULL root (empty tree) then just return NULL */
+	if (rbinfo==NULL || rbinfo->rb_root==NULL)
+		return(NULL);
+
+	x=RB_ENTRY(_lookup)(mode, key, rbinfo);
+
+	return((x==RBNULL) ? NULL : RB_GET(x, key));
+}
+#endif /* no_lookup */
+
+/* --------------------------------------------------------------------- */
+
+/* Search for and if not found and insert is true, will add a new
+** node in. Returns a pointer to the new node, or the node found
+*/
+static struct RB_ENTRY(node) *
+RB_ENTRY(_traverse)(int insert, const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x,*y,*z;
+	int cmp;
+	int found=0;
+	int cmpmods();
+
+	y=RBNULL; /* points to the parent of x */
+	x=rbinfo->rb_root;
+
+	/* walk x down the tree */
+	while(x!=RBNULL && found==0)
+	{
+		y=x;
+		/* printf("key=%s, RB_GET(x, key)=%s\n", key, RB_GET(x, key)); */
+#ifndef RB_CUSTOMIZE
+		cmp=RB_CMP(key, RB_GET(x, key), rbinfo->rb_config);
+#else
+		cmp=RB_CMP(key, RB_GET(x, key));
+#endif /* RB_CUSTOMIZE */
+
+		if (cmp<0)
+			x=x->left;
+		else if (cmp>0)
+			x=x->right;
+		else
+			found=1;
+	}
+
+	if (found || !insert)
+		return(x);
+
+	if ((z=RB_ENTRY(_alloc)())==NULL)
+	{
+		/* Whoops, no memory */
+		return(RBNULL);
+	}
+
+	RB_SET(z, key, key);
+	z->up=y;
+	if (y==RBNULL)
+	{
+		rbinfo->rb_root=z;
+	}
+	else
+	{
+#ifndef RB_CUSTOMIZE
+		cmp=RB_CMP(RB_GET(z, key), RB_GET(y, key), rbinfo->rb_config);
+#else
+		cmp=RB_CMP(RB_GET(z, key), RB_GET(y, key));
+#endif /* RB_CUSTOMIZE */
+		if (cmp<0)
+			y->left=z;
+		else
+			y->right=z;
+	}
+
+	z->left=RBNULL;
+	z->right=RBNULL;
+
+	/* colour this new node red */
+	z->colour=RED;
+
+	/* Having added a red node, we must now walk back up the tree balancing
+	** it, by a series of rotations and changing of colours
+	*/
+	x=z;
+
+	/* While we are not at the top and our parent node is red
+	** N.B. Since the root node is garanteed black, then we
+	** are also going to stop if we are the child of the root
+	*/
+
+	while(x != rbinfo->rb_root && (x->up->colour == RED))
+	{
+		/* if our parent is on the left side of our grandparent */
+		if (x->up == x->up->up->left)
+		{
+			/* get the right side of our grandparent (uncle?) */
+			y=x->up->up->right;
+			if (y->colour == RED)
+			{
+				/* make our parent black */
+				x->up->colour = BLACK;
+				/* make our uncle black */
+				y->colour = BLACK;
+				/* make our grandparent red */
+				x->up->up->colour = RED;
+
+				/* now consider our grandparent */
+				x=x->up->up;
+			}
+			else
+			{
+				/* if we are on the right side of our parent */
+				if (x == x->up->right)
+				{
+					/* Move up to our parent */
+					x=x->up;
+					RB_ENTRY(_left_rotate)(&rbinfo->rb_root, x);
+				}
+
+				/* make our parent black */
+				x->up->colour = BLACK;
+				/* make our grandparent red */
+				x->up->up->colour = RED;
+				/* right rotate our grandparent */
+				RB_ENTRY(_right_rotate)(&rbinfo->rb_root, x->up->up);
+			}
+		}
+		else
+		{
+			/* everything here is the same as above, but
+			** exchanging left for right
+			*/
+
+			y=x->up->up->left;
+			if (y->colour == RED)
+			{
+				x->up->colour = BLACK;
+				y->colour = BLACK;
+				x->up->up->colour = RED;
+
+				x=x->up->up;
+			}
+			else
+			{
+				if (x == x->up->left)
+				{
+					x=x->up;
+					RB_ENTRY(_right_rotate)(&rbinfo->rb_root, x);
+				}
+
+				x->up->colour = BLACK;
+				x->up->up->colour = RED;
+				RB_ENTRY(_left_rotate)(&rbinfo->rb_root, x->up->up);
+			}
+		}
+	}
+
+	/* Set the root node black */
+	(rbinfo->rb_root)->colour = BLACK;
+
+	return(z);
+}
+
+#ifndef no_lookup
+/* Search for a key according to mode (see redblack.h)
+*/
+static struct RB_ENTRY(node) *
+RB_ENTRY(_lookup)(int mode, const RB_ENTRY(data_t) *key, struct RB_ENTRY(tree) *rbinfo)
+{
+	struct RB_ENTRY(node) *x,*y;
+	int cmp=0;
+	int found=0;
+
+	y=RBNULL; /* points to the parent of x */
+	x=rbinfo->rb_root;
+
+	if (mode==RB_LUFIRST)
+	{
+		/* Keep going left until we hit a NULL */
+		while(x!=RBNULL)
+		{
+			y=x;
+			x=x->left;
+		}
+
+		return(y);
+	}
+	else if (mode==RB_LULAST)
+	{
+		/* Keep going right until we hit a NULL */
+		while(x!=RBNULL)
+		{
+			y=x;
+			x=x->right;
+		}
+
+		return(y);
+	}
+
+	/* walk x down the tree */
+	while(x!=RBNULL && found==0)
+	{
+		y=x;
+		/* printf("key=%s, RB_GET(x, key)=%s\n", key, RB_GET(x, key)); */
+#ifndef RB_CUSTOMIZE
+		cmp=RB_CMP(key, RB_GET(x, key), rbinfo->rb_config);
+#else
+		cmp=RB_CMP(key, RB_GET(x, key));
+#endif /* RB_CUSTOMIZE */
+
+
+		if (cmp<0)
+			x=x->left;
+		else if (cmp>0)
+			x=x->right;
+		else
+			found=1;
+	}
+
+	if (found && (mode==RB_LUEQUAL || mode==RB_LUGTEQ || mode==RB_LULTEQ))
+		return(x);
+
+	if (!found && (mode==RB_LUEQUAL || mode==RB_LUNEXT || mode==RB_LUPREV))
+		return(RBNULL);
+
+	if (mode==RB_LUGTEQ || (!found && mode==RB_LUGREAT))
+	{
+		if (cmp>0)
+			return(RB_ENTRY(_successor)(y));
+		else
+			return(y);
+	}
+
+	if (mode==RB_LULTEQ || (!found && mode==RB_LULESS))
+	{
+		if (cmp<0)
+			return(RB_ENTRY(_predecessor)(y));
+		else
+			return(y);
+	}
+
+	if (mode==RB_LUNEXT || (found && mode==RB_LUGREAT))
+		return(RB_ENTRY(_successor)(x));
+
+	if (mode==RB_LUPREV || (found && mode==RB_LULESS))
+		return(RB_ENTRY(_predecessor)(x));
+
+	/* Shouldn't get here */
+	return(RBNULL);
+}
+#endif /* no_lookup */
+
+#ifndef no_destroy
+/*
+ * Destroy all the elements blow us in the tree
+ * only useful as part of a complete tree destroy.
+ */
+static void
+RB_ENTRY(_destroy)(struct RB_ENTRY(node) *x)
+{
+	if (x!=RBNULL)
+	{
+		if (x->left!=RBNULL)
+			RB_ENTRY(_destroy)(x->left);
+		if (x->right!=RBNULL)
+			RB_ENTRY(_destroy)(x->right);
+		RB_ENTRY(_free)(x);
+	}
+}
+#endif /* no_destroy */
+
+/*
+** Rotate our tree thus:-
+**
+**             X        rb_left_rotate(X)--->            Y
+**           /   \                                     /   \
+**          A     Y     <---rb_right_rotate(Y)        X     C
+**              /   \                               /   \
+**             B     C                             A     B
+**
+** N.B. This does not change the ordering.
+**
+** We assume that neither X or Y is NULL
+*/
+
+static void
+RB_ENTRY(_left_rotate)(struct RB_ENTRY(node) **rootp, struct RB_ENTRY(node) *x)
+{
+	struct RB_ENTRY(node) *y;
+
+	assert(x!=RBNULL);
+	assert(x->right!=RBNULL);
+
+	y=x->right; /* set Y */
+
+	/* Turn Y's left subtree into X's right subtree (move B)*/
+	x->right = y->left;
+
+	/* If B is not null, set it's parent to be X */
+	if (y->left != RBNULL)
+		y->left->up = x;
+
+	/* Set Y's parent to be what X's parent was */
+	y->up = x->up;
+
+	/* if X was the root */
+	if (x->up == RBNULL)
+	{
+		*rootp=y;
+	}
+	else
+	{
+		/* Set X's parent's left or right pointer to be Y */
+		if (x == x->up->left)
+		{
+			x->up->left=y;
+		}
+		else
+		{
+			x->up->right=y;
+		}
+	}
+
+	/* Put X on Y's left */
+	y->left=x;
+
+	/* Set X's parent to be Y */
+	x->up = y;
+}
+
+static void
+RB_ENTRY(_right_rotate)(struct RB_ENTRY(node) **rootp, struct RB_ENTRY(node) *y)
+{
+	struct RB_ENTRY(node) *x;
+
+	assert(y!=RBNULL);
+	assert(y->left!=RBNULL);
+
+	x=y->left; /* set X */
+
+	/* Turn X's right subtree into Y's left subtree (move B) */
+	y->left = x->right;
+
+	/* If B is not null, set it's parent to be Y */
+	if (x->right != RBNULL)
+		x->right->up = y;
+
+	/* Set X's parent to be what Y's parent was */
+	x->up = y->up;
+
+	/* if Y was the root */
+	if (y->up == RBNULL)
+	{
+		*rootp=x;
+	}
+	else
+	{
+		/* Set Y's parent's left or right pointer to be X */
+		if (y == y->up->left)
+		{
+			y->up->left=x;
+		}
+		else
+		{
+			y->up->right=x;
+		}
+	}
+
+	/* Put Y on X's right */
+	x->right=y;
+
+	/* Set Y's parent to be X */
+	y->up = x;
+}
+
+/* Return a pointer to the smallest key greater than x
+*/
+static struct RB_ENTRY(node) *
+RB_ENTRY(_successor)(const struct RB_ENTRY(node) *x)
+{
+	struct RB_ENTRY(node) *y;
+
+	if (x->right!=RBNULL)
+	{
+		/* If right is not NULL then go right one and
+		** then keep going left until we find a node with
+		** no left pointer.
+		*/
+		for (y=x->right; y->left!=RBNULL; y=y->left);
+	}
+	else
+	{
+		/* Go up the tree until we get to a node that is on the
+		** left of its parent (or the root) and then return the
+		** parent.
+		*/
+		y=x->up;
+		while(y!=RBNULL && x==y->right)
+		{
+			x=y;
+			y=y->up;
+		}
+	}
+	return(y);
+}
+
+/* Return a pointer to the largest key smaller than x
+*/
+static struct RB_ENTRY(node) *
+RB_ENTRY(_predecessor)(const struct RB_ENTRY(node) *x)
+{
+	struct RB_ENTRY(node) *y;
+
+	if (x->left!=RBNULL)
+	{
+		/* If left is not NULL then go left one and
+		** then keep going right until we find a node with
+		** no right pointer.
+		*/
+		for (y=x->left; y->right!=RBNULL; y=y->right);
+	}
+	else
+	{
+		/* Go up the tree until we get to a node that is on the
+		** right of its parent (or the root) and then return the
+		** parent.
+		*/
+		y=x->up;
+		while(y!=RBNULL && x==y->left)
+		{
+			x=y;
+			y=y->up;
+		}
+	}
+	return(y);
+}
+
+#ifndef no_delete
+/* Delete the node z, and free up the space
+*/
+static void
+RB_ENTRY(_delete)(struct RB_ENTRY(node) **rootp, struct RB_ENTRY(node) *z)
+{
+	struct RB_ENTRY(node) *x, *y;
+
+	if (z->left == RBNULL || z->right == RBNULL)
+		y=z;
+	else
+		y=RB_ENTRY(_successor)(z);
+
+	if (y->left != RBNULL)
+		x=y->left;
+	else
+		x=y->right;
+
+	x->up = y->up;
+
+	if (y->up == RBNULL)
+	{
+		*rootp=x;
+	}
+	else
+	{
+		if (y==y->up->left)
+			y->up->left = x;
+		else
+			y->up->right = x;
+	}
+
+	if (y!=z)
+	{
+		RB_SET(z, key, RB_GET(y, key));
+	}
+
+	if (y->colour == BLACK)
+		RB_ENTRY(_delete_fix)(rootp, x);
+
+	RB_ENTRY(_free)(y);
+}
+
+/* Restore the reb-black properties after a delete */
+static void
+RB_ENTRY(_delete_fix)(struct RB_ENTRY(node) **rootp, struct RB_ENTRY(node) *x)
+{
+	struct RB_ENTRY(node) *w;
+
+	while (x!=*rootp && x->colour==BLACK)
+	{
+		if (x==x->up->left)
+		{
+			w=x->up->right;
+			if (w->colour==RED)
+			{
+				w->colour=BLACK;
+				x->up->colour=RED;
+				rb_left_rotate(rootp, x->up);
+				w=x->up->right;
+			}
+
+			if (w->left->colour==BLACK && w->right->colour==BLACK)
+			{
+				w->colour=RED;
+				x=x->up;
+			}
+			else
+			{
+				if (w->right->colour == BLACK)
+				{
+					w->left->colour=BLACK;
+					w->colour=RED;
+					RB_ENTRY(_right_rotate)(rootp, w);
+					w=x->up->right;
+				}
+
+
+				w->colour=x->up->colour;
+				x->up->colour = BLACK;
+				w->right->colour = BLACK;
+				RB_ENTRY(_left_rotate)(rootp, x->up);
+				x=*rootp;
+			}
+		}
+		else
+		{
+			w=x->up->left;
+			if (w->colour==RED)
+			{
+				w->colour=BLACK;
+				x->up->colour=RED;
+				RB_ENTRY(_right_rotate)(rootp, x->up);
+				w=x->up->left;
+			}
+
+			if (w->right->colour==BLACK && w->left->colour==BLACK)
+			{
+				w->colour=RED;
+				x=x->up;
+			}
+			else
+			{
+				if (w->left->colour == BLACK)
+				{
+					w->right->colour=BLACK;
+					w->colour=RED;
+					RB_ENTRY(_left_rotate)(rootp, w);
+					w=x->up->left;
+				}
+
+				w->colour=x->up->colour;
+				x->up->colour = BLACK;
+				w->left->colour = BLACK;
+				RB_ENTRY(_right_rotate)(rootp, x->up);
+				x=*rootp;
+			}
+		}
+	}
+
+	x->colour=BLACK;
+}
+#endif /* no_delete */
+
+#ifndef no_walk
+static void
+RB_ENTRY(_walk)(const struct RB_ENTRY(node) *x, void (*action)(const RB_ENTRY(data_t) *, const VISIT, const int, void *), void *arg, int level)
+{
+	if (x==RBNULL)
+		return;
+
+	if (x->left==RBNULL && x->right==RBNULL)
+	{
+		/* leaf */
+		(*action)(RB_GET(x, key), leaf, level, arg);
+	}
+	else
+	{
+		(*action)(RB_GET(x, key), preorder, level, arg);
+
+		RB_ENTRY(_walk)(x->left, action, arg, level+1);
+
+		(*action)(RB_GET(x, key), postorder, level, arg);
+
+		RB_ENTRY(_walk)(x->right, action, arg, level+1);
+
+		(*action)(RB_GET(x, key), endorder, level, arg);
+	}
+}
+#endif /* no_walk */
+
+#ifndef no_readlist
+static RBLIST *
+RB_ENTRY(_openlist)(const struct RB_ENTRY(node) *rootp)
+{
+	RBLIST *rblistp;
+
+	rblistp=(RBLIST *) malloc(sizeof(RBLIST));
+	if (!rblistp)
+		return(NULL);
+
+	rblistp->rootp=rootp;
+	rblistp->nextp=rootp;
+
+	if (rootp!=RBNULL)
+	{
+		while(rblistp->nextp->left!=RBNULL)
+		{
+			rblistp->nextp=rblistp->nextp->left;
+		}
+	}
+
+	return(rblistp);
+}
+
+static const RB_ENTRY(data_t) * 
+RB_ENTRY(_readlist)(RBLIST *rblistp)
+{
+	const RB_ENTRY(data_t) *key=NULL;
+
+	if (rblistp!=NULL && rblistp->nextp!=RBNULL)
+	{
+		key=RB_GET(rblistp->nextp, key);
+		rblistp->nextp=RB_ENTRY(_successor)(rblistp->nextp);
+	}
+
+	return(key);
+}
+
+static void
+rb_closelist(RBLIST *rblistp)
+{
+	if (rblistp)
+		free(rblistp);
+}
+#endif /* no_readlist */
+
+#if defined(RB_USE_SBRK)
+/* Allocate space for our nodes, allowing us to get space from
+** sbrk in larger chucks.
+*/
+static struct RB_ENTRY(node) *rbfreep=NULL;
+
+#define RB_ENTRY(NODE)ALLOC_CHUNK_SIZE 1000
+static struct RB_ENTRY(node) *
+RB_ENTRY(_alloc)()
+{
+	struct RB_ENTRY(node) *x;
+	int i;
+
+	if (rbfreep==NULL)
+	{
+		/* must grab some more space */
+		rbfreep=(struct RB_ENTRY(node) *) sbrk(sizeof(struct RB_ENTRY(node)) * RB_ENTRY(NODE)ALLOC_CHUNK_SIZE);
+
+		if (rbfreep==(struct RB_ENTRY(node) *) -1)
+		{
+			return(NULL);
+		}
+
+		/* tie them together in a linked list (use the up pointer) */
+		for (i=0, x=rbfreep; i<RB_ENTRY(NODE)ALLOC_CHUNK_SIZE-1; i++, x++)
+		{
+			x->up = (x+1);
+		}
+		x->up=NULL;
+	}
+
+	x=rbfreep;
+	rbfreep = rbfreep->up;
+#ifdef RB_ALLOC
+ 	RB_ALLOC(ACCESS(x, key));
+#endif /* RB_ALLOC */
+	return(x);
+}
+
+/* free (dealloc) an RB_ENTRY(node) structure - add it onto the front of the list
+** N.B. RB_ENTRY(node) need not have been allocated through rb_alloc()
+*/
+static void
+RB_ENTRY(_free)(struct RB_ENTRY(node) *x)
+{
+#ifdef RB_FREE
+ 	RB_FREE(ACCESS(x, key));
+#endif /* RB_FREE */
+	x->up=rbfreep;
+	rbfreep=x;
+}
+
+#endif
+
+#if 0
+int
+RB_ENTRY(_check)(struct RB_ENTRY(node) *rootp)
+{
+	if (rootp==NULL || rootp==RBNULL)
+		return(0);
+
+	if (rootp->up!=RBNULL)
+	{
+		fprintf(stderr, "Root up pointer not RBNULL");
+		dumptree(rootp, 0);
+		return(1);
+	}
+
+	if (RB_ENTRY(_check)1(rootp))
+	{
+		RB_ENTRY(dumptree)(rootp, 0);
+		return(1);
+	}
+
+	if (RB_ENTRY(count_black)(rootp)==-1)
+	{
+		RB_ENTRY(dumptree)(rootp, 0);
+		return(-1);
+	}
+
+	return(0);
+}
+
+int
+RB_ENTRY(_check1)(struct RB_ENTRY(node) *x)
+{
+	if (x->left==NULL || x->right==NULL)
+	{
+		fprintf(stderr, "Left or right is NULL");
+		return(1);
+	}
+
+	if (x->colour==RED)
+	{
+		if (x->left->colour!=BLACK && x->right->colour!=BLACK)
+		{
+			fprintf(stderr, "Children of red node not both black, x=%ld", x);
+			return(1);
+		}
+	}
+
+	if (x->left != RBNULL)
+	{
+		if (x->left->up != x)
+		{
+			fprintf(stderr, "x->left->up != x, x=%ld", x);
+			return(1);
+		}
+
+		if (rb_check1(x->left))
+			return(1);
+	}		
+
+	if (x->right != RBNULL)
+	{
+		if (x->right->up != x)
+		{
+			fprintf(stderr, "x->right->up != x, x=%ld", x);
+			return(1);
+		}
+
+		if (rb_check1(x->right))
+			return(1);
+	}		
+	return(0);
+}
+
+RB_ENTRY(count_black)(struct RB_ENTRY(node) *x)
+{
+	int nleft, nright;
+
+	if (x==RBNULL)
+		return(1);
+
+	nleft=RB_ENTRY(count_black)(x->left);
+	nright=RB_ENTRY(count_black)(x->right);
+
+	if (nleft==-1 || nright==-1)
+		return(-1);
+
+	if (nleft != nright)
+	{
+		fprintf(stderr, "Black count not equal on left & right, x=%ld", x);
+		return(-1);
+	}
+
+	if (x->colour == BLACK)
+	{
+		nleft++;
+	}
+
+	return(nleft);
+}
+
+RB_ENTRY(dumptree)(struct RB_ENTRY(node) *x, int n)
+{
+	char *prkey();
+
+	if (x!=NULL && x!=RBNULL)
+	{
+		n++;
+		fprintf(stderr, "Tree: %*s %ld: left=%ld, right=%ld, colour=%s, key=%s",
+			n,
+			"",
+			x,
+			x->left,
+			x->right,
+			(x->colour==BLACK) ? "BLACK" : "RED",
+			prkey(RB_GET(x, key)));
+
+		RB_ENTRY(dumptree)(x->left, n);
+		RB_ENTRY(dumptree)(x->right, n);
+	}	
+}
+#endif
+
+/*
+ * $Log: redblack.c,v $
+ * Revision 1.9  2003/10/24 01:31:21  damo
+ * Patches from Eric Raymond: %prefix is implemented.  Various other small
+ * changes avoid stepping on global namespaces and improve the documentation.
+ *
+ * Revision 1.8  2002/08/26 05:33:47  damo
+ * Some minor fixes:-
+ * Stopped ./configure warning about stuff being in the wrong order
+ * Fixed compiler warning about const (not sure about this)
+ * Changed directory of redblack.c in documentation
+ *
+ * Revision 1.7  2002/08/26 03:11:40  damo
+ * Fixed up a bunch of compiler warnings when compiling example4
+ *
+ * Tidies up the Makefile.am & Specfile.
+ *
+ * Renamed redblack to rbgen
+ *
+ * Revision 1.6  2002/08/26 01:03:35  damo
+ * Patch from Eric Raymond to change the way the library is used:-
+ *
+ * Eric's idea is to convert libredblack into a piece of in-line code
+ * generated by another program. This should be faster, smaller and easier
+ * to use.
+ *
+ * This is the first check-in of his code before I start futzing with it!
+ *
+ * Revision 1.5  2002/01/30 07:54:53  damo
+ * Fixed up the libtool versioning stuff (finally)
+ * Fixed bug 500600 (not detecting a NULL return from malloc)
+ * Fixed bug 509485 (no longer needs search.h)
+ * Cleaned up debugging section
+ * Allow multiple inclusions of redblack.h
+ * Thanks to Matthias Andree for reporting (and fixing) these
+ *
+ * Revision 1.4  2000/06/06 14:43:43  damo
+ * Added all the rbwalk & rbopenlist stuff. Fixed up malloc instead of sbrk.
+ * Added two new examples
+ *
+ * Revision 1.3  2000/05/24 06:45:27  damo
+ * Converted everything over to using const
+ * Added a new example1.c file to demonstrate the worst case scenario
+ * Minor fixups of the spec file
+ *
+ * Revision 1.2  2000/05/24 06:17:10  damo
+ * Fixed up the License (now the LGPL)
+ *
+ * Revision 1.1  2000/05/24 04:15:53  damo
+ * Initial import of files. Versions are now all over the place. Oh well
+ *
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redblack.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,188 @@
+/*
+ * RCS $Id: redblack.h,v 1.9 2003/10/24 01:31:21 damo Exp $
+ */
+
+/*
+   Redblack balanced tree algorithm
+   Copyright (C) Damian Ivereigh 2000
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version. See the file COPYING for details.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Header file for redblack.c, should be included by any code that 
+** uses redblack.c since it defines the functions 
+*/ 
+
+/* Stop multiple includes */
+#ifndef _REDBLACK_H
+
+#ifndef RB_CUSTOMIZE
+/*
+ * Without customization, the data member in the tree nodes is a void
+ * pointer, and you need to pass in a comparison function to be
+ * applied at runtime.  With customization, you specify the data type
+ * as the macro RB_ENTRY(data_t) (has to be a macro because compilers
+ * gag on typdef void) and the name of the compare function as the
+ * value of the macro RB_CMP. Because the comparison function is
+ * compiled in, RB_CMP only needs to take two arguments.  If your
+ * content type is not a pointer, define INLINE to get direct access.
+ */
+#define rbdata_t	void
+#define RB_CMP(s, t, e)	(*rbinfo->rb_cmp)(s, t, e)
+#undef RB_INLINE
+#define RB_ENTRY(name)	rb##name
+#endif /* RB_CUSTOMIZE */
+
+#ifndef RB_STATIC
+#define RB_STATIC
+#endif
+
+/* Modes for rblookup */
+#define RB_NONE -1	    /* None of those below */
+#define RB_LUEQUAL 0	/* Only exact match */
+#define RB_LUGTEQ 1		/* Exact match or greater */
+#define RB_LULTEQ 2		/* Exact match or less */
+#define RB_LULESS 3		/* Less than key (not equal to) */
+#define RB_LUGREAT 4	/* Greater than key (not equal to) */
+#define RB_LUNEXT 5		/* Next key after current */
+#define RB_LUPREV 6		/* Prev key before current */
+#define RB_LUFIRST 7	/* First key in index */
+#define RB_LULAST 8		/* Last key in index */
+
+/* For rbwalk - pinched from search.h */
+typedef enum
+{
+  preorder,
+  postorder,
+  endorder,
+  leaf
+}
+VISIT;
+
+struct RB_ENTRY(lists) { 
+const struct RB_ENTRY(node) *rootp; 
+const struct RB_ENTRY(node) *nextp; 
+}; 
+
+#define RBLIST struct RB_ENTRY(lists) 
+
+struct RB_ENTRY(tree) {
+#ifndef RB_CUSTOMIZE
+		/* comparison routine */
+int (*rb_cmp)(const void *, const void *, const void *);
+		/* config data to be passed to rb_cmp */
+const void *rb_config;
+		/* root of tree */
+#endif /* RB_CUSTOMIZE */
+struct RB_ENTRY(node) *rb_root;
+};
+
+#ifndef RB_CUSTOMIZE
+RB_STATIC struct RB_ENTRY(tree) *rbinit(int (*)(const void *, const void *, const void *),
+		 const void *);
+#else
+RB_STATIC struct RB_ENTRY(tree) *RB_ENTRY(init)(void);
+#endif /* RB_CUSTOMIZE */
+
+#ifndef no_delete
+RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(delete)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_find
+RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(find)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_lookup
+RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(lookup)(int, const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_search
+RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(search)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_destroy
+RB_STATIC void RB_ENTRY(destroy)(struct RB_ENTRY(tree) *);
+#endif
+
+#ifndef no_walk
+RB_STATIC void RB_ENTRY(walk)(const struct RB_ENTRY(tree) *,
+		void (*)(const RB_ENTRY(data_t) *, const VISIT, const int, void *),
+		void *); 
+#endif
+
+#ifndef no_readlist
+RB_STATIC RBLIST *RB_ENTRY(openlist)(const struct RB_ENTRY(tree) *); 
+RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(readlist)(RBLIST *); 
+RB_STATIC void RB_ENTRY(closelist)(RBLIST *); 
+#endif
+
+/* Some useful macros */
+#define rbmin(rbinfo) RB_ENTRY(lookup)(RB_LUFIRST, NULL, (rbinfo))
+#define rbmax(rbinfo) RB_ENTRY(lookup)(RB_LULAST, NULL, (rbinfo))
+
+#define _REDBLACK_H
+#endif /* _REDBLACK_H */
+
+/*
+ *
+ * $Log: redblack.h,v $
+ * Revision 1.9  2003/10/24 01:31:21  damo
+ * Patches from Eric Raymond: %prefix is implemented.  Various other small
+ * changes avoid stepping on global namespaces and improve the documentation.
+ *
+ * Revision 1.8  2003/10/23 04:18:47  damo
+ * Fixed up the rbgen stuff ready for the 1.3 release
+ *
+ * Revision 1.7  2002/08/26 03:11:40  damo
+ * Fixed up a bunch of compiler warnings when compiling example4
+ *
+ * Tidies up the Makefile.am & Specfile.
+ *
+ * Renamed redblack to rbgen
+ *
+ * Revision 1.6  2002/08/26 01:03:35  damo
+ * Patch from Eric Raymond to change the way the library is used:-
+ *
+ * Eric's idea is to convert libredblack into a piece of in-line code
+ * generated by another program. This should be faster, smaller and easier
+ * to use.
+ *
+ * This is the first check-in of his code before I start futzing with it!
+ *
+ * Revision 1.5  2002/01/30 07:54:53  damo
+ * Fixed up the libtool versioning stuff (finally)
+ * Fixed bug 500600 (not detecting a NULL return from malloc)
+ * Fixed bug 509485 (no longer needs search.h)
+ * Cleaned up debugging section
+ * Allow multiple inclusions of redblack.h
+ * Thanks to Matthias Andree for reporting (and fixing) these
+ *
+ * Revision 1.4  2000/06/06 14:43:43  damo
+ * Added all the rbwalk & rbopenlist stuff. Fixed up malloc instead of sbrk.
+ * Added two new examples
+ *
+ * Revision 1.3  2000/05/24 06:45:27  damo
+ * Converted everything over to using const
+ * Added a new example1.c file to demonstrate the worst case scenario
+ * Minor fixups of the spec file
+ *
+ * Revision 1.2  2000/05/24 06:17:10  damo
+ * Fixed up the License (now the LGPL)
+ *
+ * Revision 1.1  2000/05/24 04:15:53  damo
+ * Initial import of files. Versions are now all over the place. Oh well
+ *
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/services.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,177 @@
+/*
+ * services.c : GeeXboX uShare UPnP services handler.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#include "ushare.h"
+#include "services.h"
+#include "cms.h"
+#include "cds.h"
+#include "msr.h"
+#include "trace.h"
+
+/* Represent the ObjectID argument. */
+#define ARG_OBJECT_ID "ObjectID"
+
+/* Represent the ContainerID argument. */
+#define ARG_CONTAINER_ID "ContainerID"
+
+extern struct service_action_t cds_service_actions[];
+extern struct service_action_t cms_service_actions[];
+extern struct service_action_t msr_service_actions[];
+
+static struct service_t services[] = {
+  {
+    CDS_SERVICE_ID,
+    CDS_SERVICE_TYPE,
+    cds_service_actions
+  },
+  {
+    CMS_SERVICE_ID,
+    CMS_SERVICE_TYPE,
+    cms_service_actions
+  },
+  {
+    MSR_SERVICE_ID,
+    MSR_SERVICE_TYPE,
+    msr_service_actions
+  },
+  { NULL, NULL, NULL }
+};
+
+bool
+find_service_action (struct Upnp_Action_Request *request,
+                     struct service_t **service,
+                     struct service_action_t **action)
+{
+  int c, d;
+
+  *service = NULL;
+  *action = NULL;
+
+  if (!request || !request->ActionName)
+    return false;
+
+  for (c = 0; services[c].id != NULL; c++)
+    if (!strcmp (services[c].id, request->ServiceID))
+    {
+      *service = &services[c];
+      for (d = 0; services[c].actions[d].name; d++)
+      {
+        if (!strcmp (services[c].actions[d].name, request->ActionName))
+        {
+          *action = &services[c].actions[d];
+          return true;
+        }
+      }
+      return false;
+    }
+
+  return false;
+}
+
+bool
+upnp_add_response (struct action_event_t *event, char *key, const char *value)
+{
+  char *val;
+  int res;
+
+  if (!event || !event->status || !key || !value)
+    return false;
+
+  val = strdup (value);
+  if (!val)
+    return false;
+
+  res = UpnpAddToActionResponse (&event->request->ActionResult,
+                                 event->request->ActionName,
+                                 event->service->type, key, val);
+
+  if (res != UPNP_E_SUCCESS)
+    {
+      free (val);
+      return false;
+    }
+
+  free (val);
+  return true;
+}
+
+char *
+upnp_get_string (struct Upnp_Action_Request *request, const char *key)
+{
+  IXML_Node *node = NULL;
+
+  if (!request || !request->ActionRequest || !key)
+    return NULL;
+
+  node = (IXML_Node *) request->ActionRequest;
+  if (!node)
+  {
+    log_verbose ("Invalid action request document\n");
+    return NULL;
+  }
+
+  node = ixmlNode_getFirstChild (node);
+  if (!node)
+  {
+    log_verbose ("Invalid action request document\n");
+    return NULL;
+  }
+
+  node = ixmlNode_getFirstChild (node);
+  for (; node; node = ixmlNode_getNextSibling (node))
+    if (!strcmp (ixmlNode_getNodeName (node), key))
+    {
+      node = ixmlNode_getFirstChild (node);
+      if (!node)
+        return strdup ("");
+      return strdup (ixmlNode_getNodeValue (node));
+    }
+
+  log_verbose ("Missing action request argument (%s)\n", key);
+
+  return NULL;
+}
+
+int
+upnp_get_ui4 (struct Upnp_Action_Request *request, const char *key)
+{
+  char *value;
+  int val;
+
+  if (!request || !key)
+    return 0;
+
+  value = upnp_get_string (request, key);
+  if (!value && !strcmp (key, ARG_OBJECT_ID))
+    value = upnp_get_string (request, ARG_CONTAINER_ID);
+
+  if (!value)
+    return 0;
+
+  val = atoi (value);
+  free (value);
+
+  return val;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/services.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,53 @@
+/*
+ * services.h : GeeXboX uShare UPnP services handler header.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _SERVICES_H_
+#define _SERVICES_H_
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+#include "ushare.h"
+
+struct service_action_t {
+  char *name;
+  bool (*function) (struct action_event_t *);
+};
+
+struct service_t {
+  char *id;
+  char *type;
+  struct service_action_t *actions;
+};
+
+#define SERVICE_CONTENT_TYPE "text/xml"
+
+bool find_service_action (struct Upnp_Action_Request *request,
+                          struct service_t **service,
+                          struct service_action_t **action);
+
+bool upnp_add_response (struct action_event_t *event,
+                        char *key, const char *value);
+
+char * upnp_get_string (struct Upnp_Action_Request *request, const char *key);
+
+int upnp_get_ui4 (struct Upnp_Action_Request *request, const char *key);
+
+#endif /* _SERVICES_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/trace.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,64 @@
+/*
+ * trace.c : GeeXboX uShare log facility.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdbool.h>
+
+#include "config.h"
+#include "trace.h"
+#include "ushare.h"
+
+extern struct ushare_t *ut;
+
+void
+print_log (log_level level, const char *format, ...)
+{
+  va_list va;
+  bool is_daemon = ut ? ut->daemon : false;
+  bool is_verbose = ut ? ut->verbose : false;
+
+  if (!format)
+    return;
+
+  if (!is_verbose && level >= ULOG_VERBOSE)
+    return;
+
+  va_start (va, format);
+  if (is_daemon)
+  {
+    int flags = LOG_DAEMON;
+    flags |= level == ULOG_ERROR ? LOG_ERR : LOG_NOTICE;
+    vsyslog (flags, format, va);
+  }
+  else
+  {
+    FILE *output = level == ULOG_ERROR ? stderr : stdout;
+    vfprintf (output, format, va);
+  }
+  va_end (va);
+}
+
+inline void
+start_log (void)
+{
+  openlog (PACKAGE_NAME, LOG_PID, LOG_DAEMON);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/trace.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,55 @@
+/*
+ * trace.h : GeeXboX uShare log facility headers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+typedef enum {
+  ULOG_NORMAL = 1,
+  ULOG_ERROR = 2,
+  ULOG_VERBOSE = 3,
+} log_level;
+
+void print_log (log_level level, const char *format, ...)
+  __attribute__ ((format (printf, 2, 3)));
+inline void start_log (void);
+
+/* log_info
+ * Normal print, to replace printf
+ */
+#define log_info(s, str...) {          \
+  print_log (ULOG_NORMAL, (s), ##str); \
+  }
+
+/* log_error
+ * Error messages, output to stderr
+ */
+#define log_error(s, str...) {        \
+  print_log (ULOG_ERROR, (s), ##str); \
+  }
+
+/* log_verbose
+ * Output only in verbose mode
+ */
+#define log_verbose(s, str...) {        \
+  print_log (ULOG_VERBOSE, (s), ##str); \
+  }
+
+#endif /* _TRACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ushare.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,911 @@
+/*
+ * ushare.c : GeeXboX uShare UPnP Media Server.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+
+#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <net/if_dl.h>
+#endif
+
+#if (defined(__APPLE__))
+#include <net/route.h>
+#endif
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <fcntl.h>
+
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+#if (defined(HAVE_SETLOCALE) && defined(CONFIG_NLS))
+# include <locale.h>
+#endif
+
+#include "config.h"
+#include "ushare.h"
+#include "services.h"
+#include "http.h"
+#include "metadata.h"
+#include "util_iconv.h"
+#include "content.h"
+#include "cfgparser.h"
+#include "gettext.h"
+#include "trace.h"
+#include "buffer.h"
+#include "ctrl_telnet.h"
+#include "recpt1.h"
+
+struct ushare_t *ut = NULL;
+
+static struct ushare_t * ushare_new (void)
+    __attribute__ ((malloc));
+
+static struct ushare_t *
+ushare_new (void)
+{
+  struct ushare_t *ut = (struct ushare_t *) malloc (sizeof (struct ushare_t));
+  if (!ut)
+    return NULL;
+
+  ut->name = strdup (DEFAULT_USHARE_NAME);
+  ut->interface = strdup (DEFAULT_USHARE_IFACE);
+  ut->model_name = strdup (DEFAULT_USHARE_NAME);
+  ut->contentlist = NULL;
+  ut->rb = rbinit (rb_compare, NULL);
+  ut->root_entry = NULL;
+  ut->nr_entries = 0;
+  ut->starting_id = STARTING_ENTRY_ID_DEFAULT;
+  ut->init = 0;
+  ut->dev = 0;
+  ut->udn = NULL;
+  ut->ip = NULL;
+  ut->port = 0; /* Randomly attributed by libupnp */
+  ut->telnet_port = CTRL_TELNET_PORT;
+  ut->presentation = NULL;
+  ut->use_presentation = true;
+  ut->use_telnet = true;
+#ifdef HAVE_DLNA
+  ut->dlna_enabled = false;
+  ut->dlna = NULL;
+  ut->dlna_flags = DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE |
+                   DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE |
+                   DLNA_ORG_FLAG_CONNECTION_STALL |
+                   DLNA_ORG_FLAG_DLNA_V15;
+#endif /* HAVE_DLNA */
+  ut->xbox360 = false;
+  ut->verbose = false;
+  ut->daemon = false;
+  ut->override_iconv_err = false;
+  ut->cfg_file = NULL;
+
+  pthread_mutex_init (&ut->termination_mutex, NULL);
+  pthread_cond_init (&ut->termination_cond, NULL);
+
+  return ut;
+}
+
+static void
+ushare_free (struct ushare_t *ut)
+{
+  if (!ut)
+    return;
+
+  if (ut->name)
+    free (ut->name);
+  if (ut->interface)
+    free (ut->interface);
+  if (ut->model_name)
+    free (ut->model_name);
+  if (ut->contentlist)
+    content_free (ut->contentlist);
+  if (ut->rb)
+    rbdestroy (ut->rb);
+  if (ut->root_entry)
+    upnp_entry_free (ut, ut->root_entry);
+  if (ut->udn)
+    free (ut->udn);
+  if (ut->ip)
+    free (ut->ip);
+  if (ut->presentation)
+    buffer_free (ut->presentation);
+#ifdef HAVE_DLNA
+  if (ut->dlna_enabled)
+  {
+    if (ut->dlna)
+      dlna_uninit (ut->dlna);
+    ut->dlna = NULL;
+  }
+#endif /* HAVE_DLNA */
+  if (ut->cfg_file)
+    free (ut->cfg_file);
+
+  pthread_cond_destroy (&ut->termination_cond);
+  pthread_mutex_destroy (&ut->termination_mutex);
+
+  free (ut);
+}
+
+static void
+ushare_signal_exit (void)
+{
+  pthread_mutex_lock (&ut->termination_mutex);
+  pthread_cond_signal (&ut->termination_cond);
+  pthread_mutex_unlock (&ut->termination_mutex);
+}
+
+static void
+handle_action_request (struct Upnp_Action_Request *request)
+{
+  struct service_t *service;
+  struct service_action_t *action;
+  char val[256];
+  uint32_t ip;
+
+  if (!request || !ut)
+    return;
+
+  if (request->ErrCode != UPNP_E_SUCCESS)
+    return;
+
+  if (strcmp (request->DevUDN + 5, ut->udn))
+    return;
+
+  ip = request->CtrlPtIPAddr.s_addr;
+  ip = ntohl (ip);
+  sprintf (val, "%d.%d.%d.%d",
+           (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
+
+  if (ut->verbose)
+  {
+    DOMString str = ixmlPrintDocument (request->ActionRequest);
+    log_verbose ("***************************************************\n");
+    log_verbose ("**             New Action Request                **\n");
+    log_verbose ("***************************************************\n");
+    log_verbose ("ServiceID: %s\n", request->ServiceID);
+    log_verbose ("ActionName: %s\n", request->ActionName);
+    log_verbose ("CtrlPtIP: %s\n", val);
+    log_verbose ("Action Request:\n%s\n", str);
+    ixmlFreeDOMString (str);
+  }
+  
+  if (find_service_action (request, &service, &action))
+    {
+      struct action_event_t event;
+
+      event.request = request;
+      event.status = true;
+      event.service = service;
+
+      if (action->function (&event) && event.status)
+        request->ErrCode = UPNP_E_SUCCESS;
+
+      if (ut->verbose)
+      {
+        DOMString str = ixmlPrintDocument (request->ActionResult);
+        log_verbose ("Action Result:\n%s", str);
+        log_verbose ("***************************************************\n");
+        log_verbose ("\n");
+        ixmlFreeDOMString (str);
+      }
+      
+      return;
+    }
+
+  if (service) /* Invalid Action name */
+    strcpy (request->ErrStr, "Unknown Service Action");
+  else /* Invalid Service name */
+    strcpy (request->ErrStr, "Unknown Service ID");
+
+  request->ActionResult = NULL;
+  request->ErrCode = UPNP_SOAP_E_INVALID_ACTION;
+}
+
+static int
+device_callback_event_handler (Upnp_EventType type, void *event,
+                               void *cookie __attribute__((unused)))
+{
+  switch (type)
+    {
+    case UPNP_CONTROL_ACTION_REQUEST:
+      handle_action_request ((struct Upnp_Action_Request *) event);
+      break;
+    case UPNP_CONTROL_ACTION_COMPLETE:
+    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+    case UPNP_CONTROL_GET_VAR_REQUEST:
+      break;
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+static int
+finish_upnp (struct ushare_t *ut)
+{
+  if (!ut)
+    return -1;
+
+  log_info (_("Stopping UPnP Service ...\n"));
+  UpnpUnRegisterRootDevice (ut->dev);
+  UpnpFinish ();
+
+  return UPNP_E_SUCCESS;
+}
+
+static int
+init_upnp (struct ushare_t *ut)
+{
+  char *description = NULL;
+  int res;
+  size_t len;
+
+  if (!ut || !ut->name || !ut->udn || !ut->ip)
+    return -1;
+
+#ifdef HAVE_DLNA
+  if (ut->dlna_enabled)
+  {
+    len = 0;
+    description =
+      dlna_dms_description_get (ut->name,
+                                "GeeXboX Team",
+                                "http://ushare.geexbox.org/",
+                                "uShare : DLNA Media Server",
+                                ut->model_name,
+                                "001",
+                                "http://ushare.geexbox.org/",
+                                "USHARE-01",
+                                ut->udn,
+                                "/web/ushare.html",
+                                "/web/cms.xml",
+                                "/web/cms_control",
+                                "/web/cms_event",
+                                "/web/cds.xml",
+                                "/web/cds_control",
+                                "/web/cds_event");
+    if (!description)
+      return -1;
+  }
+  else
+  {
+#endif /* HAVE_DLNA */ 
+  len = strlen (UPNP_DESCRIPTION) + strlen (ut->name)
+    + strlen (ut->model_name) + strlen (ut->udn) + 1;
+  description = (char *) malloc (len * sizeof (char));
+  memset (description, 0, len);
+  sprintf (description, UPNP_DESCRIPTION, ut->name, ut->model_name, ut->udn);
+#ifdef HAVE_DLNA
+  }
+#endif /* HAVE_DLNA */
+
+  log_info (_("Initializing UPnP subsystem ...\n"));
+  res = UpnpInit (ut->ip, ut->port);
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot initialize UPnP subsystem\n"));
+    return -1;
+  }
+
+  if (UpnpSetMaxContentLength (UPNP_MAX_CONTENT_LENGTH) != UPNP_E_SUCCESS)
+    log_info (_("Could not set Max content UPnP\n"));
+
+  if (ut->xbox360)
+    log_info (_("Starting in XboX 360 compliant profile ...\n"));
+
+#ifdef HAVE_DLNA
+  if (ut->dlna_enabled)
+  {
+    log_info (_("Starting in DLNA compliant profile ...\n"));
+    ut->dlna = dlna_init ();
+    dlna_set_verbosity (ut->dlna, ut->verbose ? 1 : 0);
+    dlna_set_extension_check (ut->dlna, 1);
+    dlna_register_all_media_profiles (ut->dlna);
+  }
+#endif /* HAVE_DLNA */
+  
+  ut->port = UpnpGetServerPort();
+  log_info (_("UPnP MediaServer listening on %s:%d\n"),
+            UpnpGetServerIpAddress (), ut->port);
+
+  UpnpEnableWebserver (TRUE);
+
+  res = UpnpSetVirtualDirCallbacks (&virtual_dir_callbacks);
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot set virtual directory callbacks\n"));
+    free (description);
+    return -1;
+  }
+
+  res = UpnpAddVirtualDir (VIRTUAL_DIR);
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot add virtual directory for web server\n"));
+    free (description);
+    return -1;
+  }
+
+  res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1,
+                                 device_callback_event_handler,
+                                 NULL, &(ut->dev));
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot register UPnP device\n"));
+    free (description);
+    return -1;
+  }
+
+  res = UpnpUnRegisterRootDevice (ut->dev);
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot unregister UPnP device\n"));
+    free (description);
+    return -1;
+  }
+
+  res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1,
+                                 device_callback_event_handler,
+                                 NULL, &(ut->dev));
+  if (res != UPNP_E_SUCCESS)
+  {
+    log_error (_("Cannot register UPnP device\n"));
+    free (description);
+    return -1;
+  }
+
+  log_info (_("Sending UPnP advertisement for device ...\n"));
+  UpnpSendAdvertisement (ut->dev, 1800);
+
+  log_info (_("Listening for control point connections ...\n"));
+
+  if (description)
+    free (description);
+
+  return 0;
+}
+
+static bool
+has_iface (char *interface)
+{
+#ifdef HAVE_IFADDRS_H
+  struct ifaddrs *itflist, *itf;
+
+  if (!interface)
+    return false;
+
+  if (getifaddrs (&itflist) < 0)
+  {
+    perror ("getifaddrs");
+    return false;
+  }
+
+  itf = itflist;
+  while (itf)
+  {
+    if ((itf->ifa_flags & IFF_UP)
+        && !strncmp (itf->ifa_name, interface, IFNAMSIZ))
+    {
+      log_error (_("Interface %s is down.\n"), interface);
+      log_error (_("Recheck uShare's configuration and try again !\n"));
+      freeifaddrs (itflist);
+      return true;
+    }
+    itf = itf->ifa_next;
+  }
+
+  freeifaddrs (itf);
+#else  
+  int sock, i, n;
+  struct ifconf ifc;
+  struct ifreq ifr;
+  char buff[8192];
+
+  if (!interface)
+    return false;
+
+  /* determine UDN according to MAC address */
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+  {
+    perror ("socket");
+    return false;
+  }
+
+  /* get list of available interfaces */
+  ifc.ifc_len = sizeof (buff);
+  ifc.ifc_buf = buff;
+
+  if (ioctl (sock, SIOCGIFCONF, &ifc) < 0)
+  {
+    perror ("ioctl");
+    close (sock);
+    return false;
+  }
+
+  n = ifc.ifc_len / sizeof (struct ifreq);
+  for (i = n - 1 ; i >= 0 ; i--)
+  {
+    ifr = ifc.ifc_req[i];
+
+    if (strncmp (ifr.ifr_name, interface, IFNAMSIZ))
+      continue;
+
+    if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
+    {
+      perror ("ioctl");
+      close (sock);
+      return false;
+    }
+
+    if (!(ifr.ifr_flags & IFF_UP))
+    {
+      /* interface is down */
+      log_error (_("Interface %s is down.\n"), interface);
+      log_error (_("Recheck uShare's configuration and try again !\n"));
+      close (sock);
+      return false;
+    }
+
+    /* found right interface */
+    close (sock);
+    return true;
+  }
+  close (sock);
+#endif
+  
+  log_error (_("Can't find interface %s.\n"),interface);
+  log_error (_("Recheck uShare's configuration and try again !\n"));
+
+  return false;
+}
+
+static char *
+create_udn (char *interface)
+{
+  int sock = -1;
+  char *buf;
+  unsigned char *ptr;
+
+#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
+  int mib[6];
+  size_t len;
+  struct if_msghdr *ifm;
+  struct sockaddr_dl *sdl;
+#else /* Linux */
+  struct ifreq ifr;
+#endif
+
+  if (!interface)
+    return NULL;
+
+#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
+  mib[0] = CTL_NET;
+  mib[1] = AF_ROUTE;
+  mib[2] = 0;
+  mib[3] = AF_LINK;
+  mib[4] = NET_RT_IFLIST;
+
+  mib[5] = if_nametoindex (interface);
+  if (mib[5] == 0)
+  {
+    perror ("if_nametoindex");
+    return NULL;
+  }
+
+  if (sysctl (mib, 6, NULL, &len, NULL, 0) < 0)
+  {
+    perror ("sysctl");
+    return NULL;
+  }
+
+  buf = malloc (len);
+  if (sysctl (mib, 6, buf, &len, NULL, 0) < 0)
+  {
+    perror ("sysctl");
+    return NULL;
+  }
+
+  ifm = (struct if_msghdr *) buf;
+  sdl = (struct sockaddr_dl*) (ifm + 1);
+  ptr = (unsigned char *) LLADDR (sdl);
+#else /* Linux */
+  /* determine UDN according to MAC address */
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+  {
+    perror ("socket");
+    return NULL;
+  }
+
+  strcpy (ifr.ifr_name, interface);
+  strcpy (ifr.ifr_hwaddr.sa_data, "");
+
+  if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
+  {
+    perror ("ioctl");
+    return NULL;
+  }
+
+  buf = (char *) malloc (64 * sizeof (char));
+  memset (buf, 0, 64);
+  ptr = (unsigned char *) ifr.ifr_hwaddr.sa_data;
+#endif /* (defined(BSD) || defined(__FreeBSD__)) */
+
+  snprintf (buf, 64, "%s-%02x%02x%02x%02x%02x%02x", DEFAULT_UUID,
+            (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+            (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377));
+
+  if (sock)
+    close (sock);
+
+  return buf;
+}
+
+static char *
+get_iface_address (char *interface)
+{
+  int sock;
+  uint32_t ip;
+  struct ifreq ifr;
+  char *val;
+
+  if (!interface)
+    return NULL;
+
+  /* determine UDN according to MAC address */
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+  {
+    perror ("socket");
+    return NULL;
+  }
+
+  strcpy (ifr.ifr_name, interface);
+  ifr.ifr_addr.sa_family = AF_INET;
+
+  if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)
+  {
+    perror ("ioctl");
+    close (sock);
+    return NULL;
+  }
+
+  val = (char *) malloc (16 * sizeof (char));
+  ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
+  ip = ntohl (ip);
+  sprintf (val, "%d.%d.%d.%d",
+           (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
+
+  close (sock);
+
+  return val;
+}
+
+static int
+restart_upnp (struct ushare_t *ut)
+{
+  finish_upnp (ut);
+
+  if (ut->udn)
+    free (ut->udn);
+  ut->udn = create_udn (ut->interface);
+  if (!ut->udn)
+    return -1;
+
+  if (ut->ip)
+    free (ut->ip);
+  ut->ip = get_iface_address (ut->interface);
+  if (!ut->ip)
+    return -1;
+
+  return (init_upnp (ut));
+}
+
+static void
+UPnPBreak (int s __attribute__ ((unused)))
+{
+  ushare_signal_exit ();
+}
+
+static void
+reload_config (int s __attribute__ ((unused)))
+{
+  struct ushare_t *ut2;
+  bool reload = false;
+
+  log_info (_("Reloading configuration...\n"));
+
+  ut2 = ushare_new ();
+  if (!ut || !ut2)
+    return;
+
+  if (parse_config_file (ut2) < 0)
+    return;
+
+  if (ut->name && strcmp (ut->name, ut2->name))
+  {
+    free (ut->name);
+    ut->name = ut2->name;
+    ut2->name = NULL;
+    reload = true;
+  }
+
+  if (ut->interface && strcmp (ut->interface, ut2->interface))
+  {
+    if (!has_iface (ut2->interface))
+    {
+      ushare_free (ut2);
+      raise (SIGINT);
+    }
+    else
+    {
+      free (ut->interface);
+      ut->interface = ut2->interface;
+      ut2->interface = NULL;
+      reload = true;
+    }
+  }
+
+  if (ut->port != ut2->port)
+  {
+    ut->port = ut2->port;
+    reload = true;
+  }
+
+  if (reload)
+  {
+    if (restart_upnp (ut) < 0)
+    {
+      ushare_free (ut2);
+      raise (SIGINT);
+    }
+  }
+
+  if (ut->contentlist)
+    content_free (ut->contentlist);
+  ut->contentlist = ut2->contentlist;
+  ut2->contentlist = NULL;
+  ushare_free (ut2);
+
+  if (ut->contentlist)
+  {
+    free_metadata_list (ut);
+    build_metadata_list (ut);
+  }
+  else
+  {
+    log_error (_("Error: no content directory to be shared.\n"));
+    raise (SIGINT);
+  }
+}
+
+inline void
+display_headers (void)
+{
+  printf (_("%s (version %s), a lightweight UPnP A/V and DLNA Media Server.\n"),
+          PACKAGE_NAME, VERSION);
+  printf (_("Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n"));
+  printf (_("See http://ushare.geexbox.org/ for updates.\n"));
+}
+
+inline static void
+setup_i18n(void)
+{
+#ifdef CONFIG_NLS
+#ifdef HAVE_SETLOCALE
+  setlocale (LC_ALL, "");
+#endif
+#if (!defined(BSD) && !defined(__FreeBSD__))
+  bindtextdomain (PACKAGE, LOCALEDIR);
+#endif
+  textdomain (PACKAGE);
+#endif
+}
+
+#define SHUTDOWN_MSG _("Server is shutting down: other clients will be notified soon, Bye bye ...\n")
+
+static void
+ushare_kill (ctrl_telnet_client *client,
+             int argc __attribute__((unused)),
+             char **argv __attribute__((unused)))
+{
+  if (ut->use_telnet)
+  {
+    ctrl_telnet_client_send (client, SHUTDOWN_MSG);
+    client->exiting = true;
+  }
+  ushare_signal_exit ();
+}
+
+//main (int argc, char **argv)
+void *
+dlna_startup (void *p)
+{
+  ut = ushare_new ();
+  log_verbose ("dlna_startup() start\n");
+
+  if (!ut)
+    return NULL;
+
+  setup_i18n ();
+  setup_iconv ();
+
+#if 0
+  /* Parse args before cfg file, as we may override the default file */
+  if (parse_command_line (ut, argc, argv) < 0)
+  {
+    ushare_free (ut);
+    return NULL;
+  }
+#endif
+
+  if (parse_config_file (ut) < 0)
+  {
+    /* fprintf here, because syslog not yet ready */
+    fprintf (stderr, _("Warning: can't parse file \"%s\".\n"),
+             ut->cfg_file ? ut->cfg_file : SYSCONFDIR "/" USHARE_CONFIG_FILE);
+  }
+  ut->verbose = true;
+  ut->port = 0;
+  ut->use_presentation = false;
+  ut->use_telnet = false;
+  ut->dlna_enabled = true;
+  ut->override_iconv_err = false;
+  ut->xbox360 = true;
+  ut->daemon = false;
+  //ut->interface = "192.168.1.34";
+  ut->contentlist = "/tmp";
+
+  if (ut->xbox360)
+  {
+    char *name;
+
+    name = malloc (strlen (XBOX_MODEL_NAME) + strlen (ut->model_name) + 4);
+    sprintf (name, "%s (%s)", XBOX_MODEL_NAME, ut->model_name);
+    free (ut->model_name);
+    ut->model_name = strdup (name);
+    free (name);
+
+    ut->starting_id = STARTING_ENTRY_ID_XBOX360;
+  }
+
+  if (ut->daemon)
+  {
+    /* starting syslog feature as soon as possible */
+    start_log ();
+  }
+
+  if (!ut->contentlist)
+  {
+    log_error (_("Error: no content directory to be shared.\n"));
+    ushare_free (ut);
+    return NULL;
+  }
+
+  if (!has_iface (ut->interface))
+  {
+    ushare_free (ut);
+    return NULL;
+  }
+
+  ut->udn = create_udn (ut->interface);
+  if (!ut->udn)
+  {
+    ushare_free (ut);
+    return NULL;
+  }
+
+  ut->ip = get_iface_address (ut->interface);
+  if (!ut->ip)
+  {
+    ushare_free (ut);
+    return NULL;
+  }
+
+  if (ut->daemon)
+  {
+    int err;
+    err = daemon (0, 0);
+    if (err == -1)
+    {
+      log_error (_("Error: failed to daemonize program : %s\n"),
+                 strerror (err));
+      ushare_free (ut);
+      return NULL;
+    }
+  }
+  else
+  {
+    display_headers ();
+  }
+
+#if 0
+  signal (SIGINT, UPnPBreak);
+  signal (SIGTERM, UPnPBreak);
+  signal (SIGUSR1, UPnPBreak);
+  signal (SIGUSR2, UPnPBreak);
+  signal (SIGPIPE, UPnPBreak);
+  signal (SIGHUP, reload_config);
+#endif
+
+  if (ut->use_telnet)
+  {
+    if (ctrl_telnet_start (ut->telnet_port) < 0)
+    {
+      ushare_free (ut);
+      return NULL;
+    }
+    
+    ctrl_telnet_register ("kill", ushare_kill,
+                          _("Terminates the uShare server"));
+  }
+  log_verbose ("init_upnp() start\n");
+  
+  if (init_upnp (ut) < 0)
+  {
+    finish_upnp (ut);
+    ushare_free (ut);
+    return NULL;
+  }
+
+  build_metadata_list (ut);
+
+  log_verbose ("uShare mutex lock.\n");
+  /* Let main sleep until it's time to die... */
+  pthread_mutex_lock (&ut->termination_mutex);
+  log_verbose ("uShare cond wait.\n");
+  pthread_cond_wait (&ut->termination_cond, &ut->termination_mutex);
+  pthread_mutex_unlock (&ut->termination_mutex);
+  log_verbose ("uShare finish.\n");
+
+  if (ut->use_telnet)
+    ctrl_telnet_stop ();
+  finish_upnp (ut);
+  free_metadata_list (ut);
+  ushare_free (ut);
+  finish_iconv ();
+
+  log_verbose ("dlna_start() finish\n");
+  /* it should never be executed */
+  return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ushare.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,136 @@
+/*
+ * ushare.h : GeeXboX uShare UPnP Media Server header.
+ * Originally developped for the GeeXboX project.
+ * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
+ * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _USHARE_H_
+#define _USHARE_H_
+
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#ifdef HAVE_DLNA
+#include <dlna.h>
+#endif /* HAVE_DLNA */
+
+#include "content.h"
+#include "buffer.h"
+#include "redblack.h"
+
+#define VIRTUAL_DIR "/web"
+#define XBOX_MODEL_NAME "Windows Media Connect Compatible"
+#define DEFAULT_UUID "898f9738-d930-4db4-a3cf"
+
+#define STREAM_FILE_NAME "stream.ts"
+#define STREAM_LOCATION VIRTUAL_DIR "/" STREAM_FILE_NAME
+
+#define UPNP_MAX_CONTENT_LENGTH 4096
+
+#define STARTING_ENTRY_ID_DEFAULT 0
+#define STARTING_ENTRY_ID_XBOX360 100000
+
+#define UPNP_DESCRIPTION \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" \
+"  <specVersion>" \
+"    <major>1</major>" \
+"    <minor>0</minor>" \
+"  </specVersion>" \
+"  <device>" \
+"    <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>" \
+"    <friendlyName>%s: 1</friendlyName>" \
+"    <manufacturer>GeeXboX Team</manufacturer>" \
+"    <manufacturerURL>http://ushare.geexbox.org/</manufacturerURL>" \
+"    <modelDescription>GeeXboX uShare : UPnP Media Server</modelDescription>" \
+"    <modelName>%s</modelName>" \
+"    <modelNumber>001</modelNumber>" \
+"    <modelURL>http://ushare.geexbox.org/</modelURL>" \
+"    <serialNumber>GEEXBOX-USHARE-01</serialNumber>" \
+"    <UDN>uuid:%s</UDN>" \
+"    <serviceList>" \
+"      <service>" \
+"        <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>" \
+"        <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>" \
+"        <SCPDURL>/web/cms.xml</SCPDURL>" \
+"        <controlURL>/web/cms_control</controlURL>" \
+"        <eventSubURL>/web/cms_event</eventSubURL>" \
+"      </service>" \
+"      <service>" \
+"        <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>" \
+"        <serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>" \
+"        <SCPDURL>/web/cds.xml</SCPDURL>" \
+"        <controlURL>/web/cds_control</controlURL>" \
+"        <eventSubURL>/web/cds_event</eventSubURL>" \
+"      </service>" \
+"      <service>" \
+"        <serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType>\n" \
+"        <serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId>\n" \
+"        <SCPDURL>/web/msr.xml</SCPDURL>" \
+"        <controlURL>/web/msr_control</controlURL>" \
+"        <eventSubURL>/web/msr_event</eventSubURL>" \
+"      </service>\n" \
+"    </serviceList>" \
+"    <presentationURL>/web/ushare.html</presentationURL>" \
+"  </device>" \
+"</root>"
+
+struct ushare_t {
+  char *name;
+  char *interface;
+  char *model_name;
+  content_list *contentlist;
+  struct rbtree *rb;
+  struct upnp_entry_t *root_entry;
+  int nr_entries;
+  int starting_id;
+  int init;
+  UpnpDevice_Handle dev;
+  char *udn;
+  char *ip;
+  unsigned short port;
+  unsigned short telnet_port;
+  struct buffer_t *presentation;
+  bool use_presentation;
+  bool use_telnet;
+#ifdef HAVE_DLNA
+  bool dlna_enabled;
+  dlna_t *dlna;
+  dlna_org_flags_t dlna_flags;
+#endif /* HAVE_DLNA */
+  bool xbox360;
+  bool verbose;
+  bool daemon;
+  bool override_iconv_err;
+  char *cfg_file;
+  pthread_mutex_t termination_mutex;
+  pthread_cond_t termination_cond;
+};
+
+struct action_event_t {
+  struct Upnp_Action_Request *request;
+  bool status;
+  struct service_t *service;
+};
+
+inline void display_headers (void);
+void * dlna_startup(void *p);
+
+#endif /* _USHARE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util_iconv.c	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,170 @@
+/*
+ * util_iconv.c : GeeXboX uShare iconv string encoding utlities.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util_iconv.h"
+
+#if HAVE_ICONV
+#include <iconv.h>
+static iconv_t cd = 0;
+#endif
+
+#if HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+void
+setup_iconv (void)
+{
+#if HAVE_ICONV && HAVE_LANGINFO_CODESET
+  char *mycodeset = NULL;
+
+  mycodeset = nl_langinfo (CODESET);
+  if (!mycodeset)
+    return;
+
+  /**
+   * Setup conversion descriptor if user's console is non-UTF-8. Otherwise
+   * we can just leave cd as NULL
+   */
+  if (strcmp (mycodeset, UTF8))
+  {
+    cd = iconv_open (UTF8, mycodeset);
+    if (cd == (iconv_t) (-1))
+    {
+      perror ("iconv_open");
+      cd = 0;
+    }
+  }
+#endif
+}
+
+void
+finish_iconv (void)
+{
+#if HAVE_ICONV
+  if (!cd)
+    return;
+  if (iconv_close (cd) < 0)
+    perror ("iconv_close");
+  cd = 0;
+#endif
+}
+
+/**
+ * iconv_convert : convert a string, using the current codeset
+ * return: a malloc'd string with the converted result
+ */
+char *
+iconv_convert (const char *input)
+{
+#if HAVE_ICONV
+  size_t inputsize = strlen (input) + 1;
+  size_t dummy = 0;
+  size_t length = 0;
+  char *result;
+  char *inptr, *outptr;
+  size_t insize, outsize;
+
+  /* conversion not necessary. save our time. */
+  if (!cd)
+    return strdup (input);
+
+  /* Determine the length we need. */
+  iconv (cd, NULL, NULL, NULL, &dummy);
+  {
+    static char tmpbuf[BUFSIZ];
+    inptr = (char*) input;
+    insize = inputsize;
+    while (insize > 0)
+    {
+      outptr = tmpbuf;
+      outsize = BUFSIZ;
+      if (iconv (cd, &inptr, &insize, &outptr, &outsize) == (size_t) (-1))
+      {
+        /**
+         * if error is EINVAL or EILSEQ, conversion must be stoped,
+         * but if it is E2BIG (not enough space in buffer), we just loop again
+         */
+        if( errno != E2BIG)
+        {
+          perror ("error iconv");
+          return NULL;
+        }
+      }
+      length += outptr - tmpbuf;
+    }
+
+    outptr = tmpbuf;
+    outsize = BUFSIZ;
+    if (iconv (cd, NULL, NULL, &outptr, &outsize) == (size_t) (-1))
+    {
+      perror ("error iconv");
+      return NULL;
+    }
+    length += outptr - tmpbuf;
+  }
+
+  /* length determined, allocate result space */
+  if ((result = (char*) malloc (length * sizeof (char))) == NULL)
+  {
+    perror ("error malloc");
+    return NULL;
+  }
+
+  /* Do the conversion for real. */
+  iconv (cd, NULL, NULL, NULL, &dummy);
+  {
+    inptr = (char*) input;
+    insize = inputsize;
+    outptr = result;
+    outsize = length;
+    while (insize > 0)
+    {
+      if (iconv (cd, &inptr, &insize, &outptr, &outsize) == (size_t) (-1))
+      {
+        if (errno != E2BIG)
+        {
+          perror ("error iconv");
+          free (result);
+          return NULL;
+        }
+      }
+    }
+    if (iconv (cd, NULL, NULL, &outptr, &outsize) == (size_t) (-1))
+    {
+      perror ("error iconv");
+      free (result);
+      return NULL;
+    }
+
+    if (outsize != 0)
+      abort ();
+  }
+
+  return result;
+#else
+  return strdup (input);
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util_iconv.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,31 @@
+/*
+ * util_iconv.h : GeeXboX uShare iconv string encoding utilities headers.
+ * Originally developped for the GeeXboX project.
+ * Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _UTIL_ICONV_H_
+#define _UTIL_ICONV_H_
+
+void setup_iconv (void);
+void finish_iconv (void);
+char *iconv_convert (const char *inbuf)
+    __attribute__ ((malloc, nonnull, format_arg (1)));
+
+#define UTF8 "UTF-8"
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/version.h	Sun Oct 03 11:35:19 2010 +0900
@@ -0,0 +1,1 @@
+const char *version = "r100:aeba1988234f (2010-02-13)";