Mercurial > pt1.oyama
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[] = { + {'"' , """}, + {'&' , "&"}, + {'\'', "'"}, + {'<' , "<"}, + {'>' , ">"}, + {'\n', "
"}, + {'\r', "
"}, + {'\t', "	"}, + {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